MM-27456: Use reflect-free serialization for hot structs (#15171)

Automatic Merge
Этот коммит содержится в:
Agniva De Sarker
2020-08-13 13:05:57 +05:30
коммит произвёл GitHub
родитель 32b7d2b5f1
Коммит 91a76b2df9
34 изменённых файлов: 6224 добавлений и 6 удалений

10
vendor/github.com/tinylib/msgp/.gitignore сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,10 @@
_generated/generated.go
_generated/generated_test.go
_generated/*_gen.go
_generated/*_gen_test.go
_generated/embeddedStruct/*_gen.go
_generated/embeddedStruct/*_gen_test.go
msgp/defgen_test.go
msgp/cover.out
*~
*.coverprofile

11
vendor/github.com/tinylib/msgp/.travis.yml сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,11 @@
language: go
go:
- 1.12.x
- tip
env:
- GIMME_ARCH=amd64
- GIMME_ARCH=386
script: "make travis"

52
vendor/github.com/tinylib/msgp/Makefile сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,52 @@
# NOTE: This Makefile is only necessary if you
# plan on developing the msgp tool and library.
# Installation can still be performed with a
# normal `go install`.
# generated integration test files
GGEN = ./_generated/generated.go ./_generated/generated_test.go
# generated unit test files
MGEN = ./msgp/defgen_test.go
SHELL := /bin/bash
BIN = $(GOBIN)/msgp
.PHONY: clean wipe install get-deps bench all
$(BIN): */*.go
@go install ./...
install: $(BIN)
$(GGEN): ./_generated/def.go
go generate ./_generated
$(MGEN): ./msgp/defs_test.go
go generate ./msgp
test: all
go test ./... ./_generated
bench: all
go test -bench ./...
clean:
$(RM) $(GGEN) $(MGEN)
wipe: clean
$(RM) $(BIN)
get-deps:
go get -d -t ./...
all: install $(GGEN) $(MGEN)
# travis CI enters here
travis:
go get -d -t ./...
go build -o "$${GOPATH%%:*}/bin/msgp" .
go generate ./msgp
go generate ./_generated
go test -v ./... ./_generated

102
vendor/github.com/tinylib/msgp/README.md сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,102 @@
MessagePack Code Generator [![Build Status](https://travis-ci.org/tinylib/msgp.svg?branch=master)](https://travis-ci.org/tinylib/msgp)
=======
This is a code generation tool and serialization library for [MessagePack](http://msgpack.org). You can read more about MessagePack [in the wiki](http://github.com/tinylib/msgp/wiki), or at [msgpack.org](http://msgpack.org).
### Why?
- Use Go as your schema language
- Performance
- [JSON interop](http://godoc.org/github.com/tinylib/msgp/msgp#CopyToJSON)
- [User-defined extensions](http://github.com/tinylib/msgp/wiki/Using-Extensions)
- Type safety
- Encoding flexibility
### Quickstart
In a source file, include the following directive:
```go
//go:generate msgp
```
The `msgp` command will generate serialization methods for all exported type declarations in the file.
You can [read more about the code generation options here](http://github.com/tinylib/msgp/wiki/Using-the-Code-Generator).
### Use
Field names can be set in much the same way as the `encoding/json` package. For example:
```go
type Person struct {
Name string `msg:"name"`
Address string `msg:"address"`
Age int `msg:"age"`
Hidden string `msg:"-"` // this field is ignored
unexported bool // this field is also ignored
}
```
By default, the code generator will satisfy `msgp.Sizer`, `msgp.Encodable`, `msgp.Decodable`,
`msgp.Marshaler`, and `msgp.Unmarshaler`. Carefully-designed applications can use these methods to do
marshalling/unmarshalling with zero heap allocations.
While `msgp.Marshaler` and `msgp.Unmarshaler` are quite similar to the standard library's
`json.Marshaler` and `json.Unmarshaler`, `msgp.Encodable` and `msgp.Decodable` are useful for
stream serialization. (`*msgp.Writer` and `*msgp.Reader` are essentially protocol-aware versions
of `*bufio.Writer` and `*bufio.Reader`, respectively.)
### Features
- Extremely fast generated code
- Test and benchmark generation
- JSON interoperability (see `msgp.CopyToJSON() and msgp.UnmarshalAsJSON()`)
- Support for complex type declarations
- Native support for Go's `time.Time`, `complex64`, and `complex128` types
- Generation of both `[]byte`-oriented and `io.Reader/io.Writer`-oriented methods
- Support for arbitrary type system extensions
- [Preprocessor directives](http://github.com/tinylib/msgp/wiki/Preprocessor-Directives)
- File-based dependency model means fast codegen regardless of source tree size.
Consider the following:
```go
const Eight = 8
type MyInt int
type Data []byte
type Struct struct {
Which map[string]*MyInt `msg:"which"`
Other Data `msg:"other"`
Nums [Eight]float64 `msg:"nums"`
}
```
As long as the declarations of `MyInt` and `Data` are in the same file as `Struct`, the parser will determine that the type information for `MyInt` and `Data` can be passed into the definition of `Struct` before its methods are generated.
#### Extensions
MessagePack supports defining your own types through "extensions," which are just a tuple of
the data "type" (`int8`) and the raw binary. You [can see a worked example in the wiki.](http://github.com/tinylib/msgp/wiki/Using-Extensions)
### Status
Mostly stable, in that no breaking changes have been made to the `/msgp` library in more than a year. Newer versions
of the code may generate different code than older versions for performance reasons. I (@philhofer) am aware of a
number of stability-critical commercial applications that use this code with good results. But, caveat emptor.
You can read more about how `msgp` maps MessagePack types onto Go types [in the wiki](http://github.com/tinylib/msgp/wiki).
Here some of the known limitations/restrictions:
- Identifiers from outside the processed source file are assumed (optimistically) to satisfy the generator's interfaces. If this isn't the case, your code will fail to compile.
- Like most serializers, `chan` and `func` fields are ignored, as well as non-exported fields.
- Encoding of `interface{}` is limited to built-ins or types that have explicit encoding methods.
- _Maps must have `string` keys._ This is intentional (as it preserves JSON interop.) Although non-string map keys are not forbidden by the MessagePack standard, many serializers impose this restriction. (It also means *any* well-formed `struct` can be de-serialized into a `map[string]interface{}`.) The only exception to this rule is that the deserializers will allow you to read map keys encoded as `bin` types, due to the fact that some legacy encodings permitted this. (However, those values will still be cast to Go `string`s, and they will be converted to `str` types when re-encoded. It is the responsibility of the user to ensure that map keys are UTF-8 safe in this case.) The same rules hold true for JSON translation.
If the output compiles, then there's a pretty good chance things are fine. (Plus, we generate tests for you.) *Please, please, please* file an issue if you think the generator is writing broken code.
### Performance
If you like benchmarks, see [here](http://bravenewgeek.com/so-you-wanna-go-fast/) and [here](https://github.com/alecthomas/go_serialization_benchmarks).
As one might expect, the generated methods that deal with `[]byte` are faster for small objects, but the `io.Reader/Writer` methods are generally more memory-efficient (and, at some point, faster) for large (> 2KB) objects.

231
vendor/github.com/tinylib/msgp/gen/decode.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,231 @@
package gen
import (
"io"
"strconv"
)
func decode(w io.Writer) *decodeGen {
return &decodeGen{
p: printer{w: w},
hasfield: false,
}
}
type decodeGen struct {
passes
p printer
hasfield bool
ctx *Context
}
func (d *decodeGen) Method() Method { return Decode }
func (d *decodeGen) needsField() {
if d.hasfield {
return
}
d.p.print("\nvar field []byte; _ = field")
d.hasfield = true
}
func (d *decodeGen) Execute(p Elem) error {
p = d.applyall(p)
if p == nil {
return nil
}
d.hasfield = false
if !d.p.ok() {
return d.p.err
}
if !IsPrintable(p) {
return nil
}
d.ctx = &Context{}
d.p.comment("DecodeMsg implements msgp.Decodable")
d.p.printf("\nfunc (%s %s) DecodeMsg(dc *msgp.Reader) (err error) {", p.Varname(), methodReceiver(p))
next(d, p)
d.p.nakedReturn()
unsetReceiver(p)
return d.p.err
}
func (d *decodeGen) gStruct(s *Struct) {
if !d.p.ok() {
return
}
if s.AsTuple {
d.structAsTuple(s)
} else {
d.structAsMap(s)
}
return
}
func (d *decodeGen) assignAndCheck(name string, typ string) {
if !d.p.ok() {
return
}
d.p.printf("\n%s, err = dc.Read%s()", name, typ)
d.p.wrapErrCheck(d.ctx.ArgsStr())
}
func (d *decodeGen) structAsTuple(s *Struct) {
nfields := len(s.Fields)
sz := randIdent()
d.p.declare(sz, u32)
d.assignAndCheck(sz, arrayHeader)
d.p.arrayCheck(strconv.Itoa(nfields), sz)
for i := range s.Fields {
if !d.p.ok() {
return
}
d.ctx.PushString(s.Fields[i].FieldName)
next(d, s.Fields[i].FieldElem)
d.ctx.Pop()
}
}
func (d *decodeGen) structAsMap(s *Struct) {
d.needsField()
sz := randIdent()
d.p.declare(sz, u32)
d.assignAndCheck(sz, mapHeader)
d.p.printf("\nfor %s > 0 {\n%s--", sz, sz)
d.assignAndCheck("field", mapKey)
d.p.print("\nswitch msgp.UnsafeString(field) {")
for i := range s.Fields {
d.ctx.PushString(s.Fields[i].FieldName)
d.p.printf("\ncase \"%s\":", s.Fields[i].FieldTag)
next(d, s.Fields[i].FieldElem)
d.ctx.Pop()
if !d.p.ok() {
return
}
}
d.p.print("\ndefault:\nerr = dc.Skip()")
d.p.wrapErrCheck(d.ctx.ArgsStr())
d.p.closeblock() // close switch
d.p.closeblock() // close for loop
}
func (d *decodeGen) gBase(b *BaseElem) {
if !d.p.ok() {
return
}
// open block for 'tmp'
var tmp string
if b.Convert {
tmp = randIdent()
d.p.printf("\n{ var %s %s", tmp, b.BaseType())
}
vname := b.Varname() // e.g. "z.FieldOne"
bname := b.BaseName() // e.g. "Float64"
// handle special cases
// for object type.
switch b.Value {
case Bytes:
if b.Convert {
d.p.printf("\n%s, err = dc.ReadBytes([]byte(%s))", tmp, vname)
} else {
d.p.printf("\n%s, err = dc.ReadBytes(%s)", vname, vname)
}
case IDENT:
d.p.printf("\nerr = %s.DecodeMsg(dc)", vname)
case Ext:
d.p.printf("\nerr = dc.ReadExtension(%s)", vname)
default:
if b.Convert {
d.p.printf("\n%s, err = dc.Read%s()", tmp, bname)
} else {
d.p.printf("\n%s, err = dc.Read%s()", vname, bname)
}
}
d.p.wrapErrCheck(d.ctx.ArgsStr())
// close block for 'tmp'
if b.Convert {
if b.ShimMode == Cast {
d.p.printf("\n%s = %s(%s)\n}", vname, b.FromBase(), tmp)
} else {
d.p.printf("\n%s, err = %s(%s)\n}", vname, b.FromBase(), tmp)
d.p.wrapErrCheck(d.ctx.ArgsStr())
}
}
}
func (d *decodeGen) gMap(m *Map) {
if !d.p.ok() {
return
}
sz := randIdent()
// resize or allocate map
d.p.declare(sz, u32)
d.assignAndCheck(sz, mapHeader)
d.p.resizeMap(sz, m)
// for element in map, read string/value
// pair and assign
d.p.printf("\nfor %s > 0 {\n%s--", sz, sz)
d.p.declare(m.Keyidx, "string")
d.p.declare(m.Validx, m.Value.TypeName())
d.assignAndCheck(m.Keyidx, stringTyp)
d.ctx.PushVar(m.Keyidx)
next(d, m.Value)
d.p.mapAssign(m)
d.ctx.Pop()
d.p.closeblock()
}
func (d *decodeGen) gSlice(s *Slice) {
if !d.p.ok() {
return
}
sz := randIdent()
d.p.declare(sz, u32)
d.assignAndCheck(sz, arrayHeader)
d.p.resizeSlice(sz, s)
d.p.rangeBlock(d.ctx, s.Index, s.Varname(), d, s.Els)
}
func (d *decodeGen) gArray(a *Array) {
if !d.p.ok() {
return
}
// special case if we have [const]byte
if be, ok := a.Els.(*BaseElem); ok && (be.Value == Byte || be.Value == Uint8) {
d.p.printf("\nerr = dc.ReadExactBytes((%s)[:])", a.Varname())
d.p.wrapErrCheck(d.ctx.ArgsStr())
return
}
sz := randIdent()
d.p.declare(sz, u32)
d.assignAndCheck(sz, arrayHeader)
d.p.arrayCheck(coerceArraySize(a.Size), sz)
d.p.rangeBlock(d.ctx, a.Index, a.Varname(), d, a.Els)
}
func (d *decodeGen) gPtr(p *Ptr) {
if !d.p.ok() {
return
}
d.p.print("\nif dc.IsNil() {")
d.p.print("\nerr = dc.ReadNil()")
d.p.wrapErrCheck(d.ctx.ArgsStr())
d.p.printf("\n%s = nil\n} else {", p.Varname())
d.p.initPtr(p)
next(d, p.Value)
d.p.closeblock()
}

739
vendor/github.com/tinylib/msgp/gen/elem.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,739 @@
package gen
import (
"fmt"
"strings"
)
var (
identNext = 0
identPrefix = "za"
)
func resetIdent(prefix string) {
identPrefix = prefix
identNext = 0
}
// generate a random identifier name
func randIdent() string {
identNext++
return fmt.Sprintf("%s%04d", identPrefix, identNext)
}
// This code defines the type declaration tree.
//
// Consider the following:
//
// type Marshaler struct {
// Thing1 *float64 `msg:"thing1"`
// Body []byte `msg:"body"`
// }
//
// A parser using this generator as a backend
// should parse the above into:
//
// var val Elem = &Ptr{
// name: "z",
// Value: &Struct{
// Name: "Marshaler",
// Fields: []StructField{
// {
// FieldTag: "thing1",
// FieldElem: &Ptr{
// name: "z.Thing1",
// Value: &BaseElem{
// name: "*z.Thing1",
// Value: Float64,
// Convert: false,
// },
// },
// },
// {
// FieldTag: "body",
// FieldElem: &BaseElem{
// name: "z.Body",
// Value: Bytes,
// Convert: false,
// },
// },
// },
// },
// }
// Base is one of the
// base types
type Primitive uint8
// this is effectively the
// list of currently available
// ReadXxxx / WriteXxxx methods.
const (
Invalid Primitive = iota
Bytes
String
Float32
Float64
Complex64
Complex128
Uint
Uint8
Uint16
Uint32
Uint64
Byte
Int
Int8
Int16
Int32
Int64
Bool
Intf // interface{}
Time // time.Time
Ext // extension
IDENT // IDENT means an unrecognized identifier
)
// all of the recognized identities
// that map to primitive types
var primitives = map[string]Primitive{
"[]byte": Bytes,
"string": String,
"float32": Float32,
"float64": Float64,
"complex64": Complex64,
"complex128": Complex128,
"uint": Uint,
"uint8": Uint8,
"uint16": Uint16,
"uint32": Uint32,
"uint64": Uint64,
"byte": Byte,
"rune": Int32,
"int": Int,
"int8": Int8,
"int16": Int16,
"int32": Int32,
"int64": Int64,
"bool": Bool,
"interface{}": Intf,
"time.Time": Time,
"msgp.Extension": Ext,
}
// types built into the library
// that satisfy all of the
// interfaces.
var builtins = map[string]struct{}{
"msgp.Raw": struct{}{},
"msgp.Number": struct{}{},
}
// common data/methods for every Elem
type common struct{ vname, alias string }
func (c *common) SetVarname(s string) { c.vname = s }
func (c *common) Varname() string { return c.vname }
func (c *common) Alias(typ string) { c.alias = typ }
func (c *common) hidden() {}
func IsPrintable(e Elem) bool {
if be, ok := e.(*BaseElem); ok && !be.Printable() {
return false
}
return true
}
// Elem is a go type capable of being
// serialized into MessagePack. It is
// implemented by *Ptr, *Struct, *Array,
// *Slice, *Map, and *BaseElem.
type Elem interface {
// SetVarname sets this nodes
// variable name and recursively
// sets the names of all its children.
// In general, this should only be
// called on the parent of the tree.
SetVarname(s string)
// Varname returns the variable
// name of the element.
Varname() string
// TypeName is the canonical
// go type name of the node
// e.g. "string", "int", "map[string]float64"
// OR the alias name, if it has been set.
TypeName() string
// Alias sets a type (alias) name
Alias(typ string)
// Copy should perform a deep copy of the object
Copy() Elem
// Complexity returns a measure of the
// complexity of element (greater than
// or equal to 1.)
Complexity() int
// ZeroExpr returns the expression for the correct zero/empty
// value. Can be used for assignment.
// Returns "" if zero/empty not supported for this Elem.
ZeroExpr() string
// IfZeroExpr returns the expression to compare to zero/empty
// for this type. It is meant to be used in an if statement
// and may include the simple statement form followed by
// semicolon and then the expression.
// Returns "" if zero/empty not supported for this Elem.
IfZeroExpr() string
hidden()
}
// Ident returns the *BaseElem that corresponds
// to the provided identity.
func Ident(id string) *BaseElem {
p, ok := primitives[id]
if ok {
return &BaseElem{Value: p}
}
be := &BaseElem{Value: IDENT}
be.Alias(id)
return be
}
type Array struct {
common
Index string // index variable name
Size string // array size
Els Elem // child
}
func (a *Array) SetVarname(s string) {
a.common.SetVarname(s)
ridx:
a.Index = randIdent()
// try to avoid using the same
// index as a parent slice
if strings.Contains(a.Varname(), a.Index) {
goto ridx
}
a.Els.SetVarname(fmt.Sprintf("%s[%s]", a.Varname(), a.Index))
}
func (a *Array) TypeName() string {
if a.common.alias != "" {
return a.common.alias
}
a.common.Alias(fmt.Sprintf("[%s]%s", a.Size, a.Els.TypeName()))
return a.common.alias
}
func (a *Array) Copy() Elem {
b := *a
b.Els = a.Els.Copy()
return &b
}
func (a *Array) Complexity() int { return 1 + a.Els.Complexity() }
// ZeroExpr returns the zero/empty expression or empty string if not supported. Unsupported for this case.
func (a *Array) ZeroExpr() string { return "" }
// IfZeroExpr unsupported
func (a *Array) IfZeroExpr() string { return "" }
// Map is a map[string]Elem
type Map struct {
common
Keyidx string // key variable name
Validx string // value variable name
Value Elem // value element
}
func (m *Map) SetVarname(s string) {
m.common.SetVarname(s)
ridx:
m.Keyidx = randIdent()
m.Validx = randIdent()
// just in case
if m.Keyidx == m.Validx {
goto ridx
}
m.Value.SetVarname(m.Validx)
}
func (m *Map) TypeName() string {
if m.common.alias != "" {
return m.common.alias
}
m.common.Alias("map[string]" + m.Value.TypeName())
return m.common.alias
}
func (m *Map) Copy() Elem {
g := *m
g.Value = m.Value.Copy()
return &g
}
func (m *Map) Complexity() int { return 2 + m.Value.Complexity() }
// ZeroExpr returns the zero/empty expression or empty string if not supported. Always "nil" for this case.
func (m *Map) ZeroExpr() string { return "nil" }
// IfZeroExpr returns the expression to compare to zero/empty.
func (m *Map) IfZeroExpr() string { return m.Varname() + " == nil" }
type Slice struct {
common
Index string
Els Elem // The type of each element
}
func (s *Slice) SetVarname(a string) {
s.common.SetVarname(a)
s.Index = randIdent()
varName := s.Varname()
if varName[0] == '*' {
// Pointer-to-slice requires parenthesis for slicing.
varName = "(" + varName + ")"
}
s.Els.SetVarname(fmt.Sprintf("%s[%s]", varName, s.Index))
}
func (s *Slice) TypeName() string {
if s.common.alias != "" {
return s.common.alias
}
s.common.Alias("[]" + s.Els.TypeName())
return s.common.alias
}
func (s *Slice) Copy() Elem {
z := *s
z.Els = s.Els.Copy()
return &z
}
func (s *Slice) Complexity() int {
return 1 + s.Els.Complexity()
}
// ZeroExpr returns the zero/empty expression or empty string if not supported. Always "nil" for this case.
func (s *Slice) ZeroExpr() string { return "nil" }
// IfZeroExpr returns the expression to compare to zero/empty.
func (s *Slice) IfZeroExpr() string { return s.Varname() + " == nil" }
type Ptr struct {
common
Value Elem
}
func (s *Ptr) SetVarname(a string) {
s.common.SetVarname(a)
// struct fields are dereferenced
// automatically...
switch x := s.Value.(type) {
case *Struct:
// struct fields are automatically dereferenced
x.SetVarname(a)
return
case *BaseElem:
// identities have pointer receivers
if x.Value == IDENT {
x.SetVarname(a)
} else {
x.SetVarname("*" + a)
}
return
default:
s.Value.SetVarname("*" + a)
return
}
}
func (s *Ptr) TypeName() string {
if s.common.alias != "" {
return s.common.alias
}
s.common.Alias("*" + s.Value.TypeName())
return s.common.alias
}
func (s *Ptr) Copy() Elem {
v := *s
v.Value = s.Value.Copy()
return &v
}
func (s *Ptr) Complexity() int { return 1 + s.Value.Complexity() }
func (s *Ptr) Needsinit() bool {
if be, ok := s.Value.(*BaseElem); ok && be.needsref {
return false
}
return true
}
// ZeroExpr returns the zero/empty expression or empty string if not supported. Always "nil" for this case.
func (s *Ptr) ZeroExpr() string { return "nil" }
// IfZeroExpr returns the expression to compare to zero/empty.
func (s *Ptr) IfZeroExpr() string { return s.Varname() + " == nil" }
type Struct struct {
common
Fields []StructField // field list
AsTuple bool // write as an array instead of a map
}
func (s *Struct) TypeName() string {
if s.common.alias != "" {
return s.common.alias
}
str := "struct{\n"
for i := range s.Fields {
str += s.Fields[i].FieldName +
" " + s.Fields[i].FieldElem.TypeName() +
" " + s.Fields[i].RawTag + ";\n"
}
str += "}"
s.common.Alias(str)
return s.common.alias
}
func (s *Struct) SetVarname(a string) {
s.common.SetVarname(a)
writeStructFields(s.Fields, a)
}
func (s *Struct) Copy() Elem {
g := *s
g.Fields = make([]StructField, len(s.Fields))
copy(g.Fields, s.Fields)
for i := range s.Fields {
g.Fields[i].FieldElem = s.Fields[i].FieldElem.Copy()
}
return &g
}
func (s *Struct) Complexity() int {
c := 1
for i := range s.Fields {
c += s.Fields[i].FieldElem.Complexity()
}
return c
}
// ZeroExpr returns the zero/empty expression or empty string if not supported.
func (s *Struct) ZeroExpr() string {
if s.alias == "" {
return "" // structs with no names not supported (for now)
}
return "(" + s.TypeName() + "{})"
}
// IfZeroExpr returns the expression to compare to zero/empty.
func (s *Struct) IfZeroExpr() string {
if s.alias == "" {
return "" // structs with no names not supported (for now)
}
return s.Varname() + " == " + s.ZeroExpr()
}
// AnyHasTagPart returns true if HasTagPart(p) is true for any field.
func (s *Struct) AnyHasTagPart(pname string) bool {
for _, sf := range s.Fields {
if sf.HasTagPart(pname) {
return true
}
}
return false
}
type StructField struct {
FieldTag string // the string inside the `msg:""` tag up to the first comma
FieldTagParts []string // the string inside the `msg:""` tag split by commas
RawTag string // the full struct tag
FieldName string // the name of the struct field
FieldElem Elem // the field type
}
// HasTagPart returns true if the specified tag part (option) is present.
func (sf *StructField) HasTagPart(pname string) bool {
if len(sf.FieldTagParts) < 2 {
return false
}
for _, p := range sf.FieldTagParts[1:] {
if p == pname {
return true
}
}
return false
}
type ShimMode int
const (
Cast ShimMode = iota
Convert
)
// BaseElem is an element that
// can be represented by a primitive
// MessagePack type.
type BaseElem struct {
common
ShimMode ShimMode // Method used to shim
ShimToBase string // shim to base type, or empty
ShimFromBase string // shim from base type, or empty
Value Primitive // Type of element
Convert bool // should we do an explicit conversion?
mustinline bool // must inline; not printable
needsref bool // needs reference for shim
}
func (s *BaseElem) Printable() bool { return !s.mustinline }
func (s *BaseElem) Alias(typ string) {
s.common.Alias(typ)
if s.Value != IDENT {
s.Convert = true
}
if strings.Contains(typ, ".") {
s.mustinline = true
}
}
func (s *BaseElem) SetVarname(a string) {
// extensions whose parents
// are not pointers need to
// be explicitly referenced
if s.Value == Ext || s.needsref {
if strings.HasPrefix(a, "*") {
s.common.SetVarname(a[1:])
return
}
s.common.SetVarname("&" + a)
return
}
s.common.SetVarname(a)
}
// TypeName returns the syntactically correct Go
// type name for the base element.
func (s *BaseElem) TypeName() string {
if s.common.alias != "" {
return s.common.alias
}
s.common.Alias(s.BaseType())
return s.common.alias
}
// ToBase, used if Convert==true, is used as tmp = {{ToBase}}({{Varname}})
func (s *BaseElem) ToBase() string {
if s.ShimToBase != "" {
return s.ShimToBase
}
return s.BaseType()
}
// FromBase, used if Convert==true, is used as {{Varname}} = {{FromBase}}(tmp)
func (s *BaseElem) FromBase() string {
if s.ShimFromBase != "" {
return s.ShimFromBase
}
return s.TypeName()
}
// BaseName returns the string form of the
// base type (e.g. Float64, Ident, etc)
func (s *BaseElem) BaseName() string {
// time is a special case;
// we strip the package prefix
if s.Value == Time {
return "Time"
}
return s.Value.String()
}
func (s *BaseElem) BaseType() string {
switch s.Value {
case IDENT:
return s.TypeName()
// exceptions to the naming/capitalization
// rule:
case Intf:
return "interface{}"
case Bytes:
return "[]byte"
case Time:
return "time.Time"
case Ext:
return "msgp.Extension"
// everything else is base.String() with
// the first letter as lowercase
default:
return strings.ToLower(s.BaseName())
}
}
func (s *BaseElem) Needsref(b bool) {
s.needsref = b
}
func (s *BaseElem) Copy() Elem {
g := *s
return &g
}
func (s *BaseElem) Complexity() int {
if s.Convert && !s.mustinline {
return 2
}
// we need to return 1 if !printable(),
// in order to make sure that stuff gets
// inlined appropriately
return 1
}
// Resolved returns whether or not
// the type of the element is
// a primitive or a builtin provided
// by the package.
func (s *BaseElem) Resolved() bool {
if s.Value == IDENT {
_, ok := builtins[s.TypeName()]
return ok
}
return true
}
// ZeroExpr returns the zero/empty expression or empty string if not supported.
func (s *BaseElem) ZeroExpr() string {
switch s.Value {
case Bytes:
return "nil"
case String:
return "\"\""
case Complex64, Complex128:
return "complex(0,0)"
case Float32,
Float64,
Uint,
Uint8,
Uint16,
Uint32,
Uint64,
Byte,
Int,
Int8,
Int16,
Int32,
Int64:
return "0"
case Bool:
return "false"
case Time:
return "(time.Time{})"
}
return ""
}
// IfZeroExpr returns the expression to compare to zero/empty.
func (s *BaseElem) IfZeroExpr() string {
z := s.ZeroExpr()
if z == "" {
return ""
}
return s.Varname() + " == " + z
}
func (k Primitive) String() string {
switch k {
case String:
return "String"
case Bytes:
return "Bytes"
case Float32:
return "Float32"
case Float64:
return "Float64"
case Complex64:
return "Complex64"
case Complex128:
return "Complex128"
case Uint:
return "Uint"
case Uint8:
return "Uint8"
case Uint16:
return "Uint16"
case Uint32:
return "Uint32"
case Uint64:
return "Uint64"
case Byte:
return "Byte"
case Int:
return "Int"
case Int8:
return "Int8"
case Int16:
return "Int16"
case Int32:
return "Int32"
case Int64:
return "Int64"
case Bool:
return "Bool"
case Intf:
return "Intf"
case Time:
return "time.Time"
case Ext:
return "Extension"
case IDENT:
return "Ident"
default:
return "INVALID"
}
}
// writeStructFields is a trampoline for writeBase for
// all of the fields in a struct
func writeStructFields(s []StructField, name string) {
for i := range s {
s[i].FieldElem.SetVarname(fmt.Sprintf("%s.%s", name, s[i].FieldName))
}
}
// coerceArraySize ensures we can compare constant array lengths.
//
// msgpack array headers are 32 bit unsigned, which is reflected in the
// ArrayHeader implementation in this library using uint32. On the Go side, we
// can declare array lengths as any constant integer width, which breaks when
// attempting a direct comparison to an array header's uint32.
//
func coerceArraySize(asz string) string {
return fmt.Sprintf("uint32(%s)", asz)
}

270
vendor/github.com/tinylib/msgp/gen/encode.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,270 @@
package gen
import (
"fmt"
"io"
"strings"
"github.com/tinylib/msgp/msgp"
)
func encode(w io.Writer) *encodeGen {
return &encodeGen{
p: printer{w: w},
}
}
type encodeGen struct {
passes
p printer
fuse []byte
ctx *Context
}
func (e *encodeGen) Method() Method { return Encode }
func (e *encodeGen) Apply(dirs []string) error {
return nil
}
func (e *encodeGen) writeAndCheck(typ string, argfmt string, arg interface{}) {
e.p.printf("\nerr = en.Write%s(%s)", typ, fmt.Sprintf(argfmt, arg))
e.p.wrapErrCheck(e.ctx.ArgsStr())
}
func (e *encodeGen) fuseHook() {
if len(e.fuse) > 0 {
e.appendraw(e.fuse)
e.fuse = e.fuse[:0]
}
}
func (e *encodeGen) Fuse(b []byte) {
if len(e.fuse) > 0 {
e.fuse = append(e.fuse, b...)
} else {
e.fuse = b
}
}
func (e *encodeGen) Execute(p Elem) error {
if !e.p.ok() {
return e.p.err
}
p = e.applyall(p)
if p == nil {
return nil
}
if !IsPrintable(p) {
return nil
}
e.ctx = &Context{}
e.p.comment("EncodeMsg implements msgp.Encodable")
e.p.printf("\nfunc (%s %s) EncodeMsg(en *msgp.Writer) (err error) {", p.Varname(), imutMethodReceiver(p))
next(e, p)
e.p.nakedReturn()
return e.p.err
}
func (e *encodeGen) gStruct(s *Struct) {
if !e.p.ok() {
return
}
if s.AsTuple {
e.tuple(s)
} else {
e.structmap(s)
}
return
}
func (e *encodeGen) tuple(s *Struct) {
nfields := len(s.Fields)
data := msgp.AppendArrayHeader(nil, uint32(nfields))
e.p.printf("\n// array header, size %d", nfields)
e.Fuse(data)
if len(s.Fields) == 0 {
e.fuseHook()
}
for i := range s.Fields {
if !e.p.ok() {
return
}
e.ctx.PushString(s.Fields[i].FieldName)
next(e, s.Fields[i].FieldElem)
e.ctx.Pop()
}
}
func (e *encodeGen) appendraw(bts []byte) {
e.p.print("\nerr = en.Append(")
for i, b := range bts {
if i != 0 {
e.p.print(", ")
}
e.p.printf("0x%x", b)
}
e.p.print(")\nif err != nil { return }")
}
func (e *encodeGen) structmap(s *Struct) {
oeIdentPrefix := randIdent()
var data []byte
nfields := len(s.Fields)
bm := bmask{
bitlen: nfields,
varname: oeIdentPrefix + "Mask",
}
omitempty := s.AnyHasTagPart("omitempty")
var fieldNVar string
if omitempty {
fieldNVar = oeIdentPrefix + "Len"
e.p.printf("\n// omitempty: check for empty values")
e.p.printf("\n%s := uint32(%d)", fieldNVar, nfields)
e.p.printf("\n%s", bm.typeDecl())
for i, sf := range s.Fields {
if !e.p.ok() {
return
}
if ize := sf.FieldElem.IfZeroExpr(); ize != "" && sf.HasTagPart("omitempty") {
e.p.printf("\nif %s {", ize)
e.p.printf("\n%s--", fieldNVar)
e.p.printf("\n%s", bm.setStmt(i))
e.p.printf("\n}")
}
}
e.p.printf("\n// variable map header, size %s", fieldNVar)
e.p.varWriteMapHeader("en", fieldNVar, nfields)
e.p.print("\nif err != nil { return }")
if !e.p.ok() {
return
}
// quick return for the case where the entire thing is empty, but only at the top level
if !strings.Contains(s.Varname(), ".") {
e.p.printf("\nif %s == 0 { return }", fieldNVar)
}
} else {
// non-omitempty version
data = msgp.AppendMapHeader(nil, uint32(nfields))
e.p.printf("\n// map header, size %d", nfields)
e.Fuse(data)
if len(s.Fields) == 0 {
e.fuseHook()
}
}
for i := range s.Fields {
if !e.p.ok() {
return
}
// if field is omitempty, wrap with if statement based on the emptymask
oeField := s.Fields[i].HasTagPart("omitempty") && s.Fields[i].FieldElem.IfZeroExpr() != ""
if oeField {
e.p.printf("\nif %s == 0 { // if not empty", bm.readExpr(i))
}
data = msgp.AppendString(nil, s.Fields[i].FieldTag)
e.p.printf("\n// write %q", s.Fields[i].FieldTag)
e.Fuse(data)
e.fuseHook()
e.ctx.PushString(s.Fields[i].FieldName)
next(e, s.Fields[i].FieldElem)
e.ctx.Pop()
if oeField {
e.p.print("\n}") // close if statement
}
}
}
func (e *encodeGen) gMap(m *Map) {
if !e.p.ok() {
return
}
e.fuseHook()
vname := m.Varname()
e.writeAndCheck(mapHeader, lenAsUint32, vname)
e.p.printf("\nfor %s, %s := range %s {", m.Keyidx, m.Validx, vname)
e.writeAndCheck(stringTyp, literalFmt, m.Keyidx)
e.ctx.PushVar(m.Keyidx)
next(e, m.Value)
e.ctx.Pop()
e.p.closeblock()
}
func (e *encodeGen) gPtr(s *Ptr) {
if !e.p.ok() {
return
}
e.fuseHook()
e.p.printf("\nif %s == nil { err = en.WriteNil(); if err != nil { return; } } else {", s.Varname())
next(e, s.Value)
e.p.closeblock()
}
func (e *encodeGen) gSlice(s *Slice) {
if !e.p.ok() {
return
}
e.fuseHook()
e.writeAndCheck(arrayHeader, lenAsUint32, s.Varname())
e.p.rangeBlock(e.ctx, s.Index, s.Varname(), e, s.Els)
}
func (e *encodeGen) gArray(a *Array) {
if !e.p.ok() {
return
}
e.fuseHook()
// shortcut for [const]byte
if be, ok := a.Els.(*BaseElem); ok && (be.Value == Byte || be.Value == Uint8) {
e.p.printf("\nerr = en.WriteBytes((%s)[:])", a.Varname())
e.p.wrapErrCheck(e.ctx.ArgsStr())
return
}
e.writeAndCheck(arrayHeader, literalFmt, coerceArraySize(a.Size))
e.p.rangeBlock(e.ctx, a.Index, a.Varname(), e, a.Els)
}
func (e *encodeGen) gBase(b *BaseElem) {
if !e.p.ok() {
return
}
e.fuseHook()
vname := b.Varname()
if b.Convert {
if b.ShimMode == Cast {
vname = tobaseConvert(b)
} else {
vname = randIdent()
e.p.printf("\nvar %s %s", vname, b.BaseType())
e.p.printf("\n%s, err = %s", vname, tobaseConvert(b))
e.p.wrapErrCheck(e.ctx.ArgsStr())
}
}
if b.Value == IDENT { // unknown identity
e.p.printf("\nerr = %s.EncodeMsg(en)", vname)
e.p.wrapErrCheck(e.ctx.ArgsStr())
} else { // typical case
e.writeAndCheck(b.BaseName(), literalFmt, vname)
}
}

283
vendor/github.com/tinylib/msgp/gen/marshal.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,283 @@
package gen
import (
"fmt"
"io"
"strings"
"github.com/tinylib/msgp/msgp"
)
func marshal(w io.Writer) *marshalGen {
return &marshalGen{
p: printer{w: w},
}
}
type marshalGen struct {
passes
p printer
fuse []byte
ctx *Context
}
func (m *marshalGen) Method() Method { return Marshal }
func (m *marshalGen) Apply(dirs []string) error {
return nil
}
func (m *marshalGen) Execute(p Elem) error {
if !m.p.ok() {
return m.p.err
}
p = m.applyall(p)
if p == nil {
return nil
}
if !IsPrintable(p) {
return nil
}
m.ctx = &Context{}
m.p.comment("MarshalMsg implements msgp.Marshaler")
// save the vname before
// calling methodReceiver so
// that z.Msgsize() is printed correctly
c := p.Varname()
m.p.printf("\nfunc (%s %s) MarshalMsg(b []byte) (o []byte, err error) {", p.Varname(), imutMethodReceiver(p))
m.p.printf("\no = msgp.Require(b, %s.Msgsize())", c)
next(m, p)
m.p.nakedReturn()
return m.p.err
}
func (m *marshalGen) rawAppend(typ string, argfmt string, arg interface{}) {
m.p.printf("\no = msgp.Append%s(o, %s)", typ, fmt.Sprintf(argfmt, arg))
}
func (m *marshalGen) fuseHook() {
if len(m.fuse) > 0 {
m.rawbytes(m.fuse)
m.fuse = m.fuse[:0]
}
}
func (m *marshalGen) Fuse(b []byte) {
if len(m.fuse) == 0 {
m.fuse = b
} else {
m.fuse = append(m.fuse, b...)
}
}
func (m *marshalGen) gStruct(s *Struct) {
if !m.p.ok() {
return
}
if s.AsTuple {
m.tuple(s)
} else {
m.mapstruct(s)
}
return
}
func (m *marshalGen) tuple(s *Struct) {
data := make([]byte, 0, 5)
data = msgp.AppendArrayHeader(data, uint32(len(s.Fields)))
m.p.printf("\n// array header, size %d", len(s.Fields))
m.Fuse(data)
if len(s.Fields) == 0 {
m.fuseHook()
}
for i := range s.Fields {
if !m.p.ok() {
return
}
m.ctx.PushString(s.Fields[i].FieldName)
next(m, s.Fields[i].FieldElem)
m.ctx.Pop()
}
}
func (m *marshalGen) mapstruct(s *Struct) {
oeIdentPrefix := randIdent()
var data []byte
nfields := len(s.Fields)
bm := bmask{
bitlen: nfields,
varname: oeIdentPrefix + "Mask",
}
omitempty := s.AnyHasTagPart("omitempty")
var fieldNVar string
if omitempty {
fieldNVar = oeIdentPrefix + "Len"
m.p.printf("\n// omitempty: check for empty values")
m.p.printf("\n%s := uint32(%d)", fieldNVar, nfields)
m.p.printf("\n%s", bm.typeDecl())
for i, sf := range s.Fields {
if !m.p.ok() {
return
}
if ize := sf.FieldElem.IfZeroExpr(); ize != "" && sf.HasTagPart("omitempty") {
m.p.printf("\nif %s {", ize)
m.p.printf("\n%s--", fieldNVar)
m.p.printf("\n%s", bm.setStmt(i))
m.p.printf("\n}")
}
}
m.p.printf("\n// variable map header, size %s", fieldNVar)
m.p.varAppendMapHeader("o", fieldNVar, nfields)
if !m.p.ok() {
return
}
// quick return for the case where the entire thing is empty, but only at the top level
if !strings.Contains(s.Varname(), ".") {
m.p.printf("\nif %s == 0 { return }", fieldNVar)
}
} else {
// non-omitempty version
data = make([]byte, 0, 64)
data = msgp.AppendMapHeader(data, uint32(len(s.Fields)))
m.p.printf("\n// map header, size %d", len(s.Fields))
m.Fuse(data)
if len(s.Fields) == 0 {
m.fuseHook()
}
}
for i := range s.Fields {
if !m.p.ok() {
return
}
// if field is omitempty, wrap with if statement based on the emptymask
oeField := s.Fields[i].HasTagPart("omitempty") && s.Fields[i].FieldElem.IfZeroExpr() != ""
if oeField {
m.p.printf("\nif %s == 0 { // if not empty", bm.readExpr(i))
}
data = msgp.AppendString(nil, s.Fields[i].FieldTag)
m.p.printf("\n// string %q", s.Fields[i].FieldTag)
m.Fuse(data)
m.fuseHook()
m.ctx.PushString(s.Fields[i].FieldName)
next(m, s.Fields[i].FieldElem)
m.ctx.Pop()
if oeField {
m.p.printf("\n}") // close if statement
}
}
}
// append raw data
func (m *marshalGen) rawbytes(bts []byte) {
m.p.print("\no = append(o, ")
for _, b := range bts {
m.p.printf("0x%x,", b)
}
m.p.print(")")
}
func (m *marshalGen) gMap(s *Map) {
if !m.p.ok() {
return
}
m.fuseHook()
vname := s.Varname()
m.rawAppend(mapHeader, lenAsUint32, vname)
m.p.printf("\nfor %s, %s := range %s {", s.Keyidx, s.Validx, vname)
m.rawAppend(stringTyp, literalFmt, s.Keyidx)
m.ctx.PushVar(s.Keyidx)
next(m, s.Value)
m.ctx.Pop()
m.p.closeblock()
}
func (m *marshalGen) gSlice(s *Slice) {
if !m.p.ok() {
return
}
m.fuseHook()
vname := s.Varname()
m.rawAppend(arrayHeader, lenAsUint32, vname)
m.p.rangeBlock(m.ctx, s.Index, vname, m, s.Els)
}
func (m *marshalGen) gArray(a *Array) {
if !m.p.ok() {
return
}
m.fuseHook()
if be, ok := a.Els.(*BaseElem); ok && be.Value == Byte {
m.rawAppend("Bytes", "(%s)[:]", a.Varname())
return
}
m.rawAppend(arrayHeader, literalFmt, coerceArraySize(a.Size))
m.p.rangeBlock(m.ctx, a.Index, a.Varname(), m, a.Els)
}
func (m *marshalGen) gPtr(p *Ptr) {
if !m.p.ok() {
return
}
m.fuseHook()
m.p.printf("\nif %s == nil {\no = msgp.AppendNil(o)\n} else {", p.Varname())
next(m, p.Value)
m.p.closeblock()
}
func (m *marshalGen) gBase(b *BaseElem) {
if !m.p.ok() {
return
}
m.fuseHook()
vname := b.Varname()
if b.Convert {
if b.ShimMode == Cast {
vname = tobaseConvert(b)
} else {
vname = randIdent()
m.p.printf("\nvar %s %s", vname, b.BaseType())
m.p.printf("\n%s, err = %s", vname, tobaseConvert(b))
m.p.wrapErrCheck(m.ctx.ArgsStr())
}
}
var echeck bool
switch b.Value {
case IDENT:
echeck = true
m.p.printf("\no, err = %s.MarshalMsg(o)", vname)
case Intf, Ext:
echeck = true
m.p.printf("\no, err = msgp.Append%s(o, %s)", b.BaseName(), vname)
default:
m.rawAppend(b.BaseName(), literalFmt, vname)
}
if echeck {
m.p.wrapErrCheck(m.ctx.ArgsStr())
}
}

292
vendor/github.com/tinylib/msgp/gen/size.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,292 @@
package gen
import (
"fmt"
"io"
"strconv"
"github.com/tinylib/msgp/msgp"
)
type sizeState uint8
const (
// need to write "s = ..."
assign sizeState = iota
// need to write "s += ..."
add
// can just append "+ ..."
expr
)
func sizes(w io.Writer) *sizeGen {
return &sizeGen{
p: printer{w: w},
state: assign,
}
}
type sizeGen struct {
passes
p printer
state sizeState
ctx *Context
}
func (s *sizeGen) Method() Method { return Size }
func (s *sizeGen) Apply(dirs []string) error {
return nil
}
func builtinSize(typ string) string {
return "msgp." + typ + "Size"
}
// this lets us chain together addition
// operations where possible
func (s *sizeGen) addConstant(sz string) {
if !s.p.ok() {
return
}
switch s.state {
case assign:
s.p.print("\ns = " + sz)
s.state = expr
return
case add:
s.p.print("\ns += " + sz)
s.state = expr
return
case expr:
s.p.print(" + " + sz)
return
}
panic("unknown size state")
}
func (s *sizeGen) Execute(p Elem) error {
if !s.p.ok() {
return s.p.err
}
p = s.applyall(p)
if p == nil {
return nil
}
if !IsPrintable(p) {
return nil
}
s.ctx = &Context{}
s.ctx.PushString(p.TypeName())
s.p.comment("Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message")
s.p.printf("\nfunc (%s %s) Msgsize() (s int) {", p.Varname(), imutMethodReceiver(p))
s.state = assign
next(s, p)
s.p.nakedReturn()
return s.p.err
}
func (s *sizeGen) gStruct(st *Struct) {
if !s.p.ok() {
return
}
nfields := uint32(len(st.Fields))
if st.AsTuple {
data := msgp.AppendArrayHeader(nil, nfields)
s.addConstant(strconv.Itoa(len(data)))
for i := range st.Fields {
if !s.p.ok() {
return
}
next(s, st.Fields[i].FieldElem)
}
} else {
data := msgp.AppendMapHeader(nil, nfields)
s.addConstant(strconv.Itoa(len(data)))
for i := range st.Fields {
data = data[:0]
data = msgp.AppendString(data, st.Fields[i].FieldTag)
s.addConstant(strconv.Itoa(len(data)))
next(s, st.Fields[i].FieldElem)
}
}
}
func (s *sizeGen) gPtr(p *Ptr) {
s.state = add // inner must use add
s.p.printf("\nif %s == nil {\ns += msgp.NilSize\n} else {", p.Varname())
next(s, p.Value)
s.state = add // closing block; reset to add
s.p.closeblock()
}
func (s *sizeGen) gSlice(sl *Slice) {
if !s.p.ok() {
return
}
s.addConstant(builtinSize(arrayHeader))
// if the slice's element is a fixed size
// (e.g. float64, [32]int, etc.), then
// print the length times the element size directly
if str, ok := fixedsizeExpr(sl.Els); ok {
s.addConstant(fmt.Sprintf("(%s * (%s))", lenExpr(sl), str))
return
}
// add inside the range block, and immediately after
s.state = add
s.p.rangeBlock(s.ctx, sl.Index, sl.Varname(), s, sl.Els)
s.state = add
}
func (s *sizeGen) gArray(a *Array) {
if !s.p.ok() {
return
}
s.addConstant(builtinSize(arrayHeader))
// if the array's children are a fixed
// size, we can compile an expression
// that always represents the array's wire size
if str, ok := fixedsizeExpr(a); ok {
s.addConstant(str)
return
}
s.state = add
s.p.rangeBlock(s.ctx, a.Index, a.Varname(), s, a.Els)
s.state = add
}
func (s *sizeGen) gMap(m *Map) {
s.addConstant(builtinSize(mapHeader))
vn := m.Varname()
s.p.printf("\nif %s != nil {", vn)
s.p.printf("\nfor %s, %s := range %s {", m.Keyidx, m.Validx, vn)
s.p.printf("\n_ = %s", m.Validx) // we may not use the value
s.p.printf("\ns += msgp.StringPrefixSize + len(%s)", m.Keyidx)
s.state = expr
s.ctx.PushVar(m.Keyidx)
next(s, m.Value)
s.ctx.Pop()
s.p.closeblock()
s.p.closeblock()
s.state = add
}
func (s *sizeGen) gBase(b *BaseElem) {
if !s.p.ok() {
return
}
if b.Convert && b.ShimMode == Convert {
s.state = add
vname := randIdent()
s.p.printf("\nvar %s %s", vname, b.BaseType())
// ensure we don't get "unused variable" warnings from outer slice iterations
s.p.printf("\n_ = %s", b.Varname())
s.p.printf("\ns += %s", basesizeExpr(b.Value, vname, b.BaseName()))
s.state = expr
} else {
vname := b.Varname()
if b.Convert {
vname = tobaseConvert(b)
}
s.addConstant(basesizeExpr(b.Value, vname, b.BaseName()))
}
}
// returns "len(slice)"
func lenExpr(sl *Slice) string {
return "len(" + sl.Varname() + ")"
}
// is a given primitive always the same (max)
// size on the wire?
func fixedSize(p Primitive) bool {
switch p {
case Intf, Ext, IDENT, Bytes, String:
return false
default:
return true
}
}
// strip reference from string
func stripRef(s string) string {
if s[0] == '&' {
return s[1:]
}
return s
}
// return a fixed-size expression, if possible.
// only possible for *BaseElem and *Array.
// returns (expr, ok)
func fixedsizeExpr(e Elem) (string, bool) {
switch e := e.(type) {
case *Array:
if str, ok := fixedsizeExpr(e.Els); ok {
return fmt.Sprintf("(%s * (%s))", e.Size, str), true
}
case *BaseElem:
if fixedSize(e.Value) {
return builtinSize(e.BaseName()), true
}
case *Struct:
var str string
for _, f := range e.Fields {
if fs, ok := fixedsizeExpr(f.FieldElem); ok {
if str == "" {
str = fs
} else {
str += "+" + fs
}
} else {
return "", false
}
}
var hdrlen int
mhdr := msgp.AppendMapHeader(nil, uint32(len(e.Fields)))
hdrlen += len(mhdr)
var strbody []byte
for _, f := range e.Fields {
strbody = msgp.AppendString(strbody[:0], f.FieldTag)
hdrlen += len(strbody)
}
return fmt.Sprintf("%d + %s", hdrlen, str), true
}
return "", false
}
// print size expression of a variable name
func basesizeExpr(value Primitive, vname, basename string) string {
switch value {
case Ext:
return "msgp.ExtensionPrefixSize + " + stripRef(vname) + ".Len()"
case Intf:
return "msgp.GuessSize(" + vname + ")"
case IDENT:
return vname + ".Msgsize()"
case Bytes:
return "msgp.BytesPrefixSize + len(" + vname + ")"
case String:
return "msgp.StringPrefixSize + len(" + vname + ")"
default:
return builtinSize(basename)
}
}

519
vendor/github.com/tinylib/msgp/gen/spec.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,519 @@
package gen
import (
"bytes"
"fmt"
"io"
)
const (
lenAsUint32 = "uint32(len(%s))"
literalFmt = "%s"
intFmt = "%d"
quotedFmt = `"%s"`
mapHeader = "MapHeader"
arrayHeader = "ArrayHeader"
mapKey = "MapKeyPtr"
stringTyp = "String"
u32 = "uint32"
)
// Method is a bitfield representing something that the
// generator knows how to print.
type Method uint8
// are the bits in 'f' set in 'm'?
func (m Method) isset(f Method) bool { return (m&f == f) }
// String implements fmt.Stringer
func (m Method) String() string {
switch m {
case 0, invalidmeth:
return "<invalid method>"
case Decode:
return "decode"
case Encode:
return "encode"
case Marshal:
return "marshal"
case Unmarshal:
return "unmarshal"
case Size:
return "size"
case Test:
return "test"
default:
// return e.g. "decode+encode+test"
modes := [...]Method{Decode, Encode, Marshal, Unmarshal, Size, Test}
any := false
nm := ""
for _, mm := range modes {
if m.isset(mm) {
if any {
nm += "+" + mm.String()
} else {
nm += mm.String()
any = true
}
}
}
return nm
}
}
func strtoMeth(s string) Method {
switch s {
case "encode":
return Encode
case "decode":
return Decode
case "marshal":
return Marshal
case "unmarshal":
return Unmarshal
case "size":
return Size
case "test":
return Test
default:
return 0
}
}
const (
Decode Method = 1 << iota // msgp.Decodable
Encode // msgp.Encodable
Marshal // msgp.Marshaler
Unmarshal // msgp.Unmarshaler
Size // msgp.Sizer
Test // generate tests
invalidmeth // this isn't a method
encodetest = Encode | Decode | Test // tests for Encodable and Decodable
marshaltest = Marshal | Unmarshal | Test // tests for Marshaler and Unmarshaler
)
type Printer struct {
gens []generator
}
func NewPrinter(m Method, out io.Writer, tests io.Writer) *Printer {
if m.isset(Test) && tests == nil {
panic("cannot print tests with 'nil' tests argument!")
}
gens := make([]generator, 0, 7)
if m.isset(Decode) {
gens = append(gens, decode(out))
}
if m.isset(Encode) {
gens = append(gens, encode(out))
}
if m.isset(Marshal) {
gens = append(gens, marshal(out))
}
if m.isset(Unmarshal) {
gens = append(gens, unmarshal(out))
}
if m.isset(Size) {
gens = append(gens, sizes(out))
}
if m.isset(marshaltest) {
gens = append(gens, mtest(tests))
}
if m.isset(encodetest) {
gens = append(gens, etest(tests))
}
if len(gens) == 0 {
panic("NewPrinter called with invalid method flags")
}
return &Printer{gens: gens}
}
// TransformPass is a pass that transforms individual
// elements. (Note that if the returned is different from
// the argument, it should not point to the same objects.)
type TransformPass func(Elem) Elem
// IgnoreTypename is a pass that just ignores
// types of a given name.
func IgnoreTypename(name string) TransformPass {
return func(e Elem) Elem {
if e.TypeName() == name {
return nil
}
return e
}
}
// ApplyDirective applies a directive to a named pass
// and all of its dependents.
func (p *Printer) ApplyDirective(pass Method, t TransformPass) {
for _, g := range p.gens {
if g.Method().isset(pass) {
g.Add(t)
}
}
}
// Print prints an Elem.
func (p *Printer) Print(e Elem) error {
for _, g := range p.gens {
// Elem.SetVarname() is called before the Print() step in parse.FileSet.PrintTo().
// Elem.SetVarname() generates identifiers as it walks the Elem. This can cause
// collisions between idents created during SetVarname and idents created during Print,
// hence the separate prefixes.
resetIdent("zb")
err := g.Execute(e)
resetIdent("za")
if err != nil {
return err
}
}
return nil
}
type contextItem interface {
Arg() string
}
type contextString string
func (c contextString) Arg() string {
return fmt.Sprintf("%q", c)
}
type contextVar string
func (c contextVar) Arg() string {
return string(c)
}
type Context struct {
path []contextItem
}
func (c *Context) PushString(s string) {
c.path = append(c.path, contextString(s))
}
func (c *Context) PushVar(s string) {
c.path = append(c.path, contextVar(s))
}
func (c *Context) Pop() {
c.path = c.path[:len(c.path)-1]
}
func (c *Context) ArgsStr() string {
var out string
for idx, p := range c.path {
if idx > 0 {
out += ", "
}
out += p.Arg()
}
return out
}
// generator is the interface through
// which code is generated.
type generator interface {
Method() Method
Add(p TransformPass)
Execute(Elem) error // execute writes the method for the provided object.
}
type passes []TransformPass
func (p *passes) Add(t TransformPass) {
*p = append(*p, t)
}
func (p *passes) applyall(e Elem) Elem {
for _, t := range *p {
e = t(e)
if e == nil {
return nil
}
}
return e
}
type traversal interface {
gMap(*Map)
gSlice(*Slice)
gArray(*Array)
gPtr(*Ptr)
gBase(*BaseElem)
gStruct(*Struct)
}
// type-switch dispatch to the correct
// method given the type of 'e'
func next(t traversal, e Elem) {
switch e := e.(type) {
case *Map:
t.gMap(e)
case *Struct:
t.gStruct(e)
case *Slice:
t.gSlice(e)
case *Array:
t.gArray(e)
case *Ptr:
t.gPtr(e)
case *BaseElem:
t.gBase(e)
default:
panic("bad element type")
}
}
// possibly-immutable method receiver
func imutMethodReceiver(p Elem) string {
switch e := p.(type) {
case *Struct:
// TODO(HACK): actually do real math here.
if len(e.Fields) <= 3 {
for i := range e.Fields {
if be, ok := e.Fields[i].FieldElem.(*BaseElem); !ok || (be.Value == IDENT || be.Value == Bytes) {
goto nope
}
}
return p.TypeName()
}
nope:
return "*" + p.TypeName()
// gets dereferenced automatically
case *Array:
return "*" + p.TypeName()
// everything else can be
// by-value.
default:
return p.TypeName()
}
}
// if necessary, wraps a type
// so that its method receiver
// is of the write type.
func methodReceiver(p Elem) string {
switch p.(type) {
// structs and arrays are
// dereferenced automatically,
// so no need to alter varname
case *Struct, *Array:
return "*" + p.TypeName()
// set variable name to
// *varname
default:
p.SetVarname("(*" + p.Varname() + ")")
return "*" + p.TypeName()
}
}
func unsetReceiver(p Elem) {
switch p.(type) {
case *Struct, *Array:
default:
p.SetVarname("z")
}
}
// shared utility for generators
type printer struct {
w io.Writer
err error
}
// writes "var {{name}} {{typ}};"
func (p *printer) declare(name string, typ string) {
p.printf("\nvar %s %s", name, typ)
}
// does:
//
// if m == nil {
// m = make(type, size)
// } else if len(m) > 0 {
// for key := range m { delete(m, key) }
// }
//
func (p *printer) resizeMap(size string, m *Map) {
vn := m.Varname()
if !p.ok() {
return
}
p.printf("\nif %s == nil {", vn)
p.printf("\n%s = make(%s, %s)", vn, m.TypeName(), size)
p.printf("\n} else if len(%s) > 0 {", vn)
p.clearMap(vn)
p.closeblock()
}
// assign key to value based on varnames
func (p *printer) mapAssign(m *Map) {
if !p.ok() {
return
}
p.printf("\n%s[%s] = %s", m.Varname(), m.Keyidx, m.Validx)
}
// clear map keys
func (p *printer) clearMap(name string) {
p.printf("\nfor key := range %[1]s { delete(%[1]s, key) }", name)
}
func (p *printer) wrapErrCheck(ctx string) {
p.print("\nif err != nil {")
p.printf("\nerr = msgp.WrapError(err, %s)", ctx)
p.printf("\nreturn")
p.print("\n}")
}
func (p *printer) resizeSlice(size string, s *Slice) {
p.printf("\nif cap(%[1]s) >= int(%[2]s) { %[1]s = (%[1]s)[:%[2]s] } else { %[1]s = make(%[3]s, %[2]s) }", s.Varname(), size, s.TypeName())
}
func (p *printer) arrayCheck(want string, got string) {
p.printf("\nif %[1]s != %[2]s { err = msgp.ArrayError{Wanted: %[2]s, Got: %[1]s}; return }", got, want)
}
func (p *printer) closeblock() { p.print("\n}") }
// does:
//
// for idx := range iter {
// {{generate inner}}
// }
//
func (p *printer) rangeBlock(ctx *Context, idx string, iter string, t traversal, inner Elem) {
ctx.PushVar(idx)
p.printf("\n for %s := range %s {", idx, iter)
next(t, inner)
p.closeblock()
ctx.Pop()
}
func (p *printer) nakedReturn() {
if p.ok() {
p.print("\nreturn\n}\n")
}
}
func (p *printer) comment(s string) {
p.print("\n// " + s)
}
func (p *printer) printf(format string, args ...interface{}) {
if p.err == nil {
_, p.err = fmt.Fprintf(p.w, format, args...)
}
}
func (p *printer) print(format string) {
if p.err == nil {
_, p.err = io.WriteString(p.w, format)
}
}
func (p *printer) initPtr(pt *Ptr) {
if pt.Needsinit() {
vname := pt.Varname()
p.printf("\nif %s == nil { %s = new(%s); }", vname, vname, pt.Value.TypeName())
}
}
func (p *printer) ok() bool { return p.err == nil }
func tobaseConvert(b *BaseElem) string {
return b.ToBase() + "(" + b.Varname() + ")"
}
func (p *printer) varWriteMapHeader(receiver string, sizeVarname string, maxSize int) {
if maxSize <= 15 {
p.printf("\nerr = %s.Append(0x80 | uint8(%s))", receiver, sizeVarname)
} else {
p.printf("\nerr = %s.WriteMapHeader(%s)", receiver, sizeVarname)
}
}
func (p *printer) varAppendMapHeader(sliceVarname string, sizeVarname string, maxSize int) {
if maxSize <= 15 {
p.printf("\n%s = append(%s, 0x80 | uint8(%s))", sliceVarname, sliceVarname, sizeVarname)
} else {
p.printf("\n%s = msgp.AppendMapHeader(%s, %s)", sliceVarname, sliceVarname, sizeVarname)
}
}
// bmask is a bitmask of a the specified number of bits
type bmask struct {
bitlen int
varname string
}
// typeDecl returns the variable declaration as a var statement
func (b *bmask) typeDecl() string {
return fmt.Sprintf("var %s %s /* %d bits */", b.varname, b.typeName(), b.bitlen)
}
// typeName returns the type, e.g. "uint8" or "[2]uint64"
func (b *bmask) typeName() string {
if b.bitlen <= 8 {
return "uint8"
}
if b.bitlen <= 16 {
return "uint16"
}
if b.bitlen <= 32 {
return "uint32"
}
if b.bitlen <= 64 {
return "uint64"
}
return fmt.Sprintf("[%d]uint64", (b.bitlen+64-1)/64)
}
// readExpr returns the expression to read from a position in the bitmask.
// Compare ==0 for false or !=0 for true.
func (b *bmask) readExpr(bitoffset int) string {
if bitoffset < 0 || bitoffset >= b.bitlen {
panic(fmt.Errorf("bitoffset %d out of range for bitlen %d", bitoffset, b.bitlen))
}
var buf bytes.Buffer
buf.Grow(len(b.varname) + 16)
buf.WriteByte('(')
buf.WriteString(b.varname)
if b.bitlen > 64 {
fmt.Fprintf(&buf, "[%d]", (bitoffset / 64))
}
buf.WriteByte('&')
fmt.Fprintf(&buf, "0x%X", (uint64(1) << (uint64(bitoffset) % 64)))
buf.WriteByte(')')
return buf.String()
}
// setStmt returns the statement to set the specified bit in the bitmask.
func (b *bmask) setStmt(bitoffset int) string {
var buf bytes.Buffer
buf.Grow(len(b.varname) + 16)
buf.WriteString(b.varname)
if b.bitlen > 64 {
fmt.Fprintf(&buf, "[%d]", (bitoffset / 64))
}
fmt.Fprintf(&buf, " |= 0x%X", (uint64(1) << (uint64(bitoffset) % 64)))
return buf.String()
}

182
vendor/github.com/tinylib/msgp/gen/testgen.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,182 @@
package gen
import (
"io"
"text/template"
)
var (
marshalTestTempl = template.New("MarshalTest")
encodeTestTempl = template.New("EncodeTest")
)
// TODO(philhofer):
// for simplicity's sake, right now
// we can only generate tests for types
// that can be initialized with the
// "Type{}" syntax.
// we should support all the types.
func mtest(w io.Writer) *mtestGen {
return &mtestGen{w: w}
}
type mtestGen struct {
passes
w io.Writer
}
func (m *mtestGen) Execute(p Elem) error {
p = m.applyall(p)
if p != nil && IsPrintable(p) {
switch p.(type) {
case *Struct, *Array, *Slice, *Map:
return marshalTestTempl.Execute(m.w, p)
}
}
return nil
}
func (m *mtestGen) Method() Method { return marshaltest }
type etestGen struct {
passes
w io.Writer
}
func etest(w io.Writer) *etestGen {
return &etestGen{w: w}
}
func (e *etestGen) Execute(p Elem) error {
p = e.applyall(p)
if p != nil && IsPrintable(p) {
switch p.(type) {
case *Struct, *Array, *Slice, *Map:
return encodeTestTempl.Execute(e.w, p)
}
}
return nil
}
func (e *etestGen) Method() Method { return encodetest }
func init() {
template.Must(marshalTestTempl.Parse(`func TestMarshalUnmarshal{{.TypeName}}(t *testing.T) {
v := {{.TypeName}}{}
bts, err := v.MarshalMsg(nil)
if err != nil {
t.Fatal(err)
}
left, err := v.UnmarshalMsg(bts)
if err != nil {
t.Fatal(err)
}
if len(left) > 0 {
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
}
left, err = msgp.Skip(bts)
if err != nil {
t.Fatal(err)
}
if len(left) > 0 {
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
}
}
func BenchmarkMarshalMsg{{.TypeName}}(b *testing.B) {
v := {{.TypeName}}{}
b.ReportAllocs()
b.ResetTimer()
for i:=0; i<b.N; i++ {
v.MarshalMsg(nil)
}
}
func BenchmarkAppendMsg{{.TypeName}}(b *testing.B) {
v := {{.TypeName}}{}
bts := make([]byte, 0, v.Msgsize())
bts, _ = v.MarshalMsg(bts[0:0])
b.SetBytes(int64(len(bts)))
b.ReportAllocs()
b.ResetTimer()
for i:=0; i<b.N; i++ {
bts, _ = v.MarshalMsg(bts[0:0])
}
}
func BenchmarkUnmarshal{{.TypeName}}(b *testing.B) {
v := {{.TypeName}}{}
bts, _ := v.MarshalMsg(nil)
b.ReportAllocs()
b.SetBytes(int64(len(bts)))
b.ResetTimer()
for i:=0; i<b.N; i++ {
_, err := v.UnmarshalMsg(bts)
if err != nil {
b.Fatal(err)
}
}
}
`))
template.Must(encodeTestTempl.Parse(`func TestEncodeDecode{{.TypeName}}(t *testing.T) {
v := {{.TypeName}}{}
var buf bytes.Buffer
msgp.Encode(&buf, &v)
m := v.Msgsize()
if buf.Len() > m {
t.Log("WARNING: TestEncodeDecode{{.TypeName}} Msgsize() is inaccurate")
}
vn := {{.TypeName}}{}
err := msgp.Decode(&buf, &vn)
if err != nil {
t.Error(err)
}
buf.Reset()
msgp.Encode(&buf, &v)
err = msgp.NewReader(&buf).Skip()
if err != nil {
t.Error(err)
}
}
func BenchmarkEncode{{.TypeName}}(b *testing.B) {
v := {{.TypeName}}{}
var buf bytes.Buffer
msgp.Encode(&buf, &v)
b.SetBytes(int64(buf.Len()))
en := msgp.NewWriter(msgp.Nowhere)
b.ReportAllocs()
b.ResetTimer()
for i:=0; i<b.N; i++ {
v.EncodeMsg(en)
}
en.Flush()
}
func BenchmarkDecode{{.TypeName}}(b *testing.B) {
v := {{.TypeName}}{}
var buf bytes.Buffer
msgp.Encode(&buf, &v)
b.SetBytes(int64(buf.Len()))
rd := msgp.NewEndlessReader(buf.Bytes(), b)
dc := msgp.NewReader(rd)
b.ReportAllocs()
b.ResetTimer()
for i:=0; i<b.N; i++ {
err := v.DecodeMsg(dc)
if err != nil {
b.Fatal(err)
}
}
}
`))
}

214
vendor/github.com/tinylib/msgp/gen/unmarshal.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,214 @@
package gen
import (
"io"
"strconv"
)
func unmarshal(w io.Writer) *unmarshalGen {
return &unmarshalGen{
p: printer{w: w},
}
}
type unmarshalGen struct {
passes
p printer
hasfield bool
ctx *Context
}
func (u *unmarshalGen) Method() Method { return Unmarshal }
func (u *unmarshalGen) needsField() {
if u.hasfield {
return
}
u.p.print("\nvar field []byte; _ = field")
u.hasfield = true
}
func (u *unmarshalGen) Execute(p Elem) error {
u.hasfield = false
if !u.p.ok() {
return u.p.err
}
p = u.applyall(p)
if p == nil {
return nil
}
if !IsPrintable(p) {
return nil
}
u.ctx = &Context{}
u.p.comment("UnmarshalMsg implements msgp.Unmarshaler")
u.p.printf("\nfunc (%s %s) UnmarshalMsg(bts []byte) (o []byte, err error) {", p.Varname(), methodReceiver(p))
next(u, p)
u.p.print("\no = bts")
u.p.nakedReturn()
unsetReceiver(p)
return u.p.err
}
// does assignment to the variable "name" with the type "base"
func (u *unmarshalGen) assignAndCheck(name string, base string) {
if !u.p.ok() {
return
}
u.p.printf("\n%s, bts, err = msgp.Read%sBytes(bts)", name, base)
u.p.wrapErrCheck(u.ctx.ArgsStr())
}
func (u *unmarshalGen) gStruct(s *Struct) {
if !u.p.ok() {
return
}
if s.AsTuple {
u.tuple(s)
} else {
u.mapstruct(s)
}
return
}
func (u *unmarshalGen) tuple(s *Struct) {
// open block
sz := randIdent()
u.p.declare(sz, u32)
u.assignAndCheck(sz, arrayHeader)
u.p.arrayCheck(strconv.Itoa(len(s.Fields)), sz)
for i := range s.Fields {
if !u.p.ok() {
return
}
u.ctx.PushString(s.Fields[i].FieldName)
next(u, s.Fields[i].FieldElem)
u.ctx.Pop()
}
}
func (u *unmarshalGen) mapstruct(s *Struct) {
u.needsField()
sz := randIdent()
u.p.declare(sz, u32)
u.assignAndCheck(sz, mapHeader)
u.p.printf("\nfor %s > 0 {", sz)
u.p.printf("\n%s--; field, bts, err = msgp.ReadMapKeyZC(bts)", sz)
u.p.wrapErrCheck(u.ctx.ArgsStr())
u.p.print("\nswitch msgp.UnsafeString(field) {")
for i := range s.Fields {
if !u.p.ok() {
return
}
u.p.printf("\ncase \"%s\":", s.Fields[i].FieldTag)
u.ctx.PushString(s.Fields[i].FieldName)
next(u, s.Fields[i].FieldElem)
u.ctx.Pop()
}
u.p.print("\ndefault:\nbts, err = msgp.Skip(bts)")
u.p.wrapErrCheck(u.ctx.ArgsStr())
u.p.print("\n}\n}") // close switch and for loop
}
func (u *unmarshalGen) gBase(b *BaseElem) {
if !u.p.ok() {
return
}
refname := b.Varname() // assigned to
lowered := b.Varname() // passed as argument
if b.Convert {
// begin 'tmp' block
refname = randIdent()
lowered = b.ToBase() + "(" + lowered + ")"
u.p.printf("\n{\nvar %s %s", refname, b.BaseType())
}
switch b.Value {
case Bytes:
u.p.printf("\n%s, bts, err = msgp.ReadBytesBytes(bts, %s)", refname, lowered)
case Ext:
u.p.printf("\nbts, err = msgp.ReadExtensionBytes(bts, %s)", lowered)
case IDENT:
u.p.printf("\nbts, err = %s.UnmarshalMsg(bts)", lowered)
default:
u.p.printf("\n%s, bts, err = msgp.Read%sBytes(bts)", refname, b.BaseName())
}
u.p.wrapErrCheck(u.ctx.ArgsStr())
if b.Convert {
// close 'tmp' block
if b.ShimMode == Cast {
u.p.printf("\n%s = %s(%s)\n", b.Varname(), b.FromBase(), refname)
} else {
u.p.printf("\n%s, err = %s(%s)", b.Varname(), b.FromBase(), refname)
u.p.wrapErrCheck(u.ctx.ArgsStr())
}
u.p.printf("}")
}
}
func (u *unmarshalGen) gArray(a *Array) {
if !u.p.ok() {
return
}
// special case for [const]byte objects
// see decode.go for symmetry
if be, ok := a.Els.(*BaseElem); ok && be.Value == Byte {
u.p.printf("\nbts, err = msgp.ReadExactBytes(bts, (%s)[:])", a.Varname())
u.p.wrapErrCheck(u.ctx.ArgsStr())
return
}
sz := randIdent()
u.p.declare(sz, u32)
u.assignAndCheck(sz, arrayHeader)
u.p.arrayCheck(coerceArraySize(a.Size), sz)
u.p.rangeBlock(u.ctx, a.Index, a.Varname(), u, a.Els)
}
func (u *unmarshalGen) gSlice(s *Slice) {
if !u.p.ok() {
return
}
sz := randIdent()
u.p.declare(sz, u32)
u.assignAndCheck(sz, arrayHeader)
u.p.resizeSlice(sz, s)
u.p.rangeBlock(u.ctx, s.Index, s.Varname(), u, s.Els)
}
func (u *unmarshalGen) gMap(m *Map) {
if !u.p.ok() {
return
}
sz := randIdent()
u.p.declare(sz, u32)
u.assignAndCheck(sz, mapHeader)
// allocate or clear map
u.p.resizeMap(sz, m)
// loop and get key,value
u.p.printf("\nfor %s > 0 {", sz)
u.p.printf("\nvar %s string; var %s %s; %s--", m.Keyidx, m.Validx, m.Value.TypeName(), sz)
u.assignAndCheck(m.Keyidx, stringTyp)
u.ctx.PushVar(m.Keyidx)
next(u, m.Value)
u.ctx.Pop()
u.p.mapAssign(m)
u.p.closeblock()
}
func (u *unmarshalGen) gPtr(p *Ptr) {
u.p.printf("\nif msgp.IsNil(bts) { bts, err = msgp.ReadNilBytes(bts); if err != nil { return }; %s = nil; } else { ", p.Varname())
u.p.initPtr(p)
next(u, p.Value)
u.p.closeblock()
}

119
vendor/github.com/tinylib/msgp/main.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,119 @@
// msgp is a code generation tool for
// creating methods to serialize and de-serialize
// Go data structures to and from MessagePack.
//
// This package is targeted at the `go generate` tool.
// To use it, include the following directive in a
// go source file with types requiring source generation:
//
// //go:generate msgp
//
// The go generate tool should set the proper environment variables for
// the generator to execute without any command-line flags. However, the
// following options are supported, if you need them:
//
// -o = output file name (default is {input}_gen.go)
// -file = input file name (or directory; default is $GOFILE, which is set by the `go generate` command)
// -io = satisfy the `msgp.Decodable` and `msgp.Encodable` interfaces (default is true)
// -marshal = satisfy the `msgp.Marshaler` and `msgp.Unmarshaler` interfaces (default is true)
// -tests = generate tests and benchmarks (default is true)
//
// For more information, please read README.md, and the wiki at github.com/tinylib/msgp
//
package main
import (
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/tinylib/msgp/gen"
"github.com/tinylib/msgp/parse"
"github.com/tinylib/msgp/printer"
"github.com/ttacon/chalk"
)
var (
out = flag.String("o", "", "output file")
file = flag.String("file", "", "input file")
encode = flag.Bool("io", true, "create Encode and Decode methods")
marshal = flag.Bool("marshal", true, "create Marshal and Unmarshal methods")
tests = flag.Bool("tests", true, "create tests and benchmarks")
unexported = flag.Bool("unexported", false, "also process unexported types")
)
func main() {
flag.Parse()
// GOFILE is set by go generate
if *file == "" {
*file = os.Getenv("GOFILE")
if *file == "" {
fmt.Println(chalk.Red.Color("No file to parse."))
os.Exit(1)
}
}
var mode gen.Method
if *encode {
mode |= (gen.Encode | gen.Decode | gen.Size)
}
if *marshal {
mode |= (gen.Marshal | gen.Unmarshal | gen.Size)
}
if *tests {
mode |= gen.Test
}
if mode&^gen.Test == 0 {
fmt.Println(chalk.Red.Color("No methods to generate; -io=false && -marshal=false"))
os.Exit(1)
}
if err := Run(*file, mode, *unexported); err != nil {
fmt.Println(chalk.Red.Color(err.Error()))
os.Exit(1)
}
}
// Run writes all methods using the associated file or path, e.g.
//
// err := msgp.Run("path/to/myfile.go", gen.Size|gen.Marshal|gen.Unmarshal|gen.Test, false)
//
func Run(gofile string, mode gen.Method, unexported bool) error {
if mode&^gen.Test == 0 {
return nil
}
fmt.Println(chalk.Magenta.Color("======== MessagePack Code Generator ======="))
fmt.Printf(chalk.Magenta.Color(">>> Input: \"%s\"\n"), gofile)
fs, err := parse.File(gofile, unexported)
if err != nil {
return err
}
if len(fs.Identities) == 0 {
fmt.Println(chalk.Magenta.Color("No types requiring code generation were found!"))
return nil
}
return printer.PrintFile(newFilename(gofile, fs.Package), fs, mode)
}
// picks a new file name based on input flags and input filename(s).
func newFilename(old string, pkg string) string {
if *out != "" {
if pre := strings.TrimPrefix(*out, old); len(pre) > 0 &&
!strings.HasSuffix(*out, ".go") {
return filepath.Join(old, *out)
}
return *out
}
if fi, err := os.Stat(old); err == nil && fi.IsDir() {
old = filepath.Join(old, pkg)
}
// new file name is old file name + _gen.go
return strings.TrimSuffix(old, ".go") + "_gen.go"
}

130
vendor/github.com/tinylib/msgp/parse/directives.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,130 @@
package parse
import (
"fmt"
"go/ast"
"strings"
"github.com/tinylib/msgp/gen"
)
const linePrefix = "//msgp:"
// func(args, fileset)
type directive func([]string, *FileSet) error
// func(passName, args, printer)
type passDirective func(gen.Method, []string, *gen.Printer) error
// map of all recognized directives
//
// to add a directive, define a func([]string, *FileSet) error
// and then add it to this list.
var directives = map[string]directive{
"shim": applyShim,
"ignore": ignore,
"tuple": astuple,
}
var passDirectives = map[string]passDirective{
"ignore": passignore,
}
func passignore(m gen.Method, text []string, p *gen.Printer) error {
pushstate(m.String())
for _, a := range text {
p.ApplyDirective(m, gen.IgnoreTypename(a))
infof("ignoring %s\n", a)
}
popstate()
return nil
}
// find all comment lines that begin with //msgp:
func yieldComments(c []*ast.CommentGroup) []string {
var out []string
for _, cg := range c {
for _, line := range cg.List {
if strings.HasPrefix(line.Text, linePrefix) {
out = append(out, strings.TrimPrefix(line.Text, linePrefix))
}
}
}
return out
}
//msgp:shim {Type} as:{Newtype} using:{toFunc/fromFunc} mode:{Mode}
func applyShim(text []string, f *FileSet) error {
if len(text) < 4 || len(text) > 5 {
return fmt.Errorf("shim directive should have 3 or 4 arguments; found %d", len(text)-1)
}
name := text[1]
be := gen.Ident(strings.TrimPrefix(strings.TrimSpace(text[2]), "as:")) // parse as::{base}
if name[0] == '*' {
name = name[1:]
be.Needsref(true)
}
be.Alias(name)
usestr := strings.TrimPrefix(strings.TrimSpace(text[3]), "using:") // parse using::{method/method}
methods := strings.Split(usestr, "/")
if len(methods) != 2 {
return fmt.Errorf("expected 2 using::{} methods; found %d (%q)", len(methods), text[3])
}
be.ShimToBase = methods[0]
be.ShimFromBase = methods[1]
if len(text) == 5 {
modestr := strings.TrimPrefix(strings.TrimSpace(text[4]), "mode:") // parse mode::{mode}
switch modestr {
case "cast":
be.ShimMode = gen.Cast
case "convert":
be.ShimMode = gen.Convert
default:
return fmt.Errorf("invalid shim mode; found %s, expected 'cast' or 'convert", modestr)
}
}
infof("%s -> %s\n", name, be.Value.String())
f.findShim(name, be)
return nil
}
//msgp:ignore {TypeA} {TypeB}...
func ignore(text []string, f *FileSet) error {
if len(text) < 2 {
return nil
}
for _, item := range text[1:] {
name := strings.TrimSpace(item)
if _, ok := f.Identities[name]; ok {
delete(f.Identities, name)
infof("ignoring %s\n", name)
}
}
return nil
}
//msgp:tuple {TypeA} {TypeB}...
func astuple(text []string, f *FileSet) error {
if len(text) < 2 {
return nil
}
for _, item := range text[1:] {
name := strings.TrimSpace(item)
if el, ok := f.Identities[name]; ok {
if st, ok := el.(*gen.Struct); ok {
st.AsTuple = true
infoln(name)
} else {
warnf("%s: only structs can be tuples\n", name)
}
}
}
return nil
}

617
vendor/github.com/tinylib/msgp/parse/getast.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,617 @@
package parse
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"reflect"
"sort"
"strings"
"github.com/tinylib/msgp/gen"
"github.com/ttacon/chalk"
)
// A FileSet is the in-memory representation of a
// parsed file.
type FileSet struct {
Package string // package name
Specs map[string]ast.Expr // type specs in file
Identities map[string]gen.Elem // processed from specs
Directives []string // raw preprocessor directives
Imports []*ast.ImportSpec // imports
}
// File parses a file at the relative path
// provided and produces a new *FileSet.
// If you pass in a path to a directory, the entire
// directory will be parsed.
// If unexport is false, only exported identifiers are included in the FileSet.
// If the resulting FileSet would be empty, an error is returned.
func File(name string, unexported bool) (*FileSet, error) {
pushstate(name)
defer popstate()
fs := &FileSet{
Specs: make(map[string]ast.Expr),
Identities: make(map[string]gen.Elem),
}
fset := token.NewFileSet()
finfo, err := os.Stat(name)
if err != nil {
return nil, err
}
if finfo.IsDir() {
pkgs, err := parser.ParseDir(fset, name, nil, parser.ParseComments)
if err != nil {
return nil, err
}
if len(pkgs) != 1 {
return nil, fmt.Errorf("multiple packages in directory: %s", name)
}
var one *ast.Package
for _, nm := range pkgs {
one = nm
break
}
fs.Package = one.Name
for _, fl := range one.Files {
pushstate(fl.Name.Name)
fs.Directives = append(fs.Directives, yieldComments(fl.Comments)...)
if !unexported {
ast.FileExports(fl)
}
fs.getTypeSpecs(fl)
popstate()
}
} else {
f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
if err != nil {
return nil, err
}
fs.Package = f.Name.Name
fs.Directives = yieldComments(f.Comments)
if !unexported {
ast.FileExports(f)
}
fs.getTypeSpecs(f)
}
if len(fs.Specs) == 0 {
return nil, fmt.Errorf("no definitions in %s", name)
}
fs.process()
fs.applyDirectives()
fs.propInline()
return fs, nil
}
// applyDirectives applies all of the directives that
// are known to the parser. additional method-specific
// directives remain in f.Directives
func (f *FileSet) applyDirectives() {
newdirs := make([]string, 0, len(f.Directives))
for _, d := range f.Directives {
chunks := strings.Split(d, " ")
if len(chunks) > 0 {
if fn, ok := directives[chunks[0]]; ok {
pushstate(chunks[0])
err := fn(chunks, f)
if err != nil {
warnln(err.Error())
}
popstate()
} else {
newdirs = append(newdirs, d)
}
}
}
f.Directives = newdirs
}
// A linkset is a graph of unresolved
// identities.
//
// Since gen.Ident can only represent
// one level of type indirection (e.g. Foo -> uint8),
// type declarations like `type Foo Bar`
// aren't resolve-able until we've processed
// everything else.
//
// The goal of this dependency resolution
// is to distill the type declaration
// into just one level of indirection.
// In other words, if we have:
//
// type A uint64
// type B A
// type C B
// type D C
//
// ... then we want to end up
// figuring out that D is just a uint64.
type linkset map[string]*gen.BaseElem
func (f *FileSet) resolve(ls linkset) {
progress := true
for progress && len(ls) > 0 {
progress = false
for name, elem := range ls {
real, ok := f.Identities[elem.TypeName()]
if ok {
// copy the old type descriptor,
// alias it to the new value,
// and insert it into the resolved
// identities list
progress = true
nt := real.Copy()
nt.Alias(name)
f.Identities[name] = nt
delete(ls, name)
}
}
}
// what's left can't be resolved
for name, elem := range ls {
warnf("couldn't resolve type %s (%s)\n", name, elem.TypeName())
}
}
// process takes the contents of f.Specs and
// uses them to populate f.Identities
func (f *FileSet) process() {
deferred := make(linkset)
parse:
for name, def := range f.Specs {
pushstate(name)
el := f.parseExpr(def)
if el == nil {
warnln("failed to parse")
popstate()
continue parse
}
// push unresolved identities into
// the graph of links and resolve after
// we've handled every possible named type.
if be, ok := el.(*gen.BaseElem); ok && be.Value == gen.IDENT {
deferred[name] = be
popstate()
continue parse
}
el.Alias(name)
f.Identities[name] = el
popstate()
}
if len(deferred) > 0 {
f.resolve(deferred)
}
}
func strToMethod(s string) gen.Method {
switch s {
case "encode":
return gen.Encode
case "decode":
return gen.Decode
case "test":
return gen.Test
case "size":
return gen.Size
case "marshal":
return gen.Marshal
case "unmarshal":
return gen.Unmarshal
default:
return 0
}
}
func (f *FileSet) applyDirs(p *gen.Printer) {
// apply directives of the form
//
// //msgp:encode ignore {{TypeName}}
//
loop:
for _, d := range f.Directives {
chunks := strings.Split(d, " ")
if len(chunks) > 1 {
for i := range chunks {
chunks[i] = strings.TrimSpace(chunks[i])
}
m := strToMethod(chunks[0])
if m == 0 {
warnf("unknown pass name: %q\n", chunks[0])
continue loop
}
if fn, ok := passDirectives[chunks[1]]; ok {
pushstate(chunks[1])
err := fn(m, chunks[2:], p)
if err != nil {
warnf("error applying directive: %s\n", err)
}
popstate()
} else {
warnf("unrecognized directive %q\n", chunks[1])
}
} else {
warnf("empty directive: %q\n", d)
}
}
}
func (f *FileSet) PrintTo(p *gen.Printer) error {
f.applyDirs(p)
names := make([]string, 0, len(f.Identities))
for name := range f.Identities {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
el := f.Identities[name]
el.SetVarname("z")
pushstate(el.TypeName())
err := p.Print(el)
popstate()
if err != nil {
return err
}
}
return nil
}
// getTypeSpecs extracts all of the *ast.TypeSpecs in the file
// into fs.Identities, but does not set the actual element
func (fs *FileSet) getTypeSpecs(f *ast.File) {
// collect all imports...
fs.Imports = append(fs.Imports, f.Imports...)
// check all declarations...
for i := range f.Decls {
// for GenDecls...
if g, ok := f.Decls[i].(*ast.GenDecl); ok {
// and check the specs...
for _, s := range g.Specs {
// for ast.TypeSpecs....
if ts, ok := s.(*ast.TypeSpec); ok {
switch ts.Type.(type) {
// this is the list of parse-able
// type specs
case *ast.StructType,
*ast.ArrayType,
*ast.StarExpr,
*ast.MapType,
*ast.Ident:
fs.Specs[ts.Name.Name] = ts.Type
}
}
}
}
}
}
func fieldName(f *ast.Field) string {
switch len(f.Names) {
case 0:
return stringify(f.Type)
case 1:
return f.Names[0].Name
default:
return f.Names[0].Name + " (and others)"
}
}
func (fs *FileSet) parseFieldList(fl *ast.FieldList) []gen.StructField {
if fl == nil || fl.NumFields() == 0 {
return nil
}
out := make([]gen.StructField, 0, fl.NumFields())
for _, field := range fl.List {
pushstate(fieldName(field))
fds := fs.getField(field)
if len(fds) > 0 {
out = append(out, fds...)
} else {
warnln("ignored.")
}
popstate()
}
return out
}
// translate *ast.Field into []gen.StructField
func (fs *FileSet) getField(f *ast.Field) []gen.StructField {
sf := make([]gen.StructField, 1)
var extension, flatten bool
// parse tag; otherwise field name is field tag
if f.Tag != nil {
body := reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Get("msg")
if body == "" {
body = reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Get("msgpack")
}
tags := strings.Split(body, ",")
if len(tags) >= 2 {
switch tags[1] {
case "extension":
extension = true
case "flatten":
flatten = true
}
}
// ignore "-" fields
if tags[0] == "-" {
return nil
}
sf[0].FieldTag = tags[0]
sf[0].FieldTagParts = tags
sf[0].RawTag = f.Tag.Value
}
ex := fs.parseExpr(f.Type)
if ex == nil {
return nil
}
// parse field name
switch len(f.Names) {
case 0:
if flatten {
return fs.getFieldsFromEmbeddedStruct(f.Type)
} else {
sf[0].FieldName = embedded(f.Type)
}
case 1:
sf[0].FieldName = f.Names[0].Name
default:
// this is for a multiple in-line declaration,
// e.g. type A struct { One, Two int }
sf = sf[0:0]
for _, nm := range f.Names {
sf = append(sf, gen.StructField{
FieldTag: nm.Name,
FieldName: nm.Name,
FieldElem: ex.Copy(),
})
}
return sf
}
sf[0].FieldElem = ex
if sf[0].FieldTag == "" {
sf[0].FieldTag = sf[0].FieldName
sf[0].FieldTagParts = []string{sf[0].FieldName}
}
// validate extension
if extension {
switch ex := ex.(type) {
case *gen.Ptr:
if b, ok := ex.Value.(*gen.BaseElem); ok {
b.Value = gen.Ext
} else {
warnln("couldn't cast to extension.")
return nil
}
case *gen.BaseElem:
ex.Value = gen.Ext
default:
warnln("couldn't cast to extension.")
return nil
}
}
return sf
}
func (fs *FileSet) getFieldsFromEmbeddedStruct(f ast.Expr) []gen.StructField {
switch f := f.(type) {
case *ast.Ident:
s := fs.Specs[f.Name]
switch s := s.(type) {
case *ast.StructType:
return fs.parseFieldList(s.Fields)
default:
return nil
}
default:
// other possibilities are disallowed
return nil
}
}
// extract embedded field name
//
// so, for a struct like
//
// type A struct {
// io.Writer
// }
//
// we want "Writer"
func embedded(f ast.Expr) string {
switch f := f.(type) {
case *ast.Ident:
return f.Name
case *ast.StarExpr:
return embedded(f.X)
case *ast.SelectorExpr:
return f.Sel.Name
default:
// other possibilities are disallowed
return ""
}
}
// stringify a field type name
func stringify(e ast.Expr) string {
switch e := e.(type) {
case *ast.Ident:
return e.Name
case *ast.StarExpr:
return "*" + stringify(e.X)
case *ast.SelectorExpr:
return stringify(e.X) + "." + e.Sel.Name
case *ast.ArrayType:
if e.Len == nil {
return "[]" + stringify(e.Elt)
}
return fmt.Sprintf("[%s]%s", stringify(e.Len), stringify(e.Elt))
case *ast.InterfaceType:
if e.Methods == nil || e.Methods.NumFields() == 0 {
return "interface{}"
}
}
return "<BAD>"
}
// recursively translate ast.Expr to gen.Elem; nil means type not supported
// expected input types:
// - *ast.MapType (map[T]J)
// - *ast.Ident (name)
// - *ast.ArrayType ([(sz)]T)
// - *ast.StarExpr (*T)
// - *ast.StructType (struct {})
// - *ast.SelectorExpr (a.B)
// - *ast.InterfaceType (interface {})
func (fs *FileSet) parseExpr(e ast.Expr) gen.Elem {
switch e := e.(type) {
case *ast.MapType:
if k, ok := e.Key.(*ast.Ident); ok && k.Name == "string" {
if in := fs.parseExpr(e.Value); in != nil {
return &gen.Map{Value: in}
}
}
return nil
case *ast.Ident:
b := gen.Ident(e.Name)
// work to resove this expression
// can be done later, once we've resolved
// everything else.
if b.Value == gen.IDENT {
if _, ok := fs.Specs[e.Name]; !ok {
warnf("non-local identifier: %s\n", e.Name)
}
}
return b
case *ast.ArrayType:
// special case for []byte
if e.Len == nil {
if i, ok := e.Elt.(*ast.Ident); ok && i.Name == "byte" {
return &gen.BaseElem{Value: gen.Bytes}
}
}
// return early if we don't know
// what the slice element type is
els := fs.parseExpr(e.Elt)
if els == nil {
return nil
}
// array and not a slice
if e.Len != nil {
switch s := e.Len.(type) {
case *ast.BasicLit:
return &gen.Array{
Size: s.Value,
Els: els,
}
case *ast.Ident:
return &gen.Array{
Size: s.String(),
Els: els,
}
case *ast.SelectorExpr:
return &gen.Array{
Size: stringify(s),
Els: els,
}
default:
return nil
}
}
return &gen.Slice{Els: els}
case *ast.StarExpr:
if v := fs.parseExpr(e.X); v != nil {
return &gen.Ptr{Value: v}
}
return nil
case *ast.StructType:
return &gen.Struct{Fields: fs.parseFieldList(e.Fields)}
case *ast.SelectorExpr:
return gen.Ident(stringify(e))
case *ast.InterfaceType:
// support `interface{}`
if len(e.Methods.List) == 0 {
return &gen.BaseElem{Value: gen.Intf}
}
return nil
default: // other types not supported
return nil
}
}
func infof(s string, v ...interface{}) {
pushstate(s)
fmt.Printf(chalk.Green.Color(strings.Join(logctx, ": ")), v...)
popstate()
}
func infoln(s string) {
pushstate(s)
fmt.Println(chalk.Green.Color(strings.Join(logctx, ": ")))
popstate()
}
func warnf(s string, v ...interface{}) {
pushstate(s)
fmt.Printf(chalk.Yellow.Color(strings.Join(logctx, ": ")), v...)
popstate()
}
func warnln(s string) {
pushstate(s)
fmt.Println(chalk.Yellow.Color(strings.Join(logctx, ": ")))
popstate()
}
func fatalf(s string, v ...interface{}) {
pushstate(s)
fmt.Printf(chalk.Red.Color(strings.Join(logctx, ": ")), v...)
popstate()
}
var logctx []string
// push logging state
func pushstate(s string) {
logctx = append(logctx, s)
}
// pop logging state
func popstate() {
logctx = logctx[:len(logctx)-1]
}

169
vendor/github.com/tinylib/msgp/parse/inline.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,169 @@
package parse
import (
"sort"
"github.com/tinylib/msgp/gen"
)
// This file defines when and how we
// propagate type information from
// one type declaration to another.
// After the processing pass, every
// non-primitive type is marshalled/unmarshalled/etc.
// through a function call. Here, we propagate
// the type information into the caller's type
// tree *if* the child type is simple enough.
//
// For example, types like
//
// type A [4]int
//
// will get pushed into parent methods,
// whereas types like
//
// type B [3]map[string]struct{A, B [4]string}
//
// will not.
// this is an approximate measure
// of the number of children in a node
const maxComplex = 5
// begin recursive search for identities with the
// given name and replace them with be
func (f *FileSet) findShim(id string, be *gen.BaseElem) {
for name, el := range f.Identities {
pushstate(name)
switch el := el.(type) {
case *gen.Struct:
for i := range el.Fields {
f.nextShim(&el.Fields[i].FieldElem, id, be)
}
case *gen.Array:
f.nextShim(&el.Els, id, be)
case *gen.Slice:
f.nextShim(&el.Els, id, be)
case *gen.Map:
f.nextShim(&el.Value, id, be)
case *gen.Ptr:
f.nextShim(&el.Value, id, be)
}
popstate()
}
// we'll need this at the top level as well
f.Identities[id] = be
}
func (f *FileSet) nextShim(ref *gen.Elem, id string, be *gen.BaseElem) {
if (*ref).TypeName() == id {
vn := (*ref).Varname()
*ref = be.Copy()
(*ref).SetVarname(vn)
} else {
switch el := (*ref).(type) {
case *gen.Struct:
for i := range el.Fields {
f.nextShim(&el.Fields[i].FieldElem, id, be)
}
case *gen.Array:
f.nextShim(&el.Els, id, be)
case *gen.Slice:
f.nextShim(&el.Els, id, be)
case *gen.Map:
f.nextShim(&el.Value, id, be)
case *gen.Ptr:
f.nextShim(&el.Value, id, be)
}
}
}
// propInline identifies and inlines candidates
func (f *FileSet) propInline() {
type gelem struct {
name string
el gen.Elem
}
all := make([]gelem, 0, len(f.Identities))
for name, el := range f.Identities {
all = append(all, gelem{name: name, el: el})
}
// make sure we process inlining determinstically:
// start with the least-complex elems;
// use identifier names as a tie-breaker
sort.Slice(all, func(i, j int) bool {
ig, jg := &all[i], &all[j]
ic, jc := ig.el.Complexity(), jg.el.Complexity()
return ic < jc || (ic == jc && ig.name < jg.name)
})
for i := range all {
name := all[i].name
pushstate(name)
switch el := all[i].el.(type) {
case *gen.Struct:
for i := range el.Fields {
f.nextInline(&el.Fields[i].FieldElem, name)
}
case *gen.Array:
f.nextInline(&el.Els, name)
case *gen.Slice:
f.nextInline(&el.Els, name)
case *gen.Map:
f.nextInline(&el.Value, name)
case *gen.Ptr:
f.nextInline(&el.Value, name)
}
popstate()
}
}
const fatalloop = `detected infinite recursion in inlining loop!
Please file a bug at github.com/tinylib/msgp/issues!
Thanks!
`
func (f *FileSet) nextInline(ref *gen.Elem, root string) {
switch el := (*ref).(type) {
case *gen.BaseElem:
// ensure that we're not inlining
// a type into itself
typ := el.TypeName()
if el.Value == gen.IDENT && typ != root {
if node, ok := f.Identities[typ]; ok && node.Complexity() < maxComplex {
infof("inlining %s\n", typ)
// This should never happen; it will cause
// infinite recursion.
if node == *ref {
panic(fatalloop)
}
*ref = node.Copy()
f.nextInline(ref, node.TypeName())
} else if !ok && !el.Resolved() {
// this is the point at which we're sure that
// we've got a type that isn't a primitive,
// a library builtin, or a processed type
warnf("unresolved identifier: %s\n", typ)
}
}
case *gen.Struct:
for i := range el.Fields {
f.nextInline(&el.Fields[i].FieldElem, root)
}
case *gen.Array:
f.nextInline(&el.Els, root)
case *gen.Slice:
f.nextInline(&el.Els, root)
case *gen.Map:
f.nextInline(&el.Value, root)
case *gen.Ptr:
f.nextInline(&el.Value, root)
default:
panic("bad elem type")
}
}

132
vendor/github.com/tinylib/msgp/printer/print.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,132 @@
package printer
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"strings"
"github.com/tinylib/msgp/gen"
"github.com/tinylib/msgp/parse"
"github.com/ttacon/chalk"
"golang.org/x/tools/imports"
)
func infof(s string, v ...interface{}) {
fmt.Printf(chalk.Magenta.Color(s), v...)
}
// PrintFile prints the methods for the provided list
// of elements to the given file name and canonical
// package path.
func PrintFile(file string, f *parse.FileSet, mode gen.Method) error {
out, tests, err := generate(f, mode)
if err != nil {
return err
}
// we'll run goimports on the main file
// in another goroutine, and run it here
// for the test file. empirically, this
// takes about the same amount of time as
// doing them in serial when GOMAXPROCS=1,
// and faster otherwise.
res := goformat(file, out.Bytes())
if tests != nil {
testfile := strings.TrimSuffix(file, ".go") + "_test.go"
err = format(testfile, tests.Bytes())
if err != nil {
return err
}
infof(">>> Wrote and formatted \"%s\"\n", testfile)
}
err = <-res
if err != nil {
return err
}
return nil
}
func format(file string, data []byte) error {
out, err := imports.Process(file, data, nil)
if err != nil {
return err
}
return ioutil.WriteFile(file, out, 0600)
}
func goformat(file string, data []byte) <-chan error {
out := make(chan error, 1)
go func(file string, data []byte, end chan error) {
end <- format(file, data)
infof(">>> Wrote and formatted \"%s\"\n", file)
}(file, data, out)
return out
}
func dedupImports(imp []string) []string {
m := make(map[string]struct{})
for i := range imp {
m[imp[i]] = struct{}{}
}
r := []string{}
for k := range m {
r = append(r, k)
}
return r
}
func generate(f *parse.FileSet, mode gen.Method) (*bytes.Buffer, *bytes.Buffer, error) {
outbuf := bytes.NewBuffer(make([]byte, 0, 4096))
writePkgHeader(outbuf, f.Package)
myImports := []string{"github.com/tinylib/msgp/msgp"}
for _, imp := range f.Imports {
if imp.Name != nil {
// have an alias, include it.
myImports = append(myImports, imp.Name.Name+` `+imp.Path.Value)
} else {
myImports = append(myImports, imp.Path.Value)
}
}
dedup := dedupImports(myImports)
writeImportHeader(outbuf, dedup...)
var testbuf *bytes.Buffer
var testwr io.Writer
if mode&gen.Test == gen.Test {
testbuf = bytes.NewBuffer(make([]byte, 0, 4096))
writePkgHeader(testbuf, f.Package)
if mode&(gen.Encode|gen.Decode) != 0 {
writeImportHeader(testbuf, "bytes", "github.com/tinylib/msgp/msgp", "testing")
} else {
writeImportHeader(testbuf, "github.com/tinylib/msgp/msgp", "testing")
}
testwr = testbuf
}
return outbuf, testbuf, f.PrintTo(gen.NewPrinter(mode, outbuf, testwr))
}
func writePkgHeader(b *bytes.Buffer, name string) {
b.WriteString("package ")
b.WriteString(name)
b.WriteByte('\n')
// write generated code marker
// https://github.com/tinylib/msgp/issues/229
// https://golang.org/s/generatedcode
b.WriteString("// Code generated by github.com/tinylib/msgp DO NOT EDIT.\n\n")
}
func writeImportHeader(b *bytes.Buffer, imports ...string) {
b.WriteString("import (\n")
for _, im := range imports {
if im[len(im)-1] == '"' {
// support aliased imports
fmt.Fprintf(b, "\t%s\n", im)
} else {
fmt.Fprintf(b, "\t%q\n", im)
}
}
b.WriteString(")\n\n")
}