20
vendor/github.com/golang/freetype/AUTHORS
generated
vendored
Normal file
20
vendor/github.com/golang/freetype/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# This is the official list of Freetype-Go authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
#
|
||||
# Freetype-Go is derived from Freetype, which is written in C. The latter
|
||||
# is copyright 1996-2010 David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Google Inc.
|
||||
Jeff R. Allen <jra@nella.org>
|
||||
Maksim Kochkin <maxxarts@gmail.com>
|
||||
Michael Fogleman <fogleman@gmail.com>
|
||||
Rémy Oudompheng <oudomphe@phare.normalesup.org>
|
||||
Roger Peppe <rogpeppe@gmail.com>
|
||||
Steven Edwards <steven@stephenwithav.com>
|
||||
38
vendor/github.com/golang/freetype/CONTRIBUTORS
generated
vendored
Normal file
38
vendor/github.com/golang/freetype/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# This is the official list of people who can contribute
|
||||
# (and typically have contributed) code to the Freetype-Go repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# The submission process automatically checks to make sure
|
||||
# that people submitting code are listed in this file (by email address).
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# http://code.google.com/legal/individual-cla-v1.0.html
|
||||
# http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
#
|
||||
# The agreement for individuals can be filled out on the web.
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Andrew Gerrand <adg@golang.org>
|
||||
Jeff R. Allen <jra@nella.org> <jeff.allen@gmail.com>
|
||||
Maksim Kochkin <maxxarts@gmail.com>
|
||||
Michael Fogleman <fogleman@gmail.com>
|
||||
Nigel Tao <nigeltao@golang.org>
|
||||
Rémy Oudompheng <oudomphe@phare.normalesup.org> <remyoudompheng@gmail.com>
|
||||
Rob Pike <r@golang.org>
|
||||
Roger Peppe <rogpeppe@gmail.com>
|
||||
Russ Cox <rsc@golang.org>
|
||||
Steven Edwards <steven@stephenwithav.com>
|
||||
12
vendor/github.com/golang/freetype/LICENSE
generated
vendored
Normal file
12
vendor/github.com/golang/freetype/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
Use of the Freetype-Go software is subject to your choice of exactly one of
|
||||
the following two licenses:
|
||||
* The FreeType License, which is similar to the original BSD license with
|
||||
an advertising clause, or
|
||||
* The GNU General Public License (GPL), version 2 or later.
|
||||
|
||||
The text of these licenses are available in the licenses/ftl.txt and the
|
||||
licenses/gpl.txt files respectively. They are also available at
|
||||
http://freetype.sourceforge.net/license.html
|
||||
|
||||
The Luxi fonts in the testdata directory are licensed separately. See the
|
||||
testdata/COPYING file for details.
|
||||
245
vendor/github.com/golang/freetype/raster/geom.go
generated
vendored
Normal file
245
vendor/github.com/golang/freetype/raster/geom.go
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by your choice of either the
|
||||
// FreeType License or the GNU General Public License version 2 (or
|
||||
// any later version), both of which can be found in the LICENSE file.
|
||||
|
||||
package raster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// maxAbs returns the maximum of abs(a) and abs(b).
|
||||
func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 {
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
if b < 0 {
|
||||
b = -b
|
||||
}
|
||||
if a < b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
|
||||
func pNeg(p fixed.Point26_6) fixed.Point26_6 {
|
||||
return fixed.Point26_6{-p.X, -p.Y}
|
||||
}
|
||||
|
||||
// pDot returns the dot product p·q.
|
||||
func pDot(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 {
|
||||
px, py := int64(p.X), int64(p.Y)
|
||||
qx, qy := int64(q.X), int64(q.Y)
|
||||
return fixed.Int52_12(px*qx + py*qy)
|
||||
}
|
||||
|
||||
// pLen returns the length of the vector p.
|
||||
func pLen(p fixed.Point26_6) fixed.Int26_6 {
|
||||
// TODO(nigeltao): use fixed point math.
|
||||
x := float64(p.X)
|
||||
y := float64(p.Y)
|
||||
return fixed.Int26_6(math.Sqrt(x*x + y*y))
|
||||
}
|
||||
|
||||
// pNorm returns the vector p normalized to the given length, or zero if p is
|
||||
// degenerate.
|
||||
func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 {
|
||||
d := pLen(p)
|
||||
if d == 0 {
|
||||
return fixed.Point26_6{}
|
||||
}
|
||||
s, t := int64(length), int64(d)
|
||||
x := int64(p.X) * s / t
|
||||
y := int64(p.Y) * s / t
|
||||
return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)}
|
||||
}
|
||||
|
||||
// pRot45CW returns the vector p rotated clockwise by 45 degrees.
|
||||
//
|
||||
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
|
||||
func pRot45CW(p fixed.Point26_6) fixed.Point26_6 {
|
||||
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||
px, py := int64(p.X), int64(p.Y)
|
||||
qx := (+px - py) * 181 / 256
|
||||
qy := (+px + py) * 181 / 256
|
||||
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
|
||||
}
|
||||
|
||||
// pRot90CW returns the vector p rotated clockwise by 90 degrees.
|
||||
//
|
||||
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
|
||||
func pRot90CW(p fixed.Point26_6) fixed.Point26_6 {
|
||||
return fixed.Point26_6{-p.Y, p.X}
|
||||
}
|
||||
|
||||
// pRot135CW returns the vector p rotated clockwise by 135 degrees.
|
||||
//
|
||||
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
|
||||
func pRot135CW(p fixed.Point26_6) fixed.Point26_6 {
|
||||
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||
px, py := int64(p.X), int64(p.Y)
|
||||
qx := (-px - py) * 181 / 256
|
||||
qy := (+px - py) * 181 / 256
|
||||
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
|
||||
}
|
||||
|
||||
// pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
|
||||
//
|
||||
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
|
||||
func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 {
|
||||
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||
px, py := int64(p.X), int64(p.Y)
|
||||
qx := (+px + py) * 181 / 256
|
||||
qy := (-px + py) * 181 / 256
|
||||
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
|
||||
}
|
||||
|
||||
// pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
|
||||
//
|
||||
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
|
||||
func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 {
|
||||
return fixed.Point26_6{p.Y, -p.X}
|
||||
}
|
||||
|
||||
// pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
|
||||
//
|
||||
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
|
||||
func pRot135CCW(p fixed.Point26_6) fixed.Point26_6 {
|
||||
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||
px, py := int64(p.X), int64(p.Y)
|
||||
qx := (-px + py) * 181 / 256
|
||||
qy := (-px - py) * 181 / 256
|
||||
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
|
||||
}
|
||||
|
||||
// An Adder accumulates points on a curve.
|
||||
type Adder interface {
|
||||
// Start starts a new curve at the given point.
|
||||
Start(a fixed.Point26_6)
|
||||
// Add1 adds a linear segment to the current curve.
|
||||
Add1(b fixed.Point26_6)
|
||||
// Add2 adds a quadratic segment to the current curve.
|
||||
Add2(b, c fixed.Point26_6)
|
||||
// Add3 adds a cubic segment to the current curve.
|
||||
Add3(b, c, d fixed.Point26_6)
|
||||
}
|
||||
|
||||
// A Path is a sequence of curves, and a curve is a start point followed by a
|
||||
// sequence of linear, quadratic or cubic segments.
|
||||
type Path []fixed.Int26_6
|
||||
|
||||
// String returns a human-readable representation of a Path.
|
||||
func (p Path) String() string {
|
||||
s := ""
|
||||
for i := 0; i < len(p); {
|
||||
if i != 0 {
|
||||
s += " "
|
||||
}
|
||||
switch p[i] {
|
||||
case 0:
|
||||
s += "S0" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
|
||||
i += 4
|
||||
case 1:
|
||||
s += "A1" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
|
||||
i += 4
|
||||
case 2:
|
||||
s += "A2" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+5]))
|
||||
i += 6
|
||||
case 3:
|
||||
s += "A3" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+7]))
|
||||
i += 8
|
||||
default:
|
||||
panic("freetype/raster: bad path")
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Clear cancels any previous calls to p.Start or p.AddXxx.
|
||||
func (p *Path) Clear() {
|
||||
*p = (*p)[:0]
|
||||
}
|
||||
|
||||
// Start starts a new curve at the given point.
|
||||
func (p *Path) Start(a fixed.Point26_6) {
|
||||
*p = append(*p, 0, a.X, a.Y, 0)
|
||||
}
|
||||
|
||||
// Add1 adds a linear segment to the current curve.
|
||||
func (p *Path) Add1(b fixed.Point26_6) {
|
||||
*p = append(*p, 1, b.X, b.Y, 1)
|
||||
}
|
||||
|
||||
// Add2 adds a quadratic segment to the current curve.
|
||||
func (p *Path) Add2(b, c fixed.Point26_6) {
|
||||
*p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
|
||||
}
|
||||
|
||||
// Add3 adds a cubic segment to the current curve.
|
||||
func (p *Path) Add3(b, c, d fixed.Point26_6) {
|
||||
*p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3)
|
||||
}
|
||||
|
||||
// AddPath adds the Path q to p.
|
||||
func (p *Path) AddPath(q Path) {
|
||||
*p = append(*p, q...)
|
||||
}
|
||||
|
||||
// AddStroke adds a stroked Path.
|
||||
func (p *Path) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
|
||||
Stroke(p, q, width, cr, jr)
|
||||
}
|
||||
|
||||
// firstPoint returns the first point in a non-empty Path.
|
||||
func (p Path) firstPoint() fixed.Point26_6 {
|
||||
return fixed.Point26_6{p[1], p[2]}
|
||||
}
|
||||
|
||||
// lastPoint returns the last point in a non-empty Path.
|
||||
func (p Path) lastPoint() fixed.Point26_6 {
|
||||
return fixed.Point26_6{p[len(p)-3], p[len(p)-2]}
|
||||
}
|
||||
|
||||
// addPathReversed adds q reversed to p.
|
||||
// For example, if q consists of a linear segment from A to B followed by a
|
||||
// quadratic segment from B to C to D, then the values of q looks like:
|
||||
// index: 01234567890123
|
||||
// value: 0AA01BB12CCDD2
|
||||
// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
|
||||
func addPathReversed(p Adder, q Path) {
|
||||
if len(q) == 0 {
|
||||
return
|
||||
}
|
||||
i := len(q) - 1
|
||||
for {
|
||||
switch q[i] {
|
||||
case 0:
|
||||
return
|
||||
case 1:
|
||||
i -= 4
|
||||
p.Add1(
|
||||
fixed.Point26_6{q[i-2], q[i-1]},
|
||||
)
|
||||
case 2:
|
||||
i -= 6
|
||||
p.Add2(
|
||||
fixed.Point26_6{q[i+2], q[i+3]},
|
||||
fixed.Point26_6{q[i-2], q[i-1]},
|
||||
)
|
||||
case 3:
|
||||
i -= 8
|
||||
p.Add3(
|
||||
fixed.Point26_6{q[i+4], q[i+5]},
|
||||
fixed.Point26_6{q[i+2], q[i+3]},
|
||||
fixed.Point26_6{q[i-2], q[i-1]},
|
||||
)
|
||||
default:
|
||||
panic("freetype/raster: bad path")
|
||||
}
|
||||
}
|
||||
}
|
||||
287
vendor/github.com/golang/freetype/raster/paint.go
generated
vendored
Normal file
287
vendor/github.com/golang/freetype/raster/paint.go
generated
vendored
Normal file
@@ -0,0 +1,287 @@
|
||||
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by your choice of either the
|
||||
// FreeType License or the GNU General Public License version 2 (or
|
||||
// any later version), both of which can be found in the LICENSE file.
|
||||
|
||||
package raster
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"math"
|
||||
)
|
||||
|
||||
// A Span is a horizontal segment of pixels with constant alpha. X0 is an
|
||||
// inclusive bound and X1 is exclusive, the same as for slices. A fully opaque
|
||||
// Span has Alpha == 0xffff.
|
||||
type Span struct {
|
||||
Y, X0, X1 int
|
||||
Alpha uint32
|
||||
}
|
||||
|
||||
// A Painter knows how to paint a batch of Spans. Rasterization may involve
|
||||
// Painting multiple batches, and done will be true for the final batch. The
|
||||
// Spans' Y values are monotonically increasing during a rasterization. Paint
|
||||
// may use all of ss as scratch space during the call.
|
||||
type Painter interface {
|
||||
Paint(ss []Span, done bool)
|
||||
}
|
||||
|
||||
// The PainterFunc type adapts an ordinary function to the Painter interface.
|
||||
type PainterFunc func(ss []Span, done bool)
|
||||
|
||||
// Paint just delegates the call to f.
|
||||
func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) }
|
||||
|
||||
// An AlphaOverPainter is a Painter that paints Spans onto a *image.Alpha using
|
||||
// the Over Porter-Duff composition operator.
|
||||
type AlphaOverPainter struct {
|
||||
Image *image.Alpha
|
||||
}
|
||||
|
||||
// Paint satisfies the Painter interface.
|
||||
func (r AlphaOverPainter) Paint(ss []Span, done bool) {
|
||||
b := r.Image.Bounds()
|
||||
for _, s := range ss {
|
||||
if s.Y < b.Min.Y {
|
||||
continue
|
||||
}
|
||||
if s.Y >= b.Max.Y {
|
||||
return
|
||||
}
|
||||
if s.X0 < b.Min.X {
|
||||
s.X0 = b.Min.X
|
||||
}
|
||||
if s.X1 > b.Max.X {
|
||||
s.X1 = b.Max.X
|
||||
}
|
||||
if s.X0 >= s.X1 {
|
||||
continue
|
||||
}
|
||||
base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
|
||||
p := r.Image.Pix[base+s.X0 : base+s.X1]
|
||||
a := int(s.Alpha >> 8)
|
||||
for i, c := range p {
|
||||
v := int(c)
|
||||
p[i] = uint8((v*255 + (255-v)*a) / 255)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewAlphaOverPainter creates a new AlphaOverPainter for the given image.
|
||||
func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter {
|
||||
return AlphaOverPainter{m}
|
||||
}
|
||||
|
||||
// An AlphaSrcPainter is a Painter that paints Spans onto a *image.Alpha using
|
||||
// the Src Porter-Duff composition operator.
|
||||
type AlphaSrcPainter struct {
|
||||
Image *image.Alpha
|
||||
}
|
||||
|
||||
// Paint satisfies the Painter interface.
|
||||
func (r AlphaSrcPainter) Paint(ss []Span, done bool) {
|
||||
b := r.Image.Bounds()
|
||||
for _, s := range ss {
|
||||
if s.Y < b.Min.Y {
|
||||
continue
|
||||
}
|
||||
if s.Y >= b.Max.Y {
|
||||
return
|
||||
}
|
||||
if s.X0 < b.Min.X {
|
||||
s.X0 = b.Min.X
|
||||
}
|
||||
if s.X1 > b.Max.X {
|
||||
s.X1 = b.Max.X
|
||||
}
|
||||
if s.X0 >= s.X1 {
|
||||
continue
|
||||
}
|
||||
base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
|
||||
p := r.Image.Pix[base+s.X0 : base+s.X1]
|
||||
color := uint8(s.Alpha >> 8)
|
||||
for i := range p {
|
||||
p[i] = color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image.
|
||||
func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter {
|
||||
return AlphaSrcPainter{m}
|
||||
}
|
||||
|
||||
// An RGBAPainter is a Painter that paints Spans onto a *image.RGBA.
|
||||
type RGBAPainter struct {
|
||||
// Image is the image to compose onto.
|
||||
Image *image.RGBA
|
||||
// Op is the Porter-Duff composition operator.
|
||||
Op draw.Op
|
||||
// cr, cg, cb and ca are the 16-bit color to paint the spans.
|
||||
cr, cg, cb, ca uint32
|
||||
}
|
||||
|
||||
// Paint satisfies the Painter interface.
|
||||
func (r *RGBAPainter) Paint(ss []Span, done bool) {
|
||||
b := r.Image.Bounds()
|
||||
for _, s := range ss {
|
||||
if s.Y < b.Min.Y {
|
||||
continue
|
||||
}
|
||||
if s.Y >= b.Max.Y {
|
||||
return
|
||||
}
|
||||
if s.X0 < b.Min.X {
|
||||
s.X0 = b.Min.X
|
||||
}
|
||||
if s.X1 > b.Max.X {
|
||||
s.X1 = b.Max.X
|
||||
}
|
||||
if s.X0 >= s.X1 {
|
||||
continue
|
||||
}
|
||||
// This code mimics drawGlyphOver in $GOROOT/src/image/draw/draw.go.
|
||||
ma := s.Alpha
|
||||
const m = 1<<16 - 1
|
||||
i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4
|
||||
i1 := i0 + (s.X1-s.X0)*4
|
||||
if r.Op == draw.Over {
|
||||
for i := i0; i < i1; i += 4 {
|
||||
dr := uint32(r.Image.Pix[i+0])
|
||||
dg := uint32(r.Image.Pix[i+1])
|
||||
db := uint32(r.Image.Pix[i+2])
|
||||
da := uint32(r.Image.Pix[i+3])
|
||||
a := (m - (r.ca * ma / m)) * 0x101
|
||||
r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8)
|
||||
r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8)
|
||||
r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8)
|
||||
r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8)
|
||||
}
|
||||
} else {
|
||||
for i := i0; i < i1; i += 4 {
|
||||
r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8)
|
||||
r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8)
|
||||
r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8)
|
||||
r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetColor sets the color to paint the spans.
|
||||
func (r *RGBAPainter) SetColor(c color.Color) {
|
||||
r.cr, r.cg, r.cb, r.ca = c.RGBA()
|
||||
}
|
||||
|
||||
// NewRGBAPainter creates a new RGBAPainter for the given image.
|
||||
func NewRGBAPainter(m *image.RGBA) *RGBAPainter {
|
||||
return &RGBAPainter{Image: m}
|
||||
}
|
||||
|
||||
// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
|
||||
// be either fully opaque or fully transparent.
|
||||
type MonochromePainter struct {
|
||||
Painter Painter
|
||||
y, x0, x1 int
|
||||
}
|
||||
|
||||
// Paint delegates to the wrapped Painter after quantizing each Span's alpha
|
||||
// value and merging adjacent fully opaque Spans.
|
||||
func (m *MonochromePainter) Paint(ss []Span, done bool) {
|
||||
// We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
|
||||
j := 0
|
||||
for _, s := range ss {
|
||||
if s.Alpha >= 0x8000 {
|
||||
if m.y == s.Y && m.x1 == s.X0 {
|
||||
m.x1 = s.X1
|
||||
} else {
|
||||
ss[j] = Span{m.y, m.x0, m.x1, 1<<16 - 1}
|
||||
j++
|
||||
m.y, m.x0, m.x1 = s.Y, s.X0, s.X1
|
||||
}
|
||||
}
|
||||
}
|
||||
if done {
|
||||
// Flush the accumulated Span.
|
||||
finalSpan := Span{m.y, m.x0, m.x1, 1<<16 - 1}
|
||||
if j < len(ss) {
|
||||
ss[j] = finalSpan
|
||||
j++
|
||||
m.Painter.Paint(ss[:j], true)
|
||||
} else if j == len(ss) {
|
||||
m.Painter.Paint(ss, false)
|
||||
if cap(ss) > 0 {
|
||||
ss = ss[:1]
|
||||
} else {
|
||||
ss = make([]Span, 1)
|
||||
}
|
||||
ss[0] = finalSpan
|
||||
m.Painter.Paint(ss, true)
|
||||
} else {
|
||||
panic("unreachable")
|
||||
}
|
||||
// Reset the accumulator, so that this Painter can be re-used.
|
||||
m.y, m.x0, m.x1 = 0, 0, 0
|
||||
} else {
|
||||
m.Painter.Paint(ss[:j], false)
|
||||
}
|
||||
}
|
||||
|
||||
// NewMonochromePainter creates a new MonochromePainter that wraps the given
|
||||
// Painter.
|
||||
func NewMonochromePainter(p Painter) *MonochromePainter {
|
||||
return &MonochromePainter{Painter: p}
|
||||
}
|
||||
|
||||
// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
|
||||
// on each Span's alpha value.
|
||||
type GammaCorrectionPainter struct {
|
||||
// Painter is the wrapped Painter.
|
||||
Painter Painter
|
||||
// a is the precomputed alpha values for linear interpolation, with fully
|
||||
// opaque == 0xffff.
|
||||
a [256]uint16
|
||||
// gammaIsOne is whether gamma correction is a no-op.
|
||||
gammaIsOne bool
|
||||
}
|
||||
|
||||
// Paint delegates to the wrapped Painter after performing gamma-correction on
|
||||
// each Span.
|
||||
func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) {
|
||||
if !g.gammaIsOne {
|
||||
const n = 0x101
|
||||
for i, s := range ss {
|
||||
if s.Alpha == 0 || s.Alpha == 0xffff {
|
||||
continue
|
||||
}
|
||||
p, q := s.Alpha/n, s.Alpha%n
|
||||
// The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1].
|
||||
a := uint32(g.a[p])*(n-q) + uint32(g.a[p+1])*q
|
||||
ss[i].Alpha = (a + n/2) / n
|
||||
}
|
||||
}
|
||||
g.Painter.Paint(ss, done)
|
||||
}
|
||||
|
||||
// SetGamma sets the gamma value.
|
||||
func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
|
||||
g.gammaIsOne = gamma == 1
|
||||
if g.gammaIsOne {
|
||||
return
|
||||
}
|
||||
for i := 0; i < 256; i++ {
|
||||
a := float64(i) / 0xff
|
||||
a = math.Pow(a, gamma)
|
||||
g.a[i] = uint16(0xffff * a)
|
||||
}
|
||||
}
|
||||
|
||||
// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
|
||||
// the given Painter.
|
||||
func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
|
||||
g := &GammaCorrectionPainter{Painter: p}
|
||||
g.SetGamma(gamma)
|
||||
return g
|
||||
}
|
||||
601
vendor/github.com/golang/freetype/raster/raster.go
generated
vendored
Normal file
601
vendor/github.com/golang/freetype/raster/raster.go
generated
vendored
Normal file
@@ -0,0 +1,601 @@
|
||||
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by your choice of either the
|
||||
// FreeType License or the GNU General Public License version 2 (or
|
||||
// any later version), both of which can be found in the LICENSE file.
|
||||
|
||||
// Package raster provides an anti-aliasing 2-D rasterizer.
|
||||
//
|
||||
// It is part of the larger Freetype suite of font-related packages, but the
|
||||
// raster package is not specific to font rasterization, and can be used
|
||||
// standalone without any other Freetype package.
|
||||
//
|
||||
// Rasterization is done by the same area/coverage accumulation algorithm as
|
||||
// the Freetype "smooth" module, and the Anti-Grain Geometry library. A
|
||||
// description of the area/coverage algorithm is at
|
||||
// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
|
||||
package raster // import "github.com/golang/freetype/raster"
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
|
||||
// area/coverage for the pixel at (xi, yi).
|
||||
type cell struct {
|
||||
xi int
|
||||
area, cover int
|
||||
next int
|
||||
}
|
||||
|
||||
type Rasterizer struct {
|
||||
// If false, the default behavior is to use the even-odd winding fill
|
||||
// rule during Rasterize.
|
||||
UseNonZeroWinding bool
|
||||
// An offset (in pixels) to the painted spans.
|
||||
Dx, Dy int
|
||||
|
||||
// The width of the Rasterizer. The height is implicit in len(cellIndex).
|
||||
width int
|
||||
// splitScaleN is the scaling factor used to determine how many times
|
||||
// to decompose a quadratic or cubic segment into a linear approximation.
|
||||
splitScale2, splitScale3 int
|
||||
|
||||
// The current pen position.
|
||||
a fixed.Point26_6
|
||||
// The current cell and its area/coverage being accumulated.
|
||||
xi, yi int
|
||||
area, cover int
|
||||
|
||||
// Saved cells.
|
||||
cell []cell
|
||||
// Linked list of cells, one per row.
|
||||
cellIndex []int
|
||||
// Buffers.
|
||||
cellBuf [256]cell
|
||||
cellIndexBuf [64]int
|
||||
spanBuf [64]Span
|
||||
}
|
||||
|
||||
// findCell returns the index in r.cell for the cell corresponding to
|
||||
// (r.xi, r.yi). The cell is created if necessary.
|
||||
func (r *Rasterizer) findCell() int {
|
||||
if r.yi < 0 || r.yi >= len(r.cellIndex) {
|
||||
return -1
|
||||
}
|
||||
xi := r.xi
|
||||
if xi < 0 {
|
||||
xi = -1
|
||||
} else if xi > r.width {
|
||||
xi = r.width
|
||||
}
|
||||
i, prev := r.cellIndex[r.yi], -1
|
||||
for i != -1 && r.cell[i].xi <= xi {
|
||||
if r.cell[i].xi == xi {
|
||||
return i
|
||||
}
|
||||
i, prev = r.cell[i].next, i
|
||||
}
|
||||
c := len(r.cell)
|
||||
if c == cap(r.cell) {
|
||||
buf := make([]cell, c, 4*c)
|
||||
copy(buf, r.cell)
|
||||
r.cell = buf[0 : c+1]
|
||||
} else {
|
||||
r.cell = r.cell[0 : c+1]
|
||||
}
|
||||
r.cell[c] = cell{xi, 0, 0, i}
|
||||
if prev == -1 {
|
||||
r.cellIndex[r.yi] = c
|
||||
} else {
|
||||
r.cell[prev].next = c
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi).
|
||||
func (r *Rasterizer) saveCell() {
|
||||
if r.area != 0 || r.cover != 0 {
|
||||
i := r.findCell()
|
||||
if i != -1 {
|
||||
r.cell[i].area += r.area
|
||||
r.cell[i].cover += r.cover
|
||||
}
|
||||
r.area = 0
|
||||
r.cover = 0
|
||||
}
|
||||
}
|
||||
|
||||
// setCell sets the (xi, yi) cell that r is accumulating area/coverage for.
|
||||
func (r *Rasterizer) setCell(xi, yi int) {
|
||||
if r.xi != xi || r.yi != yi {
|
||||
r.saveCell()
|
||||
r.xi, r.yi = xi, yi
|
||||
}
|
||||
}
|
||||
|
||||
// scan accumulates area/coverage for the yi'th scanline, going from
|
||||
// x0 to x1 in the horizontal direction (in 26.6 fixed point co-ordinates)
|
||||
// and from y0f to y1f fractional vertical units within that scanline.
|
||||
func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f fixed.Int26_6) {
|
||||
// Break the 26.6 fixed point X co-ordinates into integral and fractional parts.
|
||||
x0i := int(x0) / 64
|
||||
x0f := x0 - fixed.Int26_6(64*x0i)
|
||||
x1i := int(x1) / 64
|
||||
x1f := x1 - fixed.Int26_6(64*x1i)
|
||||
|
||||
// A perfectly horizontal scan.
|
||||
if y0f == y1f {
|
||||
r.setCell(x1i, yi)
|
||||
return
|
||||
}
|
||||
dx, dy := x1-x0, y1f-y0f
|
||||
// A single cell scan.
|
||||
if x0i == x1i {
|
||||
r.area += int((x0f + x1f) * dy)
|
||||
r.cover += int(dy)
|
||||
return
|
||||
}
|
||||
// There are at least two cells. Apart from the first and last cells,
|
||||
// all intermediate cells go through the full width of the cell,
|
||||
// or 64 units in 26.6 fixed point format.
|
||||
var (
|
||||
p, q, edge0, edge1 fixed.Int26_6
|
||||
xiDelta int
|
||||
)
|
||||
if dx > 0 {
|
||||
p, q = (64-x0f)*dy, dx
|
||||
edge0, edge1, xiDelta = 0, 64, 1
|
||||
} else {
|
||||
p, q = x0f*dy, -dx
|
||||
edge0, edge1, xiDelta = 64, 0, -1
|
||||
}
|
||||
yDelta, yRem := p/q, p%q
|
||||
if yRem < 0 {
|
||||
yDelta -= 1
|
||||
yRem += q
|
||||
}
|
||||
// Do the first cell.
|
||||
xi, y := x0i, y0f
|
||||
r.area += int((x0f + edge1) * yDelta)
|
||||
r.cover += int(yDelta)
|
||||
xi, y = xi+xiDelta, y+yDelta
|
||||
r.setCell(xi, yi)
|
||||
if xi != x1i {
|
||||
// Do all the intermediate cells.
|
||||
p = 64 * (y1f - y + yDelta)
|
||||
fullDelta, fullRem := p/q, p%q
|
||||
if fullRem < 0 {
|
||||
fullDelta -= 1
|
||||
fullRem += q
|
||||
}
|
||||
yRem -= q
|
||||
for xi != x1i {
|
||||
yDelta = fullDelta
|
||||
yRem += fullRem
|
||||
if yRem >= 0 {
|
||||
yDelta += 1
|
||||
yRem -= q
|
||||
}
|
||||
r.area += int(64 * yDelta)
|
||||
r.cover += int(yDelta)
|
||||
xi, y = xi+xiDelta, y+yDelta
|
||||
r.setCell(xi, yi)
|
||||
}
|
||||
}
|
||||
// Do the last cell.
|
||||
yDelta = y1f - y
|
||||
r.area += int((edge0 + x1f) * yDelta)
|
||||
r.cover += int(yDelta)
|
||||
}
|
||||
|
||||
// Start starts a new curve at the given point.
|
||||
func (r *Rasterizer) Start(a fixed.Point26_6) {
|
||||
r.setCell(int(a.X/64), int(a.Y/64))
|
||||
r.a = a
|
||||
}
|
||||
|
||||
// Add1 adds a linear segment to the current curve.
|
||||
func (r *Rasterizer) Add1(b fixed.Point26_6) {
|
||||
x0, y0 := r.a.X, r.a.Y
|
||||
x1, y1 := b.X, b.Y
|
||||
dx, dy := x1-x0, y1-y0
|
||||
// Break the 26.6 fixed point Y co-ordinates into integral and fractional
|
||||
// parts.
|
||||
y0i := int(y0) / 64
|
||||
y0f := y0 - fixed.Int26_6(64*y0i)
|
||||
y1i := int(y1) / 64
|
||||
y1f := y1 - fixed.Int26_6(64*y1i)
|
||||
|
||||
if y0i == y1i {
|
||||
// There is only one scanline.
|
||||
r.scan(y0i, x0, y0f, x1, y1f)
|
||||
|
||||
} else if dx == 0 {
|
||||
// This is a vertical line segment. We avoid calling r.scan and instead
|
||||
// manipulate r.area and r.cover directly.
|
||||
var (
|
||||
edge0, edge1 fixed.Int26_6
|
||||
yiDelta int
|
||||
)
|
||||
if dy > 0 {
|
||||
edge0, edge1, yiDelta = 0, 64, 1
|
||||
} else {
|
||||
edge0, edge1, yiDelta = 64, 0, -1
|
||||
}
|
||||
x0i, yi := int(x0)/64, y0i
|
||||
x0fTimes2 := (int(x0) - (64 * x0i)) * 2
|
||||
// Do the first pixel.
|
||||
dcover := int(edge1 - y0f)
|
||||
darea := int(x0fTimes2 * dcover)
|
||||
r.area += darea
|
||||
r.cover += dcover
|
||||
yi += yiDelta
|
||||
r.setCell(x0i, yi)
|
||||
// Do all the intermediate pixels.
|
||||
dcover = int(edge1 - edge0)
|
||||
darea = int(x0fTimes2 * dcover)
|
||||
for yi != y1i {
|
||||
r.area += darea
|
||||
r.cover += dcover
|
||||
yi += yiDelta
|
||||
r.setCell(x0i, yi)
|
||||
}
|
||||
// Do the last pixel.
|
||||
dcover = int(y1f - edge0)
|
||||
darea = int(x0fTimes2 * dcover)
|
||||
r.area += darea
|
||||
r.cover += dcover
|
||||
|
||||
} else {
|
||||
// There are at least two scanlines. Apart from the first and last
|
||||
// scanlines, all intermediate scanlines go through the full height of
|
||||
// the row, or 64 units in 26.6 fixed point format.
|
||||
var (
|
||||
p, q, edge0, edge1 fixed.Int26_6
|
||||
yiDelta int
|
||||
)
|
||||
if dy > 0 {
|
||||
p, q = (64-y0f)*dx, dy
|
||||
edge0, edge1, yiDelta = 0, 64, 1
|
||||
} else {
|
||||
p, q = y0f*dx, -dy
|
||||
edge0, edge1, yiDelta = 64, 0, -1
|
||||
}
|
||||
xDelta, xRem := p/q, p%q
|
||||
if xRem < 0 {
|
||||
xDelta -= 1
|
||||
xRem += q
|
||||
}
|
||||
// Do the first scanline.
|
||||
x, yi := x0, y0i
|
||||
r.scan(yi, x, y0f, x+xDelta, edge1)
|
||||
x, yi = x+xDelta, yi+yiDelta
|
||||
r.setCell(int(x)/64, yi)
|
||||
if yi != y1i {
|
||||
// Do all the intermediate scanlines.
|
||||
p = 64 * dx
|
||||
fullDelta, fullRem := p/q, p%q
|
||||
if fullRem < 0 {
|
||||
fullDelta -= 1
|
||||
fullRem += q
|
||||
}
|
||||
xRem -= q
|
||||
for yi != y1i {
|
||||
xDelta = fullDelta
|
||||
xRem += fullRem
|
||||
if xRem >= 0 {
|
||||
xDelta += 1
|
||||
xRem -= q
|
||||
}
|
||||
r.scan(yi, x, edge0, x+xDelta, edge1)
|
||||
x, yi = x+xDelta, yi+yiDelta
|
||||
r.setCell(int(x)/64, yi)
|
||||
}
|
||||
}
|
||||
// Do the last scanline.
|
||||
r.scan(yi, x, edge0, x1, y1f)
|
||||
}
|
||||
// The next lineTo starts from b.
|
||||
r.a = b
|
||||
}
|
||||
|
||||
// Add2 adds a quadratic segment to the current curve.
|
||||
func (r *Rasterizer) Add2(b, c fixed.Point26_6) {
|
||||
// Calculate nSplit (the number of recursive decompositions) based on how
|
||||
// 'curvy' it is. Specifically, how much the middle point b deviates from
|
||||
// (a+c)/2.
|
||||
dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / fixed.Int26_6(r.splitScale2)
|
||||
nsplit := 0
|
||||
for dev > 0 {
|
||||
dev /= 4
|
||||
nsplit++
|
||||
}
|
||||
// dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit
|
||||
// is 16.
|
||||
const maxNsplit = 16
|
||||
if nsplit > maxNsplit {
|
||||
panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
|
||||
}
|
||||
// Recursively decompose the curve nSplit levels deep.
|
||||
var (
|
||||
pStack [2*maxNsplit + 3]fixed.Point26_6
|
||||
sStack [maxNsplit + 1]int
|
||||
i int
|
||||
)
|
||||
sStack[0] = nsplit
|
||||
pStack[0] = c
|
||||
pStack[1] = b
|
||||
pStack[2] = r.a
|
||||
for i >= 0 {
|
||||
s := sStack[i]
|
||||
p := pStack[2*i:]
|
||||
if s > 0 {
|
||||
// Split the quadratic curve p[:3] into an equivalent set of two
|
||||
// shorter curves: p[:3] and p[2:5]. The new p[4] is the old p[2],
|
||||
// and p[0] is unchanged.
|
||||
mx := p[1].X
|
||||
p[4].X = p[2].X
|
||||
p[3].X = (p[4].X + mx) / 2
|
||||
p[1].X = (p[0].X + mx) / 2
|
||||
p[2].X = (p[1].X + p[3].X) / 2
|
||||
my := p[1].Y
|
||||
p[4].Y = p[2].Y
|
||||
p[3].Y = (p[4].Y + my) / 2
|
||||
p[1].Y = (p[0].Y + my) / 2
|
||||
p[2].Y = (p[1].Y + p[3].Y) / 2
|
||||
// The two shorter curves have one less split to do.
|
||||
sStack[i] = s - 1
|
||||
sStack[i+1] = s - 1
|
||||
i++
|
||||
} else {
|
||||
// Replace the level-0 quadratic with a two-linear-piece
|
||||
// approximation.
|
||||
midx := (p[0].X + 2*p[1].X + p[2].X) / 4
|
||||
midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
|
||||
r.Add1(fixed.Point26_6{midx, midy})
|
||||
r.Add1(p[0])
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add3 adds a cubic segment to the current curve.
|
||||
func (r *Rasterizer) Add3(b, c, d fixed.Point26_6) {
|
||||
// Calculate nSplit (the number of recursive decompositions) based on how
|
||||
// 'curvy' it is.
|
||||
dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / fixed.Int26_6(r.splitScale2)
|
||||
dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / fixed.Int26_6(r.splitScale3)
|
||||
nsplit := 0
|
||||
for dev2 > 0 || dev3 > 0 {
|
||||
dev2 /= 8
|
||||
dev3 /= 4
|
||||
nsplit++
|
||||
}
|
||||
// devN is 32-bit, and nsplit++ every time we shift off 2 bits, so
|
||||
// maxNsplit is 16.
|
||||
const maxNsplit = 16
|
||||
if nsplit > maxNsplit {
|
||||
panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
|
||||
}
|
||||
// Recursively decompose the curve nSplit levels deep.
|
||||
var (
|
||||
pStack [3*maxNsplit + 4]fixed.Point26_6
|
||||
sStack [maxNsplit + 1]int
|
||||
i int
|
||||
)
|
||||
sStack[0] = nsplit
|
||||
pStack[0] = d
|
||||
pStack[1] = c
|
||||
pStack[2] = b
|
||||
pStack[3] = r.a
|
||||
for i >= 0 {
|
||||
s := sStack[i]
|
||||
p := pStack[3*i:]
|
||||
if s > 0 {
|
||||
// Split the cubic curve p[:4] into an equivalent set of two
|
||||
// shorter curves: p[:4] and p[3:7]. The new p[6] is the old p[3],
|
||||
// and p[0] is unchanged.
|
||||
m01x := (p[0].X + p[1].X) / 2
|
||||
m12x := (p[1].X + p[2].X) / 2
|
||||
m23x := (p[2].X + p[3].X) / 2
|
||||
p[6].X = p[3].X
|
||||
p[5].X = m23x
|
||||
p[1].X = m01x
|
||||
p[2].X = (m01x + m12x) / 2
|
||||
p[4].X = (m12x + m23x) / 2
|
||||
p[3].X = (p[2].X + p[4].X) / 2
|
||||
m01y := (p[0].Y + p[1].Y) / 2
|
||||
m12y := (p[1].Y + p[2].Y) / 2
|
||||
m23y := (p[2].Y + p[3].Y) / 2
|
||||
p[6].Y = p[3].Y
|
||||
p[5].Y = m23y
|
||||
p[1].Y = m01y
|
||||
p[2].Y = (m01y + m12y) / 2
|
||||
p[4].Y = (m12y + m23y) / 2
|
||||
p[3].Y = (p[2].Y + p[4].Y) / 2
|
||||
// The two shorter curves have one less split to do.
|
||||
sStack[i] = s - 1
|
||||
sStack[i+1] = s - 1
|
||||
i++
|
||||
} else {
|
||||
// Replace the level-0 cubic with a two-linear-piece approximation.
|
||||
midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
|
||||
midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
|
||||
r.Add1(fixed.Point26_6{midx, midy})
|
||||
r.Add1(p[0])
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddPath adds the given Path.
|
||||
func (r *Rasterizer) AddPath(p Path) {
|
||||
for i := 0; i < len(p); {
|
||||
switch p[i] {
|
||||
case 0:
|
||||
r.Start(
|
||||
fixed.Point26_6{p[i+1], p[i+2]},
|
||||
)
|
||||
i += 4
|
||||
case 1:
|
||||
r.Add1(
|
||||
fixed.Point26_6{p[i+1], p[i+2]},
|
||||
)
|
||||
i += 4
|
||||
case 2:
|
||||
r.Add2(
|
||||
fixed.Point26_6{p[i+1], p[i+2]},
|
||||
fixed.Point26_6{p[i+3], p[i+4]},
|
||||
)
|
||||
i += 6
|
||||
case 3:
|
||||
r.Add3(
|
||||
fixed.Point26_6{p[i+1], p[i+2]},
|
||||
fixed.Point26_6{p[i+3], p[i+4]},
|
||||
fixed.Point26_6{p[i+5], p[i+6]},
|
||||
)
|
||||
i += 8
|
||||
default:
|
||||
panic("freetype/raster: bad path")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddStroke adds a stroked Path.
|
||||
func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
|
||||
Stroke(r, q, width, cr, jr)
|
||||
}
|
||||
|
||||
// areaToAlpha converts an area value to a uint32 alpha value. A completely
|
||||
// filled pixel corresponds to an area of 64*64*2, and an alpha of 0xffff. The
|
||||
// conversion of area values greater than this depends on the winding rule:
|
||||
// even-odd or non-zero.
|
||||
func (r *Rasterizer) areaToAlpha(area int) uint32 {
|
||||
// The C Freetype implementation (version 2.3.12) does "alpha := area>>1"
|
||||
// without the +1. Round-to-nearest gives a more symmetric result than
|
||||
// round-down. The C implementation also returns 8-bit alpha, not 16-bit
|
||||
// alpha.
|
||||
a := (area + 1) >> 1
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
alpha := uint32(a)
|
||||
if r.UseNonZeroWinding {
|
||||
if alpha > 0x0fff {
|
||||
alpha = 0x0fff
|
||||
}
|
||||
} else {
|
||||
alpha &= 0x1fff
|
||||
if alpha > 0x1000 {
|
||||
alpha = 0x2000 - alpha
|
||||
} else if alpha == 0x1000 {
|
||||
alpha = 0x0fff
|
||||
}
|
||||
}
|
||||
// alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to
|
||||
// 16-bit alpha.
|
||||
return alpha<<4 | alpha>>8
|
||||
}
|
||||
|
||||
// Rasterize converts r's accumulated curves into Spans for p. The Spans passed
|
||||
// to p are non-overlapping, and sorted by Y and then X. They all have non-zero
|
||||
// width (and 0 <= X0 < X1 <= r.width) and non-zero A, except for the final
|
||||
// Span, which has Y, X0, X1 and A all equal to zero.
|
||||
func (r *Rasterizer) Rasterize(p Painter) {
|
||||
r.saveCell()
|
||||
s := 0
|
||||
for yi := 0; yi < len(r.cellIndex); yi++ {
|
||||
xi, cover := 0, 0
|
||||
for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
|
||||
if cover != 0 && r.cell[c].xi > xi {
|
||||
alpha := r.areaToAlpha(cover * 64 * 2)
|
||||
if alpha != 0 {
|
||||
xi0, xi1 := xi, r.cell[c].xi
|
||||
if xi0 < 0 {
|
||||
xi0 = 0
|
||||
}
|
||||
if xi1 >= r.width {
|
||||
xi1 = r.width
|
||||
}
|
||||
if xi0 < xi1 {
|
||||
r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
|
||||
s++
|
||||
}
|
||||
}
|
||||
}
|
||||
cover += r.cell[c].cover
|
||||
alpha := r.areaToAlpha(cover*64*2 - r.cell[c].area)
|
||||
xi = r.cell[c].xi + 1
|
||||
if alpha != 0 {
|
||||
xi0, xi1 := r.cell[c].xi, xi
|
||||
if xi0 < 0 {
|
||||
xi0 = 0
|
||||
}
|
||||
if xi1 >= r.width {
|
||||
xi1 = r.width
|
||||
}
|
||||
if xi0 < xi1 {
|
||||
r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
|
||||
s++
|
||||
}
|
||||
}
|
||||
if s > len(r.spanBuf)-2 {
|
||||
p.Paint(r.spanBuf[:s], false)
|
||||
s = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
p.Paint(r.spanBuf[:s], true)
|
||||
}
|
||||
|
||||
// Clear cancels any previous calls to r.Start or r.AddXxx.
|
||||
func (r *Rasterizer) Clear() {
|
||||
r.a = fixed.Point26_6{}
|
||||
r.xi = 0
|
||||
r.yi = 0
|
||||
r.area = 0
|
||||
r.cover = 0
|
||||
r.cell = r.cell[:0]
|
||||
for i := 0; i < len(r.cellIndex); i++ {
|
||||
r.cellIndex[i] = -1
|
||||
}
|
||||
}
|
||||
|
||||
// SetBounds sets the maximum width and height of the rasterized image and
|
||||
// calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
|
||||
func (r *Rasterizer) SetBounds(width, height int) {
|
||||
if width < 0 {
|
||||
width = 0
|
||||
}
|
||||
if height < 0 {
|
||||
height = 0
|
||||
}
|
||||
// Use the same ssN heuristic as the C Freetype (version 2.4.0)
|
||||
// implementation.
|
||||
ss2, ss3 := 32, 16
|
||||
if width > 24 || height > 24 {
|
||||
ss2, ss3 = 2*ss2, 2*ss3
|
||||
if width > 120 || height > 120 {
|
||||
ss2, ss3 = 2*ss2, 2*ss3
|
||||
}
|
||||
}
|
||||
r.width = width
|
||||
r.splitScale2 = ss2
|
||||
r.splitScale3 = ss3
|
||||
r.cell = r.cellBuf[:0]
|
||||
if height > len(r.cellIndexBuf) {
|
||||
r.cellIndex = make([]int, height)
|
||||
} else {
|
||||
r.cellIndex = r.cellIndexBuf[:height]
|
||||
}
|
||||
r.Clear()
|
||||
}
|
||||
|
||||
// NewRasterizer creates a new Rasterizer with the given bounds.
|
||||
func NewRasterizer(width, height int) *Rasterizer {
|
||||
r := new(Rasterizer)
|
||||
r.SetBounds(width, height)
|
||||
return r
|
||||
}
|
||||
483
vendor/github.com/golang/freetype/raster/stroke.go
generated
vendored
Normal file
483
vendor/github.com/golang/freetype/raster/stroke.go
generated
vendored
Normal file
@@ -0,0 +1,483 @@
|
||||
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by your choice of either the
|
||||
// FreeType License or the GNU General Public License version 2 (or
|
||||
// any later version), both of which can be found in the LICENSE file.
|
||||
|
||||
package raster
|
||||
|
||||
import (
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// Two points are considered practically equal if the square of the distance
|
||||
// between them is less than one quarter (i.e. 1024 / 4096).
|
||||
const epsilon = fixed.Int52_12(1024)
|
||||
|
||||
// A Capper signifies how to begin or end a stroked path.
|
||||
type Capper interface {
|
||||
// Cap adds a cap to p given a pivot point and the normal vector of a
|
||||
// terminal segment. The normal's length is half of the stroke width.
|
||||
Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6)
|
||||
}
|
||||
|
||||
// The CapperFunc type adapts an ordinary function to be a Capper.
|
||||
type CapperFunc func(Adder, fixed.Int26_6, fixed.Point26_6, fixed.Point26_6)
|
||||
|
||||
func (f CapperFunc) Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
|
||||
f(p, halfWidth, pivot, n1)
|
||||
}
|
||||
|
||||
// A Joiner signifies how to join interior nodes of a stroked path.
|
||||
type Joiner interface {
|
||||
// Join adds a join to the two sides of a stroked path given a pivot
|
||||
// point and the normal vectors of the trailing and leading segments.
|
||||
// Both normals have length equal to half of the stroke width.
|
||||
Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
|
||||
}
|
||||
|
||||
// The JoinerFunc type adapts an ordinary function to be a Joiner.
|
||||
type JoinerFunc func(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
|
||||
|
||||
func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
|
||||
f(lhs, rhs, halfWidth, pivot, n0, n1)
|
||||
}
|
||||
|
||||
// RoundCapper adds round caps to a stroked path.
|
||||
var RoundCapper Capper = CapperFunc(roundCapper)
|
||||
|
||||
func roundCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
|
||||
// The cubic Bézier approximation to a circle involves the magic number
|
||||
// (√2 - 1) * 4/3, which is approximately 35/64.
|
||||
const k = 35
|
||||
e := pRot90CCW(n1)
|
||||
side := pivot.Add(e)
|
||||
start, end := pivot.Sub(n1), pivot.Add(n1)
|
||||
d, e := n1.Mul(k), e.Mul(k)
|
||||
p.Add3(start.Add(e), side.Sub(d), side)
|
||||
p.Add3(side.Add(d), end.Add(e), end)
|
||||
}
|
||||
|
||||
// ButtCapper adds butt caps to a stroked path.
|
||||
var ButtCapper Capper = CapperFunc(buttCapper)
|
||||
|
||||
func buttCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
|
||||
p.Add1(pivot.Add(n1))
|
||||
}
|
||||
|
||||
// SquareCapper adds square caps to a stroked path.
|
||||
var SquareCapper Capper = CapperFunc(squareCapper)
|
||||
|
||||
func squareCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
|
||||
e := pRot90CCW(n1)
|
||||
side := pivot.Add(e)
|
||||
p.Add1(side.Sub(n1))
|
||||
p.Add1(side.Add(n1))
|
||||
p.Add1(pivot.Add(n1))
|
||||
}
|
||||
|
||||
// RoundJoiner adds round joins to a stroked path.
|
||||
var RoundJoiner Joiner = JoinerFunc(roundJoiner)
|
||||
|
||||
func roundJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
|
||||
dot := pDot(pRot90CW(n0), n1)
|
||||
if dot >= 0 {
|
||||
addArc(lhs, pivot, n0, n1)
|
||||
rhs.Add1(pivot.Sub(n1))
|
||||
} else {
|
||||
lhs.Add1(pivot.Add(n1))
|
||||
addArc(rhs, pivot, pNeg(n0), pNeg(n1))
|
||||
}
|
||||
}
|
||||
|
||||
// BevelJoiner adds bevel joins to a stroked path.
|
||||
var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
|
||||
|
||||
func bevelJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
|
||||
lhs.Add1(pivot.Add(n1))
|
||||
rhs.Add1(pivot.Sub(n1))
|
||||
}
|
||||
|
||||
// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
|
||||
// the two possible arcs is taken, i.e. the one spanning <= 180 degrees. The
|
||||
// two vectors n0 and n1 must be of equal length.
|
||||
func addArc(p Adder, pivot, n0, n1 fixed.Point26_6) {
|
||||
// r2 is the square of the length of n0.
|
||||
r2 := pDot(n0, n0)
|
||||
if r2 < epsilon {
|
||||
// The arc radius is so small that we collapse to a straight line.
|
||||
p.Add1(pivot.Add(n1))
|
||||
return
|
||||
}
|
||||
// We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus
|
||||
// a final quadratic segment from s to n1. Each 45-degree segment has
|
||||
// control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled,
|
||||
// rotated and translated. tan(π/8) is approximately 27/64.
|
||||
const tpo8 = 27
|
||||
var s fixed.Point26_6
|
||||
// We determine which octant the angle between n0 and n1 is in via three
|
||||
// dot products. m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135
|
||||
// degrees.
|
||||
m0 := pRot45CW(n0)
|
||||
m1 := pRot90CW(n0)
|
||||
m2 := pRot90CW(m0)
|
||||
if pDot(m1, n1) >= 0 {
|
||||
if pDot(n0, n1) >= 0 {
|
||||
if pDot(m2, n1) <= 0 {
|
||||
// n1 is between 0 and 45 degrees clockwise of n0.
|
||||
s = n0
|
||||
} else {
|
||||
// n1 is between 45 and 90 degrees clockwise of n0.
|
||||
p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
|
||||
s = m0
|
||||
}
|
||||
} else {
|
||||
pm1, n0t := pivot.Add(m1), n0.Mul(tpo8)
|
||||
p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
|
||||
p.Add2(pm1.Add(n0t), pm1)
|
||||
if pDot(m0, n1) >= 0 {
|
||||
// n1 is between 90 and 135 degrees clockwise of n0.
|
||||
s = m1
|
||||
} else {
|
||||
// n1 is between 135 and 180 degrees clockwise of n0.
|
||||
p.Add2(pm1.Sub(n0t), pivot.Add(m2))
|
||||
s = m2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if pDot(n0, n1) >= 0 {
|
||||
if pDot(m0, n1) >= 0 {
|
||||
// n1 is between 0 and 45 degrees counter-clockwise of n0.
|
||||
s = n0
|
||||
} else {
|
||||
// n1 is between 45 and 90 degrees counter-clockwise of n0.
|
||||
p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
|
||||
s = pNeg(m2)
|
||||
}
|
||||
} else {
|
||||
pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8)
|
||||
p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
|
||||
p.Add2(pm1.Add(n0t), pm1)
|
||||
if pDot(m2, n1) <= 0 {
|
||||
// n1 is between 90 and 135 degrees counter-clockwise of n0.
|
||||
s = pNeg(m1)
|
||||
} else {
|
||||
// n1 is between 135 and 180 degrees counter-clockwise of n0.
|
||||
p.Add2(pm1.Sub(n0t), pivot.Sub(m0))
|
||||
s = pNeg(m0)
|
||||
}
|
||||
}
|
||||
}
|
||||
// The final quadratic segment has two endpoints s and n1 and the middle
|
||||
// control point is a multiple of s.Add(n1), i.e. it is on the angle
|
||||
// bisector of those two points. The multiple ranges between 128/256 and
|
||||
// 150/256 as the angle between s and n1 ranges between 0 and 45 degrees.
|
||||
//
|
||||
// When the angle is 0 degrees (i.e. s and n1 are coincident) then
|
||||
// s.Add(n1) is twice s and so the middle control point of the degenerate
|
||||
// quadratic segment should be half s.Add(n1), and half = 128/256.
|
||||
//
|
||||
// When the angle is 45 degrees then 150/256 is the ratio of the lengths of
|
||||
// the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}.
|
||||
//
|
||||
// d is the normalized dot product between s and n1. Since the angle ranges
|
||||
// between 0 and 45 degrees then d ranges between 256/256 and 181/256.
|
||||
d := 256 * pDot(s, n1) / r2
|
||||
multiple := fixed.Int26_6(150-(150-128)*(d-181)/(256-181)) >> 2
|
||||
p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
|
||||
}
|
||||
|
||||
// midpoint returns the midpoint of two Points.
|
||||
func midpoint(a, b fixed.Point26_6) fixed.Point26_6 {
|
||||
return fixed.Point26_6{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
|
||||
}
|
||||
|
||||
// angleGreaterThan45 returns whether the angle between two vectors is more
|
||||
// than 45 degrees.
|
||||
func angleGreaterThan45(v0, v1 fixed.Point26_6) bool {
|
||||
v := pRot45CCW(v0)
|
||||
return pDot(v, v1) < 0 || pDot(pRot90CW(v), v1) < 0
|
||||
}
|
||||
|
||||
// interpolate returns the point (1-t)*a + t*b.
|
||||
func interpolate(a, b fixed.Point26_6, t fixed.Int52_12) fixed.Point26_6 {
|
||||
s := 1<<12 - t
|
||||
x := s*fixed.Int52_12(a.X) + t*fixed.Int52_12(b.X)
|
||||
y := s*fixed.Int52_12(a.Y) + t*fixed.Int52_12(b.Y)
|
||||
return fixed.Point26_6{fixed.Int26_6(x >> 12), fixed.Int26_6(y >> 12)}
|
||||
}
|
||||
|
||||
// curviest2 returns the value of t for which the quadratic parametric curve
|
||||
// (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature.
|
||||
//
|
||||
// The curvature of the parametric curve f(t) = (x(t), y(t)) is
|
||||
// |x′y″-y′x″| / (x′²+y′²)^(3/2).
|
||||
//
|
||||
// Let d = b-a and e = c-2*b+a, so that f′(t) = 2*d+2*e*t and f″(t) = 2*e.
|
||||
// The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex),
|
||||
// which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t.
|
||||
//
|
||||
// Thus, curvature is extreme where the denominator is extreme, i.e. where
|
||||
// (x′²+y′²) is extreme. The first order condition is that
|
||||
// 2*x′*x″+2*y′*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0.
|
||||
// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey).
|
||||
func curviest2(a, b, c fixed.Point26_6) fixed.Int52_12 {
|
||||
dx := int64(b.X - a.X)
|
||||
dy := int64(b.Y - a.Y)
|
||||
ex := int64(c.X - 2*b.X + a.X)
|
||||
ey := int64(c.Y - 2*b.Y + a.Y)
|
||||
if ex == 0 && ey == 0 {
|
||||
return 2048
|
||||
}
|
||||
return fixed.Int52_12(-4096 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
|
||||
}
|
||||
|
||||
// A stroker holds state for stroking a path.
|
||||
type stroker struct {
|
||||
// p is the destination that records the stroked path.
|
||||
p Adder
|
||||
// u is the half-width of the stroke.
|
||||
u fixed.Int26_6
|
||||
// cr and jr specify how to end and connect path segments.
|
||||
cr Capper
|
||||
jr Joiner
|
||||
// r is the reverse path. Stroking a path involves constructing two
|
||||
// parallel paths 2*u apart. The first path is added immediately to p,
|
||||
// the second path is accumulated in r and eventually added in reverse.
|
||||
r Path
|
||||
// a is the most recent segment point. anorm is the segment normal of
|
||||
// length u at that point.
|
||||
a, anorm fixed.Point26_6
|
||||
}
|
||||
|
||||
// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
|
||||
// defined by (k.a, b, c) achieves maximum curvature at either k.a or c.
|
||||
func (k *stroker) addNonCurvy2(b, c fixed.Point26_6) {
|
||||
// We repeatedly divide the segment at its middle until it is straight
|
||||
// enough to approximate the stroke by just translating the control points.
|
||||
// ds and ps are stacks of depths and points. t is the top of the stack.
|
||||
const maxDepth = 5
|
||||
var (
|
||||
ds [maxDepth + 1]int
|
||||
ps [2*maxDepth + 3]fixed.Point26_6
|
||||
t int
|
||||
)
|
||||
// Initially the ps stack has one quadratic segment of depth zero.
|
||||
ds[0] = 0
|
||||
ps[2] = k.a
|
||||
ps[1] = b
|
||||
ps[0] = c
|
||||
anorm := k.anorm
|
||||
var cnorm fixed.Point26_6
|
||||
|
||||
for {
|
||||
depth := ds[t]
|
||||
a := ps[2*t+2]
|
||||
b := ps[2*t+1]
|
||||
c := ps[2*t+0]
|
||||
ab := b.Sub(a)
|
||||
bc := c.Sub(b)
|
||||
abIsSmall := pDot(ab, ab) < fixed.Int52_12(1<<12)
|
||||
bcIsSmall := pDot(bc, bc) < fixed.Int52_12(1<<12)
|
||||
if abIsSmall && bcIsSmall {
|
||||
// Approximate the segment by a circular arc.
|
||||
cnorm = pRot90CCW(pNorm(bc, k.u))
|
||||
mac := midpoint(a, c)
|
||||
addArc(k.p, mac, anorm, cnorm)
|
||||
addArc(&k.r, mac, pNeg(anorm), pNeg(cnorm))
|
||||
} else if depth < maxDepth && angleGreaterThan45(ab, bc) {
|
||||
// Divide the segment in two and push both halves on the stack.
|
||||
mab := midpoint(a, b)
|
||||
mbc := midpoint(b, c)
|
||||
t++
|
||||
ds[t+0] = depth + 1
|
||||
ds[t-1] = depth + 1
|
||||
ps[2*t+2] = a
|
||||
ps[2*t+1] = mab
|
||||
ps[2*t+0] = midpoint(mab, mbc)
|
||||
ps[2*t-1] = mbc
|
||||
continue
|
||||
} else {
|
||||
// Translate the control points.
|
||||
bnorm := pRot90CCW(pNorm(c.Sub(a), k.u))
|
||||
cnorm = pRot90CCW(pNorm(bc, k.u))
|
||||
k.p.Add2(b.Add(bnorm), c.Add(cnorm))
|
||||
k.r.Add2(b.Sub(bnorm), c.Sub(cnorm))
|
||||
}
|
||||
if t == 0 {
|
||||
k.a, k.anorm = c, cnorm
|
||||
return
|
||||
}
|
||||
t--
|
||||
anorm = cnorm
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Add1 adds a linear segment to the stroker.
|
||||
func (k *stroker) Add1(b fixed.Point26_6) {
|
||||
bnorm := pRot90CCW(pNorm(b.Sub(k.a), k.u))
|
||||
if len(k.r) == 0 {
|
||||
k.p.Start(k.a.Add(bnorm))
|
||||
k.r.Start(k.a.Sub(bnorm))
|
||||
} else {
|
||||
k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm)
|
||||
}
|
||||
k.p.Add1(b.Add(bnorm))
|
||||
k.r.Add1(b.Sub(bnorm))
|
||||
k.a, k.anorm = b, bnorm
|
||||
}
|
||||
|
||||
// Add2 adds a quadratic segment to the stroker.
|
||||
func (k *stroker) Add2(b, c fixed.Point26_6) {
|
||||
ab := b.Sub(k.a)
|
||||
bc := c.Sub(b)
|
||||
abnorm := pRot90CCW(pNorm(ab, k.u))
|
||||
if len(k.r) == 0 {
|
||||
k.p.Start(k.a.Add(abnorm))
|
||||
k.r.Start(k.a.Sub(abnorm))
|
||||
} else {
|
||||
k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm)
|
||||
}
|
||||
|
||||
// Approximate nearly-degenerate quadratics by linear segments.
|
||||
abIsSmall := pDot(ab, ab) < epsilon
|
||||
bcIsSmall := pDot(bc, bc) < epsilon
|
||||
if abIsSmall || bcIsSmall {
|
||||
acnorm := pRot90CCW(pNorm(c.Sub(k.a), k.u))
|
||||
k.p.Add1(c.Add(acnorm))
|
||||
k.r.Add1(c.Sub(acnorm))
|
||||
k.a, k.anorm = c, acnorm
|
||||
return
|
||||
}
|
||||
|
||||
// The quadratic segment (k.a, b, c) has a point of maximum curvature.
|
||||
// If this occurs at an end point, we process the segment as a whole.
|
||||
t := curviest2(k.a, b, c)
|
||||
if t <= 0 || 4096 <= t {
|
||||
k.addNonCurvy2(b, c)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, we perform a de Casteljau decomposition at the point of
|
||||
// maximum curvature and process the two straighter parts.
|
||||
mab := interpolate(k.a, b, t)
|
||||
mbc := interpolate(b, c, t)
|
||||
mabc := interpolate(mab, mbc, t)
|
||||
|
||||
// If the vectors ab and bc are close to being in opposite directions,
|
||||
// then the decomposition can become unstable, so we approximate the
|
||||
// quadratic segment by two linear segments joined by an arc.
|
||||
bcnorm := pRot90CCW(pNorm(bc, k.u))
|
||||
if pDot(abnorm, bcnorm) < -fixed.Int52_12(k.u)*fixed.Int52_12(k.u)*2047/2048 {
|
||||
pArc := pDot(abnorm, bc) < 0
|
||||
|
||||
k.p.Add1(mabc.Add(abnorm))
|
||||
if pArc {
|
||||
z := pRot90CW(abnorm)
|
||||
addArc(k.p, mabc, abnorm, z)
|
||||
addArc(k.p, mabc, z, bcnorm)
|
||||
}
|
||||
k.p.Add1(mabc.Add(bcnorm))
|
||||
k.p.Add1(c.Add(bcnorm))
|
||||
|
||||
k.r.Add1(mabc.Sub(abnorm))
|
||||
if !pArc {
|
||||
z := pRot90CW(abnorm)
|
||||
addArc(&k.r, mabc, pNeg(abnorm), z)
|
||||
addArc(&k.r, mabc, z, pNeg(bcnorm))
|
||||
}
|
||||
k.r.Add1(mabc.Sub(bcnorm))
|
||||
k.r.Add1(c.Sub(bcnorm))
|
||||
|
||||
k.a, k.anorm = c, bcnorm
|
||||
return
|
||||
}
|
||||
|
||||
// Process the decomposed parts.
|
||||
k.addNonCurvy2(mab, mabc)
|
||||
k.addNonCurvy2(mbc, c)
|
||||
}
|
||||
|
||||
// Add3 adds a cubic segment to the stroker.
|
||||
func (k *stroker) Add3(b, c, d fixed.Point26_6) {
|
||||
panic("freetype/raster: stroke unimplemented for cubic segments")
|
||||
}
|
||||
|
||||
// stroke adds the stroked Path q to p, where q consists of exactly one curve.
|
||||
func (k *stroker) stroke(q Path) {
|
||||
// Stroking is implemented by deriving two paths each k.u apart from q.
|
||||
// The left-hand-side path is added immediately to k.p; the right-hand-side
|
||||
// path is accumulated in k.r. Once we've finished adding the LHS to k.p,
|
||||
// we add the RHS in reverse order.
|
||||
k.r = make(Path, 0, len(q))
|
||||
k.a = fixed.Point26_6{q[1], q[2]}
|
||||
for i := 4; i < len(q); {
|
||||
switch q[i] {
|
||||
case 1:
|
||||
k.Add1(
|
||||
fixed.Point26_6{q[i+1], q[i+2]},
|
||||
)
|
||||
i += 4
|
||||
case 2:
|
||||
k.Add2(
|
||||
fixed.Point26_6{q[i+1], q[i+2]},
|
||||
fixed.Point26_6{q[i+3], q[i+4]},
|
||||
)
|
||||
i += 6
|
||||
case 3:
|
||||
k.Add3(
|
||||
fixed.Point26_6{q[i+1], q[i+2]},
|
||||
fixed.Point26_6{q[i+3], q[i+4]},
|
||||
fixed.Point26_6{q[i+5], q[i+6]},
|
||||
)
|
||||
i += 8
|
||||
default:
|
||||
panic("freetype/raster: bad path")
|
||||
}
|
||||
}
|
||||
if len(k.r) == 0 {
|
||||
return
|
||||
}
|
||||
// TODO(nigeltao): if q is a closed curve then we should join the first and
|
||||
// last segments instead of capping them.
|
||||
k.cr.Cap(k.p, k.u, q.lastPoint(), pNeg(k.anorm))
|
||||
addPathReversed(k.p, k.r)
|
||||
pivot := q.firstPoint()
|
||||
k.cr.Cap(k.p, k.u, pivot, pivot.Sub(fixed.Point26_6{k.r[1], k.r[2]}))
|
||||
}
|
||||
|
||||
// Stroke adds q stroked with the given width to p. The result is typically
|
||||
// self-intersecting and should be rasterized with UseNonZeroWinding.
|
||||
// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
|
||||
func Stroke(p Adder, q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
|
||||
if len(q) == 0 {
|
||||
return
|
||||
}
|
||||
if cr == nil {
|
||||
cr = RoundCapper
|
||||
}
|
||||
if jr == nil {
|
||||
jr = RoundJoiner
|
||||
}
|
||||
if q[0] != 0 {
|
||||
panic("freetype/raster: bad path")
|
||||
}
|
||||
s := stroker{p: p, u: width / 2, cr: cr, jr: jr}
|
||||
i := 0
|
||||
for j := 4; j < len(q); {
|
||||
switch q[j] {
|
||||
case 0:
|
||||
s.stroke(q[i:j])
|
||||
i, j = j, j+4
|
||||
case 1:
|
||||
j += 4
|
||||
case 2:
|
||||
j += 6
|
||||
case 3:
|
||||
j += 8
|
||||
default:
|
||||
panic("freetype/raster: bad path")
|
||||
}
|
||||
}
|
||||
s.stroke(q[i:])
|
||||
}
|
||||
507
vendor/github.com/golang/freetype/truetype/face.go
generated
vendored
Normal file
507
vendor/github.com/golang/freetype/truetype/face.go
generated
vendored
Normal file
@@ -0,0 +1,507 @@
|
||||
// Copyright 2015 The Freetype-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by your choice of either the
|
||||
// FreeType License or the GNU General Public License version 2 (or
|
||||
// any later version), both of which can be found in the LICENSE file.
|
||||
|
||||
package truetype
|
||||
|
||||
import (
|
||||
"image"
|
||||
"math"
|
||||
|
||||
"github.com/golang/freetype/raster"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
func powerOf2(i int) bool {
|
||||
return i != 0 && (i&(i-1)) == 0
|
||||
}
|
||||
|
||||
// Options are optional arguments to NewFace.
|
||||
type Options struct {
|
||||
// Size is the font size in points, as in "a 10 point font size".
|
||||
//
|
||||
// A zero value means to use a 12 point font size.
|
||||
Size float64
|
||||
|
||||
// DPI is the dots-per-inch resolution.
|
||||
//
|
||||
// A zero value means to use 72 DPI.
|
||||
DPI float64
|
||||
|
||||
// Hinting is how to quantize the glyph nodes.
|
||||
//
|
||||
// A zero value means to use no hinting.
|
||||
Hinting font.Hinting
|
||||
|
||||
// GlyphCacheEntries is the number of entries in the glyph mask image
|
||||
// cache.
|
||||
//
|
||||
// If non-zero, it must be a power of 2.
|
||||
//
|
||||
// A zero value means to use 512 entries.
|
||||
GlyphCacheEntries int
|
||||
|
||||
// SubPixelsX is the number of sub-pixel locations a glyph's dot is
|
||||
// quantized to, in the horizontal direction. For example, a value of 8
|
||||
// means that the dot is quantized to 1/8th of a pixel. This quantization
|
||||
// only affects the glyph mask image, not its bounding box or advance
|
||||
// width. A higher value gives a more faithful glyph image, but reduces the
|
||||
// effectiveness of the glyph cache.
|
||||
//
|
||||
// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
|
||||
//
|
||||
// A zero value means to use 4 sub-pixel locations.
|
||||
SubPixelsX int
|
||||
|
||||
// SubPixelsY is the number of sub-pixel locations a glyph's dot is
|
||||
// quantized to, in the vertical direction. For example, a value of 8
|
||||
// means that the dot is quantized to 1/8th of a pixel. This quantization
|
||||
// only affects the glyph mask image, not its bounding box or advance
|
||||
// width. A higher value gives a more faithful glyph image, but reduces the
|
||||
// effectiveness of the glyph cache.
|
||||
//
|
||||
// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
|
||||
//
|
||||
// A zero value means to use 1 sub-pixel location.
|
||||
SubPixelsY int
|
||||
}
|
||||
|
||||
func (o *Options) size() float64 {
|
||||
if o != nil && o.Size > 0 {
|
||||
return o.Size
|
||||
}
|
||||
return 12
|
||||
}
|
||||
|
||||
func (o *Options) dpi() float64 {
|
||||
if o != nil && o.DPI > 0 {
|
||||
return o.DPI
|
||||
}
|
||||
return 72
|
||||
}
|
||||
|
||||
func (o *Options) hinting() font.Hinting {
|
||||
if o != nil {
|
||||
switch o.Hinting {
|
||||
case font.HintingVertical, font.HintingFull:
|
||||
// TODO: support vertical hinting.
|
||||
return font.HintingFull
|
||||
}
|
||||
}
|
||||
return font.HintingNone
|
||||
}
|
||||
|
||||
func (o *Options) glyphCacheEntries() int {
|
||||
if o != nil && powerOf2(o.GlyphCacheEntries) {
|
||||
return o.GlyphCacheEntries
|
||||
}
|
||||
// 512 is 128 * 4 * 1, which lets us cache 128 glyphs at 4 * 1 subpixel
|
||||
// locations in the X and Y direction.
|
||||
return 512
|
||||
}
|
||||
|
||||
func (o *Options) subPixelsX() (value uint32, halfQuantum, mask fixed.Int26_6) {
|
||||
if o != nil {
|
||||
switch o.SubPixelsX {
|
||||
case 1, 2, 4, 8, 16, 32, 64:
|
||||
return subPixels(o.SubPixelsX)
|
||||
}
|
||||
}
|
||||
// This default value of 4 isn't based on anything scientific, merely as
|
||||
// small a number as possible that looks almost as good as no quantization,
|
||||
// or returning subPixels(64).
|
||||
return subPixels(4)
|
||||
}
|
||||
|
||||
func (o *Options) subPixelsY() (value uint32, halfQuantum, mask fixed.Int26_6) {
|
||||
if o != nil {
|
||||
switch o.SubPixelsX {
|
||||
case 1, 2, 4, 8, 16, 32, 64:
|
||||
return subPixels(o.SubPixelsX)
|
||||
}
|
||||
}
|
||||
// This default value of 1 isn't based on anything scientific, merely that
|
||||
// vertical sub-pixel glyph rendering is pretty rare. Baseline locations
|
||||
// can usually afford to snap to the pixel grid, so the vertical direction
|
||||
// doesn't have the deal with the horizontal's fractional advance widths.
|
||||
return subPixels(1)
|
||||
}
|
||||
|
||||
// subPixels returns q and the bias and mask that leads to q quantized
|
||||
// sub-pixel locations per full pixel.
|
||||
//
|
||||
// For example, q == 4 leads to a bias of 8 and a mask of 0xfffffff0, or -16,
|
||||
// because we want to round fractions of fixed.Int26_6 as:
|
||||
// - 0 to 7 rounds to 0.
|
||||
// - 8 to 23 rounds to 16.
|
||||
// - 24 to 39 rounds to 32.
|
||||
// - 40 to 55 rounds to 48.
|
||||
// - 56 to 63 rounds to 64.
|
||||
// which means to add 8 and then bitwise-and with -16, in two's complement
|
||||
// representation.
|
||||
//
|
||||
// When q == 1, we want bias == 32 and mask == -64.
|
||||
// When q == 2, we want bias == 16 and mask == -32.
|
||||
// When q == 4, we want bias == 8 and mask == -16.
|
||||
// ...
|
||||
// When q == 64, we want bias == 0 and mask == -1. (The no-op case).
|
||||
// The pattern is clear.
|
||||
func subPixels(q int) (value uint32, bias, mask fixed.Int26_6) {
|
||||
return uint32(q), 32 / fixed.Int26_6(q), -64 / fixed.Int26_6(q)
|
||||
}
|
||||
|
||||
// glyphCacheEntry caches the arguments and return values of rasterize.
|
||||
type glyphCacheEntry struct {
|
||||
key glyphCacheKey
|
||||
val glyphCacheVal
|
||||
}
|
||||
|
||||
type glyphCacheKey struct {
|
||||
index Index
|
||||
fx, fy uint8
|
||||
}
|
||||
|
||||
type glyphCacheVal struct {
|
||||
advanceWidth fixed.Int26_6
|
||||
offset image.Point
|
||||
gw int
|
||||
gh int
|
||||
}
|
||||
|
||||
type indexCacheEntry struct {
|
||||
rune rune
|
||||
index Index
|
||||
}
|
||||
|
||||
// NewFace returns a new font.Face for the given Font.
|
||||
func NewFace(f *Font, opts *Options) font.Face {
|
||||
a := &face{
|
||||
f: f,
|
||||
hinting: opts.hinting(),
|
||||
scale: fixed.Int26_6(0.5 + (opts.size() * opts.dpi() * 64 / 72)),
|
||||
glyphCache: make([]glyphCacheEntry, opts.glyphCacheEntries()),
|
||||
}
|
||||
a.subPixelX, a.subPixelBiasX, a.subPixelMaskX = opts.subPixelsX()
|
||||
a.subPixelY, a.subPixelBiasY, a.subPixelMaskY = opts.subPixelsY()
|
||||
|
||||
// Fill the cache with invalid entries. Valid glyph cache entries have fx
|
||||
// and fy in the range [0, 64). Valid index cache entries have rune >= 0.
|
||||
for i := range a.glyphCache {
|
||||
a.glyphCache[i].key.fy = 0xff
|
||||
}
|
||||
for i := range a.indexCache {
|
||||
a.indexCache[i].rune = -1
|
||||
}
|
||||
|
||||
// Set the rasterizer's bounds to be big enough to handle the largest glyph.
|
||||
b := f.Bounds(a.scale)
|
||||
xmin := +int(b.Min.X) >> 6
|
||||
ymin := -int(b.Max.Y) >> 6
|
||||
xmax := +int(b.Max.X+63) >> 6
|
||||
ymax := -int(b.Min.Y-63) >> 6
|
||||
a.maxw = xmax - xmin
|
||||
a.maxh = ymax - ymin
|
||||
a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.glyphCache)))
|
||||
a.r.SetBounds(a.maxw, a.maxh)
|
||||
a.p = facePainter{a}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
type face struct {
|
||||
f *Font
|
||||
hinting font.Hinting
|
||||
scale fixed.Int26_6
|
||||
subPixelX uint32
|
||||
subPixelBiasX fixed.Int26_6
|
||||
subPixelMaskX fixed.Int26_6
|
||||
subPixelY uint32
|
||||
subPixelBiasY fixed.Int26_6
|
||||
subPixelMaskY fixed.Int26_6
|
||||
masks *image.Alpha
|
||||
glyphCache []glyphCacheEntry
|
||||
r raster.Rasterizer
|
||||
p raster.Painter
|
||||
paintOffset int
|
||||
maxw int
|
||||
maxh int
|
||||
glyphBuf GlyphBuf
|
||||
indexCache [indexCacheLen]indexCacheEntry
|
||||
|
||||
// TODO: clip rectangle?
|
||||
}
|
||||
|
||||
const indexCacheLen = 256
|
||||
|
||||
func (a *face) index(r rune) Index {
|
||||
const mask = indexCacheLen - 1
|
||||
c := &a.indexCache[r&mask]
|
||||
if c.rune == r {
|
||||
return c.index
|
||||
}
|
||||
i := a.f.Index(r)
|
||||
c.rune = r
|
||||
c.index = i
|
||||
return i
|
||||
}
|
||||
|
||||
// Close satisfies the font.Face interface.
|
||||
func (a *face) Close() error { return nil }
|
||||
|
||||
// Metrics satisfies the font.Face interface.
|
||||
func (a *face) Metrics() font.Metrics {
|
||||
scale := float64(a.scale)
|
||||
fupe := float64(a.f.FUnitsPerEm())
|
||||
return font.Metrics{
|
||||
Height: a.scale,
|
||||
Ascent: fixed.Int26_6(math.Ceil(scale * float64(+a.f.ascent) / fupe)),
|
||||
Descent: fixed.Int26_6(math.Ceil(scale * float64(-a.f.descent) / fupe)),
|
||||
}
|
||||
}
|
||||
|
||||
// Kern satisfies the font.Face interface.
|
||||
func (a *face) Kern(r0, r1 rune) fixed.Int26_6 {
|
||||
i0 := a.index(r0)
|
||||
i1 := a.index(r1)
|
||||
kern := a.f.Kern(a.scale, i0, i1)
|
||||
if a.hinting != font.HintingNone {
|
||||
kern = (kern + 32) &^ 63
|
||||
}
|
||||
return kern
|
||||
}
|
||||
|
||||
// Glyph satisfies the font.Face interface.
|
||||
func (a *face) Glyph(dot fixed.Point26_6, r rune) (
|
||||
dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
|
||||
|
||||
// Quantize to the sub-pixel granularity.
|
||||
dotX := (dot.X + a.subPixelBiasX) & a.subPixelMaskX
|
||||
dotY := (dot.Y + a.subPixelBiasY) & a.subPixelMaskY
|
||||
|
||||
// Split the coordinates into their integer and fractional parts.
|
||||
ix, fx := int(dotX>>6), dotX&0x3f
|
||||
iy, fy := int(dotY>>6), dotY&0x3f
|
||||
|
||||
index := a.index(r)
|
||||
cIndex := uint32(index)
|
||||
cIndex = cIndex*a.subPixelX - uint32(fx/a.subPixelMaskX)
|
||||
cIndex = cIndex*a.subPixelY - uint32(fy/a.subPixelMaskY)
|
||||
cIndex &= uint32(len(a.glyphCache) - 1)
|
||||
a.paintOffset = a.maxh * int(cIndex)
|
||||
k := glyphCacheKey{
|
||||
index: index,
|
||||
fx: uint8(fx),
|
||||
fy: uint8(fy),
|
||||
}
|
||||
var v glyphCacheVal
|
||||
if a.glyphCache[cIndex].key != k {
|
||||
var ok bool
|
||||
v, ok = a.rasterize(index, fx, fy)
|
||||
if !ok {
|
||||
return image.Rectangle{}, nil, image.Point{}, 0, false
|
||||
}
|
||||
a.glyphCache[cIndex] = glyphCacheEntry{k, v}
|
||||
} else {
|
||||
v = a.glyphCache[cIndex].val
|
||||
}
|
||||
|
||||
dr.Min = image.Point{
|
||||
X: ix + v.offset.X,
|
||||
Y: iy + v.offset.Y,
|
||||
}
|
||||
dr.Max = image.Point{
|
||||
X: dr.Min.X + v.gw,
|
||||
Y: dr.Min.Y + v.gh,
|
||||
}
|
||||
return dr, a.masks, image.Point{Y: a.paintOffset}, v.advanceWidth, true
|
||||
}
|
||||
|
||||
func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
|
||||
if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil {
|
||||
return fixed.Rectangle26_6{}, 0, false
|
||||
}
|
||||
xmin := +a.glyphBuf.Bounds.Min.X
|
||||
ymin := -a.glyphBuf.Bounds.Max.Y
|
||||
xmax := +a.glyphBuf.Bounds.Max.X
|
||||
ymax := -a.glyphBuf.Bounds.Min.Y
|
||||
if xmin > xmax || ymin > ymax {
|
||||
return fixed.Rectangle26_6{}, 0, false
|
||||
}
|
||||
return fixed.Rectangle26_6{
|
||||
Min: fixed.Point26_6{
|
||||
X: xmin,
|
||||
Y: ymin,
|
||||
},
|
||||
Max: fixed.Point26_6{
|
||||
X: xmax,
|
||||
Y: ymax,
|
||||
},
|
||||
}, a.glyphBuf.AdvanceWidth, true
|
||||
}
|
||||
|
||||
func (a *face) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
|
||||
if err := a.glyphBuf.Load(a.f, a.scale, a.index(r), a.hinting); err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return a.glyphBuf.AdvanceWidth, true
|
||||
}
|
||||
|
||||
// rasterize returns the advance width, integer-pixel offset to render at, and
|
||||
// the width and height of the given glyph at the given sub-pixel offsets.
|
||||
//
|
||||
// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
|
||||
func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v glyphCacheVal, ok bool) {
|
||||
if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil {
|
||||
return glyphCacheVal{}, false
|
||||
}
|
||||
// Calculate the integer-pixel bounds for the glyph.
|
||||
xmin := int(fx+a.glyphBuf.Bounds.Min.X) >> 6
|
||||
ymin := int(fy-a.glyphBuf.Bounds.Max.Y) >> 6
|
||||
xmax := int(fx+a.glyphBuf.Bounds.Max.X+0x3f) >> 6
|
||||
ymax := int(fy-a.glyphBuf.Bounds.Min.Y+0x3f) >> 6
|
||||
if xmin > xmax || ymin > ymax {
|
||||
return glyphCacheVal{}, false
|
||||
}
|
||||
// A TrueType's glyph's nodes can have negative co-ordinates, but the
|
||||
// rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
|
||||
// the pixel offsets, based on the font's FUnit metrics, that let a
|
||||
// negative co-ordinate in TrueType space be non-negative in rasterizer
|
||||
// space. xmin and ymin are typically <= 0.
|
||||
fx -= fixed.Int26_6(xmin << 6)
|
||||
fy -= fixed.Int26_6(ymin << 6)
|
||||
// Rasterize the glyph's vectors.
|
||||
a.r.Clear()
|
||||
pixOffset := a.paintOffset * a.maxw
|
||||
clear(a.masks.Pix[pixOffset : pixOffset+a.maxw*a.maxh])
|
||||
e0 := 0
|
||||
for _, e1 := range a.glyphBuf.Ends {
|
||||
a.drawContour(a.glyphBuf.Points[e0:e1], fx, fy)
|
||||
e0 = e1
|
||||
}
|
||||
a.r.Rasterize(a.p)
|
||||
return glyphCacheVal{
|
||||
a.glyphBuf.AdvanceWidth,
|
||||
image.Point{xmin, ymin},
|
||||
xmax - xmin,
|
||||
ymax - ymin,
|
||||
}, true
|
||||
}
|
||||
|
||||
func clear(pix []byte) {
|
||||
for i := range pix {
|
||||
pix[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// drawContour draws the given closed contour with the given offset.
|
||||
func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) {
|
||||
if len(ps) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// The low bit of each point's Flags value is whether the point is on the
|
||||
// curve. Truetype fonts only have quadratic Bézier curves, not cubics.
|
||||
// Thus, two consecutive off-curve points imply an on-curve point in the
|
||||
// middle of those two.
|
||||
//
|
||||
// See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details.
|
||||
|
||||
// ps[0] is a truetype.Point measured in FUnits and positive Y going
|
||||
// upwards. start is the same thing measured in fixed point units and
|
||||
// positive Y going downwards, and offset by (dx, dy).
|
||||
start := fixed.Point26_6{
|
||||
X: dx + ps[0].X,
|
||||
Y: dy - ps[0].Y,
|
||||
}
|
||||
var others []Point
|
||||
if ps[0].Flags&0x01 != 0 {
|
||||
others = ps[1:]
|
||||
} else {
|
||||
last := fixed.Point26_6{
|
||||
X: dx + ps[len(ps)-1].X,
|
||||
Y: dy - ps[len(ps)-1].Y,
|
||||
}
|
||||
if ps[len(ps)-1].Flags&0x01 != 0 {
|
||||
start = last
|
||||
others = ps[:len(ps)-1]
|
||||
} else {
|
||||
start = fixed.Point26_6{
|
||||
X: (start.X + last.X) / 2,
|
||||
Y: (start.Y + last.Y) / 2,
|
||||
}
|
||||
others = ps
|
||||
}
|
||||
}
|
||||
a.r.Start(start)
|
||||
q0, on0 := start, true
|
||||
for _, p := range others {
|
||||
q := fixed.Point26_6{
|
||||
X: dx + p.X,
|
||||
Y: dy - p.Y,
|
||||
}
|
||||
on := p.Flags&0x01 != 0
|
||||
if on {
|
||||
if on0 {
|
||||
a.r.Add1(q)
|
||||
} else {
|
||||
a.r.Add2(q0, q)
|
||||
}
|
||||
} else {
|
||||
if on0 {
|
||||
// No-op.
|
||||
} else {
|
||||
mid := fixed.Point26_6{
|
||||
X: (q0.X + q.X) / 2,
|
||||
Y: (q0.Y + q.Y) / 2,
|
||||
}
|
||||
a.r.Add2(q0, mid)
|
||||
}
|
||||
}
|
||||
q0, on0 = q, on
|
||||
}
|
||||
// Close the curve.
|
||||
if on0 {
|
||||
a.r.Add1(start)
|
||||
} else {
|
||||
a.r.Add2(q0, start)
|
||||
}
|
||||
}
|
||||
|
||||
// facePainter is like a raster.AlphaSrcPainter, with an additional Y offset
|
||||
// (face.paintOffset) to the painted spans.
|
||||
type facePainter struct {
|
||||
a *face
|
||||
}
|
||||
|
||||
func (p facePainter) Paint(ss []raster.Span, done bool) {
|
||||
m := p.a.masks
|
||||
b := m.Bounds()
|
||||
b.Min.Y = p.a.paintOffset
|
||||
b.Max.Y = p.a.paintOffset + p.a.maxh
|
||||
for _, s := range ss {
|
||||
s.Y += p.a.paintOffset
|
||||
if s.Y < b.Min.Y {
|
||||
continue
|
||||
}
|
||||
if s.Y >= b.Max.Y {
|
||||
return
|
||||
}
|
||||
if s.X0 < b.Min.X {
|
||||
s.X0 = b.Min.X
|
||||
}
|
||||
if s.X1 > b.Max.X {
|
||||
s.X1 = b.Max.X
|
||||
}
|
||||
if s.X0 >= s.X1 {
|
||||
continue
|
||||
}
|
||||
base := (s.Y-m.Rect.Min.Y)*m.Stride - m.Rect.Min.X
|
||||
p := m.Pix[base+s.X0 : base+s.X1]
|
||||
color := uint8(s.Alpha >> 8)
|
||||
for i := range p {
|
||||
p[i] = color
|
||||
}
|
||||
}
|
||||
}
|
||||
522
vendor/github.com/golang/freetype/truetype/glyph.go
generated
vendored
Normal file
522
vendor/github.com/golang/freetype/truetype/glyph.go
generated
vendored
Normal file
@@ -0,0 +1,522 @@
|
||||
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by your choice of either the
|
||||
// FreeType License or the GNU General Public License version 2 (or
|
||||
// any later version), both of which can be found in the LICENSE file.
|
||||
|
||||
package truetype
|
||||
|
||||
import (
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// TODO: implement VerticalHinting.
|
||||
|
||||
// A Point is a co-ordinate pair plus whether it is 'on' a contour or an 'off'
|
||||
// control point.
|
||||
type Point struct {
|
||||
X, Y fixed.Int26_6
|
||||
// The Flags' LSB means whether or not this Point is 'on' the contour.
|
||||
// Other bits are reserved for internal use.
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
|
||||
// series of glyphs from a Font.
|
||||
type GlyphBuf struct {
|
||||
// AdvanceWidth is the glyph's advance width.
|
||||
AdvanceWidth fixed.Int26_6
|
||||
// Bounds is the glyph's bounding box.
|
||||
Bounds fixed.Rectangle26_6
|
||||
// Points contains all Points from all contours of the glyph. If hinting
|
||||
// was used to load a glyph then Unhinted contains those Points before they
|
||||
// were hinted, and InFontUnits contains those Points before they were
|
||||
// hinted and scaled.
|
||||
Points, Unhinted, InFontUnits []Point
|
||||
// Ends is the point indexes of the end point of each contour. The length
|
||||
// of Ends is the number of contours in the glyph. The i'th contour
|
||||
// consists of points Points[Ends[i-1]:Ends[i]], where Ends[-1] is
|
||||
// interpreted to mean zero.
|
||||
Ends []int
|
||||
|
||||
font *Font
|
||||
scale fixed.Int26_6
|
||||
hinting font.Hinting
|
||||
hinter hinter
|
||||
// phantomPoints are the co-ordinates of the synthetic phantom points
|
||||
// used for hinting and bounding box calculations.
|
||||
phantomPoints [4]Point
|
||||
// pp1x is the X co-ordinate of the first phantom point. The '1' is
|
||||
// using 1-based indexing; pp1x is almost always phantomPoints[0].X.
|
||||
// TODO: eliminate this and consistently use phantomPoints[0].X.
|
||||
pp1x fixed.Int26_6
|
||||
// metricsSet is whether the glyph's metrics have been set yet. For a
|
||||
// compound glyph, a sub-glyph may override the outer glyph's metrics.
|
||||
metricsSet bool
|
||||
// tmp is a scratch buffer.
|
||||
tmp []Point
|
||||
}
|
||||
|
||||
// Flags for decoding a glyph's contours. These flags are documented at
|
||||
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
|
||||
const (
|
||||
flagOnCurve = 1 << iota
|
||||
flagXShortVector
|
||||
flagYShortVector
|
||||
flagRepeat
|
||||
flagPositiveXShortVector
|
||||
flagPositiveYShortVector
|
||||
|
||||
// The remaining flags are for internal use.
|
||||
flagTouchedX
|
||||
flagTouchedY
|
||||
)
|
||||
|
||||
// The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
|
||||
// dependent on the value of the flag{X,Y}ShortVector bits.
|
||||
const (
|
||||
flagThisXIsSame = flagPositiveXShortVector
|
||||
flagThisYIsSame = flagPositiveYShortVector
|
||||
)
|
||||
|
||||
// Load loads a glyph's contours from a Font, overwriting any previously loaded
|
||||
// contours for this GlyphBuf. scale is the number of 26.6 fixed point units in
|
||||
// 1 em, i is the glyph index, and h is the hinting policy.
|
||||
func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) error {
|
||||
g.Points = g.Points[:0]
|
||||
g.Unhinted = g.Unhinted[:0]
|
||||
g.InFontUnits = g.InFontUnits[:0]
|
||||
g.Ends = g.Ends[:0]
|
||||
g.font = f
|
||||
g.hinting = h
|
||||
g.scale = scale
|
||||
g.pp1x = 0
|
||||
g.phantomPoints = [4]Point{}
|
||||
g.metricsSet = false
|
||||
|
||||
if h != font.HintingNone {
|
||||
if err := g.hinter.init(f, scale); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := g.load(0, i, true); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
|
||||
// and should be cleaned up once we have all the testScaling tests passing,
|
||||
// plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
|
||||
pp1x := g.pp1x
|
||||
if h != font.HintingNone {
|
||||
pp1x = g.phantomPoints[0].X
|
||||
}
|
||||
if pp1x != 0 {
|
||||
for i := range g.Points {
|
||||
g.Points[i].X -= pp1x
|
||||
}
|
||||
}
|
||||
|
||||
advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
|
||||
if h != font.HintingNone {
|
||||
if len(f.hdmx) >= 8 {
|
||||
if n := u32(f.hdmx, 4); n > 3+uint32(i) {
|
||||
for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
|
||||
if fixed.Int26_6(hdmx[0]) == scale>>6 {
|
||||
advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
advanceWidth = (advanceWidth + 32) &^ 63
|
||||
}
|
||||
g.AdvanceWidth = advanceWidth
|
||||
|
||||
// Set g.Bounds to the 'control box', which is the bounding box of the
|
||||
// Bézier curves' control points. This is easier to calculate, no smaller
|
||||
// than and often equal to the tightest possible bounding box of the curves
|
||||
// themselves. This approach is what C Freetype does. We can't just scale
|
||||
// the nominal bounding box in the glyf data as the hinting process and
|
||||
// phantom point adjustment may move points outside of that box.
|
||||
if len(g.Points) == 0 {
|
||||
g.Bounds = fixed.Rectangle26_6{}
|
||||
} else {
|
||||
p := g.Points[0]
|
||||
g.Bounds.Min.X = p.X
|
||||
g.Bounds.Max.X = p.X
|
||||
g.Bounds.Min.Y = p.Y
|
||||
g.Bounds.Max.Y = p.Y
|
||||
for _, p := range g.Points[1:] {
|
||||
if g.Bounds.Min.X > p.X {
|
||||
g.Bounds.Min.X = p.X
|
||||
} else if g.Bounds.Max.X < p.X {
|
||||
g.Bounds.Max.X = p.X
|
||||
}
|
||||
if g.Bounds.Min.Y > p.Y {
|
||||
g.Bounds.Min.Y = p.Y
|
||||
} else if g.Bounds.Max.Y < p.Y {
|
||||
g.Bounds.Max.Y = p.Y
|
||||
}
|
||||
}
|
||||
// Snap the box to the grid, if hinting is on.
|
||||
if h != font.HintingNone {
|
||||
g.Bounds.Min.X &^= 63
|
||||
g.Bounds.Min.Y &^= 63
|
||||
g.Bounds.Max.X += 63
|
||||
g.Bounds.Max.X &^= 63
|
||||
g.Bounds.Max.Y += 63
|
||||
g.Bounds.Max.Y &^= 63
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) {
|
||||
// The recursion limit here is arbitrary, but defends against malformed glyphs.
|
||||
if recursion >= 32 {
|
||||
return UnsupportedError("excessive compound glyph recursion")
|
||||
}
|
||||
// Find the relevant slice of g.font.glyf.
|
||||
var g0, g1 uint32
|
||||
if g.font.locaOffsetFormat == locaOffsetFormatShort {
|
||||
g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
|
||||
g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
|
||||
} else {
|
||||
g0 = u32(g.font.loca, 4*int(i))
|
||||
g1 = u32(g.font.loca, 4*int(i)+4)
|
||||
}
|
||||
|
||||
// Decode the contour count and nominal bounding box, from the first
|
||||
// 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
|
||||
// and 6, are unused.
|
||||
glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0)
|
||||
if g0+10 <= g1 {
|
||||
glyf = g.font.glyf[g0:g1]
|
||||
ne = int(int16(u16(glyf, 0)))
|
||||
boundsXMin = fixed.Int26_6(int16(u16(glyf, 2)))
|
||||
boundsYMax = fixed.Int26_6(int16(u16(glyf, 8)))
|
||||
}
|
||||
|
||||
// Create the phantom points.
|
||||
uhm, pp1x := g.font.unscaledHMetric(i), fixed.Int26_6(0)
|
||||
uvm := g.font.unscaledVMetric(i, boundsYMax)
|
||||
g.phantomPoints = [4]Point{
|
||||
{X: boundsXMin - uhm.LeftSideBearing},
|
||||
{X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
|
||||
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
|
||||
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
|
||||
}
|
||||
if len(glyf) == 0 {
|
||||
g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true)
|
||||
copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
|
||||
g.Points = g.Points[:len(g.Points)-4]
|
||||
// TODO: also trim g.InFontUnits and g.Unhinted?
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load and hint the contours.
|
||||
if ne < 0 {
|
||||
if ne != -1 {
|
||||
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
|
||||
// "the values -2, -3, and so forth, are reserved for future use."
|
||||
return UnsupportedError("negative number of contours")
|
||||
}
|
||||
pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
|
||||
if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
np0, ne0 := len(g.Points), len(g.Ends)
|
||||
program := g.loadSimple(glyf, ne)
|
||||
g.addPhantomsAndScale(np0, np0, true, true)
|
||||
pp1x = g.Points[len(g.Points)-4].X
|
||||
if g.hinting != font.HintingNone {
|
||||
if len(program) != 0 {
|
||||
err := g.hinter.run(
|
||||
program,
|
||||
g.Points[np0:],
|
||||
g.Unhinted[np0:],
|
||||
g.InFontUnits[np0:],
|
||||
g.Ends[ne0:],
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Drop the four phantom points.
|
||||
g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
|
||||
g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
|
||||
}
|
||||
if useMyMetrics {
|
||||
copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
|
||||
}
|
||||
g.Points = g.Points[:len(g.Points)-4]
|
||||
if np0 != 0 {
|
||||
// The hinting program expects the []Ends values to be indexed
|
||||
// relative to the inner glyph, not the outer glyph, so we delay
|
||||
// adding np0 until after the hinting program (if any) has run.
|
||||
for i := ne0; i < len(g.Ends); i++ {
|
||||
g.Ends[i] += np0
|
||||
}
|
||||
}
|
||||
}
|
||||
if useMyMetrics && !g.metricsSet {
|
||||
g.metricsSet = true
|
||||
g.pp1x = pp1x
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadOffset is the initial offset for loadSimple and loadCompound. The first
|
||||
// 10 bytes are the number of contours and the bounding box.
|
||||
const loadOffset = 10
|
||||
|
||||
func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
|
||||
offset := loadOffset
|
||||
for i := 0; i < ne; i++ {
|
||||
g.Ends = append(g.Ends, 1+int(u16(glyf, offset)))
|
||||
offset += 2
|
||||
}
|
||||
|
||||
// Note the TrueType hinting instructions.
|
||||
instrLen := int(u16(glyf, offset))
|
||||
offset += 2
|
||||
program = glyf[offset : offset+instrLen]
|
||||
offset += instrLen
|
||||
|
||||
if ne == 0 {
|
||||
return program
|
||||
}
|
||||
|
||||
np0 := len(g.Points)
|
||||
np1 := np0 + int(g.Ends[len(g.Ends)-1])
|
||||
|
||||
// Decode the flags.
|
||||
for i := np0; i < np1; {
|
||||
c := uint32(glyf[offset])
|
||||
offset++
|
||||
g.Points = append(g.Points, Point{Flags: c})
|
||||
i++
|
||||
if c&flagRepeat != 0 {
|
||||
count := glyf[offset]
|
||||
offset++
|
||||
for ; count > 0; count-- {
|
||||
g.Points = append(g.Points, Point{Flags: c})
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the co-ordinates.
|
||||
var x int16
|
||||
for i := np0; i < np1; i++ {
|
||||
f := g.Points[i].Flags
|
||||
if f&flagXShortVector != 0 {
|
||||
dx := int16(glyf[offset])
|
||||
offset++
|
||||
if f&flagPositiveXShortVector == 0 {
|
||||
x -= dx
|
||||
} else {
|
||||
x += dx
|
||||
}
|
||||
} else if f&flagThisXIsSame == 0 {
|
||||
x += int16(u16(glyf, offset))
|
||||
offset += 2
|
||||
}
|
||||
g.Points[i].X = fixed.Int26_6(x)
|
||||
}
|
||||
var y int16
|
||||
for i := np0; i < np1; i++ {
|
||||
f := g.Points[i].Flags
|
||||
if f&flagYShortVector != 0 {
|
||||
dy := int16(glyf[offset])
|
||||
offset++
|
||||
if f&flagPositiveYShortVector == 0 {
|
||||
y -= dy
|
||||
} else {
|
||||
y += dy
|
||||
}
|
||||
} else if f&flagThisYIsSame == 0 {
|
||||
y += int16(u16(glyf, offset))
|
||||
offset += 2
|
||||
}
|
||||
g.Points[i].Y = fixed.Int26_6(y)
|
||||
}
|
||||
|
||||
return program
|
||||
}
|
||||
|
||||
func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index,
|
||||
glyf []byte, useMyMetrics bool) error {
|
||||
|
||||
// Flags for decoding a compound glyph. These flags are documented at
|
||||
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
|
||||
const (
|
||||
flagArg1And2AreWords = 1 << iota
|
||||
flagArgsAreXYValues
|
||||
flagRoundXYToGrid
|
||||
flagWeHaveAScale
|
||||
flagUnused
|
||||
flagMoreComponents
|
||||
flagWeHaveAnXAndYScale
|
||||
flagWeHaveATwoByTwo
|
||||
flagWeHaveInstructions
|
||||
flagUseMyMetrics
|
||||
flagOverlapCompound
|
||||
)
|
||||
np0, ne0 := len(g.Points), len(g.Ends)
|
||||
offset := loadOffset
|
||||
for {
|
||||
flags := u16(glyf, offset)
|
||||
component := Index(u16(glyf, offset+2))
|
||||
dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false
|
||||
if flags&flagArg1And2AreWords != 0 {
|
||||
dx = fixed.Int26_6(int16(u16(glyf, offset+4)))
|
||||
dy = fixed.Int26_6(int16(u16(glyf, offset+6)))
|
||||
offset += 8
|
||||
} else {
|
||||
dx = fixed.Int26_6(int16(int8(glyf[offset+4])))
|
||||
dy = fixed.Int26_6(int16(int8(glyf[offset+5])))
|
||||
offset += 6
|
||||
}
|
||||
if flags&flagArgsAreXYValues == 0 {
|
||||
return UnsupportedError("compound glyph transform vector")
|
||||
}
|
||||
if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
|
||||
hasTransform = true
|
||||
switch {
|
||||
case flags&flagWeHaveAScale != 0:
|
||||
transform[0] = int16(u16(glyf, offset+0))
|
||||
transform[3] = transform[0]
|
||||
offset += 2
|
||||
case flags&flagWeHaveAnXAndYScale != 0:
|
||||
transform[0] = int16(u16(glyf, offset+0))
|
||||
transform[3] = int16(u16(glyf, offset+2))
|
||||
offset += 4
|
||||
case flags&flagWeHaveATwoByTwo != 0:
|
||||
transform[0] = int16(u16(glyf, offset+0))
|
||||
transform[1] = int16(u16(glyf, offset+2))
|
||||
transform[2] = int16(u16(glyf, offset+4))
|
||||
transform[3] = int16(u16(glyf, offset+6))
|
||||
offset += 8
|
||||
}
|
||||
}
|
||||
savedPP := g.phantomPoints
|
||||
np0 := len(g.Points)
|
||||
componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
|
||||
if err := g.load(recursion+1, component, componentUMM); err != nil {
|
||||
return err
|
||||
}
|
||||
if flags&flagUseMyMetrics == 0 {
|
||||
g.phantomPoints = savedPP
|
||||
}
|
||||
if hasTransform {
|
||||
for j := np0; j < len(g.Points); j++ {
|
||||
p := &g.Points[j]
|
||||
newX := 0 +
|
||||
fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) +
|
||||
fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14)
|
||||
newY := 0 +
|
||||
fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) +
|
||||
fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14)
|
||||
p.X, p.Y = newX, newY
|
||||
}
|
||||
}
|
||||
dx = g.font.scale(g.scale * dx)
|
||||
dy = g.font.scale(g.scale * dy)
|
||||
if flags&flagRoundXYToGrid != 0 {
|
||||
dx = (dx + 32) &^ 63
|
||||
dy = (dy + 32) &^ 63
|
||||
}
|
||||
for j := np0; j < len(g.Points); j++ {
|
||||
p := &g.Points[j]
|
||||
p.X += dx
|
||||
p.Y += dy
|
||||
}
|
||||
// TODO: also adjust g.InFontUnits and g.Unhinted?
|
||||
if flags&flagMoreComponents == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
instrLen := 0
|
||||
if g.hinting != font.HintingNone && offset+2 <= len(glyf) {
|
||||
instrLen = int(u16(glyf, offset))
|
||||
offset += 2
|
||||
}
|
||||
|
||||
g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0)
|
||||
points, ends := g.Points[np0:], g.Ends[ne0:]
|
||||
g.Points = g.Points[:len(g.Points)-4]
|
||||
for j := range points {
|
||||
points[j].Flags &^= flagTouchedX | flagTouchedY
|
||||
}
|
||||
|
||||
if instrLen == 0 {
|
||||
if !g.metricsSet {
|
||||
copy(g.phantomPoints[:], points[len(points)-4:])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hint the compound glyph.
|
||||
program := glyf[offset : offset+instrLen]
|
||||
// Temporarily adjust the ends to be relative to this compound glyph.
|
||||
if np0 != 0 {
|
||||
for i := range ends {
|
||||
ends[i] -= np0
|
||||
}
|
||||
}
|
||||
// Hinting instructions of a composite glyph completely refer to the
|
||||
// (already) hinted subglyphs.
|
||||
g.tmp = append(g.tmp[:0], points...)
|
||||
if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
|
||||
return err
|
||||
}
|
||||
if np0 != 0 {
|
||||
for i := range ends {
|
||||
ends[i] += np0
|
||||
}
|
||||
}
|
||||
if !g.metricsSet {
|
||||
copy(g.phantomPoints[:], points[len(points)-4:])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) {
|
||||
// Add the four phantom points.
|
||||
g.Points = append(g.Points, g.phantomPoints[:]...)
|
||||
// Scale the points.
|
||||
if simple && g.hinting != font.HintingNone {
|
||||
g.InFontUnits = append(g.InFontUnits, g.Points[np1:]...)
|
||||
}
|
||||
for i := np1; i < len(g.Points); i++ {
|
||||
p := &g.Points[i]
|
||||
p.X = g.font.scale(g.scale * p.X)
|
||||
p.Y = g.font.scale(g.scale * p.Y)
|
||||
}
|
||||
if g.hinting == font.HintingNone {
|
||||
return
|
||||
}
|
||||
// Round the 1st phantom point to the grid, shifting all other points equally.
|
||||
// Note that "all other points" starts from np0, not np1.
|
||||
// TODO: delete this adjustment and the np0/np1 distinction, when
|
||||
// we update the compatibility tests to C Freetype 2.5.3.
|
||||
// See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
|
||||
if adjust {
|
||||
pp1x := g.Points[len(g.Points)-4].X
|
||||
if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
|
||||
for i := np0; i < len(g.Points); i++ {
|
||||
g.Points[i].X += dx
|
||||
}
|
||||
}
|
||||
}
|
||||
if simple {
|
||||
g.Unhinted = append(g.Unhinted, g.Points[np1:]...)
|
||||
}
|
||||
// Round the 2nd and 4th phantom point to the grid.
|
||||
p := &g.Points[len(g.Points)-3]
|
||||
p.X = (p.X + 32) &^ 63
|
||||
p = &g.Points[len(g.Points)-1]
|
||||
p.Y = (p.Y + 32) &^ 63
|
||||
}
|
||||
1770
vendor/github.com/golang/freetype/truetype/hint.go
generated
vendored
Normal file
1770
vendor/github.com/golang/freetype/truetype/hint.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
289
vendor/github.com/golang/freetype/truetype/opcodes.go
generated
vendored
Normal file
289
vendor/github.com/golang/freetype/truetype/opcodes.go
generated
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
// Copyright 2012 The Freetype-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by your choice of either the
|
||||
// FreeType License or the GNU General Public License version 2 (or
|
||||
// any later version), both of which can be found in the LICENSE file.
|
||||
|
||||
package truetype
|
||||
|
||||
// The Truetype opcodes are summarized at
|
||||
// https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html
|
||||
|
||||
const (
|
||||
opSVTCA0 = 0x00 // Set freedom and projection Vectors To Coordinate Axis
|
||||
opSVTCA1 = 0x01 // .
|
||||
opSPVTCA0 = 0x02 // Set Projection Vector To Coordinate Axis
|
||||
opSPVTCA1 = 0x03 // .
|
||||
opSFVTCA0 = 0x04 // Set Freedom Vector to Coordinate Axis
|
||||
opSFVTCA1 = 0x05 // .
|
||||
opSPVTL0 = 0x06 // Set Projection Vector To Line
|
||||
opSPVTL1 = 0x07 // .
|
||||
opSFVTL0 = 0x08 // Set Freedom Vector To Line
|
||||
opSFVTL1 = 0x09 // .
|
||||
opSPVFS = 0x0a // Set Projection Vector From Stack
|
||||
opSFVFS = 0x0b // Set Freedom Vector From Stack
|
||||
opGPV = 0x0c // Get Projection Vector
|
||||
opGFV = 0x0d // Get Freedom Vector
|
||||
opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector
|
||||
opISECT = 0x0f // moves point p to the InterSECTion of two lines
|
||||
opSRP0 = 0x10 // Set Reference Point 0
|
||||
opSRP1 = 0x11 // Set Reference Point 1
|
||||
opSRP2 = 0x12 // Set Reference Point 2
|
||||
opSZP0 = 0x13 // Set Zone Pointer 0
|
||||
opSZP1 = 0x14 // Set Zone Pointer 1
|
||||
opSZP2 = 0x15 // Set Zone Pointer 2
|
||||
opSZPS = 0x16 // Set Zone PointerS
|
||||
opSLOOP = 0x17 // Set LOOP variable
|
||||
opRTG = 0x18 // Round To Grid
|
||||
opRTHG = 0x19 // Round To Half Grid
|
||||
opSMD = 0x1a // Set Minimum Distance
|
||||
opELSE = 0x1b // ELSE clause
|
||||
opJMPR = 0x1c // JuMP Relative
|
||||
opSCVTCI = 0x1d // Set Control Value Table Cut-In
|
||||
opSSWCI = 0x1e // Set Single Width Cut-In
|
||||
opSSW = 0x1f // Set Single Width
|
||||
opDUP = 0x20 // DUPlicate top stack element
|
||||
opPOP = 0x21 // POP top stack element
|
||||
opCLEAR = 0x22 // CLEAR the stack
|
||||
opSWAP = 0x23 // SWAP the top two elements on the stack
|
||||
opDEPTH = 0x24 // DEPTH of the stack
|
||||
opCINDEX = 0x25 // Copy the INDEXed element to the top of the stack
|
||||
opMINDEX = 0x26 // Move the INDEXed element to the top of the stack
|
||||
opALIGNPTS = 0x27 // ALIGN PoinTS
|
||||
op_0x28 = 0x28 // deprecated
|
||||
opUTP = 0x29 // UnTouch Point
|
||||
opLOOPCALL = 0x2a // LOOP and CALL function
|
||||
opCALL = 0x2b // CALL function
|
||||
opFDEF = 0x2c // Function DEFinition
|
||||
opENDF = 0x2d // END Function definition
|
||||
opMDAP0 = 0x2e // Move Direct Absolute Point
|
||||
opMDAP1 = 0x2f // .
|
||||
opIUP0 = 0x30 // Interpolate Untouched Points through the outline
|
||||
opIUP1 = 0x31 // .
|
||||
opSHP0 = 0x32 // SHift Point using reference point
|
||||
opSHP1 = 0x33 // .
|
||||
opSHC0 = 0x34 // SHift Contour using reference point
|
||||
opSHC1 = 0x35 // .
|
||||
opSHZ0 = 0x36 // SHift Zone using reference point
|
||||
opSHZ1 = 0x37 // .
|
||||
opSHPIX = 0x38 // SHift point by a PIXel amount
|
||||
opIP = 0x39 // Interpolate Point
|
||||
opMSIRP0 = 0x3a // Move Stack Indirect Relative Point
|
||||
opMSIRP1 = 0x3b // .
|
||||
opALIGNRP = 0x3c // ALIGN to Reference Point
|
||||
opRTDG = 0x3d // Round To Double Grid
|
||||
opMIAP0 = 0x3e // Move Indirect Absolute Point
|
||||
opMIAP1 = 0x3f // .
|
||||
opNPUSHB = 0x40 // PUSH N Bytes
|
||||
opNPUSHW = 0x41 // PUSH N Words
|
||||
opWS = 0x42 // Write Store
|
||||
opRS = 0x43 // Read Store
|
||||
opWCVTP = 0x44 // Write Control Value Table in Pixel units
|
||||
opRCVT = 0x45 // Read Control Value Table entry
|
||||
opGC0 = 0x46 // Get Coordinate projected onto the projection vector
|
||||
opGC1 = 0x47 // .
|
||||
opSCFS = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector
|
||||
opMD0 = 0x49 // Measure Distance
|
||||
opMD1 = 0x4a // .
|
||||
opMPPEM = 0x4b // Measure Pixels Per EM
|
||||
opMPS = 0x4c // Measure Point Size
|
||||
opFLIPON = 0x4d // set the auto FLIP Boolean to ON
|
||||
opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF
|
||||
opDEBUG = 0x4f // DEBUG call
|
||||
opLT = 0x50 // Less Than
|
||||
opLTEQ = 0x51 // Less Than or EQual
|
||||
opGT = 0x52 // Greater Than
|
||||
opGTEQ = 0x53 // Greater Than or EQual
|
||||
opEQ = 0x54 // EQual
|
||||
opNEQ = 0x55 // Not EQual
|
||||
opODD = 0x56 // ODD
|
||||
opEVEN = 0x57 // EVEN
|
||||
opIF = 0x58 // IF test
|
||||
opEIF = 0x59 // End IF
|
||||
opAND = 0x5a // logical AND
|
||||
opOR = 0x5b // logical OR
|
||||
opNOT = 0x5c // logical NOT
|
||||
opDELTAP1 = 0x5d // DELTA exception P1
|
||||
opSDB = 0x5e // Set Delta Base in the graphics state
|
||||
opSDS = 0x5f // Set Delta Shift in the graphics state
|
||||
opADD = 0x60 // ADD
|
||||
opSUB = 0x61 // SUBtract
|
||||
opDIV = 0x62 // DIVide
|
||||
opMUL = 0x63 // MULtiply
|
||||
opABS = 0x64 // ABSolute value
|
||||
opNEG = 0x65 // NEGate
|
||||
opFLOOR = 0x66 // FLOOR
|
||||
opCEILING = 0x67 // CEILING
|
||||
opROUND00 = 0x68 // ROUND value
|
||||
opROUND01 = 0x69 // .
|
||||
opROUND10 = 0x6a // .
|
||||
opROUND11 = 0x6b // .
|
||||
opNROUND00 = 0x6c // No ROUNDing of value
|
||||
opNROUND01 = 0x6d // .
|
||||
opNROUND10 = 0x6e // .
|
||||
opNROUND11 = 0x6f // .
|
||||
opWCVTF = 0x70 // Write Control Value Table in Funits
|
||||
opDELTAP2 = 0x71 // DELTA exception P2
|
||||
opDELTAP3 = 0x72 // DELTA exception P3
|
||||
opDELTAC1 = 0x73 // DELTA exception C1
|
||||
opDELTAC2 = 0x74 // DELTA exception C2
|
||||
opDELTAC3 = 0x75 // DELTA exception C3
|
||||
opSROUND = 0x76 // Super ROUND
|
||||
opS45ROUND = 0x77 // Super ROUND 45 degrees
|
||||
opJROT = 0x78 // Jump Relative On True
|
||||
opJROF = 0x79 // Jump Relative On False
|
||||
opROFF = 0x7a // Round OFF
|
||||
op_0x7b = 0x7b // deprecated
|
||||
opRUTG = 0x7c // Round Up To Grid
|
||||
opRDTG = 0x7d // Round Down To Grid
|
||||
opSANGW = 0x7e // Set ANGle Weight
|
||||
opAA = 0x7f // Adjust Angle
|
||||
opFLIPPT = 0x80 // FLIP PoinT
|
||||
opFLIPRGON = 0x81 // FLIP RanGe ON
|
||||
opFLIPRGOFF = 0x82 // FLIP RanGe OFF
|
||||
op_0x83 = 0x83 // deprecated
|
||||
op_0x84 = 0x84 // deprecated
|
||||
opSCANCTRL = 0x85 // SCAN conversion ConTRoL
|
||||
opSDPVTL0 = 0x86 // Set Dual Projection Vector To Line
|
||||
opSDPVTL1 = 0x87 // .
|
||||
opGETINFO = 0x88 // GET INFOrmation
|
||||
opIDEF = 0x89 // Instruction DEFinition
|
||||
opROLL = 0x8a // ROLL the top three stack elements
|
||||
opMAX = 0x8b // MAXimum of top two stack elements
|
||||
opMIN = 0x8c // MINimum of top two stack elements
|
||||
opSCANTYPE = 0x8d // SCANTYPE
|
||||
opINSTCTRL = 0x8e // INSTRuction execution ConTRoL
|
||||
op_0x8f = 0x8f
|
||||
op_0x90 = 0x90
|
||||
op_0x91 = 0x91
|
||||
op_0x92 = 0x92
|
||||
op_0x93 = 0x93
|
||||
op_0x94 = 0x94
|
||||
op_0x95 = 0x95
|
||||
op_0x96 = 0x96
|
||||
op_0x97 = 0x97
|
||||
op_0x98 = 0x98
|
||||
op_0x99 = 0x99
|
||||
op_0x9a = 0x9a
|
||||
op_0x9b = 0x9b
|
||||
op_0x9c = 0x9c
|
||||
op_0x9d = 0x9d
|
||||
op_0x9e = 0x9e
|
||||
op_0x9f = 0x9f
|
||||
op_0xa0 = 0xa0
|
||||
op_0xa1 = 0xa1
|
||||
op_0xa2 = 0xa2
|
||||
op_0xa3 = 0xa3
|
||||
op_0xa4 = 0xa4
|
||||
op_0xa5 = 0xa5
|
||||
op_0xa6 = 0xa6
|
||||
op_0xa7 = 0xa7
|
||||
op_0xa8 = 0xa8
|
||||
op_0xa9 = 0xa9
|
||||
op_0xaa = 0xaa
|
||||
op_0xab = 0xab
|
||||
op_0xac = 0xac
|
||||
op_0xad = 0xad
|
||||
op_0xae = 0xae
|
||||
op_0xaf = 0xaf
|
||||
opPUSHB000 = 0xb0 // PUSH Bytes
|
||||
opPUSHB001 = 0xb1 // .
|
||||
opPUSHB010 = 0xb2 // .
|
||||
opPUSHB011 = 0xb3 // .
|
||||
opPUSHB100 = 0xb4 // .
|
||||
opPUSHB101 = 0xb5 // .
|
||||
opPUSHB110 = 0xb6 // .
|
||||
opPUSHB111 = 0xb7 // .
|
||||
opPUSHW000 = 0xb8 // PUSH Words
|
||||
opPUSHW001 = 0xb9 // .
|
||||
opPUSHW010 = 0xba // .
|
||||
opPUSHW011 = 0xbb // .
|
||||
opPUSHW100 = 0xbc // .
|
||||
opPUSHW101 = 0xbd // .
|
||||
opPUSHW110 = 0xbe // .
|
||||
opPUSHW111 = 0xbf // .
|
||||
opMDRP00000 = 0xc0 // Move Direct Relative Point
|
||||
opMDRP00001 = 0xc1 // .
|
||||
opMDRP00010 = 0xc2 // .
|
||||
opMDRP00011 = 0xc3 // .
|
||||
opMDRP00100 = 0xc4 // .
|
||||
opMDRP00101 = 0xc5 // .
|
||||
opMDRP00110 = 0xc6 // .
|
||||
opMDRP00111 = 0xc7 // .
|
||||
opMDRP01000 = 0xc8 // .
|
||||
opMDRP01001 = 0xc9 // .
|
||||
opMDRP01010 = 0xca // .
|
||||
opMDRP01011 = 0xcb // .
|
||||
opMDRP01100 = 0xcc // .
|
||||
opMDRP01101 = 0xcd // .
|
||||
opMDRP01110 = 0xce // .
|
||||
opMDRP01111 = 0xcf // .
|
||||
opMDRP10000 = 0xd0 // .
|
||||
opMDRP10001 = 0xd1 // .
|
||||
opMDRP10010 = 0xd2 // .
|
||||
opMDRP10011 = 0xd3 // .
|
||||
opMDRP10100 = 0xd4 // .
|
||||
opMDRP10101 = 0xd5 // .
|
||||
opMDRP10110 = 0xd6 // .
|
||||
opMDRP10111 = 0xd7 // .
|
||||
opMDRP11000 = 0xd8 // .
|
||||
opMDRP11001 = 0xd9 // .
|
||||
opMDRP11010 = 0xda // .
|
||||
opMDRP11011 = 0xdb // .
|
||||
opMDRP11100 = 0xdc // .
|
||||
opMDRP11101 = 0xdd // .
|
||||
opMDRP11110 = 0xde // .
|
||||
opMDRP11111 = 0xdf // .
|
||||
opMIRP00000 = 0xe0 // Move Indirect Relative Point
|
||||
opMIRP00001 = 0xe1 // .
|
||||
opMIRP00010 = 0xe2 // .
|
||||
opMIRP00011 = 0xe3 // .
|
||||
opMIRP00100 = 0xe4 // .
|
||||
opMIRP00101 = 0xe5 // .
|
||||
opMIRP00110 = 0xe6 // .
|
||||
opMIRP00111 = 0xe7 // .
|
||||
opMIRP01000 = 0xe8 // .
|
||||
opMIRP01001 = 0xe9 // .
|
||||
opMIRP01010 = 0xea // .
|
||||
opMIRP01011 = 0xeb // .
|
||||
opMIRP01100 = 0xec // .
|
||||
opMIRP01101 = 0xed // .
|
||||
opMIRP01110 = 0xee // .
|
||||
opMIRP01111 = 0xef // .
|
||||
opMIRP10000 = 0xf0 // .
|
||||
opMIRP10001 = 0xf1 // .
|
||||
opMIRP10010 = 0xf2 // .
|
||||
opMIRP10011 = 0xf3 // .
|
||||
opMIRP10100 = 0xf4 // .
|
||||
opMIRP10101 = 0xf5 // .
|
||||
opMIRP10110 = 0xf6 // .
|
||||
opMIRP10111 = 0xf7 // .
|
||||
opMIRP11000 = 0xf8 // .
|
||||
opMIRP11001 = 0xf9 // .
|
||||
opMIRP11010 = 0xfa // .
|
||||
opMIRP11011 = 0xfb // .
|
||||
opMIRP11100 = 0xfc // .
|
||||
opMIRP11101 = 0xfd // .
|
||||
opMIRP11110 = 0xfe // .
|
||||
opMIRP11111 = 0xff // .
|
||||
)
|
||||
|
||||
// popCount is the number of stack elements that each opcode pops.
|
||||
var popCount = [256]uint8{
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
|
||||
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 0x00 - 0x0f
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f
|
||||
1, 1, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f
|
||||
0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f
|
||||
0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f
|
||||
2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f
|
||||
2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
|
||||
2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f
|
||||
0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff
|
||||
}
|
||||
653
vendor/github.com/golang/freetype/truetype/truetype.go
generated
vendored
Normal file
653
vendor/github.com/golang/freetype/truetype/truetype.go
generated
vendored
Normal file
@@ -0,0 +1,653 @@
|
||||
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by your choice of either the
|
||||
// FreeType License or the GNU General Public License version 2 (or
|
||||
// any later version), both of which can be found in the LICENSE file.
|
||||
|
||||
// Package truetype provides a parser for the TTF and TTC file formats.
|
||||
// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
|
||||
// and http://www.microsoft.com/typography/otspec/
|
||||
//
|
||||
// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
|
||||
// metrics and control points. All these methods take a scale parameter, which
|
||||
// is the number of pixels in 1 em, expressed as a 26.6 fixed point value. For
|
||||
// example, if 1 em is 10 pixels then scale is fixed.I(10), which is equal to
|
||||
// fixed.Int26_6(10 << 6).
|
||||
//
|
||||
// To measure a TrueType font in ideal FUnit space, use scale equal to
|
||||
// font.FUnitsPerEm().
|
||||
package truetype // import "github.com/golang/freetype/truetype"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// An Index is a Font's index of a rune.
|
||||
type Index uint16
|
||||
|
||||
// A NameID identifies a name table entry.
|
||||
//
|
||||
// See https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
|
||||
type NameID uint16
|
||||
|
||||
const (
|
||||
NameIDCopyright NameID = 0
|
||||
NameIDFontFamily = 1
|
||||
NameIDFontSubfamily = 2
|
||||
NameIDUniqueSubfamilyID = 3
|
||||
NameIDFontFullName = 4
|
||||
NameIDNameTableVersion = 5
|
||||
NameIDPostscriptName = 6
|
||||
NameIDTrademarkNotice = 7
|
||||
NameIDManufacturerName = 8
|
||||
NameIDDesignerName = 9
|
||||
NameIDFontDescription = 10
|
||||
NameIDFontVendorURL = 11
|
||||
NameIDFontDesignerURL = 12
|
||||
NameIDFontLicense = 13
|
||||
NameIDFontLicenseURL = 14
|
||||
NameIDPreferredFamily = 16
|
||||
NameIDPreferredSubfamily = 17
|
||||
NameIDCompatibleName = 18
|
||||
NameIDSampleText = 19
|
||||
)
|
||||
|
||||
const (
|
||||
// A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
|
||||
// least-significant 16-bit Platform Specific ID. The magic numbers are
|
||||
// specified at https://www.microsoft.com/typography/otspec/name.htm
|
||||
unicodeEncodingBMPOnly = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0 BMP Only)
|
||||
unicodeEncodingFull = 0x00000004 // PID = 0 (Unicode), PSID = 4 (Unicode 2.0 Full Repertoire)
|
||||
microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
|
||||
microsoftUCS2Encoding = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
|
||||
microsoftUCS4Encoding = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
|
||||
)
|
||||
|
||||
// An HMetric holds the horizontal metrics of a single glyph.
|
||||
type HMetric struct {
|
||||
AdvanceWidth, LeftSideBearing fixed.Int26_6
|
||||
}
|
||||
|
||||
// A VMetric holds the vertical metrics of a single glyph.
|
||||
type VMetric struct {
|
||||
AdvanceHeight, TopSideBearing fixed.Int26_6
|
||||
}
|
||||
|
||||
// A FormatError reports that the input is not a valid TrueType font.
|
||||
type FormatError string
|
||||
|
||||
func (e FormatError) Error() string {
|
||||
return "freetype: invalid TrueType format: " + string(e)
|
||||
}
|
||||
|
||||
// An UnsupportedError reports that the input uses a valid but unimplemented
|
||||
// TrueType feature.
|
||||
type UnsupportedError string
|
||||
|
||||
func (e UnsupportedError) Error() string {
|
||||
return "freetype: unsupported TrueType feature: " + string(e)
|
||||
}
|
||||
|
||||
// u32 returns the big-endian uint32 at b[i:].
|
||||
func u32(b []byte, i int) uint32 {
|
||||
return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
|
||||
}
|
||||
|
||||
// u16 returns the big-endian uint16 at b[i:].
|
||||
func u16(b []byte, i int) uint16 {
|
||||
return uint16(b[i])<<8 | uint16(b[i+1])
|
||||
}
|
||||
|
||||
// readTable returns a slice of the TTF data given by a table's directory entry.
|
||||
func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
|
||||
offset := int(u32(offsetLength, 0))
|
||||
if offset < 0 {
|
||||
return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
|
||||
}
|
||||
length := int(u32(offsetLength, 4))
|
||||
if length < 0 {
|
||||
return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
|
||||
}
|
||||
end := offset + length
|
||||
if end < 0 || end > len(ttf) {
|
||||
return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
|
||||
}
|
||||
return ttf[offset:end], nil
|
||||
}
|
||||
|
||||
// parseSubtables returns the offset and platformID of the best subtable in
|
||||
// table, where best favors a Unicode cmap encoding, and failing that, a
|
||||
// Microsoft cmap encoding. offset is the offset of the first subtable in
|
||||
// table, and size is the size of each subtable.
|
||||
//
|
||||
// If pred is non-nil, then only subtables that satisfy that predicate will be
|
||||
// considered.
|
||||
func parseSubtables(table []byte, name string, offset, size int, pred func([]byte) bool) (
|
||||
bestOffset int, bestPID uint32, retErr error) {
|
||||
|
||||
if len(table) < 4 {
|
||||
return 0, 0, FormatError(name + " too short")
|
||||
}
|
||||
nSubtables := int(u16(table, 2))
|
||||
if len(table) < size*nSubtables+offset {
|
||||
return 0, 0, FormatError(name + " too short")
|
||||
}
|
||||
ok := false
|
||||
for i := 0; i < nSubtables; i, offset = i+1, offset+size {
|
||||
if pred != nil && !pred(table[offset:]) {
|
||||
continue
|
||||
}
|
||||
// We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
|
||||
// All values are big-endian.
|
||||
pidPsid := u32(table, offset)
|
||||
// We prefer the Unicode cmap encoding. Failing to find that, we fall
|
||||
// back onto the Microsoft cmap encoding.
|
||||
if pidPsid == unicodeEncodingBMPOnly || pidPsid == unicodeEncodingFull {
|
||||
bestOffset, bestPID, ok = offset, pidPsid>>16, true
|
||||
break
|
||||
|
||||
} else if pidPsid == microsoftSymbolEncoding ||
|
||||
pidPsid == microsoftUCS2Encoding ||
|
||||
pidPsid == microsoftUCS4Encoding {
|
||||
|
||||
bestOffset, bestPID, ok = offset, pidPsid>>16, true
|
||||
// We don't break out of the for loop, so that Unicode can override Microsoft.
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return 0, 0, UnsupportedError(name + " encoding")
|
||||
}
|
||||
return bestOffset, bestPID, nil
|
||||
}
|
||||
|
||||
const (
|
||||
locaOffsetFormatUnknown int = iota
|
||||
locaOffsetFormatShort
|
||||
locaOffsetFormatLong
|
||||
)
|
||||
|
||||
// A cm holds a parsed cmap entry.
|
||||
type cm struct {
|
||||
start, end, delta, offset uint32
|
||||
}
|
||||
|
||||
// A Font represents a Truetype font.
|
||||
type Font struct {
|
||||
// Tables sliced from the TTF data. The different tables are documented
|
||||
// at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
|
||||
cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, os2, prep, vmtx []byte
|
||||
|
||||
cmapIndexes []byte
|
||||
|
||||
// Cached values derived from the raw ttf data.
|
||||
cm []cm
|
||||
locaOffsetFormat int
|
||||
nGlyph, nHMetric, nKern int
|
||||
fUnitsPerEm int32
|
||||
ascent int32 // In FUnits.
|
||||
descent int32 // In FUnits; typically negative.
|
||||
bounds fixed.Rectangle26_6 // In FUnits.
|
||||
// Values from the maxp section.
|
||||
maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
|
||||
}
|
||||
|
||||
func (f *Font) parseCmap() error {
|
||||
const (
|
||||
cmapFormat4 = 4
|
||||
cmapFormat12 = 12
|
||||
languageIndependent = 0
|
||||
)
|
||||
|
||||
offset, _, err := parseSubtables(f.cmap, "cmap", 4, 8, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
offset = int(u32(f.cmap, offset+4))
|
||||
if offset <= 0 || offset > len(f.cmap) {
|
||||
return FormatError("bad cmap offset")
|
||||
}
|
||||
|
||||
cmapFormat := u16(f.cmap, offset)
|
||||
switch cmapFormat {
|
||||
case cmapFormat4:
|
||||
language := u16(f.cmap, offset+4)
|
||||
if language != languageIndependent {
|
||||
return UnsupportedError(fmt.Sprintf("language: %d", language))
|
||||
}
|
||||
segCountX2 := int(u16(f.cmap, offset+6))
|
||||
if segCountX2%2 == 1 {
|
||||
return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
|
||||
}
|
||||
segCount := segCountX2 / 2
|
||||
offset += 14
|
||||
f.cm = make([]cm, segCount)
|
||||
for i := 0; i < segCount; i++ {
|
||||
f.cm[i].end = uint32(u16(f.cmap, offset))
|
||||
offset += 2
|
||||
}
|
||||
offset += 2
|
||||
for i := 0; i < segCount; i++ {
|
||||
f.cm[i].start = uint32(u16(f.cmap, offset))
|
||||
offset += 2
|
||||
}
|
||||
for i := 0; i < segCount; i++ {
|
||||
f.cm[i].delta = uint32(u16(f.cmap, offset))
|
||||
offset += 2
|
||||
}
|
||||
for i := 0; i < segCount; i++ {
|
||||
f.cm[i].offset = uint32(u16(f.cmap, offset))
|
||||
offset += 2
|
||||
}
|
||||
f.cmapIndexes = f.cmap[offset:]
|
||||
return nil
|
||||
|
||||
case cmapFormat12:
|
||||
if u16(f.cmap, offset+2) != 0 {
|
||||
return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
|
||||
}
|
||||
length := u32(f.cmap, offset+4)
|
||||
language := u32(f.cmap, offset+8)
|
||||
if language != languageIndependent {
|
||||
return UnsupportedError(fmt.Sprintf("language: %d", language))
|
||||
}
|
||||
nGroups := u32(f.cmap, offset+12)
|
||||
if length != 12*nGroups+16 {
|
||||
return FormatError("inconsistent cmap length")
|
||||
}
|
||||
offset += 16
|
||||
f.cm = make([]cm, nGroups)
|
||||
for i := uint32(0); i < nGroups; i++ {
|
||||
f.cm[i].start = u32(f.cmap, offset+0)
|
||||
f.cm[i].end = u32(f.cmap, offset+4)
|
||||
f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
|
||||
offset += 12
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
|
||||
}
|
||||
|
||||
func (f *Font) parseHead() error {
|
||||
if len(f.head) != 54 {
|
||||
return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
|
||||
}
|
||||
f.fUnitsPerEm = int32(u16(f.head, 18))
|
||||
f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36)))
|
||||
f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38)))
|
||||
f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40)))
|
||||
f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42)))
|
||||
switch i := u16(f.head, 50); i {
|
||||
case 0:
|
||||
f.locaOffsetFormat = locaOffsetFormatShort
|
||||
case 1:
|
||||
f.locaOffsetFormat = locaOffsetFormatLong
|
||||
default:
|
||||
return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Font) parseHhea() error {
|
||||
if len(f.hhea) != 36 {
|
||||
return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
|
||||
}
|
||||
f.ascent = int32(int16(u16(f.hhea, 4)))
|
||||
f.descent = int32(int16(u16(f.hhea, 6)))
|
||||
f.nHMetric = int(u16(f.hhea, 34))
|
||||
if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
|
||||
return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Font) parseKern() error {
|
||||
// Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
|
||||
// "Previous versions of the 'kern' table defined both the version and nTables fields in the header
|
||||
// as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
|
||||
// (although AAT can sense an old kerning table and still make correct use of it). Microsoft
|
||||
// Windows still uses the older format for the 'kern' table and will not recognize the newer one.
|
||||
// Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
|
||||
// and Windows should use the old format."
|
||||
// Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
|
||||
// just like the C Freetype implementation.
|
||||
if len(f.kern) == 0 {
|
||||
if f.nKern != 0 {
|
||||
return FormatError("bad kern table length")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if len(f.kern) < 18 {
|
||||
return FormatError("kern data too short")
|
||||
}
|
||||
version, offset := u16(f.kern, 0), 2
|
||||
if version != 0 {
|
||||
return UnsupportedError(fmt.Sprintf("kern version: %d", version))
|
||||
}
|
||||
|
||||
n, offset := u16(f.kern, offset), offset+2
|
||||
if n == 0 {
|
||||
return UnsupportedError("kern nTables: 0")
|
||||
}
|
||||
// TODO: support multiple subtables. In practice, almost all .ttf files
|
||||
// have only one subtable, if they have a kern table at all. But it's not
|
||||
// impossible. Xolonium Regular (https://fontlibrary.org/en/font/xolonium)
|
||||
// has 3 subtables. Those subtables appear to be disjoint, rather than
|
||||
// being the same kerning pairs encoded in three different ways.
|
||||
//
|
||||
// For now, we'll use only the first subtable.
|
||||
|
||||
offset += 2 // Skip the version.
|
||||
length, offset := int(u16(f.kern, offset)), offset+2
|
||||
coverage, offset := u16(f.kern, offset), offset+2
|
||||
if coverage != 0x0001 {
|
||||
// We only support horizontal kerning.
|
||||
return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
|
||||
}
|
||||
f.nKern, offset = int(u16(f.kern, offset)), offset+2
|
||||
if 6*f.nKern != length-14 {
|
||||
return FormatError("bad kern table length")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Font) parseMaxp() error {
|
||||
if len(f.maxp) != 32 {
|
||||
return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
|
||||
}
|
||||
f.nGlyph = int(u16(f.maxp, 4))
|
||||
f.maxTwilightPoints = u16(f.maxp, 16)
|
||||
f.maxStorage = u16(f.maxp, 18)
|
||||
f.maxFunctionDefs = u16(f.maxp, 20)
|
||||
f.maxStackElements = u16(f.maxp, 24)
|
||||
return nil
|
||||
}
|
||||
|
||||
// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
|
||||
func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
|
||||
if x >= 0 {
|
||||
x += fixed.Int26_6(f.fUnitsPerEm) / 2
|
||||
} else {
|
||||
x -= fixed.Int26_6(f.fUnitsPerEm) / 2
|
||||
}
|
||||
return x / fixed.Int26_6(f.fUnitsPerEm)
|
||||
}
|
||||
|
||||
// Bounds returns the union of a Font's glyphs' bounds.
|
||||
func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 {
|
||||
b := f.bounds
|
||||
b.Min.X = f.scale(scale * b.Min.X)
|
||||
b.Min.Y = f.scale(scale * b.Min.Y)
|
||||
b.Max.X = f.scale(scale * b.Max.X)
|
||||
b.Max.Y = f.scale(scale * b.Max.Y)
|
||||
return b
|
||||
}
|
||||
|
||||
// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
|
||||
func (f *Font) FUnitsPerEm() int32 {
|
||||
return f.fUnitsPerEm
|
||||
}
|
||||
|
||||
// Index returns a Font's index for the given rune.
|
||||
func (f *Font) Index(x rune) Index {
|
||||
c := uint32(x)
|
||||
for i, j := 0, len(f.cm); i < j; {
|
||||
h := i + (j-i)/2
|
||||
cm := &f.cm[h]
|
||||
if c < cm.start {
|
||||
j = h
|
||||
} else if cm.end < c {
|
||||
i = h + 1
|
||||
} else if cm.offset == 0 {
|
||||
return Index(c + cm.delta)
|
||||
} else {
|
||||
offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
|
||||
return Index(u16(f.cmapIndexes, offset))
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Name returns the Font's name value for the given NameID. It returns "" if
|
||||
// there was an error, or if that name was not found.
|
||||
func (f *Font) Name(id NameID) string {
|
||||
x, platformID, err := parseSubtables(f.name, "name", 6, 12, func(b []byte) bool {
|
||||
return NameID(u16(b, 6)) == id
|
||||
})
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
offset, length := u16(f.name, 4)+u16(f.name, x+10), u16(f.name, x+8)
|
||||
// Return the ASCII value of the encoded string.
|
||||
// The string is encoded as UTF-16 on non-Apple platformIDs; Apple is platformID 1.
|
||||
src := f.name[offset : offset+length]
|
||||
var dst []byte
|
||||
if platformID != 1 { // UTF-16.
|
||||
if len(src)&1 != 0 {
|
||||
return ""
|
||||
}
|
||||
dst = make([]byte, len(src)/2)
|
||||
for i := range dst {
|
||||
dst[i] = printable(u16(src, 2*i))
|
||||
}
|
||||
} else { // ASCII.
|
||||
dst = make([]byte, len(src))
|
||||
for i, c := range src {
|
||||
dst[i] = printable(uint16(c))
|
||||
}
|
||||
}
|
||||
return string(dst)
|
||||
}
|
||||
|
||||
func printable(r uint16) byte {
|
||||
if 0x20 <= r && r < 0x7f {
|
||||
return byte(r)
|
||||
}
|
||||
return '?'
|
||||
}
|
||||
|
||||
// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
|
||||
// the given index.
|
||||
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
|
||||
j := int(i)
|
||||
if j < 0 || f.nGlyph <= j {
|
||||
return HMetric{}
|
||||
}
|
||||
if j >= f.nHMetric {
|
||||
p := 4 * (f.nHMetric - 1)
|
||||
return HMetric{
|
||||
AdvanceWidth: fixed.Int26_6(u16(f.hmtx, p)),
|
||||
LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
|
||||
}
|
||||
}
|
||||
return HMetric{
|
||||
AdvanceWidth: fixed.Int26_6(u16(f.hmtx, 4*j)),
|
||||
LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
|
||||
}
|
||||
}
|
||||
|
||||
// HMetric returns the horizontal metrics for the glyph with the given index.
|
||||
func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric {
|
||||
h := f.unscaledHMetric(i)
|
||||
h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
|
||||
h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
|
||||
return h
|
||||
}
|
||||
|
||||
// unscaledVMetric returns the unscaled vertical metrics for the glyph with
|
||||
// the given index. yMax is the top of the glyph's bounding box.
|
||||
func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
|
||||
j := int(i)
|
||||
if j < 0 || f.nGlyph <= j {
|
||||
return VMetric{}
|
||||
}
|
||||
if 4*j+4 <= len(f.vmtx) {
|
||||
return VMetric{
|
||||
AdvanceHeight: fixed.Int26_6(u16(f.vmtx, 4*j)),
|
||||
TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
|
||||
}
|
||||
}
|
||||
// The OS/2 table has grown over time.
|
||||
// https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
|
||||
// says that it was originally 68 bytes. Optional fields, including
|
||||
// the ascender and descender, are described at
|
||||
// http://www.microsoft.com/typography/otspec/os2.htm
|
||||
if len(f.os2) >= 72 {
|
||||
sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
|
||||
sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
|
||||
return VMetric{
|
||||
AdvanceHeight: sTypoAscender - sTypoDescender,
|
||||
TopSideBearing: sTypoAscender - yMax,
|
||||
}
|
||||
}
|
||||
return VMetric{
|
||||
AdvanceHeight: fixed.Int26_6(f.fUnitsPerEm),
|
||||
TopSideBearing: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// VMetric returns the vertical metrics for the glyph with the given index.
|
||||
func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric {
|
||||
// TODO: should 0 be bounds.YMax?
|
||||
v := f.unscaledVMetric(i, 0)
|
||||
v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
|
||||
v.TopSideBearing = f.scale(scale * v.TopSideBearing)
|
||||
return v
|
||||
}
|
||||
|
||||
// Kern returns the horizontal adjustment for the given glyph pair. A positive
|
||||
// kern means to move the glyphs further apart.
|
||||
func (f *Font) Kern(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 {
|
||||
if f.nKern == 0 {
|
||||
return 0
|
||||
}
|
||||
g := uint32(i0)<<16 | uint32(i1)
|
||||
lo, hi := 0, f.nKern
|
||||
for lo < hi {
|
||||
i := (lo + hi) / 2
|
||||
ig := u32(f.kern, 18+6*i)
|
||||
if ig < g {
|
||||
lo = i + 1
|
||||
} else if ig > g {
|
||||
hi = i
|
||||
} else {
|
||||
return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Parse returns a new Font for the given TTF or TTC data.
|
||||
//
|
||||
// For TrueType Collections, the first font in the collection is parsed.
|
||||
func Parse(ttf []byte) (font *Font, err error) {
|
||||
return parse(ttf, 0)
|
||||
}
|
||||
|
||||
func parse(ttf []byte, offset int) (font *Font, err error) {
|
||||
if len(ttf)-offset < 12 {
|
||||
err = FormatError("TTF data is too short")
|
||||
return
|
||||
}
|
||||
originalOffset := offset
|
||||
magic, offset := u32(ttf, offset), offset+4
|
||||
switch magic {
|
||||
case 0x00010000:
|
||||
// No-op.
|
||||
case 0x74746366: // "ttcf" as a big-endian uint32.
|
||||
if originalOffset != 0 {
|
||||
err = FormatError("recursive TTC")
|
||||
return
|
||||
}
|
||||
ttcVersion, offset := u32(ttf, offset), offset+4
|
||||
if ttcVersion != 0x00010000 && ttcVersion != 0x00020000 {
|
||||
err = FormatError("bad TTC version")
|
||||
return
|
||||
}
|
||||
numFonts, offset := int(u32(ttf, offset)), offset+4
|
||||
if numFonts <= 0 {
|
||||
err = FormatError("bad number of TTC fonts")
|
||||
return
|
||||
}
|
||||
if len(ttf[offset:])/4 < numFonts {
|
||||
err = FormatError("TTC offset table is too short")
|
||||
return
|
||||
}
|
||||
// TODO: provide an API to select which font in a TrueType collection to return,
|
||||
// not just the first one. This may require an API to parse a TTC's name tables,
|
||||
// so users of this package can select the font in a TTC by name.
|
||||
offset = int(u32(ttf, offset))
|
||||
if offset <= 0 || offset > len(ttf) {
|
||||
err = FormatError("bad TTC offset")
|
||||
return
|
||||
}
|
||||
return parse(ttf, offset)
|
||||
default:
|
||||
err = FormatError("bad TTF version")
|
||||
return
|
||||
}
|
||||
n, offset := int(u16(ttf, offset)), offset+2
|
||||
offset += 6 // Skip the searchRange, entrySelector and rangeShift.
|
||||
if len(ttf) < 16*n+offset {
|
||||
err = FormatError("TTF data is too short")
|
||||
return
|
||||
}
|
||||
f := new(Font)
|
||||
// Assign the table slices.
|
||||
for i := 0; i < n; i++ {
|
||||
x := 16*i + offset
|
||||
switch string(ttf[x : x+4]) {
|
||||
case "cmap":
|
||||
f.cmap, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "cvt ":
|
||||
f.cvt, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "fpgm":
|
||||
f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "glyf":
|
||||
f.glyf, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "hdmx":
|
||||
f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "head":
|
||||
f.head, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "hhea":
|
||||
f.hhea, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "hmtx":
|
||||
f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "kern":
|
||||
f.kern, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "loca":
|
||||
f.loca, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "maxp":
|
||||
f.maxp, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "name":
|
||||
f.name, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "OS/2":
|
||||
f.os2, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "prep":
|
||||
f.prep, err = readTable(ttf, ttf[x+8:x+16])
|
||||
case "vmtx":
|
||||
f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Parse and sanity-check the TTF data.
|
||||
if err = f.parseHead(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = f.parseMaxp(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = f.parseCmap(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = f.parseKern(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = f.parseHhea(); err != nil {
|
||||
return
|
||||
}
|
||||
font = f
|
||||
return
|
||||
}
|
||||
1
vendor/github.com/golang/groupcache/.gitignore
generated
vendored
Normal file
1
vendor/github.com/golang/groupcache/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*~
|
||||
19
vendor/github.com/golang/groupcache/.travis.yml
generated
vendored
Normal file
19
vendor/github.com/golang/groupcache/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
language: go
|
||||
go_import_path: github.com/golang/groupcache
|
||||
|
||||
os: linux
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
script:
|
||||
- go test ./...
|
||||
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- master
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/pkg
|
||||
191
vendor/github.com/golang/groupcache/LICENSE
generated
vendored
Normal file
191
vendor/github.com/golang/groupcache/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
74
vendor/github.com/golang/groupcache/README.md
generated
vendored
Normal file
74
vendor/github.com/golang/groupcache/README.md
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
# groupcache
|
||||
|
||||
## Summary
|
||||
|
||||
groupcache is a distributed caching and cache-filling library, intended as a
|
||||
replacement for a pool of memcached nodes in many cases.
|
||||
|
||||
For API docs and examples, see http://godoc.org/github.com/golang/groupcache
|
||||
|
||||
## Comparison to memcached
|
||||
|
||||
### **Like memcached**, groupcache:
|
||||
|
||||
* shards by key to select which peer is responsible for that key
|
||||
|
||||
### **Unlike memcached**, groupcache:
|
||||
|
||||
* does not require running a separate set of servers, thus massively
|
||||
reducing deployment/configuration pain. groupcache is a client
|
||||
library as well as a server. It connects to its own peers, forming
|
||||
a distributed cache.
|
||||
|
||||
* comes with a cache filling mechanism. Whereas memcached just says
|
||||
"Sorry, cache miss", often resulting in a thundering herd of
|
||||
database (or whatever) loads from an unbounded number of clients
|
||||
(which has resulted in several fun outages), groupcache coordinates
|
||||
cache fills such that only one load in one process of an entire
|
||||
replicated set of processes populates the cache, then multiplexes
|
||||
the loaded value to all callers.
|
||||
|
||||
* does not support versioned values. If key "foo" is value "bar",
|
||||
key "foo" must always be "bar". There are neither cache expiration
|
||||
times, nor explicit cache evictions. Thus there is also no CAS,
|
||||
nor Increment/Decrement. This also means that groupcache....
|
||||
|
||||
* ... supports automatic mirroring of super-hot items to multiple
|
||||
processes. This prevents memcached hot spotting where a machine's
|
||||
CPU and/or NIC are overloaded by very popular keys/values.
|
||||
|
||||
* is currently only available for Go. It's very unlikely that I
|
||||
(bradfitz@) will port the code to any other language.
|
||||
|
||||
## Loading process
|
||||
|
||||
In a nutshell, a groupcache lookup of **Get("foo")** looks like:
|
||||
|
||||
(On machine #5 of a set of N machines running the same code)
|
||||
|
||||
1. Is the value of "foo" in local memory because it's super hot? If so, use it.
|
||||
|
||||
2. Is the value of "foo" in local memory because peer #5 (the current
|
||||
peer) is the owner of it? If so, use it.
|
||||
|
||||
3. Amongst all the peers in my set of N, am I the owner of the key
|
||||
"foo"? (e.g. does it consistent hash to 5?) If so, load it. If
|
||||
other callers come in, via the same process or via RPC requests
|
||||
from peers, they block waiting for the load to finish and get the
|
||||
same answer. If not, RPC to the peer that's the owner and get
|
||||
the answer. If the RPC fails, just load it locally (still with
|
||||
local dup suppression).
|
||||
|
||||
## Users
|
||||
|
||||
groupcache is in production use by dl.google.com (its original user),
|
||||
parts of Blogger, parts of Google Code, parts of Google Fiber, parts
|
||||
of Google production monitoring systems, etc.
|
||||
|
||||
## Presentations
|
||||
|
||||
See http://talks.golang.org/2013/oscon-dl.slide
|
||||
|
||||
## Help
|
||||
|
||||
Use the golang-nuts mailing list for any discussion or questions.
|
||||
175
vendor/github.com/golang/groupcache/byteview.go
generated
vendored
Normal file
175
vendor/github.com/golang/groupcache/byteview.go
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
Copyright 2012 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package groupcache
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A ByteView holds an immutable view of bytes.
|
||||
// Internally it wraps either a []byte or a string,
|
||||
// but that detail is invisible to callers.
|
||||
//
|
||||
// A ByteView is meant to be used as a value type, not
|
||||
// a pointer (like a time.Time).
|
||||
type ByteView struct {
|
||||
// If b is non-nil, b is used, else s is used.
|
||||
b []byte
|
||||
s string
|
||||
}
|
||||
|
||||
// Len returns the view's length.
|
||||
func (v ByteView) Len() int {
|
||||
if v.b != nil {
|
||||
return len(v.b)
|
||||
}
|
||||
return len(v.s)
|
||||
}
|
||||
|
||||
// ByteSlice returns a copy of the data as a byte slice.
|
||||
func (v ByteView) ByteSlice() []byte {
|
||||
if v.b != nil {
|
||||
return cloneBytes(v.b)
|
||||
}
|
||||
return []byte(v.s)
|
||||
}
|
||||
|
||||
// String returns the data as a string, making a copy if necessary.
|
||||
func (v ByteView) String() string {
|
||||
if v.b != nil {
|
||||
return string(v.b)
|
||||
}
|
||||
return v.s
|
||||
}
|
||||
|
||||
// At returns the byte at index i.
|
||||
func (v ByteView) At(i int) byte {
|
||||
if v.b != nil {
|
||||
return v.b[i]
|
||||
}
|
||||
return v.s[i]
|
||||
}
|
||||
|
||||
// Slice slices the view between the provided from and to indices.
|
||||
func (v ByteView) Slice(from, to int) ByteView {
|
||||
if v.b != nil {
|
||||
return ByteView{b: v.b[from:to]}
|
||||
}
|
||||
return ByteView{s: v.s[from:to]}
|
||||
}
|
||||
|
||||
// SliceFrom slices the view from the provided index until the end.
|
||||
func (v ByteView) SliceFrom(from int) ByteView {
|
||||
if v.b != nil {
|
||||
return ByteView{b: v.b[from:]}
|
||||
}
|
||||
return ByteView{s: v.s[from:]}
|
||||
}
|
||||
|
||||
// Copy copies b into dest and returns the number of bytes copied.
|
||||
func (v ByteView) Copy(dest []byte) int {
|
||||
if v.b != nil {
|
||||
return copy(dest, v.b)
|
||||
}
|
||||
return copy(dest, v.s)
|
||||
}
|
||||
|
||||
// Equal returns whether the bytes in b are the same as the bytes in
|
||||
// b2.
|
||||
func (v ByteView) Equal(b2 ByteView) bool {
|
||||
if b2.b == nil {
|
||||
return v.EqualString(b2.s)
|
||||
}
|
||||
return v.EqualBytes(b2.b)
|
||||
}
|
||||
|
||||
// EqualString returns whether the bytes in b are the same as the bytes
|
||||
// in s.
|
||||
func (v ByteView) EqualString(s string) bool {
|
||||
if v.b == nil {
|
||||
return v.s == s
|
||||
}
|
||||
l := v.Len()
|
||||
if len(s) != l {
|
||||
return false
|
||||
}
|
||||
for i, bi := range v.b {
|
||||
if bi != s[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// EqualBytes returns whether the bytes in b are the same as the bytes
|
||||
// in b2.
|
||||
func (v ByteView) EqualBytes(b2 []byte) bool {
|
||||
if v.b != nil {
|
||||
return bytes.Equal(v.b, b2)
|
||||
}
|
||||
l := v.Len()
|
||||
if len(b2) != l {
|
||||
return false
|
||||
}
|
||||
for i, bi := range b2 {
|
||||
if bi != v.s[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Reader returns an io.ReadSeeker for the bytes in v.
|
||||
func (v ByteView) Reader() io.ReadSeeker {
|
||||
if v.b != nil {
|
||||
return bytes.NewReader(v.b)
|
||||
}
|
||||
return strings.NewReader(v.s)
|
||||
}
|
||||
|
||||
// ReadAt implements io.ReaderAt on the bytes in v.
|
||||
func (v ByteView) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
if off < 0 {
|
||||
return 0, errors.New("view: invalid offset")
|
||||
}
|
||||
if off >= int64(v.Len()) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = v.SliceFrom(int(off)).Copy(p)
|
||||
if n < len(p) {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// WriteTo implements io.WriterTo on the bytes in v.
|
||||
func (v ByteView) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var m int
|
||||
if v.b != nil {
|
||||
m, err = w.Write(v.b)
|
||||
} else {
|
||||
m, err = io.WriteString(w, v.s)
|
||||
}
|
||||
if err == nil && m < v.Len() {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
n = int64(m)
|
||||
return
|
||||
}
|
||||
81
vendor/github.com/golang/groupcache/consistenthash/consistenthash.go
generated
vendored
Normal file
81
vendor/github.com/golang/groupcache/consistenthash/consistenthash.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2013 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package consistenthash provides an implementation of a ring hash.
|
||||
package consistenthash
|
||||
|
||||
import (
|
||||
"hash/crc32"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Hash func(data []byte) uint32
|
||||
|
||||
type Map struct {
|
||||
hash Hash
|
||||
replicas int
|
||||
keys []int // Sorted
|
||||
hashMap map[int]string
|
||||
}
|
||||
|
||||
func New(replicas int, fn Hash) *Map {
|
||||
m := &Map{
|
||||
replicas: replicas,
|
||||
hash: fn,
|
||||
hashMap: make(map[int]string),
|
||||
}
|
||||
if m.hash == nil {
|
||||
m.hash = crc32.ChecksumIEEE
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// IsEmpty returns true if there are no items available.
|
||||
func (m *Map) IsEmpty() bool {
|
||||
return len(m.keys) == 0
|
||||
}
|
||||
|
||||
// Add adds some keys to the hash.
|
||||
func (m *Map) Add(keys ...string) {
|
||||
for _, key := range keys {
|
||||
for i := 0; i < m.replicas; i++ {
|
||||
hash := int(m.hash([]byte(strconv.Itoa(i) + key)))
|
||||
m.keys = append(m.keys, hash)
|
||||
m.hashMap[hash] = key
|
||||
}
|
||||
}
|
||||
sort.Ints(m.keys)
|
||||
}
|
||||
|
||||
// Get gets the closest item in the hash to the provided key.
|
||||
func (m *Map) Get(key string) string {
|
||||
if m.IsEmpty() {
|
||||
return ""
|
||||
}
|
||||
|
||||
hash := int(m.hash([]byte(key)))
|
||||
|
||||
// Binary search for appropriate replica.
|
||||
idx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash })
|
||||
|
||||
// Means we have cycled back to the first replica.
|
||||
if idx == len(m.keys) {
|
||||
idx = 0
|
||||
}
|
||||
|
||||
return m.hashMap[m.keys[idx]]
|
||||
}
|
||||
492
vendor/github.com/golang/groupcache/groupcache.go
generated
vendored
Normal file
492
vendor/github.com/golang/groupcache/groupcache.go
generated
vendored
Normal file
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
Copyright 2012 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package groupcache provides a data loading mechanism with caching
|
||||
// and de-duplication that works across a set of peer processes.
|
||||
//
|
||||
// Each data Get first consults its local cache, otherwise delegates
|
||||
// to the requested key's canonical owner, which then checks its cache
|
||||
// or finally gets the data. In the common case, many concurrent
|
||||
// cache misses across a set of peers for the same key result in just
|
||||
// one cache fill.
|
||||
package groupcache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
pb "github.com/golang/groupcache/groupcachepb"
|
||||
"github.com/golang/groupcache/lru"
|
||||
"github.com/golang/groupcache/singleflight"
|
||||
)
|
||||
|
||||
// A Getter loads data for a key.
|
||||
type Getter interface {
|
||||
// Get returns the value identified by key, populating dest.
|
||||
//
|
||||
// The returned data must be unversioned. That is, key must
|
||||
// uniquely describe the loaded data, without an implicit
|
||||
// current time, and without relying on cache expiration
|
||||
// mechanisms.
|
||||
Get(ctx context.Context, key string, dest Sink) error
|
||||
}
|
||||
|
||||
// A GetterFunc implements Getter with a function.
|
||||
type GetterFunc func(ctx context.Context, key string, dest Sink) error
|
||||
|
||||
func (f GetterFunc) Get(ctx context.Context, key string, dest Sink) error {
|
||||
return f(ctx, key, dest)
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.RWMutex
|
||||
groups = make(map[string]*Group)
|
||||
|
||||
initPeerServerOnce sync.Once
|
||||
initPeerServer func()
|
||||
)
|
||||
|
||||
// GetGroup returns the named group previously created with NewGroup, or
|
||||
// nil if there's no such group.
|
||||
func GetGroup(name string) *Group {
|
||||
mu.RLock()
|
||||
g := groups[name]
|
||||
mu.RUnlock()
|
||||
return g
|
||||
}
|
||||
|
||||
// NewGroup creates a coordinated group-aware Getter from a Getter.
|
||||
//
|
||||
// The returned Getter tries (but does not guarantee) to run only one
|
||||
// Get call at once for a given key across an entire set of peer
|
||||
// processes. Concurrent callers both in the local process and in
|
||||
// other processes receive copies of the answer once the original Get
|
||||
// completes.
|
||||
//
|
||||
// The group name must be unique for each getter.
|
||||
func NewGroup(name string, cacheBytes int64, getter Getter) *Group {
|
||||
return newGroup(name, cacheBytes, getter, nil)
|
||||
}
|
||||
|
||||
// If peers is nil, the peerPicker is called via a sync.Once to initialize it.
|
||||
func newGroup(name string, cacheBytes int64, getter Getter, peers PeerPicker) *Group {
|
||||
if getter == nil {
|
||||
panic("nil Getter")
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
initPeerServerOnce.Do(callInitPeerServer)
|
||||
if _, dup := groups[name]; dup {
|
||||
panic("duplicate registration of group " + name)
|
||||
}
|
||||
g := &Group{
|
||||
name: name,
|
||||
getter: getter,
|
||||
peers: peers,
|
||||
cacheBytes: cacheBytes,
|
||||
loadGroup: &singleflight.Group{},
|
||||
}
|
||||
if fn := newGroupHook; fn != nil {
|
||||
fn(g)
|
||||
}
|
||||
groups[name] = g
|
||||
return g
|
||||
}
|
||||
|
||||
// newGroupHook, if non-nil, is called right after a new group is created.
|
||||
var newGroupHook func(*Group)
|
||||
|
||||
// RegisterNewGroupHook registers a hook that is run each time
|
||||
// a group is created.
|
||||
func RegisterNewGroupHook(fn func(*Group)) {
|
||||
if newGroupHook != nil {
|
||||
panic("RegisterNewGroupHook called more than once")
|
||||
}
|
||||
newGroupHook = fn
|
||||
}
|
||||
|
||||
// RegisterServerStart registers a hook that is run when the first
|
||||
// group is created.
|
||||
func RegisterServerStart(fn func()) {
|
||||
if initPeerServer != nil {
|
||||
panic("RegisterServerStart called more than once")
|
||||
}
|
||||
initPeerServer = fn
|
||||
}
|
||||
|
||||
func callInitPeerServer() {
|
||||
if initPeerServer != nil {
|
||||
initPeerServer()
|
||||
}
|
||||
}
|
||||
|
||||
// A Group is a cache namespace and associated data loaded spread over
|
||||
// a group of 1 or more machines.
|
||||
type Group struct {
|
||||
name string
|
||||
getter Getter
|
||||
peersOnce sync.Once
|
||||
peers PeerPicker
|
||||
cacheBytes int64 // limit for sum of mainCache and hotCache size
|
||||
|
||||
// mainCache is a cache of the keys for which this process
|
||||
// (amongst its peers) is authoritative. That is, this cache
|
||||
// contains keys which consistent hash on to this process's
|
||||
// peer number.
|
||||
mainCache cache
|
||||
|
||||
// hotCache contains keys/values for which this peer is not
|
||||
// authoritative (otherwise they would be in mainCache), but
|
||||
// are popular enough to warrant mirroring in this process to
|
||||
// avoid going over the network to fetch from a peer. Having
|
||||
// a hotCache avoids network hotspotting, where a peer's
|
||||
// network card could become the bottleneck on a popular key.
|
||||
// This cache is used sparingly to maximize the total number
|
||||
// of key/value pairs that can be stored globally.
|
||||
hotCache cache
|
||||
|
||||
// loadGroup ensures that each key is only fetched once
|
||||
// (either locally or remotely), regardless of the number of
|
||||
// concurrent callers.
|
||||
loadGroup flightGroup
|
||||
|
||||
_ int32 // force Stats to be 8-byte aligned on 32-bit platforms
|
||||
|
||||
// Stats are statistics on the group.
|
||||
Stats Stats
|
||||
}
|
||||
|
||||
// flightGroup is defined as an interface which flightgroup.Group
|
||||
// satisfies. We define this so that we may test with an alternate
|
||||
// implementation.
|
||||
type flightGroup interface {
|
||||
// Done is called when Do is done.
|
||||
Do(key string, fn func() (interface{}, error)) (interface{}, error)
|
||||
}
|
||||
|
||||
// Stats are per-group statistics.
|
||||
type Stats struct {
|
||||
Gets AtomicInt // any Get request, including from peers
|
||||
CacheHits AtomicInt // either cache was good
|
||||
PeerLoads AtomicInt // either remote load or remote cache hit (not an error)
|
||||
PeerErrors AtomicInt
|
||||
Loads AtomicInt // (gets - cacheHits)
|
||||
LoadsDeduped AtomicInt // after singleflight
|
||||
LocalLoads AtomicInt // total good local loads
|
||||
LocalLoadErrs AtomicInt // total bad local loads
|
||||
ServerRequests AtomicInt // gets that came over the network from peers
|
||||
}
|
||||
|
||||
// Name returns the name of the group.
|
||||
func (g *Group) Name() string {
|
||||
return g.name
|
||||
}
|
||||
|
||||
func (g *Group) initPeers() {
|
||||
if g.peers == nil {
|
||||
g.peers = getPeers(g.name)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Group) Get(ctx context.Context, key string, dest Sink) error {
|
||||
g.peersOnce.Do(g.initPeers)
|
||||
g.Stats.Gets.Add(1)
|
||||
if dest == nil {
|
||||
return errors.New("groupcache: nil dest Sink")
|
||||
}
|
||||
value, cacheHit := g.lookupCache(key)
|
||||
|
||||
if cacheHit {
|
||||
g.Stats.CacheHits.Add(1)
|
||||
return setSinkView(dest, value)
|
||||
}
|
||||
|
||||
// Optimization to avoid double unmarshalling or copying: keep
|
||||
// track of whether the dest was already populated. One caller
|
||||
// (if local) will set this; the losers will not. The common
|
||||
// case will likely be one caller.
|
||||
destPopulated := false
|
||||
value, destPopulated, err := g.load(ctx, key, dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if destPopulated {
|
||||
return nil
|
||||
}
|
||||
return setSinkView(dest, value)
|
||||
}
|
||||
|
||||
// load loads key either by invoking the getter locally or by sending it to another machine.
|
||||
func (g *Group) load(ctx context.Context, key string, dest Sink) (value ByteView, destPopulated bool, err error) {
|
||||
g.Stats.Loads.Add(1)
|
||||
viewi, err := g.loadGroup.Do(key, func() (interface{}, error) {
|
||||
// Check the cache again because singleflight can only dedup calls
|
||||
// that overlap concurrently. It's possible for 2 concurrent
|
||||
// requests to miss the cache, resulting in 2 load() calls. An
|
||||
// unfortunate goroutine scheduling would result in this callback
|
||||
// being run twice, serially. If we don't check the cache again,
|
||||
// cache.nbytes would be incremented below even though there will
|
||||
// be only one entry for this key.
|
||||
//
|
||||
// Consider the following serialized event ordering for two
|
||||
// goroutines in which this callback gets called twice for the
|
||||
// same key:
|
||||
// 1: Get("key")
|
||||
// 2: Get("key")
|
||||
// 1: lookupCache("key")
|
||||
// 2: lookupCache("key")
|
||||
// 1: load("key")
|
||||
// 2: load("key")
|
||||
// 1: loadGroup.Do("key", fn)
|
||||
// 1: fn()
|
||||
// 2: loadGroup.Do("key", fn)
|
||||
// 2: fn()
|
||||
if value, cacheHit := g.lookupCache(key); cacheHit {
|
||||
g.Stats.CacheHits.Add(1)
|
||||
return value, nil
|
||||
}
|
||||
g.Stats.LoadsDeduped.Add(1)
|
||||
var value ByteView
|
||||
var err error
|
||||
if peer, ok := g.peers.PickPeer(key); ok {
|
||||
value, err = g.getFromPeer(ctx, peer, key)
|
||||
if err == nil {
|
||||
g.Stats.PeerLoads.Add(1)
|
||||
return value, nil
|
||||
}
|
||||
g.Stats.PeerErrors.Add(1)
|
||||
// TODO(bradfitz): log the peer's error? keep
|
||||
// log of the past few for /groupcachez? It's
|
||||
// probably boring (normal task movement), so not
|
||||
// worth logging I imagine.
|
||||
}
|
||||
value, err = g.getLocally(ctx, key, dest)
|
||||
if err != nil {
|
||||
g.Stats.LocalLoadErrs.Add(1)
|
||||
return nil, err
|
||||
}
|
||||
g.Stats.LocalLoads.Add(1)
|
||||
destPopulated = true // only one caller of load gets this return value
|
||||
g.populateCache(key, value, &g.mainCache)
|
||||
return value, nil
|
||||
})
|
||||
if err == nil {
|
||||
value = viewi.(ByteView)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Group) getLocally(ctx context.Context, key string, dest Sink) (ByteView, error) {
|
||||
err := g.getter.Get(ctx, key, dest)
|
||||
if err != nil {
|
||||
return ByteView{}, err
|
||||
}
|
||||
return dest.view()
|
||||
}
|
||||
|
||||
func (g *Group) getFromPeer(ctx context.Context, peer ProtoGetter, key string) (ByteView, error) {
|
||||
req := &pb.GetRequest{
|
||||
Group: &g.name,
|
||||
Key: &key,
|
||||
}
|
||||
res := &pb.GetResponse{}
|
||||
err := peer.Get(ctx, req, res)
|
||||
if err != nil {
|
||||
return ByteView{}, err
|
||||
}
|
||||
value := ByteView{b: res.Value}
|
||||
// TODO(bradfitz): use res.MinuteQps or something smart to
|
||||
// conditionally populate hotCache. For now just do it some
|
||||
// percentage of the time.
|
||||
if rand.Intn(10) == 0 {
|
||||
g.populateCache(key, value, &g.hotCache)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (g *Group) lookupCache(key string) (value ByteView, ok bool) {
|
||||
if g.cacheBytes <= 0 {
|
||||
return
|
||||
}
|
||||
value, ok = g.mainCache.get(key)
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
value, ok = g.hotCache.get(key)
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Group) populateCache(key string, value ByteView, cache *cache) {
|
||||
if g.cacheBytes <= 0 {
|
||||
return
|
||||
}
|
||||
cache.add(key, value)
|
||||
|
||||
// Evict items from cache(s) if necessary.
|
||||
for {
|
||||
mainBytes := g.mainCache.bytes()
|
||||
hotBytes := g.hotCache.bytes()
|
||||
if mainBytes+hotBytes <= g.cacheBytes {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(bradfitz): this is good-enough-for-now logic.
|
||||
// It should be something based on measurements and/or
|
||||
// respecting the costs of different resources.
|
||||
victim := &g.mainCache
|
||||
if hotBytes > mainBytes/8 {
|
||||
victim = &g.hotCache
|
||||
}
|
||||
victim.removeOldest()
|
||||
}
|
||||
}
|
||||
|
||||
// CacheType represents a type of cache.
|
||||
type CacheType int
|
||||
|
||||
const (
|
||||
// The MainCache is the cache for items that this peer is the
|
||||
// owner for.
|
||||
MainCache CacheType = iota + 1
|
||||
|
||||
// The HotCache is the cache for items that seem popular
|
||||
// enough to replicate to this node, even though it's not the
|
||||
// owner.
|
||||
HotCache
|
||||
)
|
||||
|
||||
// CacheStats returns stats about the provided cache within the group.
|
||||
func (g *Group) CacheStats(which CacheType) CacheStats {
|
||||
switch which {
|
||||
case MainCache:
|
||||
return g.mainCache.stats()
|
||||
case HotCache:
|
||||
return g.hotCache.stats()
|
||||
default:
|
||||
return CacheStats{}
|
||||
}
|
||||
}
|
||||
|
||||
// cache is a wrapper around an *lru.Cache that adds synchronization,
|
||||
// makes values always be ByteView, and counts the size of all keys and
|
||||
// values.
|
||||
type cache struct {
|
||||
mu sync.RWMutex
|
||||
nbytes int64 // of all keys and values
|
||||
lru *lru.Cache
|
||||
nhit, nget int64
|
||||
nevict int64 // number of evictions
|
||||
}
|
||||
|
||||
func (c *cache) stats() CacheStats {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return CacheStats{
|
||||
Bytes: c.nbytes,
|
||||
Items: c.itemsLocked(),
|
||||
Gets: c.nget,
|
||||
Hits: c.nhit,
|
||||
Evictions: c.nevict,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cache) add(key string, value ByteView) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.lru == nil {
|
||||
c.lru = &lru.Cache{
|
||||
OnEvicted: func(key lru.Key, value interface{}) {
|
||||
val := value.(ByteView)
|
||||
c.nbytes -= int64(len(key.(string))) + int64(val.Len())
|
||||
c.nevict++
|
||||
},
|
||||
}
|
||||
}
|
||||
c.lru.Add(key, value)
|
||||
c.nbytes += int64(len(key)) + int64(value.Len())
|
||||
}
|
||||
|
||||
func (c *cache) get(key string) (value ByteView, ok bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.nget++
|
||||
if c.lru == nil {
|
||||
return
|
||||
}
|
||||
vi, ok := c.lru.Get(key)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
c.nhit++
|
||||
return vi.(ByteView), true
|
||||
}
|
||||
|
||||
func (c *cache) removeOldest() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.lru != nil {
|
||||
c.lru.RemoveOldest()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cache) bytes() int64 {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.nbytes
|
||||
}
|
||||
|
||||
func (c *cache) items() int64 {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.itemsLocked()
|
||||
}
|
||||
|
||||
func (c *cache) itemsLocked() int64 {
|
||||
if c.lru == nil {
|
||||
return 0
|
||||
}
|
||||
return int64(c.lru.Len())
|
||||
}
|
||||
|
||||
// An AtomicInt is an int64 to be accessed atomically.
|
||||
type AtomicInt int64
|
||||
|
||||
// Add atomically adds n to i.
|
||||
func (i *AtomicInt) Add(n int64) {
|
||||
atomic.AddInt64((*int64)(i), n)
|
||||
}
|
||||
|
||||
// Get atomically gets the value of i.
|
||||
func (i *AtomicInt) Get() int64 {
|
||||
return atomic.LoadInt64((*int64)(i))
|
||||
}
|
||||
|
||||
func (i *AtomicInt) String() string {
|
||||
return strconv.FormatInt(i.Get(), 10)
|
||||
}
|
||||
|
||||
// CacheStats are returned by stats accessors on Group.
|
||||
type CacheStats struct {
|
||||
Bytes int64
|
||||
Items int64
|
||||
Gets int64
|
||||
Hits int64
|
||||
Evictions int64
|
||||
}
|
||||
65
vendor/github.com/golang/groupcache/groupcachepb/groupcache.pb.go
generated
vendored
Normal file
65
vendor/github.com/golang/groupcache/groupcachepb/groupcache.pb.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// Code generated by protoc-gen-go.
|
||||
// source: groupcache.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
package groupcachepb
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import json "encoding/json"
|
||||
import math "math"
|
||||
|
||||
// Reference proto, json, and math imports to suppress error if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = &json.SyntaxError{}
|
||||
var _ = math.Inf
|
||||
|
||||
type GetRequest struct {
|
||||
Group *string `protobuf:"bytes,1,req,name=group" json:"group,omitempty"`
|
||||
Key *string `protobuf:"bytes,2,req,name=key" json:"key,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *GetRequest) Reset() { *m = GetRequest{} }
|
||||
func (m *GetRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetRequest) ProtoMessage() {}
|
||||
|
||||
func (m *GetRequest) GetGroup() string {
|
||||
if m != nil && m.Group != nil {
|
||||
return *m.Group
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *GetRequest) GetKey() string {
|
||||
if m != nil && m.Key != nil {
|
||||
return *m.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetResponse struct {
|
||||
Value []byte `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"`
|
||||
MinuteQps *float64 `protobuf:"fixed64,2,opt,name=minute_qps" json:"minute_qps,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *GetResponse) Reset() { *m = GetResponse{} }
|
||||
func (m *GetResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetResponse) ProtoMessage() {}
|
||||
|
||||
func (m *GetResponse) GetValue() []byte {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *GetResponse) GetMinuteQps() float64 {
|
||||
if m != nil && m.MinuteQps != nil {
|
||||
return *m.MinuteQps
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
}
|
||||
34
vendor/github.com/golang/groupcache/groupcachepb/groupcache.proto
generated
vendored
Normal file
34
vendor/github.com/golang/groupcache/groupcachepb/groupcache.proto
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2012 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package groupcachepb;
|
||||
|
||||
message GetRequest {
|
||||
required string group = 1;
|
||||
required string key = 2; // not actually required/guaranteed to be UTF-8
|
||||
}
|
||||
|
||||
message GetResponse {
|
||||
optional bytes value = 1;
|
||||
optional double minute_qps = 2;
|
||||
}
|
||||
|
||||
service GroupCache {
|
||||
rpc Get(GetRequest) returns (GetResponse) {
|
||||
};
|
||||
}
|
||||
231
vendor/github.com/golang/groupcache/http.go
generated
vendored
Normal file
231
vendor/github.com/golang/groupcache/http.go
generated
vendored
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
Copyright 2013 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package groupcache
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/groupcache/consistenthash"
|
||||
pb "github.com/golang/groupcache/groupcachepb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
const defaultBasePath = "/_groupcache/"
|
||||
|
||||
const defaultReplicas = 50
|
||||
|
||||
// HTTPPool implements PeerPicker for a pool of HTTP peers.
|
||||
type HTTPPool struct {
|
||||
// Context optionally specifies a context for the server to use when it
|
||||
// receives a request.
|
||||
// If nil, the server uses the request's context
|
||||
Context func(*http.Request) context.Context
|
||||
|
||||
// Transport optionally specifies an http.RoundTripper for the client
|
||||
// to use when it makes a request.
|
||||
// If nil, the client uses http.DefaultTransport.
|
||||
Transport func(context.Context) http.RoundTripper
|
||||
|
||||
// this peer's base URL, e.g. "https://example.net:8000"
|
||||
self string
|
||||
|
||||
// opts specifies the options.
|
||||
opts HTTPPoolOptions
|
||||
|
||||
mu sync.Mutex // guards peers and httpGetters
|
||||
peers *consistenthash.Map
|
||||
httpGetters map[string]*httpGetter // keyed by e.g. "http://10.0.0.2:8008"
|
||||
}
|
||||
|
||||
// HTTPPoolOptions are the configurations of a HTTPPool.
|
||||
type HTTPPoolOptions struct {
|
||||
// BasePath specifies the HTTP path that will serve groupcache requests.
|
||||
// If blank, it defaults to "/_groupcache/".
|
||||
BasePath string
|
||||
|
||||
// Replicas specifies the number of key replicas on the consistent hash.
|
||||
// If blank, it defaults to 50.
|
||||
Replicas int
|
||||
|
||||
// HashFn specifies the hash function of the consistent hash.
|
||||
// If blank, it defaults to crc32.ChecksumIEEE.
|
||||
HashFn consistenthash.Hash
|
||||
}
|
||||
|
||||
// NewHTTPPool initializes an HTTP pool of peers, and registers itself as a PeerPicker.
|
||||
// For convenience, it also registers itself as an http.Handler with http.DefaultServeMux.
|
||||
// The self argument should be a valid base URL that points to the current server,
|
||||
// for example "http://example.net:8000".
|
||||
func NewHTTPPool(self string) *HTTPPool {
|
||||
p := NewHTTPPoolOpts(self, nil)
|
||||
http.Handle(p.opts.BasePath, p)
|
||||
return p
|
||||
}
|
||||
|
||||
var httpPoolMade bool
|
||||
|
||||
// NewHTTPPoolOpts initializes an HTTP pool of peers with the given options.
|
||||
// Unlike NewHTTPPool, this function does not register the created pool as an HTTP handler.
|
||||
// The returned *HTTPPool implements http.Handler and must be registered using http.Handle.
|
||||
func NewHTTPPoolOpts(self string, o *HTTPPoolOptions) *HTTPPool {
|
||||
if httpPoolMade {
|
||||
panic("groupcache: NewHTTPPool must be called only once")
|
||||
}
|
||||
httpPoolMade = true
|
||||
|
||||
p := &HTTPPool{
|
||||
self: self,
|
||||
httpGetters: make(map[string]*httpGetter),
|
||||
}
|
||||
if o != nil {
|
||||
p.opts = *o
|
||||
}
|
||||
if p.opts.BasePath == "" {
|
||||
p.opts.BasePath = defaultBasePath
|
||||
}
|
||||
if p.opts.Replicas == 0 {
|
||||
p.opts.Replicas = defaultReplicas
|
||||
}
|
||||
p.peers = consistenthash.New(p.opts.Replicas, p.opts.HashFn)
|
||||
|
||||
RegisterPeerPicker(func() PeerPicker { return p })
|
||||
return p
|
||||
}
|
||||
|
||||
// Set updates the pool's list of peers.
|
||||
// Each peer value should be a valid base URL,
|
||||
// for example "http://example.net:8000".
|
||||
func (p *HTTPPool) Set(peers ...string) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.peers = consistenthash.New(p.opts.Replicas, p.opts.HashFn)
|
||||
p.peers.Add(peers...)
|
||||
p.httpGetters = make(map[string]*httpGetter, len(peers))
|
||||
for _, peer := range peers {
|
||||
p.httpGetters[peer] = &httpGetter{transport: p.Transport, baseURL: peer + p.opts.BasePath}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *HTTPPool) PickPeer(key string) (ProtoGetter, bool) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if p.peers.IsEmpty() {
|
||||
return nil, false
|
||||
}
|
||||
if peer := p.peers.Get(key); peer != p.self {
|
||||
return p.httpGetters[peer], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// Parse request.
|
||||
if !strings.HasPrefix(r.URL.Path, p.opts.BasePath) {
|
||||
panic("HTTPPool serving unexpected path: " + r.URL.Path)
|
||||
}
|
||||
parts := strings.SplitN(r.URL.Path[len(p.opts.BasePath):], "/", 2)
|
||||
if len(parts) != 2 {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
groupName := parts[0]
|
||||
key := parts[1]
|
||||
|
||||
// Fetch the value for this group/key.
|
||||
group := GetGroup(groupName)
|
||||
if group == nil {
|
||||
http.Error(w, "no such group: "+groupName, http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
var ctx context.Context
|
||||
if p.Context != nil {
|
||||
ctx = p.Context(r)
|
||||
} else {
|
||||
ctx = r.Context()
|
||||
}
|
||||
|
||||
group.Stats.ServerRequests.Add(1)
|
||||
var value []byte
|
||||
err := group.Get(ctx, key, AllocatingByteSliceSink(&value))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Write the value to the response body as a proto message.
|
||||
body, err := proto.Marshal(&pb.GetResponse{Value: value})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/x-protobuf")
|
||||
w.Write(body)
|
||||
}
|
||||
|
||||
type httpGetter struct {
|
||||
transport func(context.Context) http.RoundTripper
|
||||
baseURL string
|
||||
}
|
||||
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() interface{} { return new(bytes.Buffer) },
|
||||
}
|
||||
|
||||
func (h *httpGetter) Get(ctx context.Context, in *pb.GetRequest, out *pb.GetResponse) error {
|
||||
u := fmt.Sprintf(
|
||||
"%v%v/%v",
|
||||
h.baseURL,
|
||||
url.QueryEscape(in.GetGroup()),
|
||||
url.QueryEscape(in.GetKey()),
|
||||
)
|
||||
req, err := http.NewRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
tr := http.DefaultTransport
|
||||
if h.transport != nil {
|
||||
tr = h.transport(ctx)
|
||||
}
|
||||
res, err := tr.RoundTrip(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("server returned: %v", res.Status)
|
||||
}
|
||||
b := bufferPool.Get().(*bytes.Buffer)
|
||||
b.Reset()
|
||||
defer bufferPool.Put(b)
|
||||
_, err = io.Copy(b, res.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading response body: %v", err)
|
||||
}
|
||||
err = proto.Unmarshal(b.Bytes(), out)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decoding response body: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
133
vendor/github.com/golang/groupcache/lru/lru.go
generated
vendored
Normal file
133
vendor/github.com/golang/groupcache/lru/lru.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
Copyright 2013 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package lru implements an LRU cache.
|
||||
package lru
|
||||
|
||||
import "container/list"
|
||||
|
||||
// Cache is an LRU cache. It is not safe for concurrent access.
|
||||
type Cache struct {
|
||||
// MaxEntries is the maximum number of cache entries before
|
||||
// an item is evicted. Zero means no limit.
|
||||
MaxEntries int
|
||||
|
||||
// OnEvicted optionally specifies a callback function to be
|
||||
// executed when an entry is purged from the cache.
|
||||
OnEvicted func(key Key, value interface{})
|
||||
|
||||
ll *list.List
|
||||
cache map[interface{}]*list.Element
|
||||
}
|
||||
|
||||
// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators
|
||||
type Key interface{}
|
||||
|
||||
type entry struct {
|
||||
key Key
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// New creates a new Cache.
|
||||
// If maxEntries is zero, the cache has no limit and it's assumed
|
||||
// that eviction is done by the caller.
|
||||
func New(maxEntries int) *Cache {
|
||||
return &Cache{
|
||||
MaxEntries: maxEntries,
|
||||
ll: list.New(),
|
||||
cache: make(map[interface{}]*list.Element),
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a value to the cache.
|
||||
func (c *Cache) Add(key Key, value interface{}) {
|
||||
if c.cache == nil {
|
||||
c.cache = make(map[interface{}]*list.Element)
|
||||
c.ll = list.New()
|
||||
}
|
||||
if ee, ok := c.cache[key]; ok {
|
||||
c.ll.MoveToFront(ee)
|
||||
ee.Value.(*entry).value = value
|
||||
return
|
||||
}
|
||||
ele := c.ll.PushFront(&entry{key, value})
|
||||
c.cache[key] = ele
|
||||
if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries {
|
||||
c.RemoveOldest()
|
||||
}
|
||||
}
|
||||
|
||||
// Get looks up a key's value from the cache.
|
||||
func (c *Cache) Get(key Key) (value interface{}, ok bool) {
|
||||
if c.cache == nil {
|
||||
return
|
||||
}
|
||||
if ele, hit := c.cache[key]; hit {
|
||||
c.ll.MoveToFront(ele)
|
||||
return ele.Value.(*entry).value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache.
|
||||
func (c *Cache) Remove(key Key) {
|
||||
if c.cache == nil {
|
||||
return
|
||||
}
|
||||
if ele, hit := c.cache[key]; hit {
|
||||
c.removeElement(ele)
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveOldest removes the oldest item from the cache.
|
||||
func (c *Cache) RemoveOldest() {
|
||||
if c.cache == nil {
|
||||
return
|
||||
}
|
||||
ele := c.ll.Back()
|
||||
if ele != nil {
|
||||
c.removeElement(ele)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) removeElement(e *list.Element) {
|
||||
c.ll.Remove(e)
|
||||
kv := e.Value.(*entry)
|
||||
delete(c.cache, kv.key)
|
||||
if c.OnEvicted != nil {
|
||||
c.OnEvicted(kv.key, kv.value)
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the number of items in the cache.
|
||||
func (c *Cache) Len() int {
|
||||
if c.cache == nil {
|
||||
return 0
|
||||
}
|
||||
return c.ll.Len()
|
||||
}
|
||||
|
||||
// Clear purges all stored items from the cache.
|
||||
func (c *Cache) Clear() {
|
||||
if c.OnEvicted != nil {
|
||||
for _, e := range c.cache {
|
||||
kv := e.Value.(*entry)
|
||||
c.OnEvicted(kv.key, kv.value)
|
||||
}
|
||||
}
|
||||
c.ll = nil
|
||||
c.cache = nil
|
||||
}
|
||||
85
vendor/github.com/golang/groupcache/peers.go
generated
vendored
Normal file
85
vendor/github.com/golang/groupcache/peers.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2012 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// peers.go defines how processes find and communicate with their peers.
|
||||
|
||||
package groupcache
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "github.com/golang/groupcache/groupcachepb"
|
||||
)
|
||||
|
||||
// Context is an alias to context.Context for backwards compatibility purposes.
|
||||
type Context = context.Context
|
||||
|
||||
// ProtoGetter is the interface that must be implemented by a peer.
|
||||
type ProtoGetter interface {
|
||||
Get(ctx context.Context, in *pb.GetRequest, out *pb.GetResponse) error
|
||||
}
|
||||
|
||||
// PeerPicker is the interface that must be implemented to locate
|
||||
// the peer that owns a specific key.
|
||||
type PeerPicker interface {
|
||||
// PickPeer returns the peer that owns the specific key
|
||||
// and true to indicate that a remote peer was nominated.
|
||||
// It returns nil, false if the key owner is the current peer.
|
||||
PickPeer(key string) (peer ProtoGetter, ok bool)
|
||||
}
|
||||
|
||||
// NoPeers is an implementation of PeerPicker that never finds a peer.
|
||||
type NoPeers struct{}
|
||||
|
||||
func (NoPeers) PickPeer(key string) (peer ProtoGetter, ok bool) { return }
|
||||
|
||||
var (
|
||||
portPicker func(groupName string) PeerPicker
|
||||
)
|
||||
|
||||
// RegisterPeerPicker registers the peer initialization function.
|
||||
// It is called once, when the first group is created.
|
||||
// Either RegisterPeerPicker or RegisterPerGroupPeerPicker should be
|
||||
// called exactly once, but not both.
|
||||
func RegisterPeerPicker(fn func() PeerPicker) {
|
||||
if portPicker != nil {
|
||||
panic("RegisterPeerPicker called more than once")
|
||||
}
|
||||
portPicker = func(_ string) PeerPicker { return fn() }
|
||||
}
|
||||
|
||||
// RegisterPerGroupPeerPicker registers the peer initialization function,
|
||||
// which takes the groupName, to be used in choosing a PeerPicker.
|
||||
// It is called once, when the first group is created.
|
||||
// Either RegisterPeerPicker or RegisterPerGroupPeerPicker should be
|
||||
// called exactly once, but not both.
|
||||
func RegisterPerGroupPeerPicker(fn func(groupName string) PeerPicker) {
|
||||
if portPicker != nil {
|
||||
panic("RegisterPeerPicker called more than once")
|
||||
}
|
||||
portPicker = fn
|
||||
}
|
||||
|
||||
func getPeers(groupName string) PeerPicker {
|
||||
if portPicker == nil {
|
||||
return NoPeers{}
|
||||
}
|
||||
pk := portPicker(groupName)
|
||||
if pk == nil {
|
||||
pk = NoPeers{}
|
||||
}
|
||||
return pk
|
||||
}
|
||||
64
vendor/github.com/golang/groupcache/singleflight/singleflight.go
generated
vendored
Normal file
64
vendor/github.com/golang/groupcache/singleflight/singleflight.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright 2012 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package singleflight provides a duplicate function call suppression
|
||||
// mechanism.
|
||||
package singleflight
|
||||
|
||||
import "sync"
|
||||
|
||||
// call is an in-flight or completed Do call
|
||||
type call struct {
|
||||
wg sync.WaitGroup
|
||||
val interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
// Group represents a class of work and forms a namespace in which
|
||||
// units of work can be executed with duplicate suppression.
|
||||
type Group struct {
|
||||
mu sync.Mutex // protects m
|
||||
m map[string]*call // lazily initialized
|
||||
}
|
||||
|
||||
// Do executes and returns the results of the given function, making
|
||||
// sure that only one execution is in-flight for a given key at a
|
||||
// time. If a duplicate comes in, the duplicate caller waits for the
|
||||
// original to complete and receives the same results.
|
||||
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
|
||||
g.mu.Lock()
|
||||
if g.m == nil {
|
||||
g.m = make(map[string]*call)
|
||||
}
|
||||
if c, ok := g.m[key]; ok {
|
||||
g.mu.Unlock()
|
||||
c.wg.Wait()
|
||||
return c.val, c.err
|
||||
}
|
||||
c := new(call)
|
||||
c.wg.Add(1)
|
||||
g.m[key] = c
|
||||
g.mu.Unlock()
|
||||
|
||||
c.val, c.err = fn()
|
||||
c.wg.Done()
|
||||
|
||||
g.mu.Lock()
|
||||
delete(g.m, key)
|
||||
g.mu.Unlock()
|
||||
|
||||
return c.val, c.err
|
||||
}
|
||||
322
vendor/github.com/golang/groupcache/sinks.go
generated
vendored
Normal file
322
vendor/github.com/golang/groupcache/sinks.go
generated
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
Copyright 2012 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package groupcache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
// A Sink receives data from a Get call.
|
||||
//
|
||||
// Implementation of Getter must call exactly one of the Set methods
|
||||
// on success.
|
||||
type Sink interface {
|
||||
// SetString sets the value to s.
|
||||
SetString(s string) error
|
||||
|
||||
// SetBytes sets the value to the contents of v.
|
||||
// The caller retains ownership of v.
|
||||
SetBytes(v []byte) error
|
||||
|
||||
// SetProto sets the value to the encoded version of m.
|
||||
// The caller retains ownership of m.
|
||||
SetProto(m proto.Message) error
|
||||
|
||||
// view returns a frozen view of the bytes for caching.
|
||||
view() (ByteView, error)
|
||||
}
|
||||
|
||||
func cloneBytes(b []byte) []byte {
|
||||
c := make([]byte, len(b))
|
||||
copy(c, b)
|
||||
return c
|
||||
}
|
||||
|
||||
func setSinkView(s Sink, v ByteView) error {
|
||||
// A viewSetter is a Sink that can also receive its value from
|
||||
// a ByteView. This is a fast path to minimize copies when the
|
||||
// item was already cached locally in memory (where it's
|
||||
// cached as a ByteView)
|
||||
type viewSetter interface {
|
||||
setView(v ByteView) error
|
||||
}
|
||||
if vs, ok := s.(viewSetter); ok {
|
||||
return vs.setView(v)
|
||||
}
|
||||
if v.b != nil {
|
||||
return s.SetBytes(v.b)
|
||||
}
|
||||
return s.SetString(v.s)
|
||||
}
|
||||
|
||||
// StringSink returns a Sink that populates the provided string pointer.
|
||||
func StringSink(sp *string) Sink {
|
||||
return &stringSink{sp: sp}
|
||||
}
|
||||
|
||||
type stringSink struct {
|
||||
sp *string
|
||||
v ByteView
|
||||
// TODO(bradfitz): track whether any Sets were called.
|
||||
}
|
||||
|
||||
func (s *stringSink) view() (ByteView, error) {
|
||||
// TODO(bradfitz): return an error if no Set was called
|
||||
return s.v, nil
|
||||
}
|
||||
|
||||
func (s *stringSink) SetString(v string) error {
|
||||
s.v.b = nil
|
||||
s.v.s = v
|
||||
*s.sp = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringSink) SetBytes(v []byte) error {
|
||||
return s.SetString(string(v))
|
||||
}
|
||||
|
||||
func (s *stringSink) SetProto(m proto.Message) error {
|
||||
b, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.v.b = b
|
||||
*s.sp = string(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ByteViewSink returns a Sink that populates a ByteView.
|
||||
func ByteViewSink(dst *ByteView) Sink {
|
||||
if dst == nil {
|
||||
panic("nil dst")
|
||||
}
|
||||
return &byteViewSink{dst: dst}
|
||||
}
|
||||
|
||||
type byteViewSink struct {
|
||||
dst *ByteView
|
||||
|
||||
// if this code ever ends up tracking that at least one set*
|
||||
// method was called, don't make it an error to call set
|
||||
// methods multiple times. Lorry's payload.go does that, and
|
||||
// it makes sense. The comment at the top of this file about
|
||||
// "exactly one of the Set methods" is overly strict. We
|
||||
// really care about at least once (in a handler), but if
|
||||
// multiple handlers fail (or multiple functions in a program
|
||||
// using a Sink), it's okay to re-use the same one.
|
||||
}
|
||||
|
||||
func (s *byteViewSink) setView(v ByteView) error {
|
||||
*s.dst = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *byteViewSink) view() (ByteView, error) {
|
||||
return *s.dst, nil
|
||||
}
|
||||
|
||||
func (s *byteViewSink) SetProto(m proto.Message) error {
|
||||
b, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*s.dst = ByteView{b: b}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *byteViewSink) SetBytes(b []byte) error {
|
||||
*s.dst = ByteView{b: cloneBytes(b)}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *byteViewSink) SetString(v string) error {
|
||||
*s.dst = ByteView{s: v}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProtoSink returns a sink that unmarshals binary proto values into m.
|
||||
func ProtoSink(m proto.Message) Sink {
|
||||
return &protoSink{
|
||||
dst: m,
|
||||
}
|
||||
}
|
||||
|
||||
type protoSink struct {
|
||||
dst proto.Message // authoritative value
|
||||
typ string
|
||||
|
||||
v ByteView // encoded
|
||||
}
|
||||
|
||||
func (s *protoSink) view() (ByteView, error) {
|
||||
return s.v, nil
|
||||
}
|
||||
|
||||
func (s *protoSink) SetBytes(b []byte) error {
|
||||
err := proto.Unmarshal(b, s.dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.v.b = cloneBytes(b)
|
||||
s.v.s = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *protoSink) SetString(v string) error {
|
||||
b := []byte(v)
|
||||
err := proto.Unmarshal(b, s.dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.v.b = b
|
||||
s.v.s = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *protoSink) SetProto(m proto.Message) error {
|
||||
b, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(bradfitz): optimize for same-task case more and write
|
||||
// right through? would need to document ownership rules at
|
||||
// the same time. but then we could just assign *dst = *m
|
||||
// here. This works for now:
|
||||
err = proto.Unmarshal(b, s.dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.v.b = b
|
||||
s.v.s = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllocatingByteSliceSink returns a Sink that allocates
|
||||
// a byte slice to hold the received value and assigns
|
||||
// it to *dst. The memory is not retained by groupcache.
|
||||
func AllocatingByteSliceSink(dst *[]byte) Sink {
|
||||
return &allocBytesSink{dst: dst}
|
||||
}
|
||||
|
||||
type allocBytesSink struct {
|
||||
dst *[]byte
|
||||
v ByteView
|
||||
}
|
||||
|
||||
func (s *allocBytesSink) view() (ByteView, error) {
|
||||
return s.v, nil
|
||||
}
|
||||
|
||||
func (s *allocBytesSink) setView(v ByteView) error {
|
||||
if v.b != nil {
|
||||
*s.dst = cloneBytes(v.b)
|
||||
} else {
|
||||
*s.dst = []byte(v.s)
|
||||
}
|
||||
s.v = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *allocBytesSink) SetProto(m proto.Message) error {
|
||||
b, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.setBytesOwned(b)
|
||||
}
|
||||
|
||||
func (s *allocBytesSink) SetBytes(b []byte) error {
|
||||
return s.setBytesOwned(cloneBytes(b))
|
||||
}
|
||||
|
||||
func (s *allocBytesSink) setBytesOwned(b []byte) error {
|
||||
if s.dst == nil {
|
||||
return errors.New("nil AllocatingByteSliceSink *[]byte dst")
|
||||
}
|
||||
*s.dst = cloneBytes(b) // another copy, protecting the read-only s.v.b view
|
||||
s.v.b = b
|
||||
s.v.s = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *allocBytesSink) SetString(v string) error {
|
||||
if s.dst == nil {
|
||||
return errors.New("nil AllocatingByteSliceSink *[]byte dst")
|
||||
}
|
||||
*s.dst = []byte(v)
|
||||
s.v.b = nil
|
||||
s.v.s = v
|
||||
return nil
|
||||
}
|
||||
|
||||
// TruncatingByteSliceSink returns a Sink that writes up to len(*dst)
|
||||
// bytes to *dst. If more bytes are available, they're silently
|
||||
// truncated. If fewer bytes are available than len(*dst), *dst
|
||||
// is shrunk to fit the number of bytes available.
|
||||
func TruncatingByteSliceSink(dst *[]byte) Sink {
|
||||
return &truncBytesSink{dst: dst}
|
||||
}
|
||||
|
||||
type truncBytesSink struct {
|
||||
dst *[]byte
|
||||
v ByteView
|
||||
}
|
||||
|
||||
func (s *truncBytesSink) view() (ByteView, error) {
|
||||
return s.v, nil
|
||||
}
|
||||
|
||||
func (s *truncBytesSink) SetProto(m proto.Message) error {
|
||||
b, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.setBytesOwned(b)
|
||||
}
|
||||
|
||||
func (s *truncBytesSink) SetBytes(b []byte) error {
|
||||
return s.setBytesOwned(cloneBytes(b))
|
||||
}
|
||||
|
||||
func (s *truncBytesSink) setBytesOwned(b []byte) error {
|
||||
if s.dst == nil {
|
||||
return errors.New("nil TruncatingByteSliceSink *[]byte dst")
|
||||
}
|
||||
n := copy(*s.dst, b)
|
||||
if n < len(*s.dst) {
|
||||
*s.dst = (*s.dst)[:n]
|
||||
}
|
||||
s.v.b = b
|
||||
s.v.s = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *truncBytesSink) SetString(v string) error {
|
||||
if s.dst == nil {
|
||||
return errors.New("nil TruncatingByteSliceSink *[]byte dst")
|
||||
}
|
||||
n := copy(*s.dst, v)
|
||||
if n < len(*s.dst) {
|
||||
*s.dst = (*s.dst)[:n]
|
||||
}
|
||||
s.v.b = nil
|
||||
s.v.s = v
|
||||
return nil
|
||||
}
|
||||
12
vendor/github.com/golang/mock/AUTHORS
generated
vendored
Normal file
12
vendor/github.com/golang/mock/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# This is the official list of GoMock authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Alex Reece <awreece@gmail.com>
|
||||
Google Inc.
|
||||
37
vendor/github.com/golang/mock/CONTRIBUTORS
generated
vendored
Normal file
37
vendor/github.com/golang/mock/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# This is the official list of people who can contribute (and typically
|
||||
# have contributed) code to the gomock repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# The submission process automatically checks to make sure
|
||||
# that people submitting code are listed in this file (by email address).
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# http://code.google.com/legal/individual-cla-v1.0.html
|
||||
# http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
#
|
||||
# The agreement for individuals can be filled out on the web.
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
#
|
||||
# An entry with two email addresses specifies that the
|
||||
# first address should be used in the submit logs and
|
||||
# that the second address should be recognized as the
|
||||
# same person when interacting with Rietveld.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Aaron Jacobs <jacobsa@google.com> <aaronjjacobs@gmail.com>
|
||||
Alex Reece <awreece@gmail.com>
|
||||
David Symonds <dsymonds@golang.org>
|
||||
Ryan Barrett <ryanb@google.com>
|
||||
202
vendor/github.com/golang/mock/LICENSE
generated
vendored
Normal file
202
vendor/github.com/golang/mock/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
445
vendor/github.com/golang/mock/gomock/call.go
generated
vendored
Normal file
445
vendor/github.com/golang/mock/gomock/call.go
generated
vendored
Normal file
@@ -0,0 +1,445 @@
|
||||
// Copyright 2010 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gomock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Call represents an expected call to a mock.
|
||||
type Call struct {
|
||||
t TestHelper // for triggering test failures on invalid call setup
|
||||
|
||||
receiver interface{} // the receiver of the method call
|
||||
method string // the name of the method
|
||||
methodType reflect.Type // the type of the method
|
||||
args []Matcher // the args
|
||||
origin string // file and line number of call setup
|
||||
|
||||
preReqs []*Call // prerequisite calls
|
||||
|
||||
// Expectations
|
||||
minCalls, maxCalls int
|
||||
|
||||
numCalls int // actual number made
|
||||
|
||||
// actions are called when this Call is called. Each action gets the args and
|
||||
// can set the return values by returning a non-nil slice. Actions run in the
|
||||
// order they are created.
|
||||
actions []func([]interface{}) []interface{}
|
||||
}
|
||||
|
||||
// newCall creates a *Call. It requires the method type in order to support
|
||||
// unexported methods.
|
||||
func newCall(t TestHelper, receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {
|
||||
t.Helper()
|
||||
|
||||
// TODO: check arity, types.
|
||||
mArgs := make([]Matcher, len(args))
|
||||
for i, arg := range args {
|
||||
if m, ok := arg.(Matcher); ok {
|
||||
mArgs[i] = m
|
||||
} else if arg == nil {
|
||||
// Handle nil specially so that passing a nil interface value
|
||||
// will match the typed nils of concrete args.
|
||||
mArgs[i] = Nil()
|
||||
} else {
|
||||
mArgs[i] = Eq(arg)
|
||||
}
|
||||
}
|
||||
|
||||
// callerInfo's skip should be updated if the number of calls between the user's test
|
||||
// and this line changes, i.e. this code is wrapped in another anonymous function.
|
||||
// 0 is us, 1 is RecordCallWithMethodType(), 2 is the generated recorder, and 3 is the user's test.
|
||||
origin := callerInfo(3)
|
||||
actions := []func([]interface{}) []interface{}{func([]interface{}) []interface{} {
|
||||
// Synthesize the zero value for each of the return args' types.
|
||||
rets := make([]interface{}, methodType.NumOut())
|
||||
for i := 0; i < methodType.NumOut(); i++ {
|
||||
rets[i] = reflect.Zero(methodType.Out(i)).Interface()
|
||||
}
|
||||
return rets
|
||||
}}
|
||||
return &Call{t: t, receiver: receiver, method: method, methodType: methodType,
|
||||
args: mArgs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions}
|
||||
}
|
||||
|
||||
// AnyTimes allows the expectation to be called 0 or more times
|
||||
func (c *Call) AnyTimes() *Call {
|
||||
c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity
|
||||
return c
|
||||
}
|
||||
|
||||
// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called or if MaxTimes
|
||||
// was previously called with 1, MinTimes also sets the maximum number of calls to infinity.
|
||||
func (c *Call) MinTimes(n int) *Call {
|
||||
c.minCalls = n
|
||||
if c.maxCalls == 1 {
|
||||
c.maxCalls = 1e8
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called or if MinTimes was
|
||||
// previously called with 1, MaxTimes also sets the minimum number of calls to 0.
|
||||
func (c *Call) MaxTimes(n int) *Call {
|
||||
c.maxCalls = n
|
||||
if c.minCalls == 1 {
|
||||
c.minCalls = 0
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn declares the action to run when the call is matched.
|
||||
// The return values from this function are returned by the mocked function.
|
||||
// It takes an interface{} argument to support n-arity functions.
|
||||
func (c *Call) DoAndReturn(f interface{}) *Call {
|
||||
// TODO: Check arity and types here, rather than dying badly elsewhere.
|
||||
v := reflect.ValueOf(f)
|
||||
|
||||
c.addAction(func(args []interface{}) []interface{} {
|
||||
c.t.Helper()
|
||||
vArgs := make([]reflect.Value, len(args))
|
||||
ft := v.Type()
|
||||
if c.methodType.NumIn() != ft.NumIn() {
|
||||
c.t.Fatalf("wrong number of arguments in DoAndReturn func for %T.%v: got %d, want %d [%s]",
|
||||
c.receiver, c.method, ft.NumIn(), c.methodType.NumIn(), c.origin)
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < len(args); i++ {
|
||||
if args[i] != nil {
|
||||
vArgs[i] = reflect.ValueOf(args[i])
|
||||
} else {
|
||||
// Use the zero value for the arg.
|
||||
vArgs[i] = reflect.Zero(ft.In(i))
|
||||
}
|
||||
}
|
||||
vRets := v.Call(vArgs)
|
||||
rets := make([]interface{}, len(vRets))
|
||||
for i, ret := range vRets {
|
||||
rets[i] = ret.Interface()
|
||||
}
|
||||
return rets
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
// Do declares the action to run when the call is matched. The function's
|
||||
// return values are ignored to retain backward compatibility. To use the
|
||||
// return values call DoAndReturn.
|
||||
// It takes an interface{} argument to support n-arity functions.
|
||||
func (c *Call) Do(f interface{}) *Call {
|
||||
// TODO: Check arity and types here, rather than dying badly elsewhere.
|
||||
v := reflect.ValueOf(f)
|
||||
|
||||
c.addAction(func(args []interface{}) []interface{} {
|
||||
c.t.Helper()
|
||||
if c.methodType.NumIn() != v.Type().NumIn() {
|
||||
c.t.Fatalf("wrong number of arguments in Do func for %T.%v: got %d, want %d [%s]",
|
||||
c.receiver, c.method, v.Type().NumIn(), c.methodType.NumIn(), c.origin)
|
||||
return nil
|
||||
}
|
||||
vArgs := make([]reflect.Value, len(args))
|
||||
ft := v.Type()
|
||||
for i := 0; i < len(args); i++ {
|
||||
if args[i] != nil {
|
||||
vArgs[i] = reflect.ValueOf(args[i])
|
||||
} else {
|
||||
// Use the zero value for the arg.
|
||||
vArgs[i] = reflect.Zero(ft.In(i))
|
||||
}
|
||||
}
|
||||
v.Call(vArgs)
|
||||
return nil
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
// Return declares the values to be returned by the mocked function call.
|
||||
func (c *Call) Return(rets ...interface{}) *Call {
|
||||
c.t.Helper()
|
||||
|
||||
mt := c.methodType
|
||||
if len(rets) != mt.NumOut() {
|
||||
c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]",
|
||||
c.receiver, c.method, len(rets), mt.NumOut(), c.origin)
|
||||
}
|
||||
for i, ret := range rets {
|
||||
if got, want := reflect.TypeOf(ret), mt.Out(i); got == want {
|
||||
// Identical types; nothing to do.
|
||||
} else if got == nil {
|
||||
// Nil needs special handling.
|
||||
switch want.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
// ok
|
||||
default:
|
||||
c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]",
|
||||
i, c.receiver, c.method, want, c.origin)
|
||||
}
|
||||
} else if got.AssignableTo(want) {
|
||||
// Assignable type relation. Make the assignment now so that the generated code
|
||||
// can return the values with a type assertion.
|
||||
v := reflect.New(want).Elem()
|
||||
v.Set(reflect.ValueOf(ret))
|
||||
rets[i] = v.Interface()
|
||||
} else {
|
||||
c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]",
|
||||
i, c.receiver, c.method, got, want, c.origin)
|
||||
}
|
||||
}
|
||||
|
||||
c.addAction(func([]interface{}) []interface{} {
|
||||
return rets
|
||||
})
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Times declares the exact number of times a function call is expected to be executed.
|
||||
func (c *Call) Times(n int) *Call {
|
||||
c.minCalls, c.maxCalls = n, n
|
||||
return c
|
||||
}
|
||||
|
||||
// SetArg declares an action that will set the nth argument's value,
|
||||
// indirected through a pointer. Or, in the case of a slice, SetArg
|
||||
// will copy value's elements into the nth argument.
|
||||
func (c *Call) SetArg(n int, value interface{}) *Call {
|
||||
c.t.Helper()
|
||||
|
||||
mt := c.methodType
|
||||
// TODO: This will break on variadic methods.
|
||||
// We will need to check those at invocation time.
|
||||
if n < 0 || n >= mt.NumIn() {
|
||||
c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]",
|
||||
n, mt.NumIn(), c.origin)
|
||||
}
|
||||
// Permit setting argument through an interface.
|
||||
// In the interface case, we don't (nay, can't) check the type here.
|
||||
at := mt.In(n)
|
||||
switch at.Kind() {
|
||||
case reflect.Ptr:
|
||||
dt := at.Elem()
|
||||
if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) {
|
||||
c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]",
|
||||
n, vt, dt, c.origin)
|
||||
}
|
||||
case reflect.Interface:
|
||||
// nothing to do
|
||||
case reflect.Slice:
|
||||
// nothing to do
|
||||
default:
|
||||
c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]",
|
||||
n, at, c.origin)
|
||||
}
|
||||
|
||||
c.addAction(func(args []interface{}) []interface{} {
|
||||
v := reflect.ValueOf(value)
|
||||
switch reflect.TypeOf(args[n]).Kind() {
|
||||
case reflect.Slice:
|
||||
setSlice(args[n], v)
|
||||
default:
|
||||
reflect.ValueOf(args[n]).Elem().Set(v)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
// isPreReq returns true if other is a direct or indirect prerequisite to c.
|
||||
func (c *Call) isPreReq(other *Call) bool {
|
||||
for _, preReq := range c.preReqs {
|
||||
if other == preReq || preReq.isPreReq(other) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// After declares that the call may only match after preReq has been exhausted.
|
||||
func (c *Call) After(preReq *Call) *Call {
|
||||
c.t.Helper()
|
||||
|
||||
if c == preReq {
|
||||
c.t.Fatalf("A call isn't allowed to be its own prerequisite")
|
||||
}
|
||||
if preReq.isPreReq(c) {
|
||||
c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq)
|
||||
}
|
||||
|
||||
c.preReqs = append(c.preReqs, preReq)
|
||||
return c
|
||||
}
|
||||
|
||||
// Returns true if the minimum number of calls have been made.
|
||||
func (c *Call) satisfied() bool {
|
||||
return c.numCalls >= c.minCalls
|
||||
}
|
||||
|
||||
// Returns true if the maximum number of calls have been made.
|
||||
func (c *Call) exhausted() bool {
|
||||
return c.numCalls >= c.maxCalls
|
||||
}
|
||||
|
||||
func (c *Call) String() string {
|
||||
args := make([]string, len(c.args))
|
||||
for i, arg := range c.args {
|
||||
args[i] = arg.String()
|
||||
}
|
||||
arguments := strings.Join(args, ", ")
|
||||
return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin)
|
||||
}
|
||||
|
||||
// Tests if the given call matches the expected call.
|
||||
// If yes, returns nil. If no, returns error with message explaining why it does not match.
|
||||
func (c *Call) matches(args []interface{}) error {
|
||||
if !c.methodType.IsVariadic() {
|
||||
if len(args) != len(c.args) {
|
||||
return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d",
|
||||
c.origin, len(args), len(c.args))
|
||||
}
|
||||
|
||||
for i, m := range c.args {
|
||||
if !m.Matches(args[i]) {
|
||||
return fmt.Errorf(
|
||||
"expected call at %s doesn't match the argument at index %d.\nGot: %v\nWant: %v",
|
||||
c.origin, i, formatGottenArg(m, args[i]), m,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if len(c.args) < c.methodType.NumIn()-1 {
|
||||
return fmt.Errorf("expected call at %s has the wrong number of matchers. Got: %d, want: %d",
|
||||
c.origin, len(c.args), c.methodType.NumIn()-1)
|
||||
}
|
||||
if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) {
|
||||
return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d",
|
||||
c.origin, len(args), len(c.args))
|
||||
}
|
||||
if len(args) < len(c.args)-1 {
|
||||
return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d",
|
||||
c.origin, len(args), len(c.args)-1)
|
||||
}
|
||||
|
||||
for i, m := range c.args {
|
||||
if i < c.methodType.NumIn()-1 {
|
||||
// Non-variadic args
|
||||
if !m.Matches(args[i]) {
|
||||
return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
|
||||
c.origin, strconv.Itoa(i), formatGottenArg(m, args[i]), m)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// The last arg has a possibility of a variadic argument, so let it branch
|
||||
|
||||
// sample: Foo(a int, b int, c ...int)
|
||||
if i < len(c.args) && i < len(args) {
|
||||
if m.Matches(args[i]) {
|
||||
// Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any())
|
||||
// Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher)
|
||||
// Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC)
|
||||
// Got Foo(a, b) want Foo(matcherA, matcherB)
|
||||
// Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// The number of actual args don't match the number of matchers,
|
||||
// or the last matcher is a slice and the last arg is not.
|
||||
// If this function still matches it is because the last matcher
|
||||
// matches all the remaining arguments or the lack of any.
|
||||
// Convert the remaining arguments, if any, into a slice of the
|
||||
// expected type.
|
||||
vArgsType := c.methodType.In(c.methodType.NumIn() - 1)
|
||||
vArgs := reflect.MakeSlice(vArgsType, 0, len(args)-i)
|
||||
for _, arg := range args[i:] {
|
||||
vArgs = reflect.Append(vArgs, reflect.ValueOf(arg))
|
||||
}
|
||||
if m.Matches(vArgs.Interface()) {
|
||||
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any())
|
||||
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher)
|
||||
// Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any())
|
||||
// Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher)
|
||||
break
|
||||
}
|
||||
// Wrong number of matchers or not match. Fail.
|
||||
// Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD)
|
||||
// Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD)
|
||||
// Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE)
|
||||
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD)
|
||||
// Got Foo(a, b, c) want Foo(matcherA, matcherB)
|
||||
|
||||
return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
|
||||
c.origin, strconv.Itoa(i), formatGottenArg(m, args[i:]), c.args[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all prerequisite calls have been satisfied.
|
||||
for _, preReqCall := range c.preReqs {
|
||||
if !preReqCall.satisfied() {
|
||||
return fmt.Errorf("expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v",
|
||||
c.origin, preReqCall, c)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the call is not exhausted.
|
||||
if c.exhausted() {
|
||||
return fmt.Errorf("expected call at %s has already been called the max number of times", c.origin)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dropPrereqs tells the expected Call to not re-check prerequisite calls any
|
||||
// longer, and to return its current set.
|
||||
func (c *Call) dropPrereqs() (preReqs []*Call) {
|
||||
preReqs = c.preReqs
|
||||
c.preReqs = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Call) call() []func([]interface{}) []interface{} {
|
||||
c.numCalls++
|
||||
return c.actions
|
||||
}
|
||||
|
||||
// InOrder declares that the given calls should occur in order.
|
||||
func InOrder(calls ...*Call) {
|
||||
for i := 1; i < len(calls); i++ {
|
||||
calls[i].After(calls[i-1])
|
||||
}
|
||||
}
|
||||
|
||||
func setSlice(arg interface{}, v reflect.Value) {
|
||||
va := reflect.ValueOf(arg)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
va.Index(i).Set(v.Index(i))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Call) addAction(action func([]interface{}) []interface{}) {
|
||||
c.actions = append(c.actions, action)
|
||||
}
|
||||
|
||||
func formatGottenArg(m Matcher, arg interface{}) string {
|
||||
got := fmt.Sprintf("%v (%T)", arg, arg)
|
||||
if gs, ok := m.(GotFormatter); ok {
|
||||
got = gs.Got(arg)
|
||||
}
|
||||
return got
|
||||
}
|
||||
113
vendor/github.com/golang/mock/gomock/callset.go
generated
vendored
Normal file
113
vendor/github.com/golang/mock/gomock/callset.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2011 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gomock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// callSet represents a set of expected calls, indexed by receiver and method
|
||||
// name.
|
||||
type callSet struct {
|
||||
// Calls that are still expected.
|
||||
expected map[callSetKey][]*Call
|
||||
// Calls that have been exhausted.
|
||||
exhausted map[callSetKey][]*Call
|
||||
}
|
||||
|
||||
// callSetKey is the key in the maps in callSet
|
||||
type callSetKey struct {
|
||||
receiver interface{}
|
||||
fname string
|
||||
}
|
||||
|
||||
func newCallSet() *callSet {
|
||||
return &callSet{make(map[callSetKey][]*Call), make(map[callSetKey][]*Call)}
|
||||
}
|
||||
|
||||
// Add adds a new expected call.
|
||||
func (cs callSet) Add(call *Call) {
|
||||
key := callSetKey{call.receiver, call.method}
|
||||
m := cs.expected
|
||||
if call.exhausted() {
|
||||
m = cs.exhausted
|
||||
}
|
||||
m[key] = append(m[key], call)
|
||||
}
|
||||
|
||||
// Remove removes an expected call.
|
||||
func (cs callSet) Remove(call *Call) {
|
||||
key := callSetKey{call.receiver, call.method}
|
||||
calls := cs.expected[key]
|
||||
for i, c := range calls {
|
||||
if c == call {
|
||||
// maintain order for remaining calls
|
||||
cs.expected[key] = append(calls[:i], calls[i+1:]...)
|
||||
cs.exhausted[key] = append(cs.exhausted[key], call)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FindMatch searches for a matching call. Returns error with explanation message if no call matched.
|
||||
func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) {
|
||||
key := callSetKey{receiver, method}
|
||||
|
||||
// Search through the expected calls.
|
||||
expected := cs.expected[key]
|
||||
var callsErrors bytes.Buffer
|
||||
for _, call := range expected {
|
||||
err := call.matches(args)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(&callsErrors, "\n%v", err)
|
||||
} else {
|
||||
return call, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't found a match then search through the exhausted calls so we
|
||||
// get useful error messages.
|
||||
exhausted := cs.exhausted[key]
|
||||
for _, call := range exhausted {
|
||||
if err := call.matches(args); err != nil {
|
||||
_, _ = fmt.Fprintf(&callsErrors, "\n%v", err)
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintf(
|
||||
&callsErrors, "all expected calls for method %q have been exhausted", method,
|
||||
)
|
||||
}
|
||||
|
||||
if len(expected)+len(exhausted) == 0 {
|
||||
_, _ = fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method)
|
||||
}
|
||||
|
||||
return nil, errors.New(callsErrors.String())
|
||||
}
|
||||
|
||||
// Failures returns the calls that are not satisfied.
|
||||
func (cs callSet) Failures() []*Call {
|
||||
failures := make([]*Call, 0, len(cs.expected))
|
||||
for _, calls := range cs.expected {
|
||||
for _, call := range calls {
|
||||
if !call.satisfied() {
|
||||
failures = append(failures, call)
|
||||
}
|
||||
}
|
||||
}
|
||||
return failures
|
||||
}
|
||||
336
vendor/github.com/golang/mock/gomock/controller.go
generated
vendored
Normal file
336
vendor/github.com/golang/mock/gomock/controller.go
generated
vendored
Normal file
@@ -0,0 +1,336 @@
|
||||
// Copyright 2010 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package gomock is a mock framework for Go.
|
||||
//
|
||||
// Standard usage:
|
||||
// (1) Define an interface that you wish to mock.
|
||||
// type MyInterface interface {
|
||||
// SomeMethod(x int64, y string)
|
||||
// }
|
||||
// (2) Use mockgen to generate a mock from the interface.
|
||||
// (3) Use the mock in a test:
|
||||
// func TestMyThing(t *testing.T) {
|
||||
// mockCtrl := gomock.NewController(t)
|
||||
// defer mockCtrl.Finish()
|
||||
//
|
||||
// mockObj := something.NewMockMyInterface(mockCtrl)
|
||||
// mockObj.EXPECT().SomeMethod(4, "blah")
|
||||
// // pass mockObj to a real object and play with it.
|
||||
// }
|
||||
//
|
||||
// By default, expected calls are not enforced to run in any particular order.
|
||||
// Call order dependency can be enforced by use of InOrder and/or Call.After.
|
||||
// Call.After can create more varied call order dependencies, but InOrder is
|
||||
// often more convenient.
|
||||
//
|
||||
// The following examples create equivalent call order dependencies.
|
||||
//
|
||||
// Example of using Call.After to chain expected call order:
|
||||
//
|
||||
// firstCall := mockObj.EXPECT().SomeMethod(1, "first")
|
||||
// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall)
|
||||
// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall)
|
||||
//
|
||||
// Example of using InOrder to declare expected call order:
|
||||
//
|
||||
// gomock.InOrder(
|
||||
// mockObj.EXPECT().SomeMethod(1, "first"),
|
||||
// mockObj.EXPECT().SomeMethod(2, "second"),
|
||||
// mockObj.EXPECT().SomeMethod(3, "third"),
|
||||
// )
|
||||
package gomock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A TestReporter is something that can be used to report test failures. It
|
||||
// is satisfied by the standard library's *testing.T.
|
||||
type TestReporter interface {
|
||||
Errorf(format string, args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// TestHelper is a TestReporter that has the Helper method. It is satisfied
|
||||
// by the standard library's *testing.T.
|
||||
type TestHelper interface {
|
||||
TestReporter
|
||||
Helper()
|
||||
}
|
||||
|
||||
// cleanuper is used to check if TestHelper also has the `Cleanup` method. A
|
||||
// common pattern is to pass in a `*testing.T` to
|
||||
// `NewController(t TestReporter)`. In Go 1.14+, `*testing.T` has a cleanup
|
||||
// method. This can be utilized to call `Finish()` so the caller of this library
|
||||
// does not have to.
|
||||
type cleanuper interface {
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// A Controller represents the top-level control of a mock ecosystem. It
|
||||
// defines the scope and lifetime of mock objects, as well as their
|
||||
// expectations. It is safe to call Controller's methods from multiple
|
||||
// goroutines. Each test should create a new Controller and invoke Finish via
|
||||
// defer.
|
||||
//
|
||||
// func TestFoo(t *testing.T) {
|
||||
// ctrl := gomock.NewController(t)
|
||||
// defer ctrl.Finish()
|
||||
// // ..
|
||||
// }
|
||||
//
|
||||
// func TestBar(t *testing.T) {
|
||||
// t.Run("Sub-Test-1", st) {
|
||||
// ctrl := gomock.NewController(st)
|
||||
// defer ctrl.Finish()
|
||||
// // ..
|
||||
// })
|
||||
// t.Run("Sub-Test-2", st) {
|
||||
// ctrl := gomock.NewController(st)
|
||||
// defer ctrl.Finish()
|
||||
// // ..
|
||||
// })
|
||||
// })
|
||||
type Controller struct {
|
||||
// T should only be called within a generated mock. It is not intended to
|
||||
// be used in user code and may be changed in future versions. T is the
|
||||
// TestReporter passed in when creating the Controller via NewController.
|
||||
// If the TestReporter does not implement a TestHelper it will be wrapped
|
||||
// with a nopTestHelper.
|
||||
T TestHelper
|
||||
mu sync.Mutex
|
||||
expectedCalls *callSet
|
||||
finished bool
|
||||
}
|
||||
|
||||
// NewController returns a new Controller. It is the preferred way to create a
|
||||
// Controller.
|
||||
//
|
||||
// New in go1.14+, if you are passing a *testing.T into this function you no
|
||||
// longer need to call ctrl.Finish() in your test methods.
|
||||
func NewController(t TestReporter) *Controller {
|
||||
h, ok := t.(TestHelper)
|
||||
if !ok {
|
||||
h = &nopTestHelper{t}
|
||||
}
|
||||
ctrl := &Controller{
|
||||
T: h,
|
||||
expectedCalls: newCallSet(),
|
||||
}
|
||||
if c, ok := isCleanuper(ctrl.T); ok {
|
||||
c.Cleanup(func() {
|
||||
ctrl.T.Helper()
|
||||
ctrl.finish(true, nil)
|
||||
})
|
||||
}
|
||||
|
||||
return ctrl
|
||||
}
|
||||
|
||||
type cancelReporter struct {
|
||||
t TestHelper
|
||||
cancel func()
|
||||
}
|
||||
|
||||
func (r *cancelReporter) Errorf(format string, args ...interface{}) {
|
||||
r.t.Errorf(format, args...)
|
||||
}
|
||||
func (r *cancelReporter) Fatalf(format string, args ...interface{}) {
|
||||
defer r.cancel()
|
||||
r.t.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
func (r *cancelReporter) Helper() {
|
||||
r.t.Helper()
|
||||
}
|
||||
|
||||
// WithContext returns a new Controller and a Context, which is cancelled on any
|
||||
// fatal failure.
|
||||
func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) {
|
||||
h, ok := t.(TestHelper)
|
||||
if !ok {
|
||||
h = &nopTestHelper{t: t}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return NewController(&cancelReporter{t: h, cancel: cancel}), ctx
|
||||
}
|
||||
|
||||
type nopTestHelper struct {
|
||||
t TestReporter
|
||||
}
|
||||
|
||||
func (h *nopTestHelper) Errorf(format string, args ...interface{}) {
|
||||
h.t.Errorf(format, args...)
|
||||
}
|
||||
func (h *nopTestHelper) Fatalf(format string, args ...interface{}) {
|
||||
h.t.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
func (h nopTestHelper) Helper() {}
|
||||
|
||||
// RecordCall is called by a mock. It should not be called by user code.
|
||||
func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call {
|
||||
ctrl.T.Helper()
|
||||
|
||||
recv := reflect.ValueOf(receiver)
|
||||
for i := 0; i < recv.Type().NumMethod(); i++ {
|
||||
if recv.Type().Method(i).Name == method {
|
||||
return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...)
|
||||
}
|
||||
}
|
||||
ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// RecordCallWithMethodType is called by a mock. It should not be called by user code.
|
||||
func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {
|
||||
ctrl.T.Helper()
|
||||
|
||||
call := newCall(ctrl.T, receiver, method, methodType, args...)
|
||||
|
||||
ctrl.mu.Lock()
|
||||
defer ctrl.mu.Unlock()
|
||||
ctrl.expectedCalls.Add(call)
|
||||
|
||||
return call
|
||||
}
|
||||
|
||||
// Call is called by a mock. It should not be called by user code.
|
||||
func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} {
|
||||
ctrl.T.Helper()
|
||||
|
||||
// Nest this code so we can use defer to make sure the lock is released.
|
||||
actions := func() []func([]interface{}) []interface{} {
|
||||
ctrl.T.Helper()
|
||||
ctrl.mu.Lock()
|
||||
defer ctrl.mu.Unlock()
|
||||
|
||||
expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args)
|
||||
if err != nil {
|
||||
// callerInfo's skip should be updated if the number of calls between the user's test
|
||||
// and this line changes, i.e. this code is wrapped in another anonymous function.
|
||||
// 0 is us, 1 is controller.Call(), 2 is the generated mock, and 3 is the user's test.
|
||||
origin := callerInfo(3)
|
||||
ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err)
|
||||
}
|
||||
|
||||
// Two things happen here:
|
||||
// * the matching call no longer needs to check prerequite calls,
|
||||
// * and the prerequite calls are no longer expected, so remove them.
|
||||
preReqCalls := expected.dropPrereqs()
|
||||
for _, preReqCall := range preReqCalls {
|
||||
ctrl.expectedCalls.Remove(preReqCall)
|
||||
}
|
||||
|
||||
actions := expected.call()
|
||||
if expected.exhausted() {
|
||||
ctrl.expectedCalls.Remove(expected)
|
||||
}
|
||||
return actions
|
||||
}()
|
||||
|
||||
var rets []interface{}
|
||||
for _, action := range actions {
|
||||
if r := action(args); r != nil {
|
||||
rets = r
|
||||
}
|
||||
}
|
||||
|
||||
return rets
|
||||
}
|
||||
|
||||
// Finish checks to see if all the methods that were expected to be called
|
||||
// were called. It should be invoked for each Controller. It is not idempotent
|
||||
// and therefore can only be invoked once.
|
||||
//
|
||||
// New in go1.14+, if you are passing a *testing.T into NewController function you no
|
||||
// longer need to call ctrl.Finish() in your test methods.
|
||||
func (ctrl *Controller) Finish() {
|
||||
// If we're currently panicking, probably because this is a deferred call.
|
||||
// This must be recovered in the deferred function.
|
||||
err := recover()
|
||||
ctrl.finish(false, err)
|
||||
}
|
||||
|
||||
func (ctrl *Controller) finish(cleanup bool, panicErr interface{}) {
|
||||
ctrl.T.Helper()
|
||||
|
||||
ctrl.mu.Lock()
|
||||
defer ctrl.mu.Unlock()
|
||||
|
||||
if ctrl.finished {
|
||||
if _, ok := isCleanuper(ctrl.T); !ok {
|
||||
ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.")
|
||||
}
|
||||
return
|
||||
}
|
||||
ctrl.finished = true
|
||||
|
||||
// Short-circuit, pass through the panic.
|
||||
if panicErr != nil {
|
||||
panic(panicErr)
|
||||
}
|
||||
|
||||
// Check that all remaining expected calls are satisfied.
|
||||
failures := ctrl.expectedCalls.Failures()
|
||||
for _, call := range failures {
|
||||
ctrl.T.Errorf("missing call(s) to %v", call)
|
||||
}
|
||||
if len(failures) != 0 {
|
||||
if !cleanup {
|
||||
ctrl.T.Fatalf("aborting test due to missing call(s)")
|
||||
return
|
||||
}
|
||||
ctrl.T.Errorf("aborting test due to missing call(s)")
|
||||
}
|
||||
}
|
||||
|
||||
// callerInfo returns the file:line of the call site. skip is the number
|
||||
// of stack frames to skip when reporting. 0 is callerInfo's call site.
|
||||
func callerInfo(skip int) string {
|
||||
if _, file, line, ok := runtime.Caller(skip + 1); ok {
|
||||
return fmt.Sprintf("%s:%d", file, line)
|
||||
}
|
||||
return "unknown file"
|
||||
}
|
||||
|
||||
// isCleanuper checks it if t's base TestReporter has a Cleanup method.
|
||||
func isCleanuper(t TestReporter) (cleanuper, bool) {
|
||||
tr := unwrapTestReporter(t)
|
||||
c, ok := tr.(cleanuper)
|
||||
return c, ok
|
||||
}
|
||||
|
||||
// unwrapTestReporter unwraps TestReporter to the base implementation.
|
||||
func unwrapTestReporter(t TestReporter) TestReporter {
|
||||
tr := t
|
||||
switch nt := t.(type) {
|
||||
case *cancelReporter:
|
||||
tr = nt.t
|
||||
if h, check := tr.(*nopTestHelper); check {
|
||||
tr = h.t
|
||||
}
|
||||
case *nopTestHelper:
|
||||
tr = nt.t
|
||||
default:
|
||||
// not wrapped
|
||||
}
|
||||
return tr
|
||||
}
|
||||
341
vendor/github.com/golang/mock/gomock/matchers.go
generated
vendored
Normal file
341
vendor/github.com/golang/mock/gomock/matchers.go
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
// Copyright 2010 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gomock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A Matcher is a representation of a class of values.
|
||||
// It is used to represent the valid or expected arguments to a mocked method.
|
||||
type Matcher interface {
|
||||
// Matches returns whether x is a match.
|
||||
Matches(x interface{}) bool
|
||||
|
||||
// String describes what the matcher matches.
|
||||
String() string
|
||||
}
|
||||
|
||||
// WantFormatter modifies the given Matcher's String() method to the given
|
||||
// Stringer. This allows for control on how the "Want" is formatted when
|
||||
// printing .
|
||||
func WantFormatter(s fmt.Stringer, m Matcher) Matcher {
|
||||
type matcher interface {
|
||||
Matches(x interface{}) bool
|
||||
}
|
||||
|
||||
return struct {
|
||||
matcher
|
||||
fmt.Stringer
|
||||
}{
|
||||
matcher: m,
|
||||
Stringer: s,
|
||||
}
|
||||
}
|
||||
|
||||
// StringerFunc type is an adapter to allow the use of ordinary functions as
|
||||
// a Stringer. If f is a function with the appropriate signature,
|
||||
// StringerFunc(f) is a Stringer that calls f.
|
||||
type StringerFunc func() string
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (f StringerFunc) String() string {
|
||||
return f()
|
||||
}
|
||||
|
||||
// GotFormatter is used to better print failure messages. If a matcher
|
||||
// implements GotFormatter, it will use the result from Got when printing
|
||||
// the failure message.
|
||||
type GotFormatter interface {
|
||||
// Got is invoked with the received value. The result is used when
|
||||
// printing the failure message.
|
||||
Got(got interface{}) string
|
||||
}
|
||||
|
||||
// GotFormatterFunc type is an adapter to allow the use of ordinary
|
||||
// functions as a GotFormatter. If f is a function with the appropriate
|
||||
// signature, GotFormatterFunc(f) is a GotFormatter that calls f.
|
||||
type GotFormatterFunc func(got interface{}) string
|
||||
|
||||
// Got implements GotFormatter.
|
||||
func (f GotFormatterFunc) Got(got interface{}) string {
|
||||
return f(got)
|
||||
}
|
||||
|
||||
// GotFormatterAdapter attaches a GotFormatter to a Matcher.
|
||||
func GotFormatterAdapter(s GotFormatter, m Matcher) Matcher {
|
||||
return struct {
|
||||
GotFormatter
|
||||
Matcher
|
||||
}{
|
||||
GotFormatter: s,
|
||||
Matcher: m,
|
||||
}
|
||||
}
|
||||
|
||||
type anyMatcher struct{}
|
||||
|
||||
func (anyMatcher) Matches(interface{}) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (anyMatcher) String() string {
|
||||
return "is anything"
|
||||
}
|
||||
|
||||
type eqMatcher struct {
|
||||
x interface{}
|
||||
}
|
||||
|
||||
func (e eqMatcher) Matches(x interface{}) bool {
|
||||
// In case, some value is nil
|
||||
if e.x == nil || x == nil {
|
||||
return reflect.DeepEqual(e.x, x)
|
||||
}
|
||||
|
||||
// Check if types assignable and convert them to common type
|
||||
x1Val := reflect.ValueOf(e.x)
|
||||
x2Val := reflect.ValueOf(x)
|
||||
|
||||
if x1Val.Type().AssignableTo(x2Val.Type()) {
|
||||
x1ValConverted := x1Val.Convert(x2Val.Type())
|
||||
return reflect.DeepEqual(x1ValConverted.Interface(), x2Val.Interface())
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (e eqMatcher) String() string {
|
||||
return fmt.Sprintf("is equal to %v (%T)", e.x, e.x)
|
||||
}
|
||||
|
||||
type nilMatcher struct{}
|
||||
|
||||
func (nilMatcher) Matches(x interface{}) bool {
|
||||
if x == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(x)
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map,
|
||||
reflect.Ptr, reflect.Slice:
|
||||
return v.IsNil()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (nilMatcher) String() string {
|
||||
return "is nil"
|
||||
}
|
||||
|
||||
type notMatcher struct {
|
||||
m Matcher
|
||||
}
|
||||
|
||||
func (n notMatcher) Matches(x interface{}) bool {
|
||||
return !n.m.Matches(x)
|
||||
}
|
||||
|
||||
func (n notMatcher) String() string {
|
||||
return "not(" + n.m.String() + ")"
|
||||
}
|
||||
|
||||
type assignableToTypeOfMatcher struct {
|
||||
targetType reflect.Type
|
||||
}
|
||||
|
||||
func (m assignableToTypeOfMatcher) Matches(x interface{}) bool {
|
||||
return reflect.TypeOf(x).AssignableTo(m.targetType)
|
||||
}
|
||||
|
||||
func (m assignableToTypeOfMatcher) String() string {
|
||||
return "is assignable to " + m.targetType.Name()
|
||||
}
|
||||
|
||||
type allMatcher struct {
|
||||
matchers []Matcher
|
||||
}
|
||||
|
||||
func (am allMatcher) Matches(x interface{}) bool {
|
||||
for _, m := range am.matchers {
|
||||
if !m.Matches(x) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (am allMatcher) String() string {
|
||||
ss := make([]string, 0, len(am.matchers))
|
||||
for _, matcher := range am.matchers {
|
||||
ss = append(ss, matcher.String())
|
||||
}
|
||||
return strings.Join(ss, "; ")
|
||||
}
|
||||
|
||||
type lenMatcher struct {
|
||||
i int
|
||||
}
|
||||
|
||||
func (m lenMatcher) Matches(x interface{}) bool {
|
||||
v := reflect.ValueOf(x)
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == m.i
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (m lenMatcher) String() string {
|
||||
return fmt.Sprintf("has length %d", m.i)
|
||||
}
|
||||
|
||||
type inAnyOrderMatcher struct {
|
||||
x interface{}
|
||||
}
|
||||
|
||||
func (m inAnyOrderMatcher) Matches(x interface{}) bool {
|
||||
given, ok := m.prepareValue(x)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
wanted, ok := m.prepareValue(m.x)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if given.Len() != wanted.Len() {
|
||||
return false
|
||||
}
|
||||
|
||||
usedFromGiven := make([]bool, given.Len())
|
||||
foundFromWanted := make([]bool, wanted.Len())
|
||||
for i := 0; i < wanted.Len(); i++ {
|
||||
wantedMatcher := Eq(wanted.Index(i).Interface())
|
||||
for j := 0; j < given.Len(); j++ {
|
||||
if usedFromGiven[j] {
|
||||
continue
|
||||
}
|
||||
if wantedMatcher.Matches(given.Index(j).Interface()) {
|
||||
foundFromWanted[i] = true
|
||||
usedFromGiven[j] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
missingFromWanted := 0
|
||||
for _, found := range foundFromWanted {
|
||||
if !found {
|
||||
missingFromWanted++
|
||||
}
|
||||
}
|
||||
extraInGiven := 0
|
||||
for _, used := range usedFromGiven {
|
||||
if !used {
|
||||
extraInGiven++
|
||||
}
|
||||
}
|
||||
|
||||
return extraInGiven == 0 && missingFromWanted == 0
|
||||
}
|
||||
|
||||
func (m inAnyOrderMatcher) prepareValue(x interface{}) (reflect.Value, bool) {
|
||||
xValue := reflect.ValueOf(x)
|
||||
switch xValue.Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return xValue, true
|
||||
default:
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
}
|
||||
|
||||
func (m inAnyOrderMatcher) String() string {
|
||||
return fmt.Sprintf("has the same elements as %v", m.x)
|
||||
}
|
||||
|
||||
// Constructors
|
||||
|
||||
// All returns a composite Matcher that returns true if and only all of the
|
||||
// matchers return true.
|
||||
func All(ms ...Matcher) Matcher { return allMatcher{ms} }
|
||||
|
||||
// Any returns a matcher that always matches.
|
||||
func Any() Matcher { return anyMatcher{} }
|
||||
|
||||
// Eq returns a matcher that matches on equality.
|
||||
//
|
||||
// Example usage:
|
||||
// Eq(5).Matches(5) // returns true
|
||||
// Eq(5).Matches(4) // returns false
|
||||
func Eq(x interface{}) Matcher { return eqMatcher{x} }
|
||||
|
||||
// Len returns a matcher that matches on length. This matcher returns false if
|
||||
// is compared to a type that is not an array, chan, map, slice, or string.
|
||||
func Len(i int) Matcher {
|
||||
return lenMatcher{i}
|
||||
}
|
||||
|
||||
// Nil returns a matcher that matches if the received value is nil.
|
||||
//
|
||||
// Example usage:
|
||||
// var x *bytes.Buffer
|
||||
// Nil().Matches(x) // returns true
|
||||
// x = &bytes.Buffer{}
|
||||
// Nil().Matches(x) // returns false
|
||||
func Nil() Matcher { return nilMatcher{} }
|
||||
|
||||
// Not reverses the results of its given child matcher.
|
||||
//
|
||||
// Example usage:
|
||||
// Not(Eq(5)).Matches(4) // returns true
|
||||
// Not(Eq(5)).Matches(5) // returns false
|
||||
func Not(x interface{}) Matcher {
|
||||
if m, ok := x.(Matcher); ok {
|
||||
return notMatcher{m}
|
||||
}
|
||||
return notMatcher{Eq(x)}
|
||||
}
|
||||
|
||||
// AssignableToTypeOf is a Matcher that matches if the parameter to the mock
|
||||
// function is assignable to the type of the parameter to this function.
|
||||
//
|
||||
// Example usage:
|
||||
// var s fmt.Stringer = &bytes.Buffer{}
|
||||
// AssignableToTypeOf(s).Matches(time.Second) // returns true
|
||||
// AssignableToTypeOf(s).Matches(99) // returns false
|
||||
//
|
||||
// var ctx = reflect.TypeOf((*context.Context)(nil)).Elem()
|
||||
// AssignableToTypeOf(ctx).Matches(context.Background()) // returns true
|
||||
func AssignableToTypeOf(x interface{}) Matcher {
|
||||
if xt, ok := x.(reflect.Type); ok {
|
||||
return assignableToTypeOfMatcher{xt}
|
||||
}
|
||||
return assignableToTypeOfMatcher{reflect.TypeOf(x)}
|
||||
}
|
||||
|
||||
// InAnyOrder is a Matcher that returns true for collections of the same elements ignoring the order.
|
||||
//
|
||||
// Example usage:
|
||||
// InAnyOrder([]int{1, 2, 3}).Matches([]int{1, 3, 2}) // returns true
|
||||
// InAnyOrder([]int{1, 2, 3}).Matches([]int{1, 2}) // returns false
|
||||
func InAnyOrder(x interface{}) Matcher {
|
||||
return inAnyOrderMatcher{x}
|
||||
}
|
||||
701
vendor/github.com/golang/mock/mockgen/mockgen.go
generated
vendored
Normal file
701
vendor/github.com/golang/mock/mockgen/mockgen.go
generated
vendored
Normal file
@@ -0,0 +1,701 @@
|
||||
// Copyright 2010 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// MockGen generates mock implementations of Go interfaces.
|
||||
package main
|
||||
|
||||
// TODO: This does not support recursive embedded interfaces.
|
||||
// TODO: This does not support embedding package-local interfaces in a separate file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/golang/mock/mockgen/model"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
toolsimports "golang.org/x/tools/imports"
|
||||
)
|
||||
|
||||
const (
|
||||
gomockImportPath = "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
var (
|
||||
version = ""
|
||||
commit = "none"
|
||||
date = "unknown"
|
||||
)
|
||||
|
||||
var (
|
||||
source = flag.String("source", "", "(source mode) Input Go source file; enables source mode.")
|
||||
destination = flag.String("destination", "", "Output file; defaults to stdout.")
|
||||
mockNames = flag.String("mock_names", "", "Comma-separated interfaceName=mockName pairs of explicit mock names to use. Mock names default to 'Mock'+ interfaceName suffix.")
|
||||
packageOut = flag.String("package", "", "Package of the generated code; defaults to the package of the input with a 'mock_' prefix.")
|
||||
selfPackage = flag.String("self_package", "", "The full package import path for the generated code. The purpose of this flag is to prevent import cycles in the generated code by trying to include its own package. This can happen if the mock's package is set to one of its inputs (usually the main one) and the output is stdio so mockgen cannot detect the final output package. Setting this flag will then tell mockgen which import to exclude.")
|
||||
writePkgComment = flag.Bool("write_package_comment", true, "Writes package documentation comment (godoc) if true.")
|
||||
copyrightFile = flag.String("copyright_file", "", "Copyright file used to add copyright header")
|
||||
|
||||
debugParser = flag.Bool("debug_parser", false, "Print out parser results only.")
|
||||
showVersion = flag.Bool("version", false, "Print version.")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if *showVersion {
|
||||
printVersion()
|
||||
return
|
||||
}
|
||||
|
||||
var pkg *model.Package
|
||||
var err error
|
||||
var packageName string
|
||||
if *source != "" {
|
||||
pkg, err = sourceMode(*source)
|
||||
} else {
|
||||
if flag.NArg() != 2 {
|
||||
usage()
|
||||
log.Fatal("Expected exactly two arguments")
|
||||
}
|
||||
packageName = flag.Arg(0)
|
||||
interfaces := strings.Split(flag.Arg(1), ",")
|
||||
if packageName == "." {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Get current directory failed: %v", err)
|
||||
}
|
||||
packageName, err = packageNameOfDir(dir)
|
||||
if err != nil {
|
||||
log.Fatalf("Parse package name failed: %v", err)
|
||||
}
|
||||
}
|
||||
pkg, err = reflectMode(packageName, interfaces)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Loading input failed: %v", err)
|
||||
}
|
||||
|
||||
if *debugParser {
|
||||
pkg.Print(os.Stdout)
|
||||
return
|
||||
}
|
||||
|
||||
dst := os.Stdout
|
||||
if len(*destination) > 0 {
|
||||
if err := os.MkdirAll(filepath.Dir(*destination), os.ModePerm); err != nil {
|
||||
log.Fatalf("Unable to create directory: %v", err)
|
||||
}
|
||||
f, err := os.Create(*destination)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed opening destination file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
dst = f
|
||||
}
|
||||
|
||||
outputPackageName := *packageOut
|
||||
if outputPackageName == "" {
|
||||
// pkg.Name in reflect mode is the base name of the import path,
|
||||
// which might have characters that are illegal to have in package names.
|
||||
outputPackageName = "mock_" + sanitize(pkg.Name)
|
||||
}
|
||||
|
||||
// outputPackagePath represents the fully qualified name of the package of
|
||||
// the generated code. Its purposes are to prevent the module from importing
|
||||
// itself and to prevent qualifying type names that come from its own
|
||||
// package (i.e. if there is a type called X then we want to print "X" not
|
||||
// "package.X" since "package" is this package). This can happen if the mock
|
||||
// is output into an already existing package.
|
||||
outputPackagePath := *selfPackage
|
||||
if outputPackagePath == "" && *destination != "" {
|
||||
dstPath, err := filepath.Abs(filepath.Dir(*destination))
|
||||
if err == nil {
|
||||
pkgPath, err := parsePackageImport(dstPath)
|
||||
if err == nil {
|
||||
outputPackagePath = pkgPath
|
||||
} else {
|
||||
log.Println("Unable to infer -self_package from destination file path:", err)
|
||||
}
|
||||
} else {
|
||||
log.Println("Unable to determine destination file path:", err)
|
||||
}
|
||||
}
|
||||
|
||||
g := new(generator)
|
||||
if *source != "" {
|
||||
g.filename = *source
|
||||
} else {
|
||||
g.srcPackage = packageName
|
||||
g.srcInterfaces = flag.Arg(1)
|
||||
}
|
||||
g.destination = *destination
|
||||
|
||||
if *mockNames != "" {
|
||||
g.mockNames = parseMockNames(*mockNames)
|
||||
}
|
||||
if *copyrightFile != "" {
|
||||
header, err := ioutil.ReadFile(*copyrightFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed reading copyright file: %v", err)
|
||||
}
|
||||
|
||||
g.copyrightHeader = string(header)
|
||||
}
|
||||
if err := g.Generate(pkg, outputPackageName, outputPackagePath); err != nil {
|
||||
log.Fatalf("Failed generating mock: %v", err)
|
||||
}
|
||||
if _, err := dst.Write(g.Output()); err != nil {
|
||||
log.Fatalf("Failed writing to destination: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func parseMockNames(names string) map[string]string {
|
||||
mocksMap := make(map[string]string)
|
||||
for _, kv := range strings.Split(names, ",") {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if len(parts) != 2 || parts[1] == "" {
|
||||
log.Fatalf("bad mock names spec: %v", kv)
|
||||
}
|
||||
mocksMap[parts[0]] = parts[1]
|
||||
}
|
||||
return mocksMap
|
||||
}
|
||||
|
||||
func usage() {
|
||||
_, _ = io.WriteString(os.Stderr, usageText)
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
const usageText = `mockgen has two modes of operation: source and reflect.
|
||||
|
||||
Source mode generates mock interfaces from a source file.
|
||||
It is enabled by using the -source flag. Other flags that
|
||||
may be useful in this mode are -imports and -aux_files.
|
||||
Example:
|
||||
mockgen -source=foo.go [other options]
|
||||
|
||||
Reflect mode generates mock interfaces by building a program
|
||||
that uses reflection to understand interfaces. It is enabled
|
||||
by passing two non-flag arguments: an import path, and a
|
||||
comma-separated list of symbols.
|
||||
Example:
|
||||
mockgen database/sql/driver Conn,Driver
|
||||
|
||||
`
|
||||
|
||||
type generator struct {
|
||||
buf bytes.Buffer
|
||||
indent string
|
||||
mockNames map[string]string // may be empty
|
||||
filename string // may be empty
|
||||
destination string // may be empty
|
||||
srcPackage, srcInterfaces string // may be empty
|
||||
copyrightHeader string
|
||||
|
||||
packageMap map[string]string // map from import path to package name
|
||||
}
|
||||
|
||||
func (g *generator) p(format string, args ...interface{}) {
|
||||
fmt.Fprintf(&g.buf, g.indent+format+"\n", args...)
|
||||
}
|
||||
|
||||
func (g *generator) in() {
|
||||
g.indent += "\t"
|
||||
}
|
||||
|
||||
func (g *generator) out() {
|
||||
if len(g.indent) > 0 {
|
||||
g.indent = g.indent[0 : len(g.indent)-1]
|
||||
}
|
||||
}
|
||||
|
||||
// sanitize cleans up a string to make a suitable package name.
|
||||
func sanitize(s string) string {
|
||||
t := ""
|
||||
for _, r := range s {
|
||||
if t == "" {
|
||||
if unicode.IsLetter(r) || r == '_' {
|
||||
t += string(r)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
|
||||
t += string(r)
|
||||
continue
|
||||
}
|
||||
}
|
||||
t += "_"
|
||||
}
|
||||
if t == "_" {
|
||||
t = "x"
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (g *generator) Generate(pkg *model.Package, outputPkgName string, outputPackagePath string) error {
|
||||
if outputPkgName != pkg.Name && *selfPackage == "" {
|
||||
// reset outputPackagePath if it's not passed in through -self_package
|
||||
outputPackagePath = ""
|
||||
}
|
||||
|
||||
if g.copyrightHeader != "" {
|
||||
lines := strings.Split(g.copyrightHeader, "\n")
|
||||
for _, line := range lines {
|
||||
g.p("// %s", line)
|
||||
}
|
||||
g.p("")
|
||||
}
|
||||
|
||||
g.p("// Code generated by MockGen. DO NOT EDIT.")
|
||||
if g.filename != "" {
|
||||
g.p("// Source: %v", g.filename)
|
||||
} else {
|
||||
g.p("// Source: %v (interfaces: %v)", g.srcPackage, g.srcInterfaces)
|
||||
}
|
||||
g.p("")
|
||||
|
||||
// Get all required imports, and generate unique names for them all.
|
||||
im := pkg.Imports()
|
||||
im[gomockImportPath] = true
|
||||
|
||||
// Only import reflect if it's used. We only use reflect in mocked methods
|
||||
// so only import if any of the mocked interfaces have methods.
|
||||
for _, intf := range pkg.Interfaces {
|
||||
if len(intf.Methods) > 0 {
|
||||
im["reflect"] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Sort keys to make import alias generation predictable
|
||||
sortedPaths := make([]string, len(im))
|
||||
x := 0
|
||||
for pth := range im {
|
||||
sortedPaths[x] = pth
|
||||
x++
|
||||
}
|
||||
sort.Strings(sortedPaths)
|
||||
|
||||
packagesName := createPackageMap(sortedPaths)
|
||||
|
||||
g.packageMap = make(map[string]string, len(im))
|
||||
localNames := make(map[string]bool, len(im))
|
||||
for _, pth := range sortedPaths {
|
||||
base, ok := packagesName[pth]
|
||||
if !ok {
|
||||
base = sanitize(path.Base(pth))
|
||||
}
|
||||
|
||||
// Local names for an imported package can usually be the basename of the import path.
|
||||
// A couple of situations don't permit that, such as duplicate local names
|
||||
// (e.g. importing "html/template" and "text/template"), or where the basename is
|
||||
// a keyword (e.g. "foo/case").
|
||||
// try base0, base1, ...
|
||||
pkgName := base
|
||||
i := 0
|
||||
for localNames[pkgName] || token.Lookup(pkgName).IsKeyword() {
|
||||
pkgName = base + strconv.Itoa(i)
|
||||
i++
|
||||
}
|
||||
|
||||
// Avoid importing package if source pkg == output pkg
|
||||
if pth == pkg.PkgPath && outputPackagePath == pkg.PkgPath {
|
||||
continue
|
||||
}
|
||||
|
||||
g.packageMap[pth] = pkgName
|
||||
localNames[pkgName] = true
|
||||
}
|
||||
|
||||
if *writePkgComment {
|
||||
g.p("// Package %v is a generated GoMock package.", outputPkgName)
|
||||
}
|
||||
g.p("package %v", outputPkgName)
|
||||
g.p("")
|
||||
g.p("import (")
|
||||
g.in()
|
||||
for pkgPath, pkgName := range g.packageMap {
|
||||
if pkgPath == outputPackagePath {
|
||||
continue
|
||||
}
|
||||
g.p("%v %q", pkgName, pkgPath)
|
||||
}
|
||||
for _, pkgPath := range pkg.DotImports {
|
||||
g.p(". %q", pkgPath)
|
||||
}
|
||||
g.out()
|
||||
g.p(")")
|
||||
|
||||
for _, intf := range pkg.Interfaces {
|
||||
if err := g.GenerateMockInterface(intf, outputPackagePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// The name of the mock type to use for the given interface identifier.
|
||||
func (g *generator) mockName(typeName string) string {
|
||||
if mockName, ok := g.mockNames[typeName]; ok {
|
||||
return mockName
|
||||
}
|
||||
|
||||
return "Mock" + typeName
|
||||
}
|
||||
|
||||
func (g *generator) GenerateMockInterface(intf *model.Interface, outputPackagePath string) error {
|
||||
mockType := g.mockName(intf.Name)
|
||||
|
||||
g.p("")
|
||||
g.p("// %v is a mock of %v interface.", mockType, intf.Name)
|
||||
g.p("type %v struct {", mockType)
|
||||
g.in()
|
||||
g.p("ctrl *gomock.Controller")
|
||||
g.p("recorder *%vMockRecorder", mockType)
|
||||
g.out()
|
||||
g.p("}")
|
||||
g.p("")
|
||||
|
||||
g.p("// %vMockRecorder is the mock recorder for %v.", mockType, mockType)
|
||||
g.p("type %vMockRecorder struct {", mockType)
|
||||
g.in()
|
||||
g.p("mock *%v", mockType)
|
||||
g.out()
|
||||
g.p("}")
|
||||
g.p("")
|
||||
|
||||
g.p("// New%v creates a new mock instance.", mockType)
|
||||
g.p("func New%v(ctrl *gomock.Controller) *%v {", mockType, mockType)
|
||||
g.in()
|
||||
g.p("mock := &%v{ctrl: ctrl}", mockType)
|
||||
g.p("mock.recorder = &%vMockRecorder{mock}", mockType)
|
||||
g.p("return mock")
|
||||
g.out()
|
||||
g.p("}")
|
||||
g.p("")
|
||||
|
||||
// XXX: possible name collision here if someone has EXPECT in their interface.
|
||||
g.p("// EXPECT returns an object that allows the caller to indicate expected use.")
|
||||
g.p("func (m *%v) EXPECT() *%vMockRecorder {", mockType, mockType)
|
||||
g.in()
|
||||
g.p("return m.recorder")
|
||||
g.out()
|
||||
g.p("}")
|
||||
|
||||
g.GenerateMockMethods(mockType, intf, outputPackagePath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type byMethodName []*model.Method
|
||||
|
||||
func (b byMethodName) Len() int { return len(b) }
|
||||
func (b byMethodName) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
func (b byMethodName) Less(i, j int) bool { return b[i].Name < b[j].Name }
|
||||
|
||||
func (g *generator) GenerateMockMethods(mockType string, intf *model.Interface, pkgOverride string) {
|
||||
sort.Sort(byMethodName(intf.Methods))
|
||||
for _, m := range intf.Methods {
|
||||
g.p("")
|
||||
_ = g.GenerateMockMethod(mockType, m, pkgOverride)
|
||||
g.p("")
|
||||
_ = g.GenerateMockRecorderMethod(mockType, m)
|
||||
}
|
||||
}
|
||||
|
||||
func makeArgString(argNames, argTypes []string) string {
|
||||
args := make([]string, len(argNames))
|
||||
for i, name := range argNames {
|
||||
// specify the type only once for consecutive args of the same type
|
||||
if i+1 < len(argTypes) && argTypes[i] == argTypes[i+1] {
|
||||
args[i] = name
|
||||
} else {
|
||||
args[i] = name + " " + argTypes[i]
|
||||
}
|
||||
}
|
||||
return strings.Join(args, ", ")
|
||||
}
|
||||
|
||||
// GenerateMockMethod generates a mock method implementation.
|
||||
// If non-empty, pkgOverride is the package in which unqualified types reside.
|
||||
func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOverride string) error {
|
||||
argNames := g.getArgNames(m)
|
||||
argTypes := g.getArgTypes(m, pkgOverride)
|
||||
argString := makeArgString(argNames, argTypes)
|
||||
|
||||
rets := make([]string, len(m.Out))
|
||||
for i, p := range m.Out {
|
||||
rets[i] = p.Type.String(g.packageMap, pkgOverride)
|
||||
}
|
||||
retString := strings.Join(rets, ", ")
|
||||
if len(rets) > 1 {
|
||||
retString = "(" + retString + ")"
|
||||
}
|
||||
if retString != "" {
|
||||
retString = " " + retString
|
||||
}
|
||||
|
||||
ia := newIdentifierAllocator(argNames)
|
||||
idRecv := ia.allocateIdentifier("m")
|
||||
|
||||
g.p("// %v mocks base method.", m.Name)
|
||||
g.p("func (%v *%v) %v(%v)%v {", idRecv, mockType, m.Name, argString, retString)
|
||||
g.in()
|
||||
g.p("%s.ctrl.T.Helper()", idRecv)
|
||||
|
||||
var callArgs string
|
||||
if m.Variadic == nil {
|
||||
if len(argNames) > 0 {
|
||||
callArgs = ", " + strings.Join(argNames, ", ")
|
||||
}
|
||||
} else {
|
||||
// Non-trivial. The generated code must build a []interface{},
|
||||
// but the variadic argument may be any type.
|
||||
idVarArgs := ia.allocateIdentifier("varargs")
|
||||
idVArg := ia.allocateIdentifier("a")
|
||||
g.p("%s := []interface{}{%s}", idVarArgs, strings.Join(argNames[:len(argNames)-1], ", "))
|
||||
g.p("for _, %s := range %s {", idVArg, argNames[len(argNames)-1])
|
||||
g.in()
|
||||
g.p("%s = append(%s, %s)", idVarArgs, idVarArgs, idVArg)
|
||||
g.out()
|
||||
g.p("}")
|
||||
callArgs = ", " + idVarArgs + "..."
|
||||
}
|
||||
if len(m.Out) == 0 {
|
||||
g.p(`%v.ctrl.Call(%v, %q%v)`, idRecv, idRecv, m.Name, callArgs)
|
||||
} else {
|
||||
idRet := ia.allocateIdentifier("ret")
|
||||
g.p(`%v := %v.ctrl.Call(%v, %q%v)`, idRet, idRecv, idRecv, m.Name, callArgs)
|
||||
|
||||
// Go does not allow "naked" type assertions on nil values, so we use the two-value form here.
|
||||
// The value of that is either (x.(T), true) or (Z, false), where Z is the zero value for T.
|
||||
// Happily, this coincides with the semantics we want here.
|
||||
retNames := make([]string, len(rets))
|
||||
for i, t := range rets {
|
||||
retNames[i] = ia.allocateIdentifier(fmt.Sprintf("ret%d", i))
|
||||
g.p("%s, _ := %s[%d].(%s)", retNames[i], idRet, i, t)
|
||||
}
|
||||
g.p("return " + strings.Join(retNames, ", "))
|
||||
}
|
||||
|
||||
g.out()
|
||||
g.p("}")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *generator) GenerateMockRecorderMethod(mockType string, m *model.Method) error {
|
||||
argNames := g.getArgNames(m)
|
||||
|
||||
var argString string
|
||||
if m.Variadic == nil {
|
||||
argString = strings.Join(argNames, ", ")
|
||||
} else {
|
||||
argString = strings.Join(argNames[:len(argNames)-1], ", ")
|
||||
}
|
||||
if argString != "" {
|
||||
argString += " interface{}"
|
||||
}
|
||||
|
||||
if m.Variadic != nil {
|
||||
if argString != "" {
|
||||
argString += ", "
|
||||
}
|
||||
argString += fmt.Sprintf("%s ...interface{}", argNames[len(argNames)-1])
|
||||
}
|
||||
|
||||
ia := newIdentifierAllocator(argNames)
|
||||
idRecv := ia.allocateIdentifier("mr")
|
||||
|
||||
g.p("// %v indicates an expected call of %v.", m.Name, m.Name)
|
||||
g.p("func (%s *%vMockRecorder) %v(%v) *gomock.Call {", idRecv, mockType, m.Name, argString)
|
||||
g.in()
|
||||
g.p("%s.mock.ctrl.T.Helper()", idRecv)
|
||||
|
||||
var callArgs string
|
||||
if m.Variadic == nil {
|
||||
if len(argNames) > 0 {
|
||||
callArgs = ", " + strings.Join(argNames, ", ")
|
||||
}
|
||||
} else {
|
||||
if len(argNames) == 1 {
|
||||
// Easy: just use ... to push the arguments through.
|
||||
callArgs = ", " + argNames[0] + "..."
|
||||
} else {
|
||||
// Hard: create a temporary slice.
|
||||
idVarArgs := ia.allocateIdentifier("varargs")
|
||||
g.p("%s := append([]interface{}{%s}, %s...)",
|
||||
idVarArgs,
|
||||
strings.Join(argNames[:len(argNames)-1], ", "),
|
||||
argNames[len(argNames)-1])
|
||||
callArgs = ", " + idVarArgs + "..."
|
||||
}
|
||||
}
|
||||
g.p(`return %s.mock.ctrl.RecordCallWithMethodType(%s.mock, "%s", reflect.TypeOf((*%s)(nil).%s)%s)`, idRecv, idRecv, m.Name, mockType, m.Name, callArgs)
|
||||
|
||||
g.out()
|
||||
g.p("}")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *generator) getArgNames(m *model.Method) []string {
|
||||
argNames := make([]string, len(m.In))
|
||||
for i, p := range m.In {
|
||||
name := p.Name
|
||||
if name == "" || name == "_" {
|
||||
name = fmt.Sprintf("arg%d", i)
|
||||
}
|
||||
argNames[i] = name
|
||||
}
|
||||
if m.Variadic != nil {
|
||||
name := m.Variadic.Name
|
||||
if name == "" {
|
||||
name = fmt.Sprintf("arg%d", len(m.In))
|
||||
}
|
||||
argNames = append(argNames, name)
|
||||
}
|
||||
return argNames
|
||||
}
|
||||
|
||||
func (g *generator) getArgTypes(m *model.Method, pkgOverride string) []string {
|
||||
argTypes := make([]string, len(m.In))
|
||||
for i, p := range m.In {
|
||||
argTypes[i] = p.Type.String(g.packageMap, pkgOverride)
|
||||
}
|
||||
if m.Variadic != nil {
|
||||
argTypes = append(argTypes, "..."+m.Variadic.Type.String(g.packageMap, pkgOverride))
|
||||
}
|
||||
return argTypes
|
||||
}
|
||||
|
||||
type identifierAllocator map[string]struct{}
|
||||
|
||||
func newIdentifierAllocator(taken []string) identifierAllocator {
|
||||
a := make(identifierAllocator, len(taken))
|
||||
for _, s := range taken {
|
||||
a[s] = struct{}{}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (o identifierAllocator) allocateIdentifier(want string) string {
|
||||
id := want
|
||||
for i := 2; ; i++ {
|
||||
if _, ok := o[id]; !ok {
|
||||
o[id] = struct{}{}
|
||||
return id
|
||||
}
|
||||
id = want + "_" + strconv.Itoa(i)
|
||||
}
|
||||
}
|
||||
|
||||
// Output returns the generator's output, formatted in the standard Go style.
|
||||
func (g *generator) Output() []byte {
|
||||
src, err := toolsimports.Process(g.destination, g.buf.Bytes(), nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to format generated source code: %s\n%s", err, g.buf.String())
|
||||
}
|
||||
return src
|
||||
}
|
||||
|
||||
// createPackageMap returns a map of import path to package name
|
||||
// for specified importPaths.
|
||||
func createPackageMap(importPaths []string) map[string]string {
|
||||
var pkg struct {
|
||||
Name string
|
||||
ImportPath string
|
||||
}
|
||||
pkgMap := make(map[string]string)
|
||||
b := bytes.NewBuffer(nil)
|
||||
args := []string{"list", "-json"}
|
||||
args = append(args, importPaths...)
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Stdout = b
|
||||
cmd.Run()
|
||||
dec := json.NewDecoder(b)
|
||||
for dec.More() {
|
||||
err := dec.Decode(&pkg)
|
||||
if err != nil {
|
||||
log.Printf("failed to decode 'go list' output: %v", err)
|
||||
continue
|
||||
}
|
||||
pkgMap[pkg.ImportPath] = pkg.Name
|
||||
}
|
||||
return pkgMap
|
||||
}
|
||||
|
||||
func printVersion() {
|
||||
if version != "" {
|
||||
fmt.Printf("v%s\nCommit: %s\nDate: %s\n", version, commit, date)
|
||||
} else {
|
||||
printModuleVersion()
|
||||
}
|
||||
}
|
||||
|
||||
// parseImportPackage get package import path via source file
|
||||
// an alternative implementation is to use:
|
||||
// cfg := &packages.Config{Mode: packages.NeedName, Tests: true, Dir: srcDir}
|
||||
// pkgs, err := packages.Load(cfg, "file="+source)
|
||||
// However, it will call "go list" and slow down the performance
|
||||
func parsePackageImport(srcDir string) (string, error) {
|
||||
moduleMode := os.Getenv("GO111MODULE")
|
||||
// trying to find the module
|
||||
if moduleMode != "off" {
|
||||
currentDir := srcDir
|
||||
for {
|
||||
dat, err := ioutil.ReadFile(filepath.Join(currentDir, "go.mod"))
|
||||
if os.IsNotExist(err) {
|
||||
if currentDir == filepath.Dir(currentDir) {
|
||||
// at the root
|
||||
break
|
||||
}
|
||||
currentDir = filepath.Dir(currentDir)
|
||||
continue
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
modulePath := modfile.ModulePath(dat)
|
||||
return filepath.ToSlash(filepath.Join(modulePath, strings.TrimPrefix(srcDir, currentDir))), nil
|
||||
}
|
||||
}
|
||||
// fall back to GOPATH mode
|
||||
goPaths := os.Getenv("GOPATH")
|
||||
if goPaths == "" {
|
||||
return "", fmt.Errorf("GOPATH is not set")
|
||||
}
|
||||
goPathList := strings.Split(goPaths, string(os.PathListSeparator))
|
||||
for _, goPath := range goPathList {
|
||||
sourceRoot := filepath.Join(goPath, "src") + string(os.PathSeparator)
|
||||
if strings.HasPrefix(srcDir, sourceRoot) {
|
||||
return filepath.ToSlash(strings.TrimPrefix(srcDir, sourceRoot)), nil
|
||||
}
|
||||
}
|
||||
return "", errOutsideGoPath
|
||||
}
|
||||
495
vendor/github.com/golang/mock/mockgen/model/model.go
generated
vendored
Normal file
495
vendor/github.com/golang/mock/mockgen/model/model.go
generated
vendored
Normal file
@@ -0,0 +1,495 @@
|
||||
// Copyright 2012 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package model contains the data model necessary for generating mock implementations.
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// pkgPath is the importable path for package model
|
||||
const pkgPath = "github.com/golang/mock/mockgen/model"
|
||||
|
||||
// Package is a Go package. It may be a subset.
|
||||
type Package struct {
|
||||
Name string
|
||||
PkgPath string
|
||||
Interfaces []*Interface
|
||||
DotImports []string
|
||||
}
|
||||
|
||||
// Print writes the package name and its exported interfaces.
|
||||
func (pkg *Package) Print(w io.Writer) {
|
||||
_, _ = fmt.Fprintf(w, "package %s\n", pkg.Name)
|
||||
for _, intf := range pkg.Interfaces {
|
||||
intf.Print(w)
|
||||
}
|
||||
}
|
||||
|
||||
// Imports returns the imports needed by the Package as a set of import paths.
|
||||
func (pkg *Package) Imports() map[string]bool {
|
||||
im := make(map[string]bool)
|
||||
for _, intf := range pkg.Interfaces {
|
||||
intf.addImports(im)
|
||||
}
|
||||
return im
|
||||
}
|
||||
|
||||
// Interface is a Go interface.
|
||||
type Interface struct {
|
||||
Name string
|
||||
Methods []*Method
|
||||
}
|
||||
|
||||
// Print writes the interface name and its methods.
|
||||
func (intf *Interface) Print(w io.Writer) {
|
||||
_, _ = fmt.Fprintf(w, "interface %s\n", intf.Name)
|
||||
for _, m := range intf.Methods {
|
||||
m.Print(w)
|
||||
}
|
||||
}
|
||||
|
||||
func (intf *Interface) addImports(im map[string]bool) {
|
||||
for _, m := range intf.Methods {
|
||||
m.addImports(im)
|
||||
}
|
||||
}
|
||||
|
||||
// AddMethod adds a new method, de-duplicating by method name.
|
||||
func (intf *Interface) AddMethod(m *Method) {
|
||||
for _, me := range intf.Methods {
|
||||
if me.Name == m.Name {
|
||||
return
|
||||
}
|
||||
}
|
||||
intf.Methods = append(intf.Methods, m)
|
||||
}
|
||||
|
||||
// Method is a single method of an interface.
|
||||
type Method struct {
|
||||
Name string
|
||||
In, Out []*Parameter
|
||||
Variadic *Parameter // may be nil
|
||||
}
|
||||
|
||||
// Print writes the method name and its signature.
|
||||
func (m *Method) Print(w io.Writer) {
|
||||
_, _ = fmt.Fprintf(w, " - method %s\n", m.Name)
|
||||
if len(m.In) > 0 {
|
||||
_, _ = fmt.Fprintf(w, " in:\n")
|
||||
for _, p := range m.In {
|
||||
p.Print(w)
|
||||
}
|
||||
}
|
||||
if m.Variadic != nil {
|
||||
_, _ = fmt.Fprintf(w, " ...:\n")
|
||||
m.Variadic.Print(w)
|
||||
}
|
||||
if len(m.Out) > 0 {
|
||||
_, _ = fmt.Fprintf(w, " out:\n")
|
||||
for _, p := range m.Out {
|
||||
p.Print(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Method) addImports(im map[string]bool) {
|
||||
for _, p := range m.In {
|
||||
p.Type.addImports(im)
|
||||
}
|
||||
if m.Variadic != nil {
|
||||
m.Variadic.Type.addImports(im)
|
||||
}
|
||||
for _, p := range m.Out {
|
||||
p.Type.addImports(im)
|
||||
}
|
||||
}
|
||||
|
||||
// Parameter is an argument or return parameter of a method.
|
||||
type Parameter struct {
|
||||
Name string // may be empty
|
||||
Type Type
|
||||
}
|
||||
|
||||
// Print writes a method parameter.
|
||||
func (p *Parameter) Print(w io.Writer) {
|
||||
n := p.Name
|
||||
if n == "" {
|
||||
n = `""`
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, " - %v: %v\n", n, p.Type.String(nil, ""))
|
||||
}
|
||||
|
||||
// Type is a Go type.
|
||||
type Type interface {
|
||||
String(pm map[string]string, pkgOverride string) string
|
||||
addImports(im map[string]bool)
|
||||
}
|
||||
|
||||
func init() {
|
||||
gob.Register(&ArrayType{})
|
||||
gob.Register(&ChanType{})
|
||||
gob.Register(&FuncType{})
|
||||
gob.Register(&MapType{})
|
||||
gob.Register(&NamedType{})
|
||||
gob.Register(&PointerType{})
|
||||
|
||||
// Call gob.RegisterName to make sure it has the consistent name registered
|
||||
// for both gob decoder and encoder.
|
||||
//
|
||||
// For a non-pointer type, gob.Register will try to get package full path by
|
||||
// calling rt.PkgPath() for a name to register. If your project has vendor
|
||||
// directory, it is possible that PkgPath will get a path like this:
|
||||
// ../../../vendor/github.com/golang/mock/mockgen/model
|
||||
gob.RegisterName(pkgPath+".PredeclaredType", PredeclaredType(""))
|
||||
}
|
||||
|
||||
// ArrayType is an array or slice type.
|
||||
type ArrayType struct {
|
||||
Len int // -1 for slices, >= 0 for arrays
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (at *ArrayType) String(pm map[string]string, pkgOverride string) string {
|
||||
s := "[]"
|
||||
if at.Len > -1 {
|
||||
s = fmt.Sprintf("[%d]", at.Len)
|
||||
}
|
||||
return s + at.Type.String(pm, pkgOverride)
|
||||
}
|
||||
|
||||
func (at *ArrayType) addImports(im map[string]bool) { at.Type.addImports(im) }
|
||||
|
||||
// ChanType is a channel type.
|
||||
type ChanType struct {
|
||||
Dir ChanDir // 0, 1 or 2
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (ct *ChanType) String(pm map[string]string, pkgOverride string) string {
|
||||
s := ct.Type.String(pm, pkgOverride)
|
||||
if ct.Dir == RecvDir {
|
||||
return "<-chan " + s
|
||||
}
|
||||
if ct.Dir == SendDir {
|
||||
return "chan<- " + s
|
||||
}
|
||||
return "chan " + s
|
||||
}
|
||||
|
||||
func (ct *ChanType) addImports(im map[string]bool) { ct.Type.addImports(im) }
|
||||
|
||||
// ChanDir is a channel direction.
|
||||
type ChanDir int
|
||||
|
||||
// Constants for channel directions.
|
||||
const (
|
||||
RecvDir ChanDir = 1
|
||||
SendDir ChanDir = 2
|
||||
)
|
||||
|
||||
// FuncType is a function type.
|
||||
type FuncType struct {
|
||||
In, Out []*Parameter
|
||||
Variadic *Parameter // may be nil
|
||||
}
|
||||
|
||||
func (ft *FuncType) String(pm map[string]string, pkgOverride string) string {
|
||||
args := make([]string, len(ft.In))
|
||||
for i, p := range ft.In {
|
||||
args[i] = p.Type.String(pm, pkgOverride)
|
||||
}
|
||||
if ft.Variadic != nil {
|
||||
args = append(args, "..."+ft.Variadic.Type.String(pm, pkgOverride))
|
||||
}
|
||||
rets := make([]string, len(ft.Out))
|
||||
for i, p := range ft.Out {
|
||||
rets[i] = p.Type.String(pm, pkgOverride)
|
||||
}
|
||||
retString := strings.Join(rets, ", ")
|
||||
if nOut := len(ft.Out); nOut == 1 {
|
||||
retString = " " + retString
|
||||
} else if nOut > 1 {
|
||||
retString = " (" + retString + ")"
|
||||
}
|
||||
return "func(" + strings.Join(args, ", ") + ")" + retString
|
||||
}
|
||||
|
||||
func (ft *FuncType) addImports(im map[string]bool) {
|
||||
for _, p := range ft.In {
|
||||
p.Type.addImports(im)
|
||||
}
|
||||
if ft.Variadic != nil {
|
||||
ft.Variadic.Type.addImports(im)
|
||||
}
|
||||
for _, p := range ft.Out {
|
||||
p.Type.addImports(im)
|
||||
}
|
||||
}
|
||||
|
||||
// MapType is a map type.
|
||||
type MapType struct {
|
||||
Key, Value Type
|
||||
}
|
||||
|
||||
func (mt *MapType) String(pm map[string]string, pkgOverride string) string {
|
||||
return "map[" + mt.Key.String(pm, pkgOverride) + "]" + mt.Value.String(pm, pkgOverride)
|
||||
}
|
||||
|
||||
func (mt *MapType) addImports(im map[string]bool) {
|
||||
mt.Key.addImports(im)
|
||||
mt.Value.addImports(im)
|
||||
}
|
||||
|
||||
// NamedType is an exported type in a package.
|
||||
type NamedType struct {
|
||||
Package string // may be empty
|
||||
Type string
|
||||
}
|
||||
|
||||
func (nt *NamedType) String(pm map[string]string, pkgOverride string) string {
|
||||
if pkgOverride == nt.Package {
|
||||
return nt.Type
|
||||
}
|
||||
prefix := pm[nt.Package]
|
||||
if prefix != "" {
|
||||
return prefix + "." + nt.Type
|
||||
}
|
||||
|
||||
return nt.Type
|
||||
}
|
||||
|
||||
func (nt *NamedType) addImports(im map[string]bool) {
|
||||
if nt.Package != "" {
|
||||
im[nt.Package] = true
|
||||
}
|
||||
}
|
||||
|
||||
// PointerType is a pointer to another type.
|
||||
type PointerType struct {
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (pt *PointerType) String(pm map[string]string, pkgOverride string) string {
|
||||
return "*" + pt.Type.String(pm, pkgOverride)
|
||||
}
|
||||
func (pt *PointerType) addImports(im map[string]bool) { pt.Type.addImports(im) }
|
||||
|
||||
// PredeclaredType is a predeclared type such as "int".
|
||||
type PredeclaredType string
|
||||
|
||||
func (pt PredeclaredType) String(map[string]string, string) string { return string(pt) }
|
||||
func (pt PredeclaredType) addImports(map[string]bool) {}
|
||||
|
||||
// The following code is intended to be called by the program generated by ../reflect.go.
|
||||
|
||||
// InterfaceFromInterfaceType returns a pointer to an interface for the
|
||||
// given reflection interface type.
|
||||
func InterfaceFromInterfaceType(it reflect.Type) (*Interface, error) {
|
||||
if it.Kind() != reflect.Interface {
|
||||
return nil, fmt.Errorf("%v is not an interface", it)
|
||||
}
|
||||
intf := &Interface{}
|
||||
|
||||
for i := 0; i < it.NumMethod(); i++ {
|
||||
mt := it.Method(i)
|
||||
// TODO: need to skip unexported methods? or just raise an error?
|
||||
m := &Method{
|
||||
Name: mt.Name,
|
||||
}
|
||||
|
||||
var err error
|
||||
m.In, m.Variadic, m.Out, err = funcArgsFromType(mt.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
intf.AddMethod(m)
|
||||
}
|
||||
|
||||
return intf, nil
|
||||
}
|
||||
|
||||
// t's Kind must be a reflect.Func.
|
||||
func funcArgsFromType(t reflect.Type) (in []*Parameter, variadic *Parameter, out []*Parameter, err error) {
|
||||
nin := t.NumIn()
|
||||
if t.IsVariadic() {
|
||||
nin--
|
||||
}
|
||||
var p *Parameter
|
||||
for i := 0; i < nin; i++ {
|
||||
p, err = parameterFromType(t.In(i))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
in = append(in, p)
|
||||
}
|
||||
if t.IsVariadic() {
|
||||
p, err = parameterFromType(t.In(nin).Elem())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
variadic = p
|
||||
}
|
||||
for i := 0; i < t.NumOut(); i++ {
|
||||
p, err = parameterFromType(t.Out(i))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out = append(out, p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parameterFromType(t reflect.Type) (*Parameter, error) {
|
||||
tt, err := typeFromType(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Parameter{Type: tt}, nil
|
||||
}
|
||||
|
||||
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
var byteType = reflect.TypeOf(byte(0))
|
||||
|
||||
func typeFromType(t reflect.Type) (Type, error) {
|
||||
// Hack workaround for https://golang.org/issue/3853.
|
||||
// This explicit check should not be necessary.
|
||||
if t == byteType {
|
||||
return PredeclaredType("byte"), nil
|
||||
}
|
||||
|
||||
if imp := t.PkgPath(); imp != "" {
|
||||
return &NamedType{
|
||||
Package: impPath(imp),
|
||||
Type: t.Name(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// only unnamed or predeclared types after here
|
||||
|
||||
// Lots of types have element types. Let's do the parsing and error checking for all of them.
|
||||
var elemType Type
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
var err error
|
||||
elemType, err = typeFromType(t.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Array:
|
||||
return &ArrayType{
|
||||
Len: t.Len(),
|
||||
Type: elemType,
|
||||
}, nil
|
||||
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
|
||||
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String:
|
||||
return PredeclaredType(t.Kind().String()), nil
|
||||
case reflect.Chan:
|
||||
var dir ChanDir
|
||||
switch t.ChanDir() {
|
||||
case reflect.RecvDir:
|
||||
dir = RecvDir
|
||||
case reflect.SendDir:
|
||||
dir = SendDir
|
||||
}
|
||||
return &ChanType{
|
||||
Dir: dir,
|
||||
Type: elemType,
|
||||
}, nil
|
||||
case reflect.Func:
|
||||
in, variadic, out, err := funcArgsFromType(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FuncType{
|
||||
In: in,
|
||||
Out: out,
|
||||
Variadic: variadic,
|
||||
}, nil
|
||||
case reflect.Interface:
|
||||
// Two special interfaces.
|
||||
if t.NumMethod() == 0 {
|
||||
return PredeclaredType("interface{}"), nil
|
||||
}
|
||||
if t == errorType {
|
||||
return PredeclaredType("error"), nil
|
||||
}
|
||||
case reflect.Map:
|
||||
kt, err := typeFromType(t.Key())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &MapType{
|
||||
Key: kt,
|
||||
Value: elemType,
|
||||
}, nil
|
||||
case reflect.Ptr:
|
||||
return &PointerType{
|
||||
Type: elemType,
|
||||
}, nil
|
||||
case reflect.Slice:
|
||||
return &ArrayType{
|
||||
Len: -1,
|
||||
Type: elemType,
|
||||
}, nil
|
||||
case reflect.Struct:
|
||||
if t.NumField() == 0 {
|
||||
return PredeclaredType("struct{}"), nil
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Struct, UnsafePointer
|
||||
return nil, fmt.Errorf("can't yet turn %v (%v) into a model.Type", t, t.Kind())
|
||||
}
|
||||
|
||||
// impPath sanitizes the package path returned by `PkgPath` method of a reflect Type so that
|
||||
// it is importable. PkgPath might return a path that includes "vendor". These paths do not
|
||||
// compile, so we need to remove everything up to and including "/vendor/".
|
||||
// See https://github.com/golang/go/issues/12019.
|
||||
func impPath(imp string) string {
|
||||
if strings.HasPrefix(imp, "vendor/") {
|
||||
imp = "/" + imp
|
||||
}
|
||||
if i := strings.LastIndex(imp, "/vendor/"); i != -1 {
|
||||
imp = imp[i+len("/vendor/"):]
|
||||
}
|
||||
return imp
|
||||
}
|
||||
|
||||
// ErrorInterface represent built-in error interface.
|
||||
var ErrorInterface = Interface{
|
||||
Name: "error",
|
||||
Methods: []*Method{
|
||||
{
|
||||
Name: "Error",
|
||||
Out: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: PredeclaredType("string"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
644
vendor/github.com/golang/mock/mockgen/parse.go
generated
vendored
Normal file
644
vendor/github.com/golang/mock/mockgen/parse.go
generated
vendored
Normal file
@@ -0,0 +1,644 @@
|
||||
// Copyright 2012 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
// This file contains the model construction by parsing source files.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/importer"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/mock/mockgen/model"
|
||||
)
|
||||
|
||||
var (
|
||||
imports = flag.String("imports", "", "(source mode) Comma-separated name=path pairs of explicit imports to use.")
|
||||
auxFiles = flag.String("aux_files", "", "(source mode) Comma-separated pkg=path pairs of auxiliary Go source files.")
|
||||
)
|
||||
|
||||
// sourceMode generates mocks via source file.
|
||||
func sourceMode(source string) (*model.Package, error) {
|
||||
srcDir, err := filepath.Abs(filepath.Dir(source))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed getting source directory: %v", err)
|
||||
}
|
||||
|
||||
packageImport, err := parsePackageImport(srcDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fs := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fs, source, nil, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed parsing source file %v: %v", source, err)
|
||||
}
|
||||
|
||||
p := &fileParser{
|
||||
fileSet: fs,
|
||||
imports: make(map[string]importedPackage),
|
||||
importedInterfaces: make(map[string]map[string]*ast.InterfaceType),
|
||||
auxInterfaces: make(map[string]map[string]*ast.InterfaceType),
|
||||
srcDir: srcDir,
|
||||
}
|
||||
|
||||
// Handle -imports.
|
||||
dotImports := make(map[string]bool)
|
||||
if *imports != "" {
|
||||
for _, kv := range strings.Split(*imports, ",") {
|
||||
eq := strings.Index(kv, "=")
|
||||
k, v := kv[:eq], kv[eq+1:]
|
||||
if k == "." {
|
||||
dotImports[v] = true
|
||||
} else {
|
||||
p.imports[k] = importedPkg{path: v}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle -aux_files.
|
||||
if err := p.parseAuxFiles(*auxFiles); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.addAuxInterfacesFromFile(packageImport, file) // this file
|
||||
|
||||
pkg, err := p.parseFile(packageImport, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for pkgPath := range dotImports {
|
||||
pkg.DotImports = append(pkg.DotImports, pkgPath)
|
||||
}
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
type importedPackage interface {
|
||||
Path() string
|
||||
Parser() *fileParser
|
||||
}
|
||||
|
||||
type importedPkg struct {
|
||||
path string
|
||||
parser *fileParser
|
||||
}
|
||||
|
||||
func (i importedPkg) Path() string { return i.path }
|
||||
func (i importedPkg) Parser() *fileParser { return i.parser }
|
||||
|
||||
// duplicateImport is a bit of a misnomer. Currently the parser can't
|
||||
// handle cases of multi-file packages importing different packages
|
||||
// under the same name. Often these imports would not be problematic,
|
||||
// so this type lets us defer raising an error unless the package name
|
||||
// is actually used.
|
||||
type duplicateImport struct {
|
||||
name string
|
||||
duplicates []string
|
||||
}
|
||||
|
||||
func (d duplicateImport) Error() string {
|
||||
return fmt.Sprintf("%q is ambiguous because of duplicate imports: %v", d.name, d.duplicates)
|
||||
}
|
||||
|
||||
func (d duplicateImport) Path() string { log.Fatal(d.Error()); return "" }
|
||||
func (d duplicateImport) Parser() *fileParser { log.Fatal(d.Error()); return nil }
|
||||
|
||||
type fileParser struct {
|
||||
fileSet *token.FileSet
|
||||
imports map[string]importedPackage // package name => imported package
|
||||
importedInterfaces map[string]map[string]*ast.InterfaceType // package (or "") => name => interface
|
||||
|
||||
auxFiles []*ast.File
|
||||
auxInterfaces map[string]map[string]*ast.InterfaceType // package (or "") => name => interface
|
||||
|
||||
srcDir string
|
||||
}
|
||||
|
||||
func (p *fileParser) errorf(pos token.Pos, format string, args ...interface{}) error {
|
||||
ps := p.fileSet.Position(pos)
|
||||
format = "%s:%d:%d: " + format
|
||||
args = append([]interface{}{ps.Filename, ps.Line, ps.Column}, args...)
|
||||
return fmt.Errorf(format, args...)
|
||||
}
|
||||
|
||||
func (p *fileParser) parseAuxFiles(auxFiles string) error {
|
||||
auxFiles = strings.TrimSpace(auxFiles)
|
||||
if auxFiles == "" {
|
||||
return nil
|
||||
}
|
||||
for _, kv := range strings.Split(auxFiles, ",") {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("bad aux file spec: %v", kv)
|
||||
}
|
||||
pkg, fpath := parts[0], parts[1]
|
||||
|
||||
file, err := parser.ParseFile(p.fileSet, fpath, nil, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.auxFiles = append(p.auxFiles, file)
|
||||
p.addAuxInterfacesFromFile(pkg, file)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fileParser) addAuxInterfacesFromFile(pkg string, file *ast.File) {
|
||||
if _, ok := p.auxInterfaces[pkg]; !ok {
|
||||
p.auxInterfaces[pkg] = make(map[string]*ast.InterfaceType)
|
||||
}
|
||||
for ni := range iterInterfaces(file) {
|
||||
p.auxInterfaces[pkg][ni.name.Name] = ni.it
|
||||
}
|
||||
}
|
||||
|
||||
// parseFile loads all file imports and auxiliary files import into the
|
||||
// fileParser, parses all file interfaces and returns package model.
|
||||
func (p *fileParser) parseFile(importPath string, file *ast.File) (*model.Package, error) {
|
||||
allImports, dotImports := importsOfFile(file)
|
||||
// Don't stomp imports provided by -imports. Those should take precedence.
|
||||
for pkg, pkgI := range allImports {
|
||||
if _, ok := p.imports[pkg]; !ok {
|
||||
p.imports[pkg] = pkgI
|
||||
}
|
||||
}
|
||||
// Add imports from auxiliary files, which might be needed for embedded interfaces.
|
||||
// Don't stomp any other imports.
|
||||
for _, f := range p.auxFiles {
|
||||
auxImports, _ := importsOfFile(f)
|
||||
for pkg, pkgI := range auxImports {
|
||||
if _, ok := p.imports[pkg]; !ok {
|
||||
p.imports[pkg] = pkgI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var is []*model.Interface
|
||||
for ni := range iterInterfaces(file) {
|
||||
i, err := p.parseInterface(ni.name.String(), importPath, ni.it)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
is = append(is, i)
|
||||
}
|
||||
return &model.Package{
|
||||
Name: file.Name.String(),
|
||||
PkgPath: importPath,
|
||||
Interfaces: is,
|
||||
DotImports: dotImports,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// parsePackage loads package specified by path, parses it and returns
|
||||
// a new fileParser with the parsed imports and interfaces.
|
||||
func (p *fileParser) parsePackage(path string) (*fileParser, error) {
|
||||
newP := &fileParser{
|
||||
fileSet: token.NewFileSet(),
|
||||
imports: make(map[string]importedPackage),
|
||||
importedInterfaces: make(map[string]map[string]*ast.InterfaceType),
|
||||
auxInterfaces: make(map[string]map[string]*ast.InterfaceType),
|
||||
srcDir: p.srcDir,
|
||||
}
|
||||
|
||||
var pkgs map[string]*ast.Package
|
||||
if imp, err := build.Import(path, newP.srcDir, build.FindOnly); err != nil {
|
||||
return nil, err
|
||||
} else if pkgs, err = parser.ParseDir(newP.fileSet, imp.Dir, nil, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
file := ast.MergePackageFiles(pkg, ast.FilterFuncDuplicates|ast.FilterUnassociatedComments|ast.FilterImportDuplicates)
|
||||
if _, ok := newP.importedInterfaces[path]; !ok {
|
||||
newP.importedInterfaces[path] = make(map[string]*ast.InterfaceType)
|
||||
}
|
||||
for ni := range iterInterfaces(file) {
|
||||
newP.importedInterfaces[path][ni.name.Name] = ni.it
|
||||
}
|
||||
imports, _ := importsOfFile(file)
|
||||
for pkgName, pkgI := range imports {
|
||||
newP.imports[pkgName] = pkgI
|
||||
}
|
||||
}
|
||||
return newP, nil
|
||||
}
|
||||
|
||||
func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*model.Interface, error) {
|
||||
iface := &model.Interface{Name: name}
|
||||
for _, field := range it.Methods.List {
|
||||
switch v := field.Type.(type) {
|
||||
case *ast.FuncType:
|
||||
if nn := len(field.Names); nn != 1 {
|
||||
return nil, fmt.Errorf("expected one name for interface %v, got %d", iface.Name, nn)
|
||||
}
|
||||
m := &model.Method{
|
||||
Name: field.Names[0].String(),
|
||||
}
|
||||
var err error
|
||||
m.In, m.Variadic, m.Out, err = p.parseFunc(pkg, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iface.AddMethod(m)
|
||||
case *ast.Ident:
|
||||
// Embedded interface in this package.
|
||||
embeddedIfaceType := p.auxInterfaces[pkg][v.String()]
|
||||
if embeddedIfaceType == nil {
|
||||
embeddedIfaceType = p.importedInterfaces[pkg][v.String()]
|
||||
}
|
||||
|
||||
var embeddedIface *model.Interface
|
||||
if embeddedIfaceType != nil {
|
||||
var err error
|
||||
embeddedIface, err = p.parseInterface(v.String(), pkg, embeddedIfaceType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// This is built-in error interface.
|
||||
if v.String() == model.ErrorInterface.Name {
|
||||
embeddedIface = &model.ErrorInterface
|
||||
} else {
|
||||
return nil, p.errorf(v.Pos(), "unknown embedded interface %s", v.String())
|
||||
}
|
||||
}
|
||||
// Copy the methods.
|
||||
for _, m := range embeddedIface.Methods {
|
||||
iface.AddMethod(m)
|
||||
}
|
||||
case *ast.SelectorExpr:
|
||||
// Embedded interface in another package.
|
||||
filePkg, sel := v.X.(*ast.Ident).String(), v.Sel.String()
|
||||
embeddedPkg, ok := p.imports[filePkg]
|
||||
if !ok {
|
||||
return nil, p.errorf(v.X.Pos(), "unknown package %s", filePkg)
|
||||
}
|
||||
|
||||
var embeddedIface *model.Interface
|
||||
var err error
|
||||
embeddedIfaceType := p.auxInterfaces[filePkg][sel]
|
||||
if embeddedIfaceType != nil {
|
||||
embeddedIface, err = p.parseInterface(sel, filePkg, embeddedIfaceType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
path := embeddedPkg.Path()
|
||||
parser := embeddedPkg.Parser()
|
||||
if parser == nil {
|
||||
ip, err := p.parsePackage(path)
|
||||
if err != nil {
|
||||
return nil, p.errorf(v.Pos(), "could not parse package %s: %v", path, err)
|
||||
}
|
||||
parser = ip
|
||||
p.imports[filePkg] = importedPkg{
|
||||
path: embeddedPkg.Path(),
|
||||
parser: parser,
|
||||
}
|
||||
}
|
||||
if embeddedIfaceType = parser.importedInterfaces[path][sel]; embeddedIfaceType == nil {
|
||||
return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", path, sel)
|
||||
}
|
||||
embeddedIface, err = parser.parseInterface(sel, path, embeddedIfaceType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Copy the methods.
|
||||
// TODO: apply shadowing rules.
|
||||
for _, m := range embeddedIface.Methods {
|
||||
iface.AddMethod(m)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("don't know how to mock method of type %T", field.Type)
|
||||
}
|
||||
}
|
||||
return iface, nil
|
||||
}
|
||||
|
||||
func (p *fileParser) parseFunc(pkg string, f *ast.FuncType) (inParam []*model.Parameter, variadic *model.Parameter, outParam []*model.Parameter, err error) {
|
||||
if f.Params != nil {
|
||||
regParams := f.Params.List
|
||||
if isVariadic(f) {
|
||||
n := len(regParams)
|
||||
varParams := regParams[n-1:]
|
||||
regParams = regParams[:n-1]
|
||||
vp, err := p.parseFieldList(pkg, varParams)
|
||||
if err != nil {
|
||||
return nil, nil, nil, p.errorf(varParams[0].Pos(), "failed parsing variadic argument: %v", err)
|
||||
}
|
||||
variadic = vp[0]
|
||||
}
|
||||
inParam, err = p.parseFieldList(pkg, regParams)
|
||||
if err != nil {
|
||||
return nil, nil, nil, p.errorf(f.Pos(), "failed parsing arguments: %v", err)
|
||||
}
|
||||
}
|
||||
if f.Results != nil {
|
||||
outParam, err = p.parseFieldList(pkg, f.Results.List)
|
||||
if err != nil {
|
||||
return nil, nil, nil, p.errorf(f.Pos(), "failed parsing returns: %v", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field) ([]*model.Parameter, error) {
|
||||
nf := 0
|
||||
for _, f := range fields {
|
||||
nn := len(f.Names)
|
||||
if nn == 0 {
|
||||
nn = 1 // anonymous parameter
|
||||
}
|
||||
nf += nn
|
||||
}
|
||||
if nf == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
ps := make([]*model.Parameter, nf)
|
||||
i := 0 // destination index
|
||||
for _, f := range fields {
|
||||
t, err := p.parseType(pkg, f.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(f.Names) == 0 {
|
||||
// anonymous arg
|
||||
ps[i] = &model.Parameter{Type: t}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
for _, name := range f.Names {
|
||||
ps[i] = &model.Parameter{Name: name.Name, Type: t}
|
||||
i++
|
||||
}
|
||||
}
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) {
|
||||
switch v := typ.(type) {
|
||||
case *ast.ArrayType:
|
||||
ln := -1
|
||||
if v.Len != nil {
|
||||
var value string
|
||||
switch val := v.Len.(type) {
|
||||
case (*ast.BasicLit):
|
||||
value = val.Value
|
||||
case (*ast.Ident):
|
||||
// when the length is a const defined locally
|
||||
value = val.Obj.Decl.(*ast.ValueSpec).Values[0].(*ast.BasicLit).Value
|
||||
case (*ast.SelectorExpr):
|
||||
// when the length is a const defined in an external package
|
||||
usedPkg, err := importer.Default().Import(fmt.Sprintf("%s", val.X))
|
||||
if err != nil {
|
||||
return nil, p.errorf(v.Len.Pos(), "unknown package in array length: %v", err)
|
||||
}
|
||||
ev, err := types.Eval(token.NewFileSet(), usedPkg, token.NoPos, val.Sel.Name)
|
||||
if err != nil {
|
||||
return nil, p.errorf(v.Len.Pos(), "unknown constant in array length: %v", err)
|
||||
}
|
||||
value = ev.Value.String()
|
||||
}
|
||||
|
||||
x, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return nil, p.errorf(v.Len.Pos(), "bad array size: %v", err)
|
||||
}
|
||||
ln = x
|
||||
}
|
||||
t, err := p.parseType(pkg, v.Elt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.ArrayType{Len: ln, Type: t}, nil
|
||||
case *ast.ChanType:
|
||||
t, err := p.parseType(pkg, v.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var dir model.ChanDir
|
||||
if v.Dir == ast.SEND {
|
||||
dir = model.SendDir
|
||||
}
|
||||
if v.Dir == ast.RECV {
|
||||
dir = model.RecvDir
|
||||
}
|
||||
return &model.ChanType{Dir: dir, Type: t}, nil
|
||||
case *ast.Ellipsis:
|
||||
// assume we're parsing a variadic argument
|
||||
return p.parseType(pkg, v.Elt)
|
||||
case *ast.FuncType:
|
||||
in, variadic, out, err := p.parseFunc(pkg, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.FuncType{In: in, Out: out, Variadic: variadic}, nil
|
||||
case *ast.Ident:
|
||||
if v.IsExported() {
|
||||
// `pkg` may be an aliased imported pkg
|
||||
// if so, patch the import w/ the fully qualified import
|
||||
maybeImportedPkg, ok := p.imports[pkg]
|
||||
if ok {
|
||||
pkg = maybeImportedPkg.Path()
|
||||
}
|
||||
// assume type in this package
|
||||
return &model.NamedType{Package: pkg, Type: v.Name}, nil
|
||||
}
|
||||
|
||||
// assume predeclared type
|
||||
return model.PredeclaredType(v.Name), nil
|
||||
case *ast.InterfaceType:
|
||||
if v.Methods != nil && len(v.Methods.List) > 0 {
|
||||
return nil, p.errorf(v.Pos(), "can't handle non-empty unnamed interface types")
|
||||
}
|
||||
return model.PredeclaredType("interface{}"), nil
|
||||
case *ast.MapType:
|
||||
key, err := p.parseType(pkg, v.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value, err := p.parseType(pkg, v.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.MapType{Key: key, Value: value}, nil
|
||||
case *ast.SelectorExpr:
|
||||
pkgName := v.X.(*ast.Ident).String()
|
||||
pkg, ok := p.imports[pkgName]
|
||||
if !ok {
|
||||
return nil, p.errorf(v.Pos(), "unknown package %q", pkgName)
|
||||
}
|
||||
return &model.NamedType{Package: pkg.Path(), Type: v.Sel.String()}, nil
|
||||
case *ast.StarExpr:
|
||||
t, err := p.parseType(pkg, v.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.PointerType{Type: t}, nil
|
||||
case *ast.StructType:
|
||||
if v.Fields != nil && len(v.Fields.List) > 0 {
|
||||
return nil, p.errorf(v.Pos(), "can't handle non-empty unnamed struct types")
|
||||
}
|
||||
return model.PredeclaredType("struct{}"), nil
|
||||
case *ast.ParenExpr:
|
||||
return p.parseType(pkg, v.X)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("don't know how to parse type %T", typ)
|
||||
}
|
||||
|
||||
// importsOfFile returns a map of package name to import path
|
||||
// of the imports in file.
|
||||
func importsOfFile(file *ast.File) (normalImports map[string]importedPackage, dotImports []string) {
|
||||
var importPaths []string
|
||||
for _, is := range file.Imports {
|
||||
if is.Name != nil {
|
||||
continue
|
||||
}
|
||||
importPath := is.Path.Value[1 : len(is.Path.Value)-1] // remove quotes
|
||||
importPaths = append(importPaths, importPath)
|
||||
}
|
||||
packagesName := createPackageMap(importPaths)
|
||||
normalImports = make(map[string]importedPackage)
|
||||
dotImports = make([]string, 0)
|
||||
for _, is := range file.Imports {
|
||||
var pkgName string
|
||||
importPath := is.Path.Value[1 : len(is.Path.Value)-1] // remove quotes
|
||||
|
||||
if is.Name != nil {
|
||||
// Named imports are always certain.
|
||||
if is.Name.Name == "_" {
|
||||
continue
|
||||
}
|
||||
pkgName = is.Name.Name
|
||||
} else {
|
||||
pkg, ok := packagesName[importPath]
|
||||
if !ok {
|
||||
// Fallback to import path suffix. Note that this is uncertain.
|
||||
_, last := path.Split(importPath)
|
||||
// If the last path component has dots, the first dot-delimited
|
||||
// field is used as the name.
|
||||
pkgName = strings.SplitN(last, ".", 2)[0]
|
||||
} else {
|
||||
pkgName = pkg
|
||||
}
|
||||
}
|
||||
|
||||
if pkgName == "." {
|
||||
dotImports = append(dotImports, importPath)
|
||||
} else {
|
||||
if pkg, ok := normalImports[pkgName]; ok {
|
||||
switch p := pkg.(type) {
|
||||
case duplicateImport:
|
||||
normalImports[pkgName] = duplicateImport{
|
||||
name: p.name,
|
||||
duplicates: append([]string{importPath}, p.duplicates...),
|
||||
}
|
||||
case importedPkg:
|
||||
normalImports[pkgName] = duplicateImport{
|
||||
name: pkgName,
|
||||
duplicates: []string{p.path, importPath},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
normalImports[pkgName] = importedPkg{path: importPath}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type namedInterface struct {
|
||||
name *ast.Ident
|
||||
it *ast.InterfaceType
|
||||
}
|
||||
|
||||
// Create an iterator over all interfaces in file.
|
||||
func iterInterfaces(file *ast.File) <-chan namedInterface {
|
||||
ch := make(chan namedInterface)
|
||||
go func() {
|
||||
for _, decl := range file.Decls {
|
||||
gd, ok := decl.(*ast.GenDecl)
|
||||
if !ok || gd.Tok != token.TYPE {
|
||||
continue
|
||||
}
|
||||
for _, spec := range gd.Specs {
|
||||
ts, ok := spec.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
it, ok := ts.Type.(*ast.InterfaceType)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
ch <- namedInterface{ts.Name, it}
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// isVariadic returns whether the function is variadic.
|
||||
func isVariadic(f *ast.FuncType) bool {
|
||||
nargs := len(f.Params.List)
|
||||
if nargs == 0 {
|
||||
return false
|
||||
}
|
||||
_, ok := f.Params.List[nargs-1].Type.(*ast.Ellipsis)
|
||||
return ok
|
||||
}
|
||||
|
||||
// packageNameOfDir get package import path via dir
|
||||
func packageNameOfDir(srcDir string) (string, error) {
|
||||
files, err := ioutil.ReadDir(srcDir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var goFilePath string
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && strings.HasSuffix(file.Name(), ".go") {
|
||||
goFilePath = file.Name()
|
||||
break
|
||||
}
|
||||
}
|
||||
if goFilePath == "" {
|
||||
return "", fmt.Errorf("go source file not found %s", srcDir)
|
||||
}
|
||||
|
||||
packageImport, err := parsePackageImport(srcDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return packageImport, nil
|
||||
}
|
||||
|
||||
var errOutsideGoPath = errors.New("source directory is outside GOPATH")
|
||||
256
vendor/github.com/golang/mock/mockgen/reflect.go
generated
vendored
Normal file
256
vendor/github.com/golang/mock/mockgen/reflect.go
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
// Copyright 2012 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
// This file contains the model construction by reflection.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/golang/mock/mockgen/model"
|
||||
)
|
||||
|
||||
var (
|
||||
progOnly = flag.Bool("prog_only", false, "(reflect mode) Only generate the reflection program; write it to stdout and exit.")
|
||||
execOnly = flag.String("exec_only", "", "(reflect mode) If set, execute this reflection program.")
|
||||
buildFlags = flag.String("build_flags", "", "(reflect mode) Additional flags for go build.")
|
||||
)
|
||||
|
||||
// reflectMode generates mocks via reflection on an interface.
|
||||
func reflectMode(importPath string, symbols []string) (*model.Package, error) {
|
||||
if *execOnly != "" {
|
||||
return run(*execOnly)
|
||||
}
|
||||
|
||||
program, err := writeProgram(importPath, symbols)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if *progOnly {
|
||||
if _, err := os.Stdout.Write(program); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
|
||||
// Try to run the reflection program in the current working directory.
|
||||
if p, err := runInDir(program, wd); err == nil {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Try to run the program in the same directory as the input package.
|
||||
if p, err := build.Import(importPath, wd, build.FindOnly); err == nil {
|
||||
dir := p.Dir
|
||||
if p, err := runInDir(program, dir); err == nil {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Try to run it in a standard temp directory.
|
||||
return runInDir(program, "")
|
||||
}
|
||||
|
||||
func writeProgram(importPath string, symbols []string) ([]byte, error) {
|
||||
var program bytes.Buffer
|
||||
data := reflectData{
|
||||
ImportPath: importPath,
|
||||
Symbols: symbols,
|
||||
}
|
||||
if err := reflectProgram.Execute(&program, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return program.Bytes(), nil
|
||||
}
|
||||
|
||||
// run the given program and parse the output as a model.Package.
|
||||
func run(program string) (*model.Package, error) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filename := f.Name()
|
||||
defer os.Remove(filename)
|
||||
if err := f.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Run the program.
|
||||
cmd := exec.Command(program, "-output", filename)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f, err = os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Process output.
|
||||
var pkg model.Package
|
||||
if err := gob.NewDecoder(f).Decode(&pkg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pkg, nil
|
||||
}
|
||||
|
||||
// runInDir writes the given program into the given dir, runs it there, and
|
||||
// parses the output as a model.Package.
|
||||
func runInDir(program []byte, dir string) (*model.Package, error) {
|
||||
// We use TempDir instead of TempFile so we can control the filename.
|
||||
tmpDir, err := ioutil.TempDir(dir, "gomock_reflect_")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := os.RemoveAll(tmpDir); err != nil {
|
||||
log.Printf("failed to remove temp directory: %s", err)
|
||||
}
|
||||
}()
|
||||
const progSource = "prog.go"
|
||||
var progBinary = "prog.bin"
|
||||
if runtime.GOOS == "windows" {
|
||||
// Windows won't execute a program unless it has a ".exe" suffix.
|
||||
progBinary += ".exe"
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmdArgs := []string{}
|
||||
cmdArgs = append(cmdArgs, "build")
|
||||
if *buildFlags != "" {
|
||||
cmdArgs = append(cmdArgs, strings.Split(*buildFlags, " ")...)
|
||||
}
|
||||
cmdArgs = append(cmdArgs, "-o", progBinary, progSource)
|
||||
|
||||
// Build the program.
|
||||
buf := bytes.NewBuffer(nil)
|
||||
cmd := exec.Command("go", cmdArgs...)
|
||||
cmd.Dir = tmpDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = io.MultiWriter(os.Stderr, buf)
|
||||
if err := cmd.Run(); err != nil {
|
||||
sErr := buf.String()
|
||||
if strings.Contains(sErr, `cannot find package "."`) &&
|
||||
strings.Contains(sErr, "github.com/golang/mock/mockgen/model") {
|
||||
fmt.Fprint(os.Stderr, "Please reference the steps in the README to fix this error:\n\thttps://github.com/golang/mock#reflect-vendoring-error.")
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return run(filepath.Join(tmpDir, progBinary))
|
||||
}
|
||||
|
||||
type reflectData struct {
|
||||
ImportPath string
|
||||
Symbols []string
|
||||
}
|
||||
|
||||
// This program reflects on an interface value, and prints the
|
||||
// gob encoding of a model.Package to standard output.
|
||||
// JSON doesn't work because of the model.Type interface.
|
||||
var reflectProgram = template.Must(template.New("program").Parse(`
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
|
||||
"github.com/golang/mock/mockgen/model"
|
||||
|
||||
pkg_ {{printf "%q" .ImportPath}}
|
||||
)
|
||||
|
||||
var output = flag.String("output", "", "The output file name, or empty to use stdout.")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
its := []struct{
|
||||
sym string
|
||||
typ reflect.Type
|
||||
}{
|
||||
{{range .Symbols}}
|
||||
{ {{printf "%q" .}}, reflect.TypeOf((*pkg_.{{.}})(nil)).Elem()},
|
||||
{{end}}
|
||||
}
|
||||
pkg := &model.Package{
|
||||
// NOTE: This behaves contrary to documented behaviour if the
|
||||
// package name is not the final component of the import path.
|
||||
// The reflect package doesn't expose the package name, though.
|
||||
Name: path.Base({{printf "%q" .ImportPath}}),
|
||||
}
|
||||
|
||||
for _, it := range its {
|
||||
intf, err := model.InterfaceFromInterfaceType(it.typ)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Reflection: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
intf.Name = it.sym
|
||||
pkg.Interfaces = append(pkg.Interfaces, intf)
|
||||
}
|
||||
|
||||
outfile := os.Stdout
|
||||
if len(*output) != 0 {
|
||||
var err error
|
||||
outfile, err = os.Create(*output)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to open output file %q", *output)
|
||||
}
|
||||
defer func() {
|
||||
if err := outfile.Close(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to close output file %q", *output)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if err := gob.NewEncoder(outfile).Encode(pkg); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gob encode: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
`))
|
||||
26
vendor/github.com/golang/mock/mockgen/version.1.11.go
generated
vendored
Normal file
26
vendor/github.com/golang/mock/mockgen/version.1.11.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build !go1.12
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
func printModuleVersion() {
|
||||
log.Printf("No version information is available for Mockgen compiled with " +
|
||||
"version 1.11")
|
||||
}
|
||||
35
vendor/github.com/golang/mock/mockgen/version.1.12.go
generated
vendored
Normal file
35
vendor/github.com/golang/mock/mockgen/version.1.12.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
// +build go1.12
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func printModuleVersion() {
|
||||
if bi, exists := debug.ReadBuildInfo(); exists {
|
||||
fmt.Println(bi.Main.Version)
|
||||
} else {
|
||||
log.Printf("No version information found. Make sure to use " +
|
||||
"GO111MODULE=on when running 'go get' in order to use specific " +
|
||||
"version of the binary.")
|
||||
}
|
||||
|
||||
}
|
||||
179
vendor/github.com/golang/protobuf/ptypes/any.go
generated
vendored
Normal file
179
vendor/github.com/golang/protobuf/ptypes/any.go
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ptypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
|
||||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
)
|
||||
|
||||
const urlPrefix = "type.googleapis.com/"
|
||||
|
||||
// AnyMessageName returns the message name contained in an anypb.Any message.
|
||||
// Most type assertions should use the Is function instead.
|
||||
//
|
||||
// Deprecated: Call the any.MessageName method instead.
|
||||
func AnyMessageName(any *anypb.Any) (string, error) {
|
||||
name, err := anyMessageName(any)
|
||||
return string(name), err
|
||||
}
|
||||
func anyMessageName(any *anypb.Any) (protoreflect.FullName, error) {
|
||||
if any == nil {
|
||||
return "", fmt.Errorf("message is nil")
|
||||
}
|
||||
name := protoreflect.FullName(any.TypeUrl)
|
||||
if i := strings.LastIndex(any.TypeUrl, "/"); i >= 0 {
|
||||
name = name[i+len("/"):]
|
||||
}
|
||||
if !name.IsValid() {
|
||||
return "", fmt.Errorf("message type url %q is invalid", any.TypeUrl)
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// MarshalAny marshals the given message m into an anypb.Any message.
|
||||
//
|
||||
// Deprecated: Call the anypb.New function instead.
|
||||
func MarshalAny(m proto.Message) (*anypb.Any, error) {
|
||||
switch dm := m.(type) {
|
||||
case DynamicAny:
|
||||
m = dm.Message
|
||||
case *DynamicAny:
|
||||
if dm == nil {
|
||||
return nil, proto.ErrNil
|
||||
}
|
||||
m = dm.Message
|
||||
}
|
||||
b, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &anypb.Any{TypeUrl: urlPrefix + proto.MessageName(m), Value: b}, nil
|
||||
}
|
||||
|
||||
// Empty returns a new message of the type specified in an anypb.Any message.
|
||||
// It returns protoregistry.NotFound if the corresponding message type could not
|
||||
// be resolved in the global registry.
|
||||
//
|
||||
// Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead
|
||||
// to resolve the message name and create a new instance of it.
|
||||
func Empty(any *anypb.Any) (proto.Message, error) {
|
||||
name, err := anyMessageName(any)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mt, err := protoregistry.GlobalTypes.FindMessageByName(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return proto.MessageV1(mt.New().Interface()), nil
|
||||
}
|
||||
|
||||
// UnmarshalAny unmarshals the encoded value contained in the anypb.Any message
|
||||
// into the provided message m. It returns an error if the target message
|
||||
// does not match the type in the Any message or if an unmarshal error occurs.
|
||||
//
|
||||
// The target message m may be a *DynamicAny message. If the underlying message
|
||||
// type could not be resolved, then this returns protoregistry.NotFound.
|
||||
//
|
||||
// Deprecated: Call the any.UnmarshalTo method instead.
|
||||
func UnmarshalAny(any *anypb.Any, m proto.Message) error {
|
||||
if dm, ok := m.(*DynamicAny); ok {
|
||||
if dm.Message == nil {
|
||||
var err error
|
||||
dm.Message, err = Empty(any)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
m = dm.Message
|
||||
}
|
||||
|
||||
anyName, err := AnyMessageName(any)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgName := proto.MessageName(m)
|
||||
if anyName != msgName {
|
||||
return fmt.Errorf("mismatched message type: got %q want %q", anyName, msgName)
|
||||
}
|
||||
return proto.Unmarshal(any.Value, m)
|
||||
}
|
||||
|
||||
// Is reports whether the Any message contains a message of the specified type.
|
||||
//
|
||||
// Deprecated: Call the any.MessageIs method instead.
|
||||
func Is(any *anypb.Any, m proto.Message) bool {
|
||||
if any == nil || m == nil {
|
||||
return false
|
||||
}
|
||||
name := proto.MessageName(m)
|
||||
if !strings.HasSuffix(any.TypeUrl, name) {
|
||||
return false
|
||||
}
|
||||
return len(any.TypeUrl) == len(name) || any.TypeUrl[len(any.TypeUrl)-len(name)-1] == '/'
|
||||
}
|
||||
|
||||
// DynamicAny is a value that can be passed to UnmarshalAny to automatically
|
||||
// allocate a proto.Message for the type specified in an anypb.Any message.
|
||||
// The allocated message is stored in the embedded proto.Message.
|
||||
//
|
||||
// Example:
|
||||
// var x ptypes.DynamicAny
|
||||
// if err := ptypes.UnmarshalAny(a, &x); err != nil { ... }
|
||||
// fmt.Printf("unmarshaled message: %v", x.Message)
|
||||
//
|
||||
// Deprecated: Use the any.UnmarshalNew method instead to unmarshal
|
||||
// the any message contents into a new instance of the underlying message.
|
||||
type DynamicAny struct{ proto.Message }
|
||||
|
||||
func (m DynamicAny) String() string {
|
||||
if m.Message == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return m.Message.String()
|
||||
}
|
||||
func (m DynamicAny) Reset() {
|
||||
if m.Message == nil {
|
||||
return
|
||||
}
|
||||
m.Message.Reset()
|
||||
}
|
||||
func (m DynamicAny) ProtoMessage() {
|
||||
return
|
||||
}
|
||||
func (m DynamicAny) ProtoReflect() protoreflect.Message {
|
||||
if m.Message == nil {
|
||||
return nil
|
||||
}
|
||||
return dynamicAny{proto.MessageReflect(m.Message)}
|
||||
}
|
||||
|
||||
type dynamicAny struct{ protoreflect.Message }
|
||||
|
||||
func (m dynamicAny) Type() protoreflect.MessageType {
|
||||
return dynamicAnyType{m.Message.Type()}
|
||||
}
|
||||
func (m dynamicAny) New() protoreflect.Message {
|
||||
return dynamicAnyType{m.Message.Type()}.New()
|
||||
}
|
||||
func (m dynamicAny) Interface() protoreflect.ProtoMessage {
|
||||
return DynamicAny{proto.MessageV1(m.Message.Interface())}
|
||||
}
|
||||
|
||||
type dynamicAnyType struct{ protoreflect.MessageType }
|
||||
|
||||
func (t dynamicAnyType) New() protoreflect.Message {
|
||||
return dynamicAny{t.MessageType.New()}
|
||||
}
|
||||
func (t dynamicAnyType) Zero() protoreflect.Message {
|
||||
return dynamicAny{t.MessageType.Zero()}
|
||||
}
|
||||
62
vendor/github.com/golang/protobuf/ptypes/any/any.pb.go
generated
vendored
Normal file
62
vendor/github.com/golang/protobuf/ptypes/any/any.pb.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/golang/protobuf/ptypes/any/any.proto
|
||||
|
||||
package any
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
anypb "google.golang.org/protobuf/types/known/anypb"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// Symbols defined in public import of google/protobuf/any.proto.
|
||||
|
||||
type Any = anypb.Any
|
||||
|
||||
var File_github_com_golang_protobuf_ptypes_any_any_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_any_any_proto_rawDesc = []byte{
|
||||
0x0a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x61, 0x6e, 0x79, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x2b, 0x5a, 0x29,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e,
|
||||
0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65,
|
||||
0x73, 0x2f, 0x61, 0x6e, 0x79, 0x3b, 0x61, 0x6e, 0x79, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_any_any_proto_goTypes = []interface{}{}
|
||||
var file_github_com_golang_protobuf_ptypes_any_any_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_github_com_golang_protobuf_ptypes_any_any_proto_init() }
|
||||
func file_github_com_golang_protobuf_ptypes_any_any_proto_init() {
|
||||
if File_github_com_golang_protobuf_ptypes_any_any_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_github_com_golang_protobuf_ptypes_any_any_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_github_com_golang_protobuf_ptypes_any_any_proto_goTypes,
|
||||
DependencyIndexes: file_github_com_golang_protobuf_ptypes_any_any_proto_depIdxs,
|
||||
}.Build()
|
||||
File_github_com_golang_protobuf_ptypes_any_any_proto = out.File
|
||||
file_github_com_golang_protobuf_ptypes_any_any_proto_rawDesc = nil
|
||||
file_github_com_golang_protobuf_ptypes_any_any_proto_goTypes = nil
|
||||
file_github_com_golang_protobuf_ptypes_any_any_proto_depIdxs = nil
|
||||
}
|
||||
10
vendor/github.com/golang/protobuf/ptypes/doc.go
generated
vendored
Normal file
10
vendor/github.com/golang/protobuf/ptypes/doc.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ptypes provides functionality for interacting with well-known types.
|
||||
//
|
||||
// Deprecated: Well-known types have specialized functionality directly
|
||||
// injected into the generated packages for each message type.
|
||||
// See the deprecation notice for each function for the suggested alternative.
|
||||
package ptypes
|
||||
76
vendor/github.com/golang/protobuf/ptypes/duration.go
generated
vendored
Normal file
76
vendor/github.com/golang/protobuf/ptypes/duration.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ptypes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
durationpb "github.com/golang/protobuf/ptypes/duration"
|
||||
)
|
||||
|
||||
// Range of google.protobuf.Duration as specified in duration.proto.
|
||||
// This is about 10,000 years in seconds.
|
||||
const (
|
||||
maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60)
|
||||
minSeconds = -maxSeconds
|
||||
)
|
||||
|
||||
// Duration converts a durationpb.Duration to a time.Duration.
|
||||
// Duration returns an error if dur is invalid or overflows a time.Duration.
|
||||
//
|
||||
// Deprecated: Call the dur.AsDuration and dur.CheckValid methods instead.
|
||||
func Duration(dur *durationpb.Duration) (time.Duration, error) {
|
||||
if err := validateDuration(dur); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d := time.Duration(dur.Seconds) * time.Second
|
||||
if int64(d/time.Second) != dur.Seconds {
|
||||
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur)
|
||||
}
|
||||
if dur.Nanos != 0 {
|
||||
d += time.Duration(dur.Nanos) * time.Nanosecond
|
||||
if (d < 0) != (dur.Nanos < 0) {
|
||||
return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur)
|
||||
}
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// DurationProto converts a time.Duration to a durationpb.Duration.
|
||||
//
|
||||
// Deprecated: Call the durationpb.New function instead.
|
||||
func DurationProto(d time.Duration) *durationpb.Duration {
|
||||
nanos := d.Nanoseconds()
|
||||
secs := nanos / 1e9
|
||||
nanos -= secs * 1e9
|
||||
return &durationpb.Duration{
|
||||
Seconds: int64(secs),
|
||||
Nanos: int32(nanos),
|
||||
}
|
||||
}
|
||||
|
||||
// validateDuration determines whether the durationpb.Duration is valid
|
||||
// according to the definition in google/protobuf/duration.proto.
|
||||
// A valid durpb.Duration may still be too large to fit into a time.Duration
|
||||
// Note that the range of durationpb.Duration is about 10,000 years,
|
||||
// while the range of time.Duration is about 290 years.
|
||||
func validateDuration(dur *durationpb.Duration) error {
|
||||
if dur == nil {
|
||||
return errors.New("duration: nil Duration")
|
||||
}
|
||||
if dur.Seconds < minSeconds || dur.Seconds > maxSeconds {
|
||||
return fmt.Errorf("duration: %v: seconds out of range", dur)
|
||||
}
|
||||
if dur.Nanos <= -1e9 || dur.Nanos >= 1e9 {
|
||||
return fmt.Errorf("duration: %v: nanos out of range", dur)
|
||||
}
|
||||
// Seconds and Nanos must have the same sign, unless d.Nanos is zero.
|
||||
if (dur.Seconds < 0 && dur.Nanos > 0) || (dur.Seconds > 0 && dur.Nanos < 0) {
|
||||
return fmt.Errorf("duration: %v: seconds and nanos have different signs", dur)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
63
vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go
generated
vendored
Normal file
63
vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/golang/protobuf/ptypes/duration/duration.proto
|
||||
|
||||
package duration
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// Symbols defined in public import of google/protobuf/duration.proto.
|
||||
|
||||
type Duration = durationpb.Duration
|
||||
|
||||
var File_github_com_golang_protobuf_ptypes_duration_duration_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc = []byte{
|
||||
0x0a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x64, 0x75, 0x72,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x35, 0x5a, 0x33, 0x67,
|
||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67,
|
||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73,
|
||||
0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes = []interface{}{}
|
||||
var file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_github_com_golang_protobuf_ptypes_duration_duration_proto_init() }
|
||||
func file_github_com_golang_protobuf_ptypes_duration_duration_proto_init() {
|
||||
if File_github_com_golang_protobuf_ptypes_duration_duration_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes,
|
||||
DependencyIndexes: file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs,
|
||||
}.Build()
|
||||
File_github_com_golang_protobuf_ptypes_duration_duration_proto = out.File
|
||||
file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc = nil
|
||||
file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes = nil
|
||||
file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs = nil
|
||||
}
|
||||
112
vendor/github.com/golang/protobuf/ptypes/timestamp.go
generated
vendored
Normal file
112
vendor/github.com/golang/protobuf/ptypes/timestamp.go
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ptypes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
timestamppb "github.com/golang/protobuf/ptypes/timestamp"
|
||||
)
|
||||
|
||||
// Range of google.protobuf.Duration as specified in timestamp.proto.
|
||||
const (
|
||||
// Seconds field of the earliest valid Timestamp.
|
||||
// This is time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
|
||||
minValidSeconds = -62135596800
|
||||
// Seconds field just after the latest valid Timestamp.
|
||||
// This is time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
|
||||
maxValidSeconds = 253402300800
|
||||
)
|
||||
|
||||
// Timestamp converts a timestamppb.Timestamp to a time.Time.
|
||||
// It returns an error if the argument is invalid.
|
||||
//
|
||||
// Unlike most Go functions, if Timestamp returns an error, the first return
|
||||
// value is not the zero time.Time. Instead, it is the value obtained from the
|
||||
// time.Unix function when passed the contents of the Timestamp, in the UTC
|
||||
// locale. This may or may not be a meaningful time; many invalid Timestamps
|
||||
// do map to valid time.Times.
|
||||
//
|
||||
// A nil Timestamp returns an error. The first return value in that case is
|
||||
// undefined.
|
||||
//
|
||||
// Deprecated: Call the ts.AsTime and ts.CheckValid methods instead.
|
||||
func Timestamp(ts *timestamppb.Timestamp) (time.Time, error) {
|
||||
// Don't return the zero value on error, because corresponds to a valid
|
||||
// timestamp. Instead return whatever time.Unix gives us.
|
||||
var t time.Time
|
||||
if ts == nil {
|
||||
t = time.Unix(0, 0).UTC() // treat nil like the empty Timestamp
|
||||
} else {
|
||||
t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC()
|
||||
}
|
||||
return t, validateTimestamp(ts)
|
||||
}
|
||||
|
||||
// TimestampNow returns a google.protobuf.Timestamp for the current time.
|
||||
//
|
||||
// Deprecated: Call the timestamppb.Now function instead.
|
||||
func TimestampNow() *timestamppb.Timestamp {
|
||||
ts, err := TimestampProto(time.Now())
|
||||
if err != nil {
|
||||
panic("ptypes: time.Now() out of Timestamp range")
|
||||
}
|
||||
return ts
|
||||
}
|
||||
|
||||
// TimestampProto converts the time.Time to a google.protobuf.Timestamp proto.
|
||||
// It returns an error if the resulting Timestamp is invalid.
|
||||
//
|
||||
// Deprecated: Call the timestamppb.New function instead.
|
||||
func TimestampProto(t time.Time) (*timestamppb.Timestamp, error) {
|
||||
ts := ×tamppb.Timestamp{
|
||||
Seconds: t.Unix(),
|
||||
Nanos: int32(t.Nanosecond()),
|
||||
}
|
||||
if err := validateTimestamp(ts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
// TimestampString returns the RFC 3339 string for valid Timestamps.
|
||||
// For invalid Timestamps, it returns an error message in parentheses.
|
||||
//
|
||||
// Deprecated: Call the ts.AsTime method instead,
|
||||
// followed by a call to the Format method on the time.Time value.
|
||||
func TimestampString(ts *timestamppb.Timestamp) string {
|
||||
t, err := Timestamp(ts)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("(%v)", err)
|
||||
}
|
||||
return t.Format(time.RFC3339Nano)
|
||||
}
|
||||
|
||||
// validateTimestamp determines whether a Timestamp is valid.
|
||||
// A valid timestamp represents a time in the range [0001-01-01, 10000-01-01)
|
||||
// and has a Nanos field in the range [0, 1e9).
|
||||
//
|
||||
// If the Timestamp is valid, validateTimestamp returns nil.
|
||||
// Otherwise, it returns an error that describes the problem.
|
||||
//
|
||||
// Every valid Timestamp can be represented by a time.Time,
|
||||
// but the converse is not true.
|
||||
func validateTimestamp(ts *timestamppb.Timestamp) error {
|
||||
if ts == nil {
|
||||
return errors.New("timestamp: nil Timestamp")
|
||||
}
|
||||
if ts.Seconds < minValidSeconds {
|
||||
return fmt.Errorf("timestamp: %v before 0001-01-01", ts)
|
||||
}
|
||||
if ts.Seconds >= maxValidSeconds {
|
||||
return fmt.Errorf("timestamp: %v after 10000-01-01", ts)
|
||||
}
|
||||
if ts.Nanos < 0 || ts.Nanos >= 1e9 {
|
||||
return fmt.Errorf("timestamp: %v: nanos not in range [0, 1e9)", ts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
64
vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
generated
vendored
Normal file
64
vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/golang/protobuf/ptypes/timestamp/timestamp.proto
|
||||
|
||||
package timestamp
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// Symbols defined in public import of google/protobuf/timestamp.proto.
|
||||
|
||||
type Timestamp = timestamppb.Timestamp
|
||||
|
||||
var File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = []byte{
|
||||
0x0a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x37,
|
||||
0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3b, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = []interface{}{}
|
||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() }
|
||||
func file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() {
|
||||
if File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes,
|
||||
DependencyIndexes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs,
|
||||
}.Build()
|
||||
File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto = out.File
|
||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = nil
|
||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = nil
|
||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = nil
|
||||
}
|
||||
16
vendor/github.com/golang/snappy/.gitignore
generated
vendored
Normal file
16
vendor/github.com/golang/snappy/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
cmd/snappytool/snappytool
|
||||
testdata/bench
|
||||
|
||||
# These explicitly listed benchmark data files are for an obsolete version of
|
||||
# snappy_test.go.
|
||||
testdata/alice29.txt
|
||||
testdata/asyoulik.txt
|
||||
testdata/fireworks.jpeg
|
||||
testdata/geo.protodata
|
||||
testdata/html
|
||||
testdata/html_x_4
|
||||
testdata/kppkn.gtb
|
||||
testdata/lcet10.txt
|
||||
testdata/paper-100k.pdf
|
||||
testdata/plrabn12.txt
|
||||
testdata/urls.10K
|
||||
18
vendor/github.com/golang/snappy/AUTHORS
generated
vendored
Normal file
18
vendor/github.com/golang/snappy/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# This is the official list of Snappy-Go authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Amazon.com, Inc
|
||||
Damian Gryski <dgryski@gmail.com>
|
||||
Eric Buth <eric@topos.com>
|
||||
Google Inc.
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Klaus Post <klauspost@gmail.com>
|
||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||
Sebastien Binet <seb.binet@gmail.com>
|
||||
41
vendor/github.com/golang/snappy/CONTRIBUTORS
generated
vendored
Normal file
41
vendor/github.com/golang/snappy/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# This is the official list of people who can contribute
|
||||
# (and typically have contributed) code to the Snappy-Go repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# The submission process automatically checks to make sure
|
||||
# that people submitting code are listed in this file (by email address).
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# http://code.google.com/legal/individual-cla-v1.0.html
|
||||
# http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
#
|
||||
# The agreement for individuals can be filled out on the web.
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Alex Legg <alexlegg@google.com>
|
||||
Damian Gryski <dgryski@gmail.com>
|
||||
Eric Buth <eric@topos.com>
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Jonathan Swinney <jswinney@amazon.com>
|
||||
Kai Backman <kaib@golang.org>
|
||||
Klaus Post <klauspost@gmail.com>
|
||||
Marc-Antoine Ruel <maruel@chromium.org>
|
||||
Nigel Tao <nigeltao@golang.org>
|
||||
Rob Pike <r@golang.org>
|
||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||
Russ Cox <rsc@golang.org>
|
||||
Sebastien Binet <seb.binet@gmail.com>
|
||||
27
vendor/github.com/golang/snappy/LICENSE
generated
vendored
Normal file
27
vendor/github.com/golang/snappy/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2011 The Snappy-Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
107
vendor/github.com/golang/snappy/README
generated
vendored
Normal file
107
vendor/github.com/golang/snappy/README
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
The Snappy compression format in the Go programming language.
|
||||
|
||||
To download and install from source:
|
||||
$ go get github.com/golang/snappy
|
||||
|
||||
Unless otherwise noted, the Snappy-Go source files are distributed
|
||||
under the BSD-style license found in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
Benchmarks.
|
||||
|
||||
The golang/snappy benchmarks include compressing (Z) and decompressing (U) ten
|
||||
or so files, the same set used by the C++ Snappy code (github.com/google/snappy
|
||||
and note the "google", not "golang"). On an "Intel(R) Core(TM) i7-3770 CPU @
|
||||
3.40GHz", Go's GOARCH=amd64 numbers as of 2016-05-29:
|
||||
|
||||
"go test -test.bench=."
|
||||
|
||||
_UFlat0-8 2.19GB/s ± 0% html
|
||||
_UFlat1-8 1.41GB/s ± 0% urls
|
||||
_UFlat2-8 23.5GB/s ± 2% jpg
|
||||
_UFlat3-8 1.91GB/s ± 0% jpg_200
|
||||
_UFlat4-8 14.0GB/s ± 1% pdf
|
||||
_UFlat5-8 1.97GB/s ± 0% html4
|
||||
_UFlat6-8 814MB/s ± 0% txt1
|
||||
_UFlat7-8 785MB/s ± 0% txt2
|
||||
_UFlat8-8 857MB/s ± 0% txt3
|
||||
_UFlat9-8 719MB/s ± 1% txt4
|
||||
_UFlat10-8 2.84GB/s ± 0% pb
|
||||
_UFlat11-8 1.05GB/s ± 0% gaviota
|
||||
|
||||
_ZFlat0-8 1.04GB/s ± 0% html
|
||||
_ZFlat1-8 534MB/s ± 0% urls
|
||||
_ZFlat2-8 15.7GB/s ± 1% jpg
|
||||
_ZFlat3-8 740MB/s ± 3% jpg_200
|
||||
_ZFlat4-8 9.20GB/s ± 1% pdf
|
||||
_ZFlat5-8 991MB/s ± 0% html4
|
||||
_ZFlat6-8 379MB/s ± 0% txt1
|
||||
_ZFlat7-8 352MB/s ± 0% txt2
|
||||
_ZFlat8-8 396MB/s ± 1% txt3
|
||||
_ZFlat9-8 327MB/s ± 1% txt4
|
||||
_ZFlat10-8 1.33GB/s ± 1% pb
|
||||
_ZFlat11-8 605MB/s ± 1% gaviota
|
||||
|
||||
|
||||
|
||||
"go test -test.bench=. -tags=noasm"
|
||||
|
||||
_UFlat0-8 621MB/s ± 2% html
|
||||
_UFlat1-8 494MB/s ± 1% urls
|
||||
_UFlat2-8 23.2GB/s ± 1% jpg
|
||||
_UFlat3-8 1.12GB/s ± 1% jpg_200
|
||||
_UFlat4-8 4.35GB/s ± 1% pdf
|
||||
_UFlat5-8 609MB/s ± 0% html4
|
||||
_UFlat6-8 296MB/s ± 0% txt1
|
||||
_UFlat7-8 288MB/s ± 0% txt2
|
||||
_UFlat8-8 309MB/s ± 1% txt3
|
||||
_UFlat9-8 280MB/s ± 1% txt4
|
||||
_UFlat10-8 753MB/s ± 0% pb
|
||||
_UFlat11-8 400MB/s ± 0% gaviota
|
||||
|
||||
_ZFlat0-8 409MB/s ± 1% html
|
||||
_ZFlat1-8 250MB/s ± 1% urls
|
||||
_ZFlat2-8 12.3GB/s ± 1% jpg
|
||||
_ZFlat3-8 132MB/s ± 0% jpg_200
|
||||
_ZFlat4-8 2.92GB/s ± 0% pdf
|
||||
_ZFlat5-8 405MB/s ± 1% html4
|
||||
_ZFlat6-8 179MB/s ± 1% txt1
|
||||
_ZFlat7-8 170MB/s ± 1% txt2
|
||||
_ZFlat8-8 189MB/s ± 1% txt3
|
||||
_ZFlat9-8 164MB/s ± 1% txt4
|
||||
_ZFlat10-8 479MB/s ± 1% pb
|
||||
_ZFlat11-8 270MB/s ± 1% gaviota
|
||||
|
||||
|
||||
|
||||
For comparison (Go's encoded output is byte-for-byte identical to C++'s), here
|
||||
are the numbers from C++ Snappy's
|
||||
|
||||
make CXXFLAGS="-O2 -DNDEBUG -g" clean snappy_unittest.log && cat snappy_unittest.log
|
||||
|
||||
BM_UFlat/0 2.4GB/s html
|
||||
BM_UFlat/1 1.4GB/s urls
|
||||
BM_UFlat/2 21.8GB/s jpg
|
||||
BM_UFlat/3 1.5GB/s jpg_200
|
||||
BM_UFlat/4 13.3GB/s pdf
|
||||
BM_UFlat/5 2.1GB/s html4
|
||||
BM_UFlat/6 1.0GB/s txt1
|
||||
BM_UFlat/7 959.4MB/s txt2
|
||||
BM_UFlat/8 1.0GB/s txt3
|
||||
BM_UFlat/9 864.5MB/s txt4
|
||||
BM_UFlat/10 2.9GB/s pb
|
||||
BM_UFlat/11 1.2GB/s gaviota
|
||||
|
||||
BM_ZFlat/0 944.3MB/s html (22.31 %)
|
||||
BM_ZFlat/1 501.6MB/s urls (47.78 %)
|
||||
BM_ZFlat/2 14.3GB/s jpg (99.95 %)
|
||||
BM_ZFlat/3 538.3MB/s jpg_200 (73.00 %)
|
||||
BM_ZFlat/4 8.3GB/s pdf (83.30 %)
|
||||
BM_ZFlat/5 903.5MB/s html4 (22.52 %)
|
||||
BM_ZFlat/6 336.0MB/s txt1 (57.88 %)
|
||||
BM_ZFlat/7 312.3MB/s txt2 (61.91 %)
|
||||
BM_ZFlat/8 353.1MB/s txt3 (54.99 %)
|
||||
BM_ZFlat/9 289.9MB/s txt4 (66.26 %)
|
||||
BM_ZFlat/10 1.2GB/s pb (19.68 %)
|
||||
BM_ZFlat/11 527.4MB/s gaviota (37.72 %)
|
||||
264
vendor/github.com/golang/snappy/decode.go
generated
vendored
Normal file
264
vendor/github.com/golang/snappy/decode.go
generated
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrCorrupt reports that the input is invalid.
|
||||
ErrCorrupt = errors.New("snappy: corrupt input")
|
||||
// ErrTooLarge reports that the uncompressed length is too large.
|
||||
ErrTooLarge = errors.New("snappy: decoded block is too large")
|
||||
// ErrUnsupported reports that the input isn't supported.
|
||||
ErrUnsupported = errors.New("snappy: unsupported input")
|
||||
|
||||
errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length")
|
||||
)
|
||||
|
||||
// DecodedLen returns the length of the decoded block.
|
||||
func DecodedLen(src []byte) (int, error) {
|
||||
v, _, err := decodedLen(src)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// decodedLen returns the length of the decoded block and the number of bytes
|
||||
// that the length header occupied.
|
||||
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
||||
v, n := binary.Uvarint(src)
|
||||
if n <= 0 || v > 0xffffffff {
|
||||
return 0, 0, ErrCorrupt
|
||||
}
|
||||
|
||||
const wordSize = 32 << (^uint(0) >> 32 & 1)
|
||||
if wordSize == 32 && v > 0x7fffffff {
|
||||
return 0, 0, ErrTooLarge
|
||||
}
|
||||
return int(v), n, nil
|
||||
}
|
||||
|
||||
const (
|
||||
decodeErrCodeCorrupt = 1
|
||||
decodeErrCodeUnsupportedLiteralLength = 2
|
||||
)
|
||||
|
||||
// Decode returns the decoded form of src. The returned slice may be a sub-
|
||||
// slice of dst if dst was large enough to hold the entire decoded block.
|
||||
// Otherwise, a newly allocated slice will be returned.
|
||||
//
|
||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||
//
|
||||
// Decode handles the Snappy block format, not the Snappy stream format.
|
||||
func Decode(dst, src []byte) ([]byte, error) {
|
||||
dLen, s, err := decodedLen(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dLen <= len(dst) {
|
||||
dst = dst[:dLen]
|
||||
} else {
|
||||
dst = make([]byte, dLen)
|
||||
}
|
||||
switch decode(dst, src[s:]) {
|
||||
case 0:
|
||||
return dst, nil
|
||||
case decodeErrCodeUnsupportedLiteralLength:
|
||||
return nil, errUnsupportedLiteralLength
|
||||
}
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
|
||||
// NewReader returns a new Reader that decompresses from r, using the framing
|
||||
// format described at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return &Reader{
|
||||
r: r,
|
||||
decoded: make([]byte, maxBlockSize),
|
||||
buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Reader is an io.Reader that can read Snappy-compressed bytes.
|
||||
//
|
||||
// Reader handles the Snappy stream format, not the Snappy block format.
|
||||
type Reader struct {
|
||||
r io.Reader
|
||||
err error
|
||||
decoded []byte
|
||||
buf []byte
|
||||
// decoded[i:j] contains decoded bytes that have not yet been passed on.
|
||||
i, j int
|
||||
readHeader bool
|
||||
}
|
||||
|
||||
// Reset discards any buffered data, resets all state, and switches the Snappy
|
||||
// reader to read from r. This permits reusing a Reader rather than allocating
|
||||
// a new one.
|
||||
func (r *Reader) Reset(reader io.Reader) {
|
||||
r.r = reader
|
||||
r.err = nil
|
||||
r.i = 0
|
||||
r.j = 0
|
||||
r.readHeader = false
|
||||
}
|
||||
|
||||
func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) {
|
||||
if _, r.err = io.ReadFull(r.r, p); r.err != nil {
|
||||
if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) {
|
||||
r.err = ErrCorrupt
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *Reader) fill() error {
|
||||
for r.i >= r.j {
|
||||
if !r.readFull(r.buf[:4], true) {
|
||||
return r.err
|
||||
}
|
||||
chunkType := r.buf[0]
|
||||
if !r.readHeader {
|
||||
if chunkType != chunkTypeStreamIdentifier {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
r.readHeader = true
|
||||
}
|
||||
chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
|
||||
if chunkLen > len(r.buf) {
|
||||
r.err = ErrUnsupported
|
||||
return r.err
|
||||
}
|
||||
|
||||
// The chunk types are specified at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
switch chunkType {
|
||||
case chunkTypeCompressedData:
|
||||
// Section 4.2. Compressed data (chunk type 0x00).
|
||||
if chunkLen < checksumSize {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
buf := r.buf[:chunkLen]
|
||||
if !r.readFull(buf, false) {
|
||||
return r.err
|
||||
}
|
||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||
buf = buf[checksumSize:]
|
||||
|
||||
n, err := DecodedLen(buf)
|
||||
if err != nil {
|
||||
r.err = err
|
||||
return r.err
|
||||
}
|
||||
if n > len(r.decoded) {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
if _, err := Decode(r.decoded, buf); err != nil {
|
||||
r.err = err
|
||||
return r.err
|
||||
}
|
||||
if crc(r.decoded[:n]) != checksum {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
r.i, r.j = 0, n
|
||||
continue
|
||||
|
||||
case chunkTypeUncompressedData:
|
||||
// Section 4.3. Uncompressed data (chunk type 0x01).
|
||||
if chunkLen < checksumSize {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
buf := r.buf[:checksumSize]
|
||||
if !r.readFull(buf, false) {
|
||||
return r.err
|
||||
}
|
||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||
// Read directly into r.decoded instead of via r.buf.
|
||||
n := chunkLen - checksumSize
|
||||
if n > len(r.decoded) {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
if !r.readFull(r.decoded[:n], false) {
|
||||
return r.err
|
||||
}
|
||||
if crc(r.decoded[:n]) != checksum {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
r.i, r.j = 0, n
|
||||
continue
|
||||
|
||||
case chunkTypeStreamIdentifier:
|
||||
// Section 4.1. Stream identifier (chunk type 0xff).
|
||||
if chunkLen != len(magicBody) {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
if !r.readFull(r.buf[:len(magicBody)], false) {
|
||||
return r.err
|
||||
}
|
||||
for i := 0; i < len(magicBody); i++ {
|
||||
if r.buf[i] != magicBody[i] {
|
||||
r.err = ErrCorrupt
|
||||
return r.err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if chunkType <= 0x7f {
|
||||
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
||||
r.err = ErrUnsupported
|
||||
return r.err
|
||||
}
|
||||
// Section 4.4 Padding (chunk type 0xfe).
|
||||
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
|
||||
if !r.readFull(r.buf[:chunkLen], false) {
|
||||
return r.err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read satisfies the io.Reader interface.
|
||||
func (r *Reader) Read(p []byte) (int, error) {
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
}
|
||||
|
||||
if err := r.fill(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n := copy(p, r.decoded[r.i:r.j])
|
||||
r.i += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ReadByte satisfies the io.ByteReader interface.
|
||||
func (r *Reader) ReadByte() (byte, error) {
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
}
|
||||
|
||||
if err := r.fill(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
c := r.decoded[r.i]
|
||||
r.i++
|
||||
return c, nil
|
||||
}
|
||||
490
vendor/github.com/golang/snappy/decode_amd64.s
generated
vendored
Normal file
490
vendor/github.com/golang/snappy/decode_amd64.s
generated
vendored
Normal file
@@ -0,0 +1,490 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// The asm code generally follows the pure Go code in decode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
|
||||
// func decode(dst, src []byte) int
|
||||
//
|
||||
// All local variables fit into registers. The non-zero stack size is only to
|
||||
// spill registers and push args when issuing a CALL. The register allocation:
|
||||
// - AX scratch
|
||||
// - BX scratch
|
||||
// - CX length or x
|
||||
// - DX offset
|
||||
// - SI &src[s]
|
||||
// - DI &dst[d]
|
||||
// + R8 dst_base
|
||||
// + R9 dst_len
|
||||
// + R10 dst_base + dst_len
|
||||
// + R11 src_base
|
||||
// + R12 src_len
|
||||
// + R13 src_base + src_len
|
||||
// - R14 used by doCopy
|
||||
// - R15 used by doCopy
|
||||
//
|
||||
// The registers R8-R13 (marked with a "+") are set at the start of the
|
||||
// function, and after a CALL returns, and are not otherwise modified.
|
||||
//
|
||||
// The d variable is implicitly DI - R8, and len(dst)-d is R10 - DI.
|
||||
// The s variable is implicitly SI - R11, and len(src)-s is R13 - SI.
|
||||
TEXT ·decode(SB), NOSPLIT, $48-56
|
||||
// Initialize SI, DI and R8-R13.
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ dst_len+8(FP), R9
|
||||
MOVQ R8, DI
|
||||
MOVQ R8, R10
|
||||
ADDQ R9, R10
|
||||
MOVQ src_base+24(FP), R11
|
||||
MOVQ src_len+32(FP), R12
|
||||
MOVQ R11, SI
|
||||
MOVQ R11, R13
|
||||
ADDQ R12, R13
|
||||
|
||||
loop:
|
||||
// for s < len(src)
|
||||
CMPQ SI, R13
|
||||
JEQ end
|
||||
|
||||
// CX = uint32(src[s])
|
||||
//
|
||||
// switch src[s] & 0x03
|
||||
MOVBLZX (SI), CX
|
||||
MOVL CX, BX
|
||||
ANDL $3, BX
|
||||
CMPL BX, $1
|
||||
JAE tagCopy
|
||||
|
||||
// ----------------------------------------
|
||||
// The code below handles literal tags.
|
||||
|
||||
// case tagLiteral:
|
||||
// x := uint32(src[s] >> 2)
|
||||
// switch
|
||||
SHRL $2, CX
|
||||
CMPL CX, $60
|
||||
JAE tagLit60Plus
|
||||
|
||||
// case x < 60:
|
||||
// s++
|
||||
INCQ SI
|
||||
|
||||
doLit:
|
||||
// This is the end of the inner "switch", when we have a literal tag.
|
||||
//
|
||||
// We assume that CX == x and x fits in a uint32, where x is the variable
|
||||
// used in the pure Go decode_other.go code.
|
||||
|
||||
// length = int(x) + 1
|
||||
//
|
||||
// Unlike the pure Go code, we don't need to check if length <= 0 because
|
||||
// CX can hold 64 bits, so the increment cannot overflow.
|
||||
INCQ CX
|
||||
|
||||
// Prepare to check if copying length bytes will run past the end of dst or
|
||||
// src.
|
||||
//
|
||||
// AX = len(dst) - d
|
||||
// BX = len(src) - s
|
||||
MOVQ R10, AX
|
||||
SUBQ DI, AX
|
||||
MOVQ R13, BX
|
||||
SUBQ SI, BX
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) copies.
|
||||
//
|
||||
// if length > 16 || len(dst)-d < 16 || len(src)-s < 16 {
|
||||
// goto callMemmove // Fall back on calling runtime·memmove.
|
||||
// }
|
||||
//
|
||||
// The C++ snappy code calls this TryFastAppend. It also checks len(src)-s
|
||||
// against 21 instead of 16, because it cannot assume that all of its input
|
||||
// is contiguous in memory and so it needs to leave enough source bytes to
|
||||
// read the next tag without refilling buffers, but Go's Decode assumes
|
||||
// contiguousness (the src argument is a []byte).
|
||||
CMPQ CX, $16
|
||||
JGT callMemmove
|
||||
CMPQ AX, $16
|
||||
JLT callMemmove
|
||||
CMPQ BX, $16
|
||||
JLT callMemmove
|
||||
|
||||
// !!! Implement the copy from src to dst as a 16-byte load and store.
|
||||
// (Decode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only length bytes, but that's
|
||||
// OK. If the input is a valid Snappy encoding then subsequent iterations
|
||||
// will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
|
||||
// non-nil error), so the overrun will be ignored.
|
||||
//
|
||||
// Note that on amd64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
MOVOU 0(SI), X0
|
||||
MOVOU X0, 0(DI)
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADDQ CX, DI
|
||||
ADDQ CX, SI
|
||||
JMP loop
|
||||
|
||||
callMemmove:
|
||||
// if length > len(dst)-d || length > len(src)-s { etc }
|
||||
CMPQ CX, AX
|
||||
JGT errCorrupt
|
||||
CMPQ CX, BX
|
||||
JGT errCorrupt
|
||||
|
||||
// copy(dst[d:], src[s:s+length])
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[d], &src[s], length), so we push
|
||||
// DI, SI and CX as arguments. Coincidentally, we also need to spill those
|
||||
// three registers to the stack, to save local variables across the CALL.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ SI, 8(SP)
|
||||
MOVQ CX, 16(SP)
|
||||
MOVQ DI, 24(SP)
|
||||
MOVQ SI, 32(SP)
|
||||
MOVQ CX, 40(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
|
||||
// Restore local variables: unspill registers from the stack and
|
||||
// re-calculate R8-R13.
|
||||
MOVQ 24(SP), DI
|
||||
MOVQ 32(SP), SI
|
||||
MOVQ 40(SP), CX
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ dst_len+8(FP), R9
|
||||
MOVQ R8, R10
|
||||
ADDQ R9, R10
|
||||
MOVQ src_base+24(FP), R11
|
||||
MOVQ src_len+32(FP), R12
|
||||
MOVQ R11, R13
|
||||
ADDQ R12, R13
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADDQ CX, DI
|
||||
ADDQ CX, SI
|
||||
JMP loop
|
||||
|
||||
tagLit60Plus:
|
||||
// !!! This fragment does the
|
||||
//
|
||||
// s += x - 58; if uint(s) > uint(len(src)) { etc }
|
||||
//
|
||||
// checks. In the asm version, we code it once instead of once per switch case.
|
||||
ADDQ CX, SI
|
||||
SUBQ $58, SI
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// case x == 60:
|
||||
CMPL CX, $61
|
||||
JEQ tagLit61
|
||||
JA tagLit62Plus
|
||||
|
||||
// x = uint32(src[s-1])
|
||||
MOVBLZX -1(SI), CX
|
||||
JMP doLit
|
||||
|
||||
tagLit61:
|
||||
// case x == 61:
|
||||
// x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||
MOVWLZX -2(SI), CX
|
||||
JMP doLit
|
||||
|
||||
tagLit62Plus:
|
||||
CMPL CX, $62
|
||||
JA tagLit63
|
||||
|
||||
// case x == 62:
|
||||
// x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||
MOVWLZX -3(SI), CX
|
||||
MOVBLZX -1(SI), BX
|
||||
SHLL $16, BX
|
||||
ORL BX, CX
|
||||
JMP doLit
|
||||
|
||||
tagLit63:
|
||||
// case x == 63:
|
||||
// x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||
MOVL -4(SI), CX
|
||||
JMP doLit
|
||||
|
||||
// The code above handles literal tags.
|
||||
// ----------------------------------------
|
||||
// The code below handles copy tags.
|
||||
|
||||
tagCopy4:
|
||||
// case tagCopy4:
|
||||
// s += 5
|
||||
ADDQ $5, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-5])>>2
|
||||
SHRQ $2, CX
|
||||
INCQ CX
|
||||
|
||||
// offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
|
||||
MOVLQZX -4(SI), DX
|
||||
JMP doCopy
|
||||
|
||||
tagCopy2:
|
||||
// case tagCopy2:
|
||||
// s += 3
|
||||
ADDQ $3, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-3])>>2
|
||||
SHRQ $2, CX
|
||||
INCQ CX
|
||||
|
||||
// offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
|
||||
MOVWQZX -2(SI), DX
|
||||
JMP doCopy
|
||||
|
||||
tagCopy:
|
||||
// We have a copy tag. We assume that:
|
||||
// - BX == src[s] & 0x03
|
||||
// - CX == src[s]
|
||||
CMPQ BX, $2
|
||||
JEQ tagCopy2
|
||||
JA tagCopy4
|
||||
|
||||
// case tagCopy1:
|
||||
// s += 2
|
||||
ADDQ $2, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
|
||||
MOVQ CX, DX
|
||||
ANDQ $0xe0, DX
|
||||
SHLQ $3, DX
|
||||
MOVBQZX -1(SI), BX
|
||||
ORQ BX, DX
|
||||
|
||||
// length = 4 + int(src[s-2])>>2&0x7
|
||||
SHRQ $2, CX
|
||||
ANDQ $7, CX
|
||||
ADDQ $4, CX
|
||||
|
||||
doCopy:
|
||||
// This is the end of the outer "switch", when we have a copy tag.
|
||||
//
|
||||
// We assume that:
|
||||
// - CX == length && CX > 0
|
||||
// - DX == offset
|
||||
|
||||
// if offset <= 0 { etc }
|
||||
CMPQ DX, $0
|
||||
JLE errCorrupt
|
||||
|
||||
// if d < offset { etc }
|
||||
MOVQ DI, BX
|
||||
SUBQ R8, BX
|
||||
CMPQ BX, DX
|
||||
JLT errCorrupt
|
||||
|
||||
// if length > len(dst)-d { etc }
|
||||
MOVQ R10, BX
|
||||
SUBQ DI, BX
|
||||
CMPQ CX, BX
|
||||
JGT errCorrupt
|
||||
|
||||
// forwardCopy(dst[d:d+length], dst[d-offset:]); d += length
|
||||
//
|
||||
// Set:
|
||||
// - R14 = len(dst)-d
|
||||
// - R15 = &dst[d-offset]
|
||||
MOVQ R10, R14
|
||||
SUBQ DI, R14
|
||||
MOVQ DI, R15
|
||||
SUBQ DX, R15
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) forward copies.
|
||||
//
|
||||
// First, try using two 8-byte load/stores, similar to the doLit technique
|
||||
// above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is
|
||||
// still OK if offset >= 8. Note that this has to be two 8-byte load/stores
|
||||
// and not one 16-byte load/store, and the first store has to be before the
|
||||
// second load, due to the overlap if offset is in the range [8, 16).
|
||||
//
|
||||
// if length > 16 || offset < 8 || len(dst)-d < 16 {
|
||||
// goto slowForwardCopy
|
||||
// }
|
||||
// copy 16 bytes
|
||||
// d += length
|
||||
CMPQ CX, $16
|
||||
JGT slowForwardCopy
|
||||
CMPQ DX, $8
|
||||
JLT slowForwardCopy
|
||||
CMPQ R14, $16
|
||||
JLT slowForwardCopy
|
||||
MOVQ 0(R15), AX
|
||||
MOVQ AX, 0(DI)
|
||||
MOVQ 8(R15), BX
|
||||
MOVQ BX, 8(DI)
|
||||
ADDQ CX, DI
|
||||
JMP loop
|
||||
|
||||
slowForwardCopy:
|
||||
// !!! If the forward copy is longer than 16 bytes, or if offset < 8, we
|
||||
// can still try 8-byte load stores, provided we can overrun up to 10 extra
|
||||
// bytes. As above, the overrun will be fixed up by subsequent iterations
|
||||
// of the outermost loop.
|
||||
//
|
||||
// The C++ snappy code calls this technique IncrementalCopyFastPath. Its
|
||||
// commentary says:
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// The main part of this loop is a simple copy of eight bytes at a time
|
||||
// until we've copied (at least) the requested amount of bytes. However,
|
||||
// if d and d-offset are less than eight bytes apart (indicating a
|
||||
// repeating pattern of length < 8), we first need to expand the pattern in
|
||||
// order to get the correct results. For instance, if the buffer looks like
|
||||
// this, with the eight-byte <d-offset> and <d> patterns marked as
|
||||
// intervals:
|
||||
//
|
||||
// abxxxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// a single eight-byte copy from <d-offset> to <d> will repeat the pattern
|
||||
// once, after which we can move <d> two bytes without moving <d-offset>:
|
||||
//
|
||||
// ababxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// and repeat the exercise until the two no longer overlap.
|
||||
//
|
||||
// This allows us to do very well in the special case of one single byte
|
||||
// repeated many times, without taking a big hit for more general cases.
|
||||
//
|
||||
// The worst case of extra writing past the end of the match occurs when
|
||||
// offset == 1 and length == 1; the last copy will read from byte positions
|
||||
// [0..7] and write to [4..11], whereas it was only supposed to write to
|
||||
// position 1. Thus, ten excess bytes.
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// That "10 byte overrun" worst case is confirmed by Go's
|
||||
// TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy
|
||||
// and finishSlowForwardCopy algorithm.
|
||||
//
|
||||
// if length > len(dst)-d-10 {
|
||||
// goto verySlowForwardCopy
|
||||
// }
|
||||
SUBQ $10, R14
|
||||
CMPQ CX, R14
|
||||
JGT verySlowForwardCopy
|
||||
|
||||
makeOffsetAtLeast8:
|
||||
// !!! As above, expand the pattern so that offset >= 8 and we can use
|
||||
// 8-byte load/stores.
|
||||
//
|
||||
// for offset < 8 {
|
||||
// copy 8 bytes from dst[d-offset:] to dst[d:]
|
||||
// length -= offset
|
||||
// d += offset
|
||||
// offset += offset
|
||||
// // The two previous lines together means that d-offset, and therefore
|
||||
// // R15, is unchanged.
|
||||
// }
|
||||
CMPQ DX, $8
|
||||
JGE fixUpSlowForwardCopy
|
||||
MOVQ (R15), BX
|
||||
MOVQ BX, (DI)
|
||||
SUBQ DX, CX
|
||||
ADDQ DX, DI
|
||||
ADDQ DX, DX
|
||||
JMP makeOffsetAtLeast8
|
||||
|
||||
fixUpSlowForwardCopy:
|
||||
// !!! Add length (which might be negative now) to d (implied by DI being
|
||||
// &dst[d]) so that d ends up at the right place when we jump back to the
|
||||
// top of the loop. Before we do that, though, we save DI to AX so that, if
|
||||
// length is positive, copying the remaining length bytes will write to the
|
||||
// right place.
|
||||
MOVQ DI, AX
|
||||
ADDQ CX, DI
|
||||
|
||||
finishSlowForwardCopy:
|
||||
// !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative
|
||||
// length means that we overrun, but as above, that will be fixed up by
|
||||
// subsequent iterations of the outermost loop.
|
||||
CMPQ CX, $0
|
||||
JLE loop
|
||||
MOVQ (R15), BX
|
||||
MOVQ BX, (AX)
|
||||
ADDQ $8, R15
|
||||
ADDQ $8, AX
|
||||
SUBQ $8, CX
|
||||
JMP finishSlowForwardCopy
|
||||
|
||||
verySlowForwardCopy:
|
||||
// verySlowForwardCopy is a simple implementation of forward copy. In C
|
||||
// parlance, this is a do/while loop instead of a while loop, since we know
|
||||
// that length > 0. In Go syntax:
|
||||
//
|
||||
// for {
|
||||
// dst[d] = dst[d - offset]
|
||||
// d++
|
||||
// length--
|
||||
// if length == 0 {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
MOVB (R15), BX
|
||||
MOVB BX, (DI)
|
||||
INCQ R15
|
||||
INCQ DI
|
||||
DECQ CX
|
||||
JNZ verySlowForwardCopy
|
||||
JMP loop
|
||||
|
||||
// The code above handles copy tags.
|
||||
// ----------------------------------------
|
||||
|
||||
end:
|
||||
// This is the end of the "for s < len(src)".
|
||||
//
|
||||
// if d != len(dst) { etc }
|
||||
CMPQ DI, R10
|
||||
JNE errCorrupt
|
||||
|
||||
// return 0
|
||||
MOVQ $0, ret+48(FP)
|
||||
RET
|
||||
|
||||
errCorrupt:
|
||||
// return decodeErrCodeCorrupt
|
||||
MOVQ $1, ret+48(FP)
|
||||
RET
|
||||
494
vendor/github.com/golang/snappy/decode_arm64.s
generated
vendored
Normal file
494
vendor/github.com/golang/snappy/decode_arm64.s
generated
vendored
Normal file
@@ -0,0 +1,494 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// The asm code generally follows the pure Go code in decode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
|
||||
// func decode(dst, src []byte) int
|
||||
//
|
||||
// All local variables fit into registers. The non-zero stack size is only to
|
||||
// spill registers and push args when issuing a CALL. The register allocation:
|
||||
// - R2 scratch
|
||||
// - R3 scratch
|
||||
// - R4 length or x
|
||||
// - R5 offset
|
||||
// - R6 &src[s]
|
||||
// - R7 &dst[d]
|
||||
// + R8 dst_base
|
||||
// + R9 dst_len
|
||||
// + R10 dst_base + dst_len
|
||||
// + R11 src_base
|
||||
// + R12 src_len
|
||||
// + R13 src_base + src_len
|
||||
// - R14 used by doCopy
|
||||
// - R15 used by doCopy
|
||||
//
|
||||
// The registers R8-R13 (marked with a "+") are set at the start of the
|
||||
// function, and after a CALL returns, and are not otherwise modified.
|
||||
//
|
||||
// The d variable is implicitly R7 - R8, and len(dst)-d is R10 - R7.
|
||||
// The s variable is implicitly R6 - R11, and len(src)-s is R13 - R6.
|
||||
TEXT ·decode(SB), NOSPLIT, $56-56
|
||||
// Initialize R6, R7 and R8-R13.
|
||||
MOVD dst_base+0(FP), R8
|
||||
MOVD dst_len+8(FP), R9
|
||||
MOVD R8, R7
|
||||
MOVD R8, R10
|
||||
ADD R9, R10, R10
|
||||
MOVD src_base+24(FP), R11
|
||||
MOVD src_len+32(FP), R12
|
||||
MOVD R11, R6
|
||||
MOVD R11, R13
|
||||
ADD R12, R13, R13
|
||||
|
||||
loop:
|
||||
// for s < len(src)
|
||||
CMP R13, R6
|
||||
BEQ end
|
||||
|
||||
// R4 = uint32(src[s])
|
||||
//
|
||||
// switch src[s] & 0x03
|
||||
MOVBU (R6), R4
|
||||
MOVW R4, R3
|
||||
ANDW $3, R3
|
||||
MOVW $1, R1
|
||||
CMPW R1, R3
|
||||
BGE tagCopy
|
||||
|
||||
// ----------------------------------------
|
||||
// The code below handles literal tags.
|
||||
|
||||
// case tagLiteral:
|
||||
// x := uint32(src[s] >> 2)
|
||||
// switch
|
||||
MOVW $60, R1
|
||||
LSRW $2, R4, R4
|
||||
CMPW R4, R1
|
||||
BLS tagLit60Plus
|
||||
|
||||
// case x < 60:
|
||||
// s++
|
||||
ADD $1, R6, R6
|
||||
|
||||
doLit:
|
||||
// This is the end of the inner "switch", when we have a literal tag.
|
||||
//
|
||||
// We assume that R4 == x and x fits in a uint32, where x is the variable
|
||||
// used in the pure Go decode_other.go code.
|
||||
|
||||
// length = int(x) + 1
|
||||
//
|
||||
// Unlike the pure Go code, we don't need to check if length <= 0 because
|
||||
// R4 can hold 64 bits, so the increment cannot overflow.
|
||||
ADD $1, R4, R4
|
||||
|
||||
// Prepare to check if copying length bytes will run past the end of dst or
|
||||
// src.
|
||||
//
|
||||
// R2 = len(dst) - d
|
||||
// R3 = len(src) - s
|
||||
MOVD R10, R2
|
||||
SUB R7, R2, R2
|
||||
MOVD R13, R3
|
||||
SUB R6, R3, R3
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) copies.
|
||||
//
|
||||
// if length > 16 || len(dst)-d < 16 || len(src)-s < 16 {
|
||||
// goto callMemmove // Fall back on calling runtime·memmove.
|
||||
// }
|
||||
//
|
||||
// The C++ snappy code calls this TryFastAppend. It also checks len(src)-s
|
||||
// against 21 instead of 16, because it cannot assume that all of its input
|
||||
// is contiguous in memory and so it needs to leave enough source bytes to
|
||||
// read the next tag without refilling buffers, but Go's Decode assumes
|
||||
// contiguousness (the src argument is a []byte).
|
||||
CMP $16, R4
|
||||
BGT callMemmove
|
||||
CMP $16, R2
|
||||
BLT callMemmove
|
||||
CMP $16, R3
|
||||
BLT callMemmove
|
||||
|
||||
// !!! Implement the copy from src to dst as a 16-byte load and store.
|
||||
// (Decode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only length bytes, but that's
|
||||
// OK. If the input is a valid Snappy encoding then subsequent iterations
|
||||
// will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
|
||||
// non-nil error), so the overrun will be ignored.
|
||||
//
|
||||
// Note that on arm64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
LDP 0(R6), (R14, R15)
|
||||
STP (R14, R15), 0(R7)
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADD R4, R7, R7
|
||||
ADD R4, R6, R6
|
||||
B loop
|
||||
|
||||
callMemmove:
|
||||
// if length > len(dst)-d || length > len(src)-s { etc }
|
||||
CMP R2, R4
|
||||
BGT errCorrupt
|
||||
CMP R3, R4
|
||||
BGT errCorrupt
|
||||
|
||||
// copy(dst[d:], src[s:s+length])
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[d], &src[s], length), so we push
|
||||
// R7, R6 and R4 as arguments. Coincidentally, we also need to spill those
|
||||
// three registers to the stack, to save local variables across the CALL.
|
||||
MOVD R7, 8(RSP)
|
||||
MOVD R6, 16(RSP)
|
||||
MOVD R4, 24(RSP)
|
||||
MOVD R7, 32(RSP)
|
||||
MOVD R6, 40(RSP)
|
||||
MOVD R4, 48(RSP)
|
||||
CALL runtime·memmove(SB)
|
||||
|
||||
// Restore local variables: unspill registers from the stack and
|
||||
// re-calculate R8-R13.
|
||||
MOVD 32(RSP), R7
|
||||
MOVD 40(RSP), R6
|
||||
MOVD 48(RSP), R4
|
||||
MOVD dst_base+0(FP), R8
|
||||
MOVD dst_len+8(FP), R9
|
||||
MOVD R8, R10
|
||||
ADD R9, R10, R10
|
||||
MOVD src_base+24(FP), R11
|
||||
MOVD src_len+32(FP), R12
|
||||
MOVD R11, R13
|
||||
ADD R12, R13, R13
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADD R4, R7, R7
|
||||
ADD R4, R6, R6
|
||||
B loop
|
||||
|
||||
tagLit60Plus:
|
||||
// !!! This fragment does the
|
||||
//
|
||||
// s += x - 58; if uint(s) > uint(len(src)) { etc }
|
||||
//
|
||||
// checks. In the asm version, we code it once instead of once per switch case.
|
||||
ADD R4, R6, R6
|
||||
SUB $58, R6, R6
|
||||
MOVD R6, R3
|
||||
SUB R11, R3, R3
|
||||
CMP R12, R3
|
||||
BGT errCorrupt
|
||||
|
||||
// case x == 60:
|
||||
MOVW $61, R1
|
||||
CMPW R1, R4
|
||||
BEQ tagLit61
|
||||
BGT tagLit62Plus
|
||||
|
||||
// x = uint32(src[s-1])
|
||||
MOVBU -1(R6), R4
|
||||
B doLit
|
||||
|
||||
tagLit61:
|
||||
// case x == 61:
|
||||
// x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||
MOVHU -2(R6), R4
|
||||
B doLit
|
||||
|
||||
tagLit62Plus:
|
||||
CMPW $62, R4
|
||||
BHI tagLit63
|
||||
|
||||
// case x == 62:
|
||||
// x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||
MOVHU -3(R6), R4
|
||||
MOVBU -1(R6), R3
|
||||
ORR R3<<16, R4
|
||||
B doLit
|
||||
|
||||
tagLit63:
|
||||
// case x == 63:
|
||||
// x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||
MOVWU -4(R6), R4
|
||||
B doLit
|
||||
|
||||
// The code above handles literal tags.
|
||||
// ----------------------------------------
|
||||
// The code below handles copy tags.
|
||||
|
||||
tagCopy4:
|
||||
// case tagCopy4:
|
||||
// s += 5
|
||||
ADD $5, R6, R6
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVD R6, R3
|
||||
SUB R11, R3, R3
|
||||
CMP R12, R3
|
||||
BGT errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-5])>>2
|
||||
MOVD $1, R1
|
||||
ADD R4>>2, R1, R4
|
||||
|
||||
// offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
|
||||
MOVWU -4(R6), R5
|
||||
B doCopy
|
||||
|
||||
tagCopy2:
|
||||
// case tagCopy2:
|
||||
// s += 3
|
||||
ADD $3, R6, R6
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVD R6, R3
|
||||
SUB R11, R3, R3
|
||||
CMP R12, R3
|
||||
BGT errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-3])>>2
|
||||
MOVD $1, R1
|
||||
ADD R4>>2, R1, R4
|
||||
|
||||
// offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
|
||||
MOVHU -2(R6), R5
|
||||
B doCopy
|
||||
|
||||
tagCopy:
|
||||
// We have a copy tag. We assume that:
|
||||
// - R3 == src[s] & 0x03
|
||||
// - R4 == src[s]
|
||||
CMP $2, R3
|
||||
BEQ tagCopy2
|
||||
BGT tagCopy4
|
||||
|
||||
// case tagCopy1:
|
||||
// s += 2
|
||||
ADD $2, R6, R6
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVD R6, R3
|
||||
SUB R11, R3, R3
|
||||
CMP R12, R3
|
||||
BGT errCorrupt
|
||||
|
||||
// offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
|
||||
MOVD R4, R5
|
||||
AND $0xe0, R5
|
||||
MOVBU -1(R6), R3
|
||||
ORR R5<<3, R3, R5
|
||||
|
||||
// length = 4 + int(src[s-2])>>2&0x7
|
||||
MOVD $7, R1
|
||||
AND R4>>2, R1, R4
|
||||
ADD $4, R4, R4
|
||||
|
||||
doCopy:
|
||||
// This is the end of the outer "switch", when we have a copy tag.
|
||||
//
|
||||
// We assume that:
|
||||
// - R4 == length && R4 > 0
|
||||
// - R5 == offset
|
||||
|
||||
// if offset <= 0 { etc }
|
||||
MOVD $0, R1
|
||||
CMP R1, R5
|
||||
BLE errCorrupt
|
||||
|
||||
// if d < offset { etc }
|
||||
MOVD R7, R3
|
||||
SUB R8, R3, R3
|
||||
CMP R5, R3
|
||||
BLT errCorrupt
|
||||
|
||||
// if length > len(dst)-d { etc }
|
||||
MOVD R10, R3
|
||||
SUB R7, R3, R3
|
||||
CMP R3, R4
|
||||
BGT errCorrupt
|
||||
|
||||
// forwardCopy(dst[d:d+length], dst[d-offset:]); d += length
|
||||
//
|
||||
// Set:
|
||||
// - R14 = len(dst)-d
|
||||
// - R15 = &dst[d-offset]
|
||||
MOVD R10, R14
|
||||
SUB R7, R14, R14
|
||||
MOVD R7, R15
|
||||
SUB R5, R15, R15
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) forward copies.
|
||||
//
|
||||
// First, try using two 8-byte load/stores, similar to the doLit technique
|
||||
// above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is
|
||||
// still OK if offset >= 8. Note that this has to be two 8-byte load/stores
|
||||
// and not one 16-byte load/store, and the first store has to be before the
|
||||
// second load, due to the overlap if offset is in the range [8, 16).
|
||||
//
|
||||
// if length > 16 || offset < 8 || len(dst)-d < 16 {
|
||||
// goto slowForwardCopy
|
||||
// }
|
||||
// copy 16 bytes
|
||||
// d += length
|
||||
CMP $16, R4
|
||||
BGT slowForwardCopy
|
||||
CMP $8, R5
|
||||
BLT slowForwardCopy
|
||||
CMP $16, R14
|
||||
BLT slowForwardCopy
|
||||
MOVD 0(R15), R2
|
||||
MOVD R2, 0(R7)
|
||||
MOVD 8(R15), R3
|
||||
MOVD R3, 8(R7)
|
||||
ADD R4, R7, R7
|
||||
B loop
|
||||
|
||||
slowForwardCopy:
|
||||
// !!! If the forward copy is longer than 16 bytes, or if offset < 8, we
|
||||
// can still try 8-byte load stores, provided we can overrun up to 10 extra
|
||||
// bytes. As above, the overrun will be fixed up by subsequent iterations
|
||||
// of the outermost loop.
|
||||
//
|
||||
// The C++ snappy code calls this technique IncrementalCopyFastPath. Its
|
||||
// commentary says:
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// The main part of this loop is a simple copy of eight bytes at a time
|
||||
// until we've copied (at least) the requested amount of bytes. However,
|
||||
// if d and d-offset are less than eight bytes apart (indicating a
|
||||
// repeating pattern of length < 8), we first need to expand the pattern in
|
||||
// order to get the correct results. For instance, if the buffer looks like
|
||||
// this, with the eight-byte <d-offset> and <d> patterns marked as
|
||||
// intervals:
|
||||
//
|
||||
// abxxxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// a single eight-byte copy from <d-offset> to <d> will repeat the pattern
|
||||
// once, after which we can move <d> two bytes without moving <d-offset>:
|
||||
//
|
||||
// ababxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// and repeat the exercise until the two no longer overlap.
|
||||
//
|
||||
// This allows us to do very well in the special case of one single byte
|
||||
// repeated many times, without taking a big hit for more general cases.
|
||||
//
|
||||
// The worst case of extra writing past the end of the match occurs when
|
||||
// offset == 1 and length == 1; the last copy will read from byte positions
|
||||
// [0..7] and write to [4..11], whereas it was only supposed to write to
|
||||
// position 1. Thus, ten excess bytes.
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// That "10 byte overrun" worst case is confirmed by Go's
|
||||
// TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy
|
||||
// and finishSlowForwardCopy algorithm.
|
||||
//
|
||||
// if length > len(dst)-d-10 {
|
||||
// goto verySlowForwardCopy
|
||||
// }
|
||||
SUB $10, R14, R14
|
||||
CMP R14, R4
|
||||
BGT verySlowForwardCopy
|
||||
|
||||
makeOffsetAtLeast8:
|
||||
// !!! As above, expand the pattern so that offset >= 8 and we can use
|
||||
// 8-byte load/stores.
|
||||
//
|
||||
// for offset < 8 {
|
||||
// copy 8 bytes from dst[d-offset:] to dst[d:]
|
||||
// length -= offset
|
||||
// d += offset
|
||||
// offset += offset
|
||||
// // The two previous lines together means that d-offset, and therefore
|
||||
// // R15, is unchanged.
|
||||
// }
|
||||
CMP $8, R5
|
||||
BGE fixUpSlowForwardCopy
|
||||
MOVD (R15), R3
|
||||
MOVD R3, (R7)
|
||||
SUB R5, R4, R4
|
||||
ADD R5, R7, R7
|
||||
ADD R5, R5, R5
|
||||
B makeOffsetAtLeast8
|
||||
|
||||
fixUpSlowForwardCopy:
|
||||
// !!! Add length (which might be negative now) to d (implied by R7 being
|
||||
// &dst[d]) so that d ends up at the right place when we jump back to the
|
||||
// top of the loop. Before we do that, though, we save R7 to R2 so that, if
|
||||
// length is positive, copying the remaining length bytes will write to the
|
||||
// right place.
|
||||
MOVD R7, R2
|
||||
ADD R4, R7, R7
|
||||
|
||||
finishSlowForwardCopy:
|
||||
// !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative
|
||||
// length means that we overrun, but as above, that will be fixed up by
|
||||
// subsequent iterations of the outermost loop.
|
||||
MOVD $0, R1
|
||||
CMP R1, R4
|
||||
BLE loop
|
||||
MOVD (R15), R3
|
||||
MOVD R3, (R2)
|
||||
ADD $8, R15, R15
|
||||
ADD $8, R2, R2
|
||||
SUB $8, R4, R4
|
||||
B finishSlowForwardCopy
|
||||
|
||||
verySlowForwardCopy:
|
||||
// verySlowForwardCopy is a simple implementation of forward copy. In C
|
||||
// parlance, this is a do/while loop instead of a while loop, since we know
|
||||
// that length > 0. In Go syntax:
|
||||
//
|
||||
// for {
|
||||
// dst[d] = dst[d - offset]
|
||||
// d++
|
||||
// length--
|
||||
// if length == 0 {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
MOVB (R15), R3
|
||||
MOVB R3, (R7)
|
||||
ADD $1, R15, R15
|
||||
ADD $1, R7, R7
|
||||
SUB $1, R4, R4
|
||||
CBNZ R4, verySlowForwardCopy
|
||||
B loop
|
||||
|
||||
// The code above handles copy tags.
|
||||
// ----------------------------------------
|
||||
|
||||
end:
|
||||
// This is the end of the "for s < len(src)".
|
||||
//
|
||||
// if d != len(dst) { etc }
|
||||
CMP R10, R7
|
||||
BNE errCorrupt
|
||||
|
||||
// return 0
|
||||
MOVD $0, ret+48(FP)
|
||||
RET
|
||||
|
||||
errCorrupt:
|
||||
// return decodeErrCodeCorrupt
|
||||
MOVD $1, R2
|
||||
MOVD R2, ret+48(FP)
|
||||
RET
|
||||
15
vendor/github.com/golang/snappy/decode_asm.go
generated
vendored
Normal file
15
vendor/github.com/golang/snappy/decode_asm.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
// +build amd64 arm64
|
||||
|
||||
package snappy
|
||||
|
||||
// decode has the same semantics as in decode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func decode(dst, src []byte) int
|
||||
115
vendor/github.com/golang/snappy/decode_other.go
generated
vendored
Normal file
115
vendor/github.com/golang/snappy/decode_other.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64,!arm64 appengine !gc noasm
|
||||
|
||||
package snappy
|
||||
|
||||
// decode writes the decoding of src to dst. It assumes that the varint-encoded
|
||||
// length of the decompressed bytes has already been read, and that len(dst)
|
||||
// equals that length.
|
||||
//
|
||||
// It returns 0 on success or a decodeErrCodeXxx error code on failure.
|
||||
func decode(dst, src []byte) int {
|
||||
var d, s, offset, length int
|
||||
for s < len(src) {
|
||||
switch src[s] & 0x03 {
|
||||
case tagLiteral:
|
||||
x := uint32(src[s] >> 2)
|
||||
switch {
|
||||
case x < 60:
|
||||
s++
|
||||
case x == 60:
|
||||
s += 2
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-1])
|
||||
case x == 61:
|
||||
s += 3
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||
case x == 62:
|
||||
s += 4
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||
case x == 63:
|
||||
s += 5
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||
}
|
||||
length = int(x) + 1
|
||||
if length <= 0 {
|
||||
return decodeErrCodeUnsupportedLiteralLength
|
||||
}
|
||||
if length > len(dst)-d || length > len(src)-s {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
copy(dst[d:], src[s:s+length])
|
||||
d += length
|
||||
s += length
|
||||
continue
|
||||
|
||||
case tagCopy1:
|
||||
s += 2
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 4 + int(src[s-2])>>2&0x7
|
||||
offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
|
||||
|
||||
case tagCopy2:
|
||||
s += 3
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 1 + int(src[s-3])>>2
|
||||
offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
|
||||
|
||||
case tagCopy4:
|
||||
s += 5
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 1 + int(src[s-5])>>2
|
||||
offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
|
||||
}
|
||||
|
||||
if offset <= 0 || d < offset || length > len(dst)-d {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
// Copy from an earlier sub-slice of dst to a later sub-slice.
|
||||
// If no overlap, use the built-in copy:
|
||||
if offset >= length {
|
||||
copy(dst[d:d+length], dst[d-offset:])
|
||||
d += length
|
||||
continue
|
||||
}
|
||||
|
||||
// Unlike the built-in copy function, this byte-by-byte copy always runs
|
||||
// forwards, even if the slices overlap. Conceptually, this is:
|
||||
//
|
||||
// d += forwardCopy(dst[d:d+length], dst[d-offset:])
|
||||
//
|
||||
// We align the slices into a and b and show the compiler they are the same size.
|
||||
// This allows the loop to run without bounds checks.
|
||||
a := dst[d : d+length]
|
||||
b := dst[d-offset:]
|
||||
b = b[:len(a)]
|
||||
for i := range a {
|
||||
a[i] = b[i]
|
||||
}
|
||||
d += length
|
||||
}
|
||||
if d != len(dst) {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
return 0
|
||||
}
|
||||
289
vendor/github.com/golang/snappy/encode.go
generated
vendored
Normal file
289
vendor/github.com/golang/snappy/encode.go
generated
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Encode returns the encoded form of src. The returned slice may be a sub-
|
||||
// slice of dst if dst was large enough to hold the entire encoded block.
|
||||
// Otherwise, a newly allocated slice will be returned.
|
||||
//
|
||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||
//
|
||||
// Encode handles the Snappy block format, not the Snappy stream format.
|
||||
func Encode(dst, src []byte) []byte {
|
||||
if n := MaxEncodedLen(len(src)); n < 0 {
|
||||
panic(ErrTooLarge)
|
||||
} else if len(dst) < n {
|
||||
dst = make([]byte, n)
|
||||
}
|
||||
|
||||
// The block starts with the varint-encoded length of the decompressed bytes.
|
||||
d := binary.PutUvarint(dst, uint64(len(src)))
|
||||
|
||||
for len(src) > 0 {
|
||||
p := src
|
||||
src = nil
|
||||
if len(p) > maxBlockSize {
|
||||
p, src = p[:maxBlockSize], p[maxBlockSize:]
|
||||
}
|
||||
if len(p) < minNonLiteralBlockSize {
|
||||
d += emitLiteral(dst[d:], p)
|
||||
} else {
|
||||
d += encodeBlock(dst[d:], p)
|
||||
}
|
||||
}
|
||||
return dst[:d]
|
||||
}
|
||||
|
||||
// inputMargin is the minimum number of extra input bytes to keep, inside
|
||||
// encodeBlock's inner loop. On some architectures, this margin lets us
|
||||
// implement a fast path for emitLiteral, where the copy of short (<= 16 byte)
|
||||
// literals can be implemented as a single load to and store from a 16-byte
|
||||
// register. That literal's actual length can be as short as 1 byte, so this
|
||||
// can copy up to 15 bytes too much, but that's OK as subsequent iterations of
|
||||
// the encoding loop will fix up the copy overrun, and this inputMargin ensures
|
||||
// that we don't overrun the dst and src buffers.
|
||||
const inputMargin = 16 - 1
|
||||
|
||||
// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that
|
||||
// could be encoded with a copy tag. This is the minimum with respect to the
|
||||
// algorithm used by encodeBlock, not a minimum enforced by the file format.
|
||||
//
|
||||
// The encoded output must start with at least a 1 byte literal, as there are
|
||||
// no previous bytes to copy. A minimal (1 byte) copy after that, generated
|
||||
// from an emitCopy call in encodeBlock's main loop, would require at least
|
||||
// another inputMargin bytes, for the reason above: we want any emitLiteral
|
||||
// calls inside encodeBlock's main loop to use the fast path if possible, which
|
||||
// requires being able to overrun by inputMargin bytes. Thus,
|
||||
// minNonLiteralBlockSize equals 1 + 1 + inputMargin.
|
||||
//
|
||||
// The C++ code doesn't use this exact threshold, but it could, as discussed at
|
||||
// https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion
|
||||
// The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an
|
||||
// optimization. It should not affect the encoded form. This is tested by
|
||||
// TestSameEncodingAsCppShortCopies.
|
||||
const minNonLiteralBlockSize = 1 + 1 + inputMargin
|
||||
|
||||
// MaxEncodedLen returns the maximum length of a snappy block, given its
|
||||
// uncompressed length.
|
||||
//
|
||||
// It will return a negative value if srcLen is too large to encode.
|
||||
func MaxEncodedLen(srcLen int) int {
|
||||
n := uint64(srcLen)
|
||||
if n > 0xffffffff {
|
||||
return -1
|
||||
}
|
||||
// Compressed data can be defined as:
|
||||
// compressed := item* literal*
|
||||
// item := literal* copy
|
||||
//
|
||||
// The trailing literal sequence has a space blowup of at most 62/60
|
||||
// since a literal of length 60 needs one tag byte + one extra byte
|
||||
// for length information.
|
||||
//
|
||||
// Item blowup is trickier to measure. Suppose the "copy" op copies
|
||||
// 4 bytes of data. Because of a special check in the encoding code,
|
||||
// we produce a 4-byte copy only if the offset is < 65536. Therefore
|
||||
// the copy op takes 3 bytes to encode, and this type of item leads
|
||||
// to at most the 62/60 blowup for representing literals.
|
||||
//
|
||||
// Suppose the "copy" op copies 5 bytes of data. If the offset is big
|
||||
// enough, it will take 5 bytes to encode the copy op. Therefore the
|
||||
// worst case here is a one-byte literal followed by a five-byte copy.
|
||||
// That is, 6 bytes of input turn into 7 bytes of "compressed" data.
|
||||
//
|
||||
// This last factor dominates the blowup, so the final estimate is:
|
||||
n = 32 + n + n/6
|
||||
if n > 0xffffffff {
|
||||
return -1
|
||||
}
|
||||
return int(n)
|
||||
}
|
||||
|
||||
var errClosed = errors.New("snappy: Writer is closed")
|
||||
|
||||
// NewWriter returns a new Writer that compresses to w.
|
||||
//
|
||||
// The Writer returned does not buffer writes. There is no need to Flush or
|
||||
// Close such a Writer.
|
||||
//
|
||||
// Deprecated: the Writer returned is not suitable for many small writes, only
|
||||
// for few large writes. Use NewBufferedWriter instead, which is efficient
|
||||
// regardless of the frequency and shape of the writes, and remember to Close
|
||||
// that Writer when done.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
obuf: make([]byte, obufLen),
|
||||
}
|
||||
}
|
||||
|
||||
// NewBufferedWriter returns a new Writer that compresses to w, using the
|
||||
// framing format described at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
//
|
||||
// The Writer returned buffers writes. Users must call Close to guarantee all
|
||||
// data has been forwarded to the underlying io.Writer. They may also call
|
||||
// Flush zero or more times before calling Close.
|
||||
func NewBufferedWriter(w io.Writer) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
ibuf: make([]byte, 0, maxBlockSize),
|
||||
obuf: make([]byte, obufLen),
|
||||
}
|
||||
}
|
||||
|
||||
// Writer is an io.Writer that can write Snappy-compressed bytes.
|
||||
//
|
||||
// Writer handles the Snappy stream format, not the Snappy block format.
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
err error
|
||||
|
||||
// ibuf is a buffer for the incoming (uncompressed) bytes.
|
||||
//
|
||||
// Its use is optional. For backwards compatibility, Writers created by the
|
||||
// NewWriter function have ibuf == nil, do not buffer incoming bytes, and
|
||||
// therefore do not need to be Flush'ed or Close'd.
|
||||
ibuf []byte
|
||||
|
||||
// obuf is a buffer for the outgoing (compressed) bytes.
|
||||
obuf []byte
|
||||
|
||||
// wroteStreamHeader is whether we have written the stream header.
|
||||
wroteStreamHeader bool
|
||||
}
|
||||
|
||||
// Reset discards the writer's state and switches the Snappy writer to write to
|
||||
// w. This permits reusing a Writer rather than allocating a new one.
|
||||
func (w *Writer) Reset(writer io.Writer) {
|
||||
w.w = writer
|
||||
w.err = nil
|
||||
if w.ibuf != nil {
|
||||
w.ibuf = w.ibuf[:0]
|
||||
}
|
||||
w.wroteStreamHeader = false
|
||||
}
|
||||
|
||||
// Write satisfies the io.Writer interface.
|
||||
func (w *Writer) Write(p []byte) (nRet int, errRet error) {
|
||||
if w.ibuf == nil {
|
||||
// Do not buffer incoming bytes. This does not perform or compress well
|
||||
// if the caller of Writer.Write writes many small slices. This
|
||||
// behavior is therefore deprecated, but still supported for backwards
|
||||
// compatibility with code that doesn't explicitly Flush or Close.
|
||||
return w.write(p)
|
||||
}
|
||||
|
||||
// The remainder of this method is based on bufio.Writer.Write from the
|
||||
// standard library.
|
||||
|
||||
for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil {
|
||||
var n int
|
||||
if len(w.ibuf) == 0 {
|
||||
// Large write, empty buffer.
|
||||
// Write directly from p to avoid copy.
|
||||
n, _ = w.write(p)
|
||||
} else {
|
||||
n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p)
|
||||
w.ibuf = w.ibuf[:len(w.ibuf)+n]
|
||||
w.Flush()
|
||||
}
|
||||
nRet += n
|
||||
p = p[n:]
|
||||
}
|
||||
if w.err != nil {
|
||||
return nRet, w.err
|
||||
}
|
||||
n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p)
|
||||
w.ibuf = w.ibuf[:len(w.ibuf)+n]
|
||||
nRet += n
|
||||
return nRet, nil
|
||||
}
|
||||
|
||||
func (w *Writer) write(p []byte) (nRet int, errRet error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
for len(p) > 0 {
|
||||
obufStart := len(magicChunk)
|
||||
if !w.wroteStreamHeader {
|
||||
w.wroteStreamHeader = true
|
||||
copy(w.obuf, magicChunk)
|
||||
obufStart = 0
|
||||
}
|
||||
|
||||
var uncompressed []byte
|
||||
if len(p) > maxBlockSize {
|
||||
uncompressed, p = p[:maxBlockSize], p[maxBlockSize:]
|
||||
} else {
|
||||
uncompressed, p = p, nil
|
||||
}
|
||||
checksum := crc(uncompressed)
|
||||
|
||||
// Compress the buffer, discarding the result if the improvement
|
||||
// isn't at least 12.5%.
|
||||
compressed := Encode(w.obuf[obufHeaderLen:], uncompressed)
|
||||
chunkType := uint8(chunkTypeCompressedData)
|
||||
chunkLen := 4 + len(compressed)
|
||||
obufEnd := obufHeaderLen + len(compressed)
|
||||
if len(compressed) >= len(uncompressed)-len(uncompressed)/8 {
|
||||
chunkType = chunkTypeUncompressedData
|
||||
chunkLen = 4 + len(uncompressed)
|
||||
obufEnd = obufHeaderLen
|
||||
}
|
||||
|
||||
// Fill in the per-chunk header that comes before the body.
|
||||
w.obuf[len(magicChunk)+0] = chunkType
|
||||
w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0)
|
||||
w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8)
|
||||
w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16)
|
||||
w.obuf[len(magicChunk)+4] = uint8(checksum >> 0)
|
||||
w.obuf[len(magicChunk)+5] = uint8(checksum >> 8)
|
||||
w.obuf[len(magicChunk)+6] = uint8(checksum >> 16)
|
||||
w.obuf[len(magicChunk)+7] = uint8(checksum >> 24)
|
||||
|
||||
if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil {
|
||||
w.err = err
|
||||
return nRet, err
|
||||
}
|
||||
if chunkType == chunkTypeUncompressedData {
|
||||
if _, err := w.w.Write(uncompressed); err != nil {
|
||||
w.err = err
|
||||
return nRet, err
|
||||
}
|
||||
}
|
||||
nRet += len(uncompressed)
|
||||
}
|
||||
return nRet, nil
|
||||
}
|
||||
|
||||
// Flush flushes the Writer to its underlying io.Writer.
|
||||
func (w *Writer) Flush() error {
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
if len(w.ibuf) == 0 {
|
||||
return nil
|
||||
}
|
||||
w.write(w.ibuf)
|
||||
w.ibuf = w.ibuf[:0]
|
||||
return w.err
|
||||
}
|
||||
|
||||
// Close calls Flush and then closes the Writer.
|
||||
func (w *Writer) Close() error {
|
||||
w.Flush()
|
||||
ret := w.err
|
||||
if w.err == nil {
|
||||
w.err = errClosed
|
||||
}
|
||||
return ret
|
||||
}
|
||||
730
vendor/github.com/golang/snappy/encode_amd64.s
generated
vendored
Normal file
730
vendor/github.com/golang/snappy/encode_amd64.s
generated
vendored
Normal file
@@ -0,0 +1,730 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// The XXX lines assemble on Go 1.4, 1.5 and 1.7, but not 1.6, due to a
|
||||
// Go toolchain regression. See https://github.com/golang/go/issues/15426 and
|
||||
// https://github.com/golang/snappy/issues/29
|
||||
//
|
||||
// As a workaround, the package was built with a known good assembler, and
|
||||
// those instructions were disassembled by "objdump -d" to yield the
|
||||
// 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
|
||||
// style comments, in AT&T asm syntax. Note that rsp here is a physical
|
||||
// register, not Go/asm's SP pseudo-register (see https://golang.org/doc/asm).
|
||||
// The instructions were then encoded as "BYTE $0x.." sequences, which assemble
|
||||
// fine on Go 1.6.
|
||||
|
||||
// The asm code generally follows the pure Go code in encode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func emitLiteral(dst, lit []byte) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - AX len(lit)
|
||||
// - BX n
|
||||
// - DX return value
|
||||
// - DI &dst[i]
|
||||
// - R10 &lit[0]
|
||||
//
|
||||
// The 24 bytes of stack space is to call runtime·memmove.
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R10 for the
|
||||
// source pointer, matches the allocation used at the call site in encodeBlock,
|
||||
// which makes it easier to manually inline this function.
|
||||
TEXT ·emitLiteral(SB), NOSPLIT, $24-56
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ lit_base+24(FP), R10
|
||||
MOVQ lit_len+32(FP), AX
|
||||
MOVQ AX, DX
|
||||
MOVL AX, BX
|
||||
SUBL $1, BX
|
||||
|
||||
CMPL BX, $60
|
||||
JLT oneByte
|
||||
CMPL BX, $256
|
||||
JLT twoBytes
|
||||
|
||||
threeBytes:
|
||||
MOVB $0xf4, 0(DI)
|
||||
MOVW BX, 1(DI)
|
||||
ADDQ $3, DI
|
||||
ADDQ $3, DX
|
||||
JMP memmove
|
||||
|
||||
twoBytes:
|
||||
MOVB $0xf0, 0(DI)
|
||||
MOVB BX, 1(DI)
|
||||
ADDQ $2, DI
|
||||
ADDQ $2, DX
|
||||
JMP memmove
|
||||
|
||||
oneByte:
|
||||
SHLB $2, BX
|
||||
MOVB BX, 0(DI)
|
||||
ADDQ $1, DI
|
||||
ADDQ $1, DX
|
||||
|
||||
memmove:
|
||||
MOVQ DX, ret+48(FP)
|
||||
|
||||
// copy(dst[i:], lit)
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||
// DI, R10 and AX as arguments.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ R10, 8(SP)
|
||||
MOVQ AX, 16(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func emitCopy(dst []byte, offset, length int) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - AX length
|
||||
// - SI &dst[0]
|
||||
// - DI &dst[i]
|
||||
// - R11 offset
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R11 for the
|
||||
// offset, matches the allocation used at the call site in encodeBlock, which
|
||||
// makes it easier to manually inline this function.
|
||||
TEXT ·emitCopy(SB), NOSPLIT, $0-48
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ DI, SI
|
||||
MOVQ offset+24(FP), R11
|
||||
MOVQ length+32(FP), AX
|
||||
|
||||
loop0:
|
||||
// for length >= 68 { etc }
|
||||
CMPL AX, $68
|
||||
JLT step1
|
||||
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
MOVB $0xfe, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $64, AX
|
||||
JMP loop0
|
||||
|
||||
step1:
|
||||
// if length > 64 { etc }
|
||||
CMPL AX, $64
|
||||
JLE step2
|
||||
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
MOVB $0xee, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $60, AX
|
||||
|
||||
step2:
|
||||
// if length >= 12 || offset >= 2048 { goto step3 }
|
||||
CMPL AX, $12
|
||||
JGE step3
|
||||
CMPL R11, $2048
|
||||
JGE step3
|
||||
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
MOVB R11, 1(DI)
|
||||
SHRL $8, R11
|
||||
SHLB $5, R11
|
||||
SUBB $4, AX
|
||||
SHLB $2, AX
|
||||
ORB AX, R11
|
||||
ORB $1, R11
|
||||
MOVB R11, 0(DI)
|
||||
ADDQ $2, DI
|
||||
|
||||
// Return the number of bytes written.
|
||||
SUBQ SI, DI
|
||||
MOVQ DI, ret+40(FP)
|
||||
RET
|
||||
|
||||
step3:
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
SUBL $1, AX
|
||||
SHLB $2, AX
|
||||
ORB $2, AX
|
||||
MOVB AX, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
|
||||
// Return the number of bytes written.
|
||||
SUBQ SI, DI
|
||||
MOVQ DI, ret+40(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func extendMatch(src []byte, i, j int) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - DX &src[0]
|
||||
// - SI &src[j]
|
||||
// - R13 &src[len(src) - 8]
|
||||
// - R14 &src[len(src)]
|
||||
// - R15 &src[i]
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R15 for a source
|
||||
// pointer, matches the allocation used at the call site in encodeBlock, which
|
||||
// makes it easier to manually inline this function.
|
||||
TEXT ·extendMatch(SB), NOSPLIT, $0-48
|
||||
MOVQ src_base+0(FP), DX
|
||||
MOVQ src_len+8(FP), R14
|
||||
MOVQ i+24(FP), R15
|
||||
MOVQ j+32(FP), SI
|
||||
ADDQ DX, R14
|
||||
ADDQ DX, R15
|
||||
ADDQ DX, SI
|
||||
MOVQ R14, R13
|
||||
SUBQ $8, R13
|
||||
|
||||
cmp8:
|
||||
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||
CMPQ SI, R13
|
||||
JA cmp1
|
||||
MOVQ (R15), AX
|
||||
MOVQ (SI), BX
|
||||
CMPQ AX, BX
|
||||
JNE bsf
|
||||
ADDQ $8, R15
|
||||
ADDQ $8, SI
|
||||
JMP cmp8
|
||||
|
||||
bsf:
|
||||
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||
// the index of the first byte that differs. The BSF instruction finds the
|
||||
// least significant 1 bit, the amd64 architecture is little-endian, and
|
||||
// the shift by 3 converts a bit index to a byte index.
|
||||
XORQ AX, BX
|
||||
BSFQ BX, BX
|
||||
SHRQ $3, BX
|
||||
ADDQ BX, SI
|
||||
|
||||
// Convert from &src[ret] to ret.
|
||||
SUBQ DX, SI
|
||||
MOVQ SI, ret+40(FP)
|
||||
RET
|
||||
|
||||
cmp1:
|
||||
// In src's tail, compare 1 byte at a time.
|
||||
CMPQ SI, R14
|
||||
JAE extendMatchEnd
|
||||
MOVB (R15), AX
|
||||
MOVB (SI), BX
|
||||
CMPB AX, BX
|
||||
JNE extendMatchEnd
|
||||
ADDQ $1, R15
|
||||
ADDQ $1, SI
|
||||
JMP cmp1
|
||||
|
||||
extendMatchEnd:
|
||||
// Convert from &src[ret] to ret.
|
||||
SUBQ DX, SI
|
||||
MOVQ SI, ret+40(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func encodeBlock(dst, src []byte) (d int)
|
||||
//
|
||||
// All local variables fit into registers, other than "var table". The register
|
||||
// allocation:
|
||||
// - AX . .
|
||||
// - BX . .
|
||||
// - CX 56 shift (note that amd64 shifts by non-immediates must use CX).
|
||||
// - DX 64 &src[0], tableSize
|
||||
// - SI 72 &src[s]
|
||||
// - DI 80 &dst[d]
|
||||
// - R9 88 sLimit
|
||||
// - R10 . &src[nextEmit]
|
||||
// - R11 96 prevHash, currHash, nextHash, offset
|
||||
// - R12 104 &src[base], skip
|
||||
// - R13 . &src[nextS], &src[len(src) - 8]
|
||||
// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x
|
||||
// - R15 112 candidate
|
||||
//
|
||||
// The second column (56, 64, etc) is the stack offset to spill the registers
|
||||
// when calling other functions. We could pack this slightly tighter, but it's
|
||||
// simpler to have a dedicated spill map independent of the function called.
|
||||
//
|
||||
// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An
|
||||
// extra 56 bytes, to call other functions, and an extra 64 bytes, to spill
|
||||
// local variables (registers) during calls gives 32768 + 56 + 64 = 32888.
|
||||
TEXT ·encodeBlock(SB), 0, $32888-56
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ src_base+24(FP), SI
|
||||
MOVQ src_len+32(FP), R14
|
||||
|
||||
// shift, tableSize := uint32(32-8), 1<<8
|
||||
MOVQ $24, CX
|
||||
MOVQ $256, DX
|
||||
|
||||
calcShift:
|
||||
// for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
|
||||
// shift--
|
||||
// }
|
||||
CMPQ DX, $16384
|
||||
JGE varTable
|
||||
CMPQ DX, R14
|
||||
JGE varTable
|
||||
SUBQ $1, CX
|
||||
SHLQ $1, DX
|
||||
JMP calcShift
|
||||
|
||||
varTable:
|
||||
// var table [maxTableSize]uint16
|
||||
//
|
||||
// In the asm code, unlike the Go code, we can zero-initialize only the
|
||||
// first tableSize elements. Each uint16 element is 2 bytes and each MOVOU
|
||||
// writes 16 bytes, so we can do only tableSize/8 writes instead of the
|
||||
// 2048 writes that would zero-initialize all of table's 32768 bytes.
|
||||
SHRQ $3, DX
|
||||
LEAQ table-32768(SP), BX
|
||||
PXOR X0, X0
|
||||
|
||||
memclr:
|
||||
MOVOU X0, 0(BX)
|
||||
ADDQ $16, BX
|
||||
SUBQ $1, DX
|
||||
JNZ memclr
|
||||
|
||||
// !!! DX = &src[0]
|
||||
MOVQ SI, DX
|
||||
|
||||
// sLimit := len(src) - inputMargin
|
||||
MOVQ R14, R9
|
||||
SUBQ $15, R9
|
||||
|
||||
// !!! Pre-emptively spill CX, DX and R9 to the stack. Their values don't
|
||||
// change for the rest of the function.
|
||||
MOVQ CX, 56(SP)
|
||||
MOVQ DX, 64(SP)
|
||||
MOVQ R9, 88(SP)
|
||||
|
||||
// nextEmit := 0
|
||||
MOVQ DX, R10
|
||||
|
||||
// s := 1
|
||||
ADDQ $1, SI
|
||||
|
||||
// nextHash := hash(load32(src, s), shift)
|
||||
MOVL 0(SI), R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
outer:
|
||||
// for { etc }
|
||||
|
||||
// skip := 32
|
||||
MOVQ $32, R12
|
||||
|
||||
// nextS := s
|
||||
MOVQ SI, R13
|
||||
|
||||
// candidate := 0
|
||||
MOVQ $0, R15
|
||||
|
||||
inner0:
|
||||
// for { etc }
|
||||
|
||||
// s := nextS
|
||||
MOVQ R13, SI
|
||||
|
||||
// bytesBetweenHashLookups := skip >> 5
|
||||
MOVQ R12, R14
|
||||
SHRQ $5, R14
|
||||
|
||||
// nextS = s + bytesBetweenHashLookups
|
||||
ADDQ R14, R13
|
||||
|
||||
// skip += bytesBetweenHashLookups
|
||||
ADDQ R14, R12
|
||||
|
||||
// if nextS > sLimit { goto emitRemainder }
|
||||
MOVQ R13, AX
|
||||
SUBQ DX, AX
|
||||
CMPQ AX, R9
|
||||
JA emitRemainder
|
||||
|
||||
// candidate = int(table[nextHash])
|
||||
// XXX: MOVWQZX table-32768(SP)(R11*2), R15
|
||||
// XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
|
||||
BYTE $0x4e
|
||||
BYTE $0x0f
|
||||
BYTE $0xb7
|
||||
BYTE $0x7c
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// table[nextHash] = uint16(s)
|
||||
MOVQ SI, AX
|
||||
SUBQ DX, AX
|
||||
|
||||
// XXX: MOVW AX, table-32768(SP)(R11*2)
|
||||
// XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
|
||||
BYTE $0x66
|
||||
BYTE $0x42
|
||||
BYTE $0x89
|
||||
BYTE $0x44
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// nextHash = hash(load32(src, nextS), shift)
|
||||
MOVL 0(R13), R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// if load32(src, s) != load32(src, candidate) { continue } break
|
||||
MOVL 0(SI), AX
|
||||
MOVL (DX)(R15*1), BX
|
||||
CMPL AX, BX
|
||||
JNE inner0
|
||||
|
||||
fourByteMatch:
|
||||
// As per the encode_other.go code:
|
||||
//
|
||||
// A 4-byte match has been found. We'll later see etc.
|
||||
|
||||
// !!! Jump to a fast path for short (<= 16 byte) literals. See the comment
|
||||
// on inputMargin in encode.go.
|
||||
MOVQ SI, AX
|
||||
SUBQ R10, AX
|
||||
CMPQ AX, $16
|
||||
JLE emitLiteralFastPath
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the emitLiteral call.
|
||||
//
|
||||
// d += emitLiteral(dst[d:], src[nextEmit:s])
|
||||
|
||||
MOVL AX, BX
|
||||
SUBL $1, BX
|
||||
|
||||
CMPL BX, $60
|
||||
JLT inlineEmitLiteralOneByte
|
||||
CMPL BX, $256
|
||||
JLT inlineEmitLiteralTwoBytes
|
||||
|
||||
inlineEmitLiteralThreeBytes:
|
||||
MOVB $0xf4, 0(DI)
|
||||
MOVW BX, 1(DI)
|
||||
ADDQ $3, DI
|
||||
JMP inlineEmitLiteralMemmove
|
||||
|
||||
inlineEmitLiteralTwoBytes:
|
||||
MOVB $0xf0, 0(DI)
|
||||
MOVB BX, 1(DI)
|
||||
ADDQ $2, DI
|
||||
JMP inlineEmitLiteralMemmove
|
||||
|
||||
inlineEmitLiteralOneByte:
|
||||
SHLB $2, BX
|
||||
MOVB BX, 0(DI)
|
||||
ADDQ $1, DI
|
||||
|
||||
inlineEmitLiteralMemmove:
|
||||
// Spill local variables (registers) onto the stack; call; unspill.
|
||||
//
|
||||
// copy(dst[i:], lit)
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||
// DI, R10 and AX as arguments.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ R10, 8(SP)
|
||||
MOVQ AX, 16(SP)
|
||||
ADDQ AX, DI // Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||
MOVQ SI, 72(SP)
|
||||
MOVQ DI, 80(SP)
|
||||
MOVQ R15, 112(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
MOVQ 56(SP), CX
|
||||
MOVQ 64(SP), DX
|
||||
MOVQ 72(SP), SI
|
||||
MOVQ 80(SP), DI
|
||||
MOVQ 88(SP), R9
|
||||
MOVQ 112(SP), R15
|
||||
JMP inner1
|
||||
|
||||
inlineEmitLiteralEnd:
|
||||
// End inline of the emitLiteral call.
|
||||
// ----------------------------------------
|
||||
|
||||
emitLiteralFastPath:
|
||||
// !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2".
|
||||
MOVB AX, BX
|
||||
SUBB $1, BX
|
||||
SHLB $2, BX
|
||||
MOVB BX, (DI)
|
||||
ADDQ $1, DI
|
||||
|
||||
// !!! Implement the copy from lit to dst as a 16-byte load and store.
|
||||
// (Encode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only len(lit) bytes, but that's
|
||||
// OK. Subsequent iterations will fix up the overrun.
|
||||
//
|
||||
// Note that on amd64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
MOVOU 0(R10), X0
|
||||
MOVOU X0, 0(DI)
|
||||
ADDQ AX, DI
|
||||
|
||||
inner1:
|
||||
// for { etc }
|
||||
|
||||
// base := s
|
||||
MOVQ SI, R12
|
||||
|
||||
// !!! offset := base - candidate
|
||||
MOVQ R12, R11
|
||||
SUBQ R15, R11
|
||||
SUBQ DX, R11
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the extendMatch call.
|
||||
//
|
||||
// s = extendMatch(src, candidate+4, s+4)
|
||||
|
||||
// !!! R14 = &src[len(src)]
|
||||
MOVQ src_len+32(FP), R14
|
||||
ADDQ DX, R14
|
||||
|
||||
// !!! R13 = &src[len(src) - 8]
|
||||
MOVQ R14, R13
|
||||
SUBQ $8, R13
|
||||
|
||||
// !!! R15 = &src[candidate + 4]
|
||||
ADDQ $4, R15
|
||||
ADDQ DX, R15
|
||||
|
||||
// !!! s += 4
|
||||
ADDQ $4, SI
|
||||
|
||||
inlineExtendMatchCmp8:
|
||||
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||
CMPQ SI, R13
|
||||
JA inlineExtendMatchCmp1
|
||||
MOVQ (R15), AX
|
||||
MOVQ (SI), BX
|
||||
CMPQ AX, BX
|
||||
JNE inlineExtendMatchBSF
|
||||
ADDQ $8, R15
|
||||
ADDQ $8, SI
|
||||
JMP inlineExtendMatchCmp8
|
||||
|
||||
inlineExtendMatchBSF:
|
||||
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||
// the index of the first byte that differs. The BSF instruction finds the
|
||||
// least significant 1 bit, the amd64 architecture is little-endian, and
|
||||
// the shift by 3 converts a bit index to a byte index.
|
||||
XORQ AX, BX
|
||||
BSFQ BX, BX
|
||||
SHRQ $3, BX
|
||||
ADDQ BX, SI
|
||||
JMP inlineExtendMatchEnd
|
||||
|
||||
inlineExtendMatchCmp1:
|
||||
// In src's tail, compare 1 byte at a time.
|
||||
CMPQ SI, R14
|
||||
JAE inlineExtendMatchEnd
|
||||
MOVB (R15), AX
|
||||
MOVB (SI), BX
|
||||
CMPB AX, BX
|
||||
JNE inlineExtendMatchEnd
|
||||
ADDQ $1, R15
|
||||
ADDQ $1, SI
|
||||
JMP inlineExtendMatchCmp1
|
||||
|
||||
inlineExtendMatchEnd:
|
||||
// End inline of the extendMatch call.
|
||||
// ----------------------------------------
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the emitCopy call.
|
||||
//
|
||||
// d += emitCopy(dst[d:], base-candidate, s-base)
|
||||
|
||||
// !!! length := s - base
|
||||
MOVQ SI, AX
|
||||
SUBQ R12, AX
|
||||
|
||||
inlineEmitCopyLoop0:
|
||||
// for length >= 68 { etc }
|
||||
CMPL AX, $68
|
||||
JLT inlineEmitCopyStep1
|
||||
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
MOVB $0xfe, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $64, AX
|
||||
JMP inlineEmitCopyLoop0
|
||||
|
||||
inlineEmitCopyStep1:
|
||||
// if length > 64 { etc }
|
||||
CMPL AX, $64
|
||||
JLE inlineEmitCopyStep2
|
||||
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
MOVB $0xee, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $60, AX
|
||||
|
||||
inlineEmitCopyStep2:
|
||||
// if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 }
|
||||
CMPL AX, $12
|
||||
JGE inlineEmitCopyStep3
|
||||
CMPL R11, $2048
|
||||
JGE inlineEmitCopyStep3
|
||||
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
MOVB R11, 1(DI)
|
||||
SHRL $8, R11
|
||||
SHLB $5, R11
|
||||
SUBB $4, AX
|
||||
SHLB $2, AX
|
||||
ORB AX, R11
|
||||
ORB $1, R11
|
||||
MOVB R11, 0(DI)
|
||||
ADDQ $2, DI
|
||||
JMP inlineEmitCopyEnd
|
||||
|
||||
inlineEmitCopyStep3:
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
SUBL $1, AX
|
||||
SHLB $2, AX
|
||||
ORB $2, AX
|
||||
MOVB AX, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
|
||||
inlineEmitCopyEnd:
|
||||
// End inline of the emitCopy call.
|
||||
// ----------------------------------------
|
||||
|
||||
// nextEmit = s
|
||||
MOVQ SI, R10
|
||||
|
||||
// if s >= sLimit { goto emitRemainder }
|
||||
MOVQ SI, AX
|
||||
SUBQ DX, AX
|
||||
CMPQ AX, R9
|
||||
JAE emitRemainder
|
||||
|
||||
// As per the encode_other.go code:
|
||||
//
|
||||
// We could immediately etc.
|
||||
|
||||
// x := load64(src, s-1)
|
||||
MOVQ -1(SI), R14
|
||||
|
||||
// prevHash := hash(uint32(x>>0), shift)
|
||||
MOVL R14, R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// table[prevHash] = uint16(s-1)
|
||||
MOVQ SI, AX
|
||||
SUBQ DX, AX
|
||||
SUBQ $1, AX
|
||||
|
||||
// XXX: MOVW AX, table-32768(SP)(R11*2)
|
||||
// XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
|
||||
BYTE $0x66
|
||||
BYTE $0x42
|
||||
BYTE $0x89
|
||||
BYTE $0x44
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// currHash := hash(uint32(x>>8), shift)
|
||||
SHRQ $8, R14
|
||||
MOVL R14, R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// candidate = int(table[currHash])
|
||||
// XXX: MOVWQZX table-32768(SP)(R11*2), R15
|
||||
// XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
|
||||
BYTE $0x4e
|
||||
BYTE $0x0f
|
||||
BYTE $0xb7
|
||||
BYTE $0x7c
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// table[currHash] = uint16(s)
|
||||
ADDQ $1, AX
|
||||
|
||||
// XXX: MOVW AX, table-32768(SP)(R11*2)
|
||||
// XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
|
||||
BYTE $0x66
|
||||
BYTE $0x42
|
||||
BYTE $0x89
|
||||
BYTE $0x44
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// if uint32(x>>8) == load32(src, candidate) { continue }
|
||||
MOVL (DX)(R15*1), BX
|
||||
CMPL R14, BX
|
||||
JEQ inner1
|
||||
|
||||
// nextHash = hash(uint32(x>>16), shift)
|
||||
SHRQ $8, R14
|
||||
MOVL R14, R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// s++
|
||||
ADDQ $1, SI
|
||||
|
||||
// break out of the inner1 for loop, i.e. continue the outer loop.
|
||||
JMP outer
|
||||
|
||||
emitRemainder:
|
||||
// if nextEmit < len(src) { etc }
|
||||
MOVQ src_len+32(FP), AX
|
||||
ADDQ DX, AX
|
||||
CMPQ R10, AX
|
||||
JEQ encodeBlockEnd
|
||||
|
||||
// d += emitLiteral(dst[d:], src[nextEmit:])
|
||||
//
|
||||
// Push args.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ $0, 8(SP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
MOVQ $0, 16(SP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
MOVQ R10, 24(SP)
|
||||
SUBQ R10, AX
|
||||
MOVQ AX, 32(SP)
|
||||
MOVQ AX, 40(SP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
|
||||
// Spill local variables (registers) onto the stack; call; unspill.
|
||||
MOVQ DI, 80(SP)
|
||||
CALL ·emitLiteral(SB)
|
||||
MOVQ 80(SP), DI
|
||||
|
||||
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||
ADDQ 48(SP), DI
|
||||
|
||||
encodeBlockEnd:
|
||||
MOVQ dst_base+0(FP), AX
|
||||
SUBQ AX, DI
|
||||
MOVQ DI, d+48(FP)
|
||||
RET
|
||||
722
vendor/github.com/golang/snappy/encode_arm64.s
generated
vendored
Normal file
722
vendor/github.com/golang/snappy/encode_arm64.s
generated
vendored
Normal file
@@ -0,0 +1,722 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// The asm code generally follows the pure Go code in encode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func emitLiteral(dst, lit []byte) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - R3 len(lit)
|
||||
// - R4 n
|
||||
// - R6 return value
|
||||
// - R8 &dst[i]
|
||||
// - R10 &lit[0]
|
||||
//
|
||||
// The 32 bytes of stack space is to call runtime·memmove.
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R10 for the
|
||||
// source pointer, matches the allocation used at the call site in encodeBlock,
|
||||
// which makes it easier to manually inline this function.
|
||||
TEXT ·emitLiteral(SB), NOSPLIT, $32-56
|
||||
MOVD dst_base+0(FP), R8
|
||||
MOVD lit_base+24(FP), R10
|
||||
MOVD lit_len+32(FP), R3
|
||||
MOVD R3, R6
|
||||
MOVW R3, R4
|
||||
SUBW $1, R4, R4
|
||||
|
||||
CMPW $60, R4
|
||||
BLT oneByte
|
||||
CMPW $256, R4
|
||||
BLT twoBytes
|
||||
|
||||
threeBytes:
|
||||
MOVD $0xf4, R2
|
||||
MOVB R2, 0(R8)
|
||||
MOVW R4, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
ADD $3, R6, R6
|
||||
B memmove
|
||||
|
||||
twoBytes:
|
||||
MOVD $0xf0, R2
|
||||
MOVB R2, 0(R8)
|
||||
MOVB R4, 1(R8)
|
||||
ADD $2, R8, R8
|
||||
ADD $2, R6, R6
|
||||
B memmove
|
||||
|
||||
oneByte:
|
||||
LSLW $2, R4, R4
|
||||
MOVB R4, 0(R8)
|
||||
ADD $1, R8, R8
|
||||
ADD $1, R6, R6
|
||||
|
||||
memmove:
|
||||
MOVD R6, ret+48(FP)
|
||||
|
||||
// copy(dst[i:], lit)
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||
// R8, R10 and R3 as arguments.
|
||||
MOVD R8, 8(RSP)
|
||||
MOVD R10, 16(RSP)
|
||||
MOVD R3, 24(RSP)
|
||||
CALL runtime·memmove(SB)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func emitCopy(dst []byte, offset, length int) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - R3 length
|
||||
// - R7 &dst[0]
|
||||
// - R8 &dst[i]
|
||||
// - R11 offset
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R11 for the
|
||||
// offset, matches the allocation used at the call site in encodeBlock, which
|
||||
// makes it easier to manually inline this function.
|
||||
TEXT ·emitCopy(SB), NOSPLIT, $0-48
|
||||
MOVD dst_base+0(FP), R8
|
||||
MOVD R8, R7
|
||||
MOVD offset+24(FP), R11
|
||||
MOVD length+32(FP), R3
|
||||
|
||||
loop0:
|
||||
// for length >= 68 { etc }
|
||||
CMPW $68, R3
|
||||
BLT step1
|
||||
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
MOVD $0xfe, R2
|
||||
MOVB R2, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
SUB $64, R3, R3
|
||||
B loop0
|
||||
|
||||
step1:
|
||||
// if length > 64 { etc }
|
||||
CMP $64, R3
|
||||
BLE step2
|
||||
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
MOVD $0xee, R2
|
||||
MOVB R2, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
SUB $60, R3, R3
|
||||
|
||||
step2:
|
||||
// if length >= 12 || offset >= 2048 { goto step3 }
|
||||
CMP $12, R3
|
||||
BGE step3
|
||||
CMPW $2048, R11
|
||||
BGE step3
|
||||
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
MOVB R11, 1(R8)
|
||||
LSRW $3, R11, R11
|
||||
AND $0xe0, R11, R11
|
||||
SUB $4, R3, R3
|
||||
LSLW $2, R3
|
||||
AND $0xff, R3, R3
|
||||
ORRW R3, R11, R11
|
||||
ORRW $1, R11, R11
|
||||
MOVB R11, 0(R8)
|
||||
ADD $2, R8, R8
|
||||
|
||||
// Return the number of bytes written.
|
||||
SUB R7, R8, R8
|
||||
MOVD R8, ret+40(FP)
|
||||
RET
|
||||
|
||||
step3:
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
SUB $1, R3, R3
|
||||
AND $0xff, R3, R3
|
||||
LSLW $2, R3, R3
|
||||
ORRW $2, R3, R3
|
||||
MOVB R3, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
|
||||
// Return the number of bytes written.
|
||||
SUB R7, R8, R8
|
||||
MOVD R8, ret+40(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func extendMatch(src []byte, i, j int) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - R6 &src[0]
|
||||
// - R7 &src[j]
|
||||
// - R13 &src[len(src) - 8]
|
||||
// - R14 &src[len(src)]
|
||||
// - R15 &src[i]
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R15 for a source
|
||||
// pointer, matches the allocation used at the call site in encodeBlock, which
|
||||
// makes it easier to manually inline this function.
|
||||
TEXT ·extendMatch(SB), NOSPLIT, $0-48
|
||||
MOVD src_base+0(FP), R6
|
||||
MOVD src_len+8(FP), R14
|
||||
MOVD i+24(FP), R15
|
||||
MOVD j+32(FP), R7
|
||||
ADD R6, R14, R14
|
||||
ADD R6, R15, R15
|
||||
ADD R6, R7, R7
|
||||
MOVD R14, R13
|
||||
SUB $8, R13, R13
|
||||
|
||||
cmp8:
|
||||
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||
CMP R13, R7
|
||||
BHI cmp1
|
||||
MOVD (R15), R3
|
||||
MOVD (R7), R4
|
||||
CMP R4, R3
|
||||
BNE bsf
|
||||
ADD $8, R15, R15
|
||||
ADD $8, R7, R7
|
||||
B cmp8
|
||||
|
||||
bsf:
|
||||
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||
// the index of the first byte that differs.
|
||||
// RBIT reverses the bit order, then CLZ counts the leading zeros, the
|
||||
// combination of which finds the least significant bit which is set.
|
||||
// The arm64 architecture is little-endian, and the shift by 3 converts
|
||||
// a bit index to a byte index.
|
||||
EOR R3, R4, R4
|
||||
RBIT R4, R4
|
||||
CLZ R4, R4
|
||||
ADD R4>>3, R7, R7
|
||||
|
||||
// Convert from &src[ret] to ret.
|
||||
SUB R6, R7, R7
|
||||
MOVD R7, ret+40(FP)
|
||||
RET
|
||||
|
||||
cmp1:
|
||||
// In src's tail, compare 1 byte at a time.
|
||||
CMP R7, R14
|
||||
BLS extendMatchEnd
|
||||
MOVB (R15), R3
|
||||
MOVB (R7), R4
|
||||
CMP R4, R3
|
||||
BNE extendMatchEnd
|
||||
ADD $1, R15, R15
|
||||
ADD $1, R7, R7
|
||||
B cmp1
|
||||
|
||||
extendMatchEnd:
|
||||
// Convert from &src[ret] to ret.
|
||||
SUB R6, R7, R7
|
||||
MOVD R7, ret+40(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func encodeBlock(dst, src []byte) (d int)
|
||||
//
|
||||
// All local variables fit into registers, other than "var table". The register
|
||||
// allocation:
|
||||
// - R3 . .
|
||||
// - R4 . .
|
||||
// - R5 64 shift
|
||||
// - R6 72 &src[0], tableSize
|
||||
// - R7 80 &src[s]
|
||||
// - R8 88 &dst[d]
|
||||
// - R9 96 sLimit
|
||||
// - R10 . &src[nextEmit]
|
||||
// - R11 104 prevHash, currHash, nextHash, offset
|
||||
// - R12 112 &src[base], skip
|
||||
// - R13 . &src[nextS], &src[len(src) - 8]
|
||||
// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x
|
||||
// - R15 120 candidate
|
||||
// - R16 . hash constant, 0x1e35a7bd
|
||||
// - R17 . &table
|
||||
// - . 128 table
|
||||
//
|
||||
// The second column (64, 72, etc) is the stack offset to spill the registers
|
||||
// when calling other functions. We could pack this slightly tighter, but it's
|
||||
// simpler to have a dedicated spill map independent of the function called.
|
||||
//
|
||||
// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An
|
||||
// extra 64 bytes, to call other functions, and an extra 64 bytes, to spill
|
||||
// local variables (registers) during calls gives 32768 + 64 + 64 = 32896.
|
||||
TEXT ·encodeBlock(SB), 0, $32896-56
|
||||
MOVD dst_base+0(FP), R8
|
||||
MOVD src_base+24(FP), R7
|
||||
MOVD src_len+32(FP), R14
|
||||
|
||||
// shift, tableSize := uint32(32-8), 1<<8
|
||||
MOVD $24, R5
|
||||
MOVD $256, R6
|
||||
MOVW $0xa7bd, R16
|
||||
MOVKW $(0x1e35<<16), R16
|
||||
|
||||
calcShift:
|
||||
// for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
|
||||
// shift--
|
||||
// }
|
||||
MOVD $16384, R2
|
||||
CMP R2, R6
|
||||
BGE varTable
|
||||
CMP R14, R6
|
||||
BGE varTable
|
||||
SUB $1, R5, R5
|
||||
LSL $1, R6, R6
|
||||
B calcShift
|
||||
|
||||
varTable:
|
||||
// var table [maxTableSize]uint16
|
||||
//
|
||||
// In the asm code, unlike the Go code, we can zero-initialize only the
|
||||
// first tableSize elements. Each uint16 element is 2 bytes and each
|
||||
// iterations writes 64 bytes, so we can do only tableSize/32 writes
|
||||
// instead of the 2048 writes that would zero-initialize all of table's
|
||||
// 32768 bytes. This clear could overrun the first tableSize elements, but
|
||||
// it won't overrun the allocated stack size.
|
||||
ADD $128, RSP, R17
|
||||
MOVD R17, R4
|
||||
|
||||
// !!! R6 = &src[tableSize]
|
||||
ADD R6<<1, R17, R6
|
||||
|
||||
memclr:
|
||||
STP.P (ZR, ZR), 64(R4)
|
||||
STP (ZR, ZR), -48(R4)
|
||||
STP (ZR, ZR), -32(R4)
|
||||
STP (ZR, ZR), -16(R4)
|
||||
CMP R4, R6
|
||||
BHI memclr
|
||||
|
||||
// !!! R6 = &src[0]
|
||||
MOVD R7, R6
|
||||
|
||||
// sLimit := len(src) - inputMargin
|
||||
MOVD R14, R9
|
||||
SUB $15, R9, R9
|
||||
|
||||
// !!! Pre-emptively spill R5, R6 and R9 to the stack. Their values don't
|
||||
// change for the rest of the function.
|
||||
MOVD R5, 64(RSP)
|
||||
MOVD R6, 72(RSP)
|
||||
MOVD R9, 96(RSP)
|
||||
|
||||
// nextEmit := 0
|
||||
MOVD R6, R10
|
||||
|
||||
// s := 1
|
||||
ADD $1, R7, R7
|
||||
|
||||
// nextHash := hash(load32(src, s), shift)
|
||||
MOVW 0(R7), R11
|
||||
MULW R16, R11, R11
|
||||
LSRW R5, R11, R11
|
||||
|
||||
outer:
|
||||
// for { etc }
|
||||
|
||||
// skip := 32
|
||||
MOVD $32, R12
|
||||
|
||||
// nextS := s
|
||||
MOVD R7, R13
|
||||
|
||||
// candidate := 0
|
||||
MOVD $0, R15
|
||||
|
||||
inner0:
|
||||
// for { etc }
|
||||
|
||||
// s := nextS
|
||||
MOVD R13, R7
|
||||
|
||||
// bytesBetweenHashLookups := skip >> 5
|
||||
MOVD R12, R14
|
||||
LSR $5, R14, R14
|
||||
|
||||
// nextS = s + bytesBetweenHashLookups
|
||||
ADD R14, R13, R13
|
||||
|
||||
// skip += bytesBetweenHashLookups
|
||||
ADD R14, R12, R12
|
||||
|
||||
// if nextS > sLimit { goto emitRemainder }
|
||||
MOVD R13, R3
|
||||
SUB R6, R3, R3
|
||||
CMP R9, R3
|
||||
BHI emitRemainder
|
||||
|
||||
// candidate = int(table[nextHash])
|
||||
MOVHU 0(R17)(R11<<1), R15
|
||||
|
||||
// table[nextHash] = uint16(s)
|
||||
MOVD R7, R3
|
||||
SUB R6, R3, R3
|
||||
|
||||
MOVH R3, 0(R17)(R11<<1)
|
||||
|
||||
// nextHash = hash(load32(src, nextS), shift)
|
||||
MOVW 0(R13), R11
|
||||
MULW R16, R11
|
||||
LSRW R5, R11, R11
|
||||
|
||||
// if load32(src, s) != load32(src, candidate) { continue } break
|
||||
MOVW 0(R7), R3
|
||||
MOVW (R6)(R15), R4
|
||||
CMPW R4, R3
|
||||
BNE inner0
|
||||
|
||||
fourByteMatch:
|
||||
// As per the encode_other.go code:
|
||||
//
|
||||
// A 4-byte match has been found. We'll later see etc.
|
||||
|
||||
// !!! Jump to a fast path for short (<= 16 byte) literals. See the comment
|
||||
// on inputMargin in encode.go.
|
||||
MOVD R7, R3
|
||||
SUB R10, R3, R3
|
||||
CMP $16, R3
|
||||
BLE emitLiteralFastPath
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the emitLiteral call.
|
||||
//
|
||||
// d += emitLiteral(dst[d:], src[nextEmit:s])
|
||||
|
||||
MOVW R3, R4
|
||||
SUBW $1, R4, R4
|
||||
|
||||
MOVW $60, R2
|
||||
CMPW R2, R4
|
||||
BLT inlineEmitLiteralOneByte
|
||||
MOVW $256, R2
|
||||
CMPW R2, R4
|
||||
BLT inlineEmitLiteralTwoBytes
|
||||
|
||||
inlineEmitLiteralThreeBytes:
|
||||
MOVD $0xf4, R1
|
||||
MOVB R1, 0(R8)
|
||||
MOVW R4, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
B inlineEmitLiteralMemmove
|
||||
|
||||
inlineEmitLiteralTwoBytes:
|
||||
MOVD $0xf0, R1
|
||||
MOVB R1, 0(R8)
|
||||
MOVB R4, 1(R8)
|
||||
ADD $2, R8, R8
|
||||
B inlineEmitLiteralMemmove
|
||||
|
||||
inlineEmitLiteralOneByte:
|
||||
LSLW $2, R4, R4
|
||||
MOVB R4, 0(R8)
|
||||
ADD $1, R8, R8
|
||||
|
||||
inlineEmitLiteralMemmove:
|
||||
// Spill local variables (registers) onto the stack; call; unspill.
|
||||
//
|
||||
// copy(dst[i:], lit)
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||
// R8, R10 and R3 as arguments.
|
||||
MOVD R8, 8(RSP)
|
||||
MOVD R10, 16(RSP)
|
||||
MOVD R3, 24(RSP)
|
||||
|
||||
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||
ADD R3, R8, R8
|
||||
MOVD R7, 80(RSP)
|
||||
MOVD R8, 88(RSP)
|
||||
MOVD R15, 120(RSP)
|
||||
CALL runtime·memmove(SB)
|
||||
MOVD 64(RSP), R5
|
||||
MOVD 72(RSP), R6
|
||||
MOVD 80(RSP), R7
|
||||
MOVD 88(RSP), R8
|
||||
MOVD 96(RSP), R9
|
||||
MOVD 120(RSP), R15
|
||||
ADD $128, RSP, R17
|
||||
MOVW $0xa7bd, R16
|
||||
MOVKW $(0x1e35<<16), R16
|
||||
B inner1
|
||||
|
||||
inlineEmitLiteralEnd:
|
||||
// End inline of the emitLiteral call.
|
||||
// ----------------------------------------
|
||||
|
||||
emitLiteralFastPath:
|
||||
// !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2".
|
||||
MOVB R3, R4
|
||||
SUBW $1, R4, R4
|
||||
AND $0xff, R4, R4
|
||||
LSLW $2, R4, R4
|
||||
MOVB R4, (R8)
|
||||
ADD $1, R8, R8
|
||||
|
||||
// !!! Implement the copy from lit to dst as a 16-byte load and store.
|
||||
// (Encode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only len(lit) bytes, but that's
|
||||
// OK. Subsequent iterations will fix up the overrun.
|
||||
//
|
||||
// Note that on arm64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
LDP 0(R10), (R0, R1)
|
||||
STP (R0, R1), 0(R8)
|
||||
ADD R3, R8, R8
|
||||
|
||||
inner1:
|
||||
// for { etc }
|
||||
|
||||
// base := s
|
||||
MOVD R7, R12
|
||||
|
||||
// !!! offset := base - candidate
|
||||
MOVD R12, R11
|
||||
SUB R15, R11, R11
|
||||
SUB R6, R11, R11
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the extendMatch call.
|
||||
//
|
||||
// s = extendMatch(src, candidate+4, s+4)
|
||||
|
||||
// !!! R14 = &src[len(src)]
|
||||
MOVD src_len+32(FP), R14
|
||||
ADD R6, R14, R14
|
||||
|
||||
// !!! R13 = &src[len(src) - 8]
|
||||
MOVD R14, R13
|
||||
SUB $8, R13, R13
|
||||
|
||||
// !!! R15 = &src[candidate + 4]
|
||||
ADD $4, R15, R15
|
||||
ADD R6, R15, R15
|
||||
|
||||
// !!! s += 4
|
||||
ADD $4, R7, R7
|
||||
|
||||
inlineExtendMatchCmp8:
|
||||
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||
CMP R13, R7
|
||||
BHI inlineExtendMatchCmp1
|
||||
MOVD (R15), R3
|
||||
MOVD (R7), R4
|
||||
CMP R4, R3
|
||||
BNE inlineExtendMatchBSF
|
||||
ADD $8, R15, R15
|
||||
ADD $8, R7, R7
|
||||
B inlineExtendMatchCmp8
|
||||
|
||||
inlineExtendMatchBSF:
|
||||
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||
// the index of the first byte that differs.
|
||||
// RBIT reverses the bit order, then CLZ counts the leading zeros, the
|
||||
// combination of which finds the least significant bit which is set.
|
||||
// The arm64 architecture is little-endian, and the shift by 3 converts
|
||||
// a bit index to a byte index.
|
||||
EOR R3, R4, R4
|
||||
RBIT R4, R4
|
||||
CLZ R4, R4
|
||||
ADD R4>>3, R7, R7
|
||||
B inlineExtendMatchEnd
|
||||
|
||||
inlineExtendMatchCmp1:
|
||||
// In src's tail, compare 1 byte at a time.
|
||||
CMP R7, R14
|
||||
BLS inlineExtendMatchEnd
|
||||
MOVB (R15), R3
|
||||
MOVB (R7), R4
|
||||
CMP R4, R3
|
||||
BNE inlineExtendMatchEnd
|
||||
ADD $1, R15, R15
|
||||
ADD $1, R7, R7
|
||||
B inlineExtendMatchCmp1
|
||||
|
||||
inlineExtendMatchEnd:
|
||||
// End inline of the extendMatch call.
|
||||
// ----------------------------------------
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the emitCopy call.
|
||||
//
|
||||
// d += emitCopy(dst[d:], base-candidate, s-base)
|
||||
|
||||
// !!! length := s - base
|
||||
MOVD R7, R3
|
||||
SUB R12, R3, R3
|
||||
|
||||
inlineEmitCopyLoop0:
|
||||
// for length >= 68 { etc }
|
||||
MOVW $68, R2
|
||||
CMPW R2, R3
|
||||
BLT inlineEmitCopyStep1
|
||||
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
MOVD $0xfe, R1
|
||||
MOVB R1, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
SUBW $64, R3, R3
|
||||
B inlineEmitCopyLoop0
|
||||
|
||||
inlineEmitCopyStep1:
|
||||
// if length > 64 { etc }
|
||||
MOVW $64, R2
|
||||
CMPW R2, R3
|
||||
BLE inlineEmitCopyStep2
|
||||
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
MOVD $0xee, R1
|
||||
MOVB R1, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
SUBW $60, R3, R3
|
||||
|
||||
inlineEmitCopyStep2:
|
||||
// if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 }
|
||||
MOVW $12, R2
|
||||
CMPW R2, R3
|
||||
BGE inlineEmitCopyStep3
|
||||
MOVW $2048, R2
|
||||
CMPW R2, R11
|
||||
BGE inlineEmitCopyStep3
|
||||
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
MOVB R11, 1(R8)
|
||||
LSRW $8, R11, R11
|
||||
LSLW $5, R11, R11
|
||||
SUBW $4, R3, R3
|
||||
AND $0xff, R3, R3
|
||||
LSLW $2, R3, R3
|
||||
ORRW R3, R11, R11
|
||||
ORRW $1, R11, R11
|
||||
MOVB R11, 0(R8)
|
||||
ADD $2, R8, R8
|
||||
B inlineEmitCopyEnd
|
||||
|
||||
inlineEmitCopyStep3:
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
SUBW $1, R3, R3
|
||||
LSLW $2, R3, R3
|
||||
ORRW $2, R3, R3
|
||||
MOVB R3, 0(R8)
|
||||
MOVW R11, 1(R8)
|
||||
ADD $3, R8, R8
|
||||
|
||||
inlineEmitCopyEnd:
|
||||
// End inline of the emitCopy call.
|
||||
// ----------------------------------------
|
||||
|
||||
// nextEmit = s
|
||||
MOVD R7, R10
|
||||
|
||||
// if s >= sLimit { goto emitRemainder }
|
||||
MOVD R7, R3
|
||||
SUB R6, R3, R3
|
||||
CMP R3, R9
|
||||
BLS emitRemainder
|
||||
|
||||
// As per the encode_other.go code:
|
||||
//
|
||||
// We could immediately etc.
|
||||
|
||||
// x := load64(src, s-1)
|
||||
MOVD -1(R7), R14
|
||||
|
||||
// prevHash := hash(uint32(x>>0), shift)
|
||||
MOVW R14, R11
|
||||
MULW R16, R11, R11
|
||||
LSRW R5, R11, R11
|
||||
|
||||
// table[prevHash] = uint16(s-1)
|
||||
MOVD R7, R3
|
||||
SUB R6, R3, R3
|
||||
SUB $1, R3, R3
|
||||
|
||||
MOVHU R3, 0(R17)(R11<<1)
|
||||
|
||||
// currHash := hash(uint32(x>>8), shift)
|
||||
LSR $8, R14, R14
|
||||
MOVW R14, R11
|
||||
MULW R16, R11, R11
|
||||
LSRW R5, R11, R11
|
||||
|
||||
// candidate = int(table[currHash])
|
||||
MOVHU 0(R17)(R11<<1), R15
|
||||
|
||||
// table[currHash] = uint16(s)
|
||||
ADD $1, R3, R3
|
||||
MOVHU R3, 0(R17)(R11<<1)
|
||||
|
||||
// if uint32(x>>8) == load32(src, candidate) { continue }
|
||||
MOVW (R6)(R15), R4
|
||||
CMPW R4, R14
|
||||
BEQ inner1
|
||||
|
||||
// nextHash = hash(uint32(x>>16), shift)
|
||||
LSR $8, R14, R14
|
||||
MOVW R14, R11
|
||||
MULW R16, R11, R11
|
||||
LSRW R5, R11, R11
|
||||
|
||||
// s++
|
||||
ADD $1, R7, R7
|
||||
|
||||
// break out of the inner1 for loop, i.e. continue the outer loop.
|
||||
B outer
|
||||
|
||||
emitRemainder:
|
||||
// if nextEmit < len(src) { etc }
|
||||
MOVD src_len+32(FP), R3
|
||||
ADD R6, R3, R3
|
||||
CMP R3, R10
|
||||
BEQ encodeBlockEnd
|
||||
|
||||
// d += emitLiteral(dst[d:], src[nextEmit:])
|
||||
//
|
||||
// Push args.
|
||||
MOVD R8, 8(RSP)
|
||||
MOVD $0, 16(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
MOVD $0, 24(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
MOVD R10, 32(RSP)
|
||||
SUB R10, R3, R3
|
||||
MOVD R3, 40(RSP)
|
||||
MOVD R3, 48(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
|
||||
// Spill local variables (registers) onto the stack; call; unspill.
|
||||
MOVD R8, 88(RSP)
|
||||
CALL ·emitLiteral(SB)
|
||||
MOVD 88(RSP), R8
|
||||
|
||||
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||
MOVD 56(RSP), R1
|
||||
ADD R1, R8, R8
|
||||
|
||||
encodeBlockEnd:
|
||||
MOVD dst_base+0(FP), R3
|
||||
SUB R3, R8, R8
|
||||
MOVD R8, d+48(FP)
|
||||
RET
|
||||
30
vendor/github.com/golang/snappy/encode_asm.go
generated
vendored
Normal file
30
vendor/github.com/golang/snappy/encode_asm.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
// +build amd64 arm64
|
||||
|
||||
package snappy
|
||||
|
||||
// emitLiteral has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func emitLiteral(dst, lit []byte) int
|
||||
|
||||
// emitCopy has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func emitCopy(dst []byte, offset, length int) int
|
||||
|
||||
// extendMatch has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func extendMatch(src []byte, i, j int) int
|
||||
|
||||
// encodeBlock has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func encodeBlock(dst, src []byte) (d int)
|
||||
238
vendor/github.com/golang/snappy/encode_other.go
generated
vendored
Normal file
238
vendor/github.com/golang/snappy/encode_other.go
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64,!arm64 appengine !gc noasm
|
||||
|
||||
package snappy
|
||||
|
||||
func load32(b []byte, i int) uint32 {
|
||||
b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line.
|
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
}
|
||||
|
||||
func load64(b []byte, i int) uint64 {
|
||||
b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line.
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
||||
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
||||
}
|
||||
|
||||
// emitLiteral writes a literal chunk and returns the number of bytes written.
|
||||
//
|
||||
// It assumes that:
|
||||
// dst is long enough to hold the encoded bytes
|
||||
// 1 <= len(lit) && len(lit) <= 65536
|
||||
func emitLiteral(dst, lit []byte) int {
|
||||
i, n := 0, uint(len(lit)-1)
|
||||
switch {
|
||||
case n < 60:
|
||||
dst[0] = uint8(n)<<2 | tagLiteral
|
||||
i = 1
|
||||
case n < 1<<8:
|
||||
dst[0] = 60<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
i = 2
|
||||
default:
|
||||
dst[0] = 61<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
dst[2] = uint8(n >> 8)
|
||||
i = 3
|
||||
}
|
||||
return i + copy(dst[i:], lit)
|
||||
}
|
||||
|
||||
// emitCopy writes a copy chunk and returns the number of bytes written.
|
||||
//
|
||||
// It assumes that:
|
||||
// dst is long enough to hold the encoded bytes
|
||||
// 1 <= offset && offset <= 65535
|
||||
// 4 <= length && length <= 65535
|
||||
func emitCopy(dst []byte, offset, length int) int {
|
||||
i := 0
|
||||
// The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The
|
||||
// threshold for this loop is a little higher (at 68 = 64 + 4), and the
|
||||
// length emitted down below is is a little lower (at 60 = 64 - 4), because
|
||||
// it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed
|
||||
// by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as
|
||||
// a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as
|
||||
// 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a
|
||||
// tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an
|
||||
// encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1.
|
||||
for length >= 68 {
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
dst[i+0] = 63<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
i += 3
|
||||
length -= 64
|
||||
}
|
||||
if length > 64 {
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
dst[i+0] = 59<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
i += 3
|
||||
length -= 60
|
||||
}
|
||||
if length >= 12 || offset >= 2048 {
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
dst[i+0] = uint8(length-1)<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
return i + 3
|
||||
}
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1
|
||||
dst[i+1] = uint8(offset)
|
||||
return i + 2
|
||||
}
|
||||
|
||||
// extendMatch returns the largest k such that k <= len(src) and that
|
||||
// src[i:i+k-j] and src[j:k] have the same contents.
|
||||
//
|
||||
// It assumes that:
|
||||
// 0 <= i && i < j && j <= len(src)
|
||||
func extendMatch(src []byte, i, j int) int {
|
||||
for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 {
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
func hash(u, shift uint32) uint32 {
|
||||
return (u * 0x1e35a7bd) >> shift
|
||||
}
|
||||
|
||||
// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It
|
||||
// assumes that the varint-encoded length of the decompressed bytes has already
|
||||
// been written.
|
||||
//
|
||||
// It also assumes that:
|
||||
// len(dst) >= MaxEncodedLen(len(src)) &&
|
||||
// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize
|
||||
func encodeBlock(dst, src []byte) (d int) {
|
||||
// Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
|
||||
// The table element type is uint16, as s < sLimit and sLimit < len(src)
|
||||
// and len(src) <= maxBlockSize and maxBlockSize == 65536.
|
||||
const (
|
||||
maxTableSize = 1 << 14
|
||||
// tableMask is redundant, but helps the compiler eliminate bounds
|
||||
// checks.
|
||||
tableMask = maxTableSize - 1
|
||||
)
|
||||
shift := uint32(32 - 8)
|
||||
for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
|
||||
shift--
|
||||
}
|
||||
// In Go, all array elements are zero-initialized, so there is no advantage
|
||||
// to a smaller tableSize per se. However, it matches the C++ algorithm,
|
||||
// and in the asm versions of this code, we can get away with zeroing only
|
||||
// the first tableSize elements.
|
||||
var table [maxTableSize]uint16
|
||||
|
||||
// sLimit is when to stop looking for offset/length copies. The inputMargin
|
||||
// lets us use a fast path for emitLiteral in the main loop, while we are
|
||||
// looking for copies.
|
||||
sLimit := len(src) - inputMargin
|
||||
|
||||
// nextEmit is where in src the next emitLiteral should start from.
|
||||
nextEmit := 0
|
||||
|
||||
// The encoded form must start with a literal, as there are no previous
|
||||
// bytes to copy, so we start looking for hash matches at s == 1.
|
||||
s := 1
|
||||
nextHash := hash(load32(src, s), shift)
|
||||
|
||||
for {
|
||||
// Copied from the C++ snappy implementation:
|
||||
//
|
||||
// Heuristic match skipping: If 32 bytes are scanned with no matches
|
||||
// found, start looking only at every other byte. If 32 more bytes are
|
||||
// scanned (or skipped), look at every third byte, etc.. When a match
|
||||
// is found, immediately go back to looking at every byte. This is a
|
||||
// small loss (~5% performance, ~0.1% density) for compressible data
|
||||
// due to more bookkeeping, but for non-compressible data (such as
|
||||
// JPEG) it's a huge win since the compressor quickly "realizes" the
|
||||
// data is incompressible and doesn't bother looking for matches
|
||||
// everywhere.
|
||||
//
|
||||
// The "skip" variable keeps track of how many bytes there are since
|
||||
// the last match; dividing it by 32 (ie. right-shifting by five) gives
|
||||
// the number of bytes to move ahead for each iteration.
|
||||
skip := 32
|
||||
|
||||
nextS := s
|
||||
candidate := 0
|
||||
for {
|
||||
s = nextS
|
||||
bytesBetweenHashLookups := skip >> 5
|
||||
nextS = s + bytesBetweenHashLookups
|
||||
skip += bytesBetweenHashLookups
|
||||
if nextS > sLimit {
|
||||
goto emitRemainder
|
||||
}
|
||||
candidate = int(table[nextHash&tableMask])
|
||||
table[nextHash&tableMask] = uint16(s)
|
||||
nextHash = hash(load32(src, nextS), shift)
|
||||
if load32(src, s) == load32(src, candidate) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// A 4-byte match has been found. We'll later see if more than 4 bytes
|
||||
// match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
|
||||
// them as literal bytes.
|
||||
d += emitLiteral(dst[d:], src[nextEmit:s])
|
||||
|
||||
// Call emitCopy, and then see if another emitCopy could be our next
|
||||
// move. Repeat until we find no match for the input immediately after
|
||||
// what was consumed by the last emitCopy call.
|
||||
//
|
||||
// If we exit this loop normally then we need to call emitLiteral next,
|
||||
// though we don't yet know how big the literal will be. We handle that
|
||||
// by proceeding to the next iteration of the main loop. We also can
|
||||
// exit this loop via goto if we get close to exhausting the input.
|
||||
for {
|
||||
// Invariant: we have a 4-byte match at s, and no need to emit any
|
||||
// literal bytes prior to s.
|
||||
base := s
|
||||
|
||||
// Extend the 4-byte match as long as possible.
|
||||
//
|
||||
// This is an inlined version of:
|
||||
// s = extendMatch(src, candidate+4, s+4)
|
||||
s += 4
|
||||
for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 {
|
||||
}
|
||||
|
||||
d += emitCopy(dst[d:], base-candidate, s-base)
|
||||
nextEmit = s
|
||||
if s >= sLimit {
|
||||
goto emitRemainder
|
||||
}
|
||||
|
||||
// We could immediately start working at s now, but to improve
|
||||
// compression we first update the hash table at s-1 and at s. If
|
||||
// another emitCopy is not our next move, also calculate nextHash
|
||||
// at s+1. At least on GOARCH=amd64, these three hash calculations
|
||||
// are faster as one load64 call (with some shifts) instead of
|
||||
// three load32 calls.
|
||||
x := load64(src, s-1)
|
||||
prevHash := hash(uint32(x>>0), shift)
|
||||
table[prevHash&tableMask] = uint16(s - 1)
|
||||
currHash := hash(uint32(x>>8), shift)
|
||||
candidate = int(table[currHash&tableMask])
|
||||
table[currHash&tableMask] = uint16(s)
|
||||
if uint32(x>>8) != load32(src, candidate) {
|
||||
nextHash = hash(uint32(x>>16), shift)
|
||||
s++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitRemainder:
|
||||
if nextEmit < len(src) {
|
||||
d += emitLiteral(dst[d:], src[nextEmit:])
|
||||
}
|
||||
return d
|
||||
}
|
||||
98
vendor/github.com/golang/snappy/snappy.go
generated
vendored
Normal file
98
vendor/github.com/golang/snappy/snappy.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package snappy implements the Snappy compression format. It aims for very
|
||||
// high speeds and reasonable compression.
|
||||
//
|
||||
// There are actually two Snappy formats: block and stream. They are related,
|
||||
// but different: trying to decompress block-compressed data as a Snappy stream
|
||||
// will fail, and vice versa. The block format is the Decode and Encode
|
||||
// functions and the stream format is the Reader and Writer types.
|
||||
//
|
||||
// The block format, the more common case, is used when the complete size (the
|
||||
// number of bytes) of the original data is known upfront, at the time
|
||||
// compression starts. The stream format, also known as the framing format, is
|
||||
// for when that isn't always true.
|
||||
//
|
||||
// The canonical, C++ implementation is at https://github.com/google/snappy and
|
||||
// it only implements the block format.
|
||||
package snappy // import "github.com/golang/snappy"
|
||||
|
||||
import (
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
/*
|
||||
Each encoded block begins with the varint-encoded length of the decoded data,
|
||||
followed by a sequence of chunks. Chunks begin and end on byte boundaries. The
|
||||
first byte of each chunk is broken into its 2 least and 6 most significant bits
|
||||
called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag.
|
||||
Zero means a literal tag. All other values mean a copy tag.
|
||||
|
||||
For literal tags:
|
||||
- If m < 60, the next 1 + m bytes are literal bytes.
|
||||
- Otherwise, let n be the little-endian unsigned integer denoted by the next
|
||||
m - 59 bytes. The next 1 + n bytes after that are literal bytes.
|
||||
|
||||
For copy tags, length bytes are copied from offset bytes ago, in the style of
|
||||
Lempel-Ziv compression algorithms. In particular:
|
||||
- For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12).
|
||||
The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10
|
||||
of the offset. The next byte is bits 0-7 of the offset.
|
||||
- For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65).
|
||||
The length is 1 + m. The offset is the little-endian unsigned integer
|
||||
denoted by the next 2 bytes.
|
||||
- For l == 3, this tag is a legacy format that is no longer issued by most
|
||||
encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in
|
||||
[1, 65). The length is 1 + m. The offset is the little-endian unsigned
|
||||
integer denoted by the next 4 bytes.
|
||||
*/
|
||||
const (
|
||||
tagLiteral = 0x00
|
||||
tagCopy1 = 0x01
|
||||
tagCopy2 = 0x02
|
||||
tagCopy4 = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
checksumSize = 4
|
||||
chunkHeaderSize = 4
|
||||
magicChunk = "\xff\x06\x00\x00" + magicBody
|
||||
magicBody = "sNaPpY"
|
||||
|
||||
// maxBlockSize is the maximum size of the input to encodeBlock. It is not
|
||||
// part of the wire format per se, but some parts of the encoder assume
|
||||
// that an offset fits into a uint16.
|
||||
//
|
||||
// Also, for the framing format (Writer type instead of Encode function),
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt says
|
||||
// that "the uncompressed data in a chunk must be no longer than 65536
|
||||
// bytes".
|
||||
maxBlockSize = 65536
|
||||
|
||||
// maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is
|
||||
// hard coded to be a const instead of a variable, so that obufLen can also
|
||||
// be a const. Their equivalence is confirmed by
|
||||
// TestMaxEncodedLenOfMaxBlockSize.
|
||||
maxEncodedLenOfMaxBlockSize = 76490
|
||||
|
||||
obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize
|
||||
obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize
|
||||
)
|
||||
|
||||
const (
|
||||
chunkTypeCompressedData = 0x00
|
||||
chunkTypeUncompressedData = 0x01
|
||||
chunkTypePadding = 0xfe
|
||||
chunkTypeStreamIdentifier = 0xff
|
||||
)
|
||||
|
||||
var crcTable = crc32.MakeTable(crc32.Castagnoli)
|
||||
|
||||
// crc implements the checksum specified in section 3 of
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
func crc(b []byte) uint32 {
|
||||
c := crc32.Update(0, crcTable, b)
|
||||
return uint32(c>>15|c<<17) + 0xa282ead8
|
||||
}
|
||||
Reference in New Issue
Block a user