Add Vat init transformer

This commit is contained in:
Rob Mulholand
2018-09-11 16:30:29 -05:00
parent 9abe3ffa68
commit ff110592bd
239 changed files with 30491 additions and 10 deletions
+218
View File
@@ -0,0 +1,218 @@
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
}
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.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.print(errcheck)
}
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
}
next(d, s.Fields[i].FieldElem)
}
}
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.p.printf("\ncase \"%s\":", s.Fields[i].FieldTag)
next(d, s.Fields[i].FieldElem)
if !d.p.ok() {
return
}
}
d.p.print("\ndefault:\nerr = dc.Skip()")
d.p.print(errcheck)
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)
}
}
// close block for 'tmp'
if b.Convert {
d.p.printf("\n%s = %s(%s)\n}", vname, b.FromBase(), tmp)
}
d.p.print(errcheck)
}
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)
next(d, m.Value)
d.p.mapAssign(m)
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(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.print(errcheck)
return
}
sz := randIdent()
d.p.declare(sz, u32)
d.assignAndCheck(sz, arrayHeader)
d.p.arrayCheck(a.Size, sz)
d.p.rangeBlock(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.print(errcheck)
d.p.printf("\n%s = nil\n} else {", p.Varname())
d.p.initPtr(p)
next(d, p.Value)
d.p.closeblock()
}
+598
View File
@@ -0,0 +1,598 @@
package gen
import (
"fmt"
"math/rand"
"strings"
)
const (
idxChars = "abcdefghijlkmnopqrstuvwxyz"
idxLen = 3
)
// generate a random identifier name
func randIdent() string {
bts := make([]byte, idxLen)
for i := range bts {
bts[i] = idxChars[rand.Intn(len(idxChars))]
}
// Use a `z` prefix so the randomly generated bytes can't conflict with
// Go keywords (such as `int` and `var`).
return "z" + string(bts)
}
// 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,
"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
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() }
// 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() }
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()
}
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
}
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() + ";\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
}
type StructField struct {
FieldTag string // the string inside the `msg:""` tag
FieldName string // the name of the struct field
FieldElem Elem // the field type
}
// BaseElem is an element that
// can be represented by a primitive
// MessagePack type.
type BaseElem struct {
common
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
}
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))
}
}
+184
View File
@@ -0,0 +1,184 @@
package gen
import (
"fmt"
"github.com/tinylib/msgp/msgp"
"io"
)
func encode(w io.Writer) *encodeGen {
return &encodeGen{
p: printer{w: w},
}
}
type encodeGen struct {
passes
p printer
fuse []byte
}
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.print(errcheck)
}
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.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)
for i := range s.Fields {
if !e.p.ok() {
return
}
next(e, s.Fields[i].FieldElem)
}
}
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 err }")
}
func (e *encodeGen) structmap(s *Struct) {
nfields := len(s.Fields)
data := msgp.AppendMapHeader(nil, uint32(nfields))
e.p.printf("\n// map header, size %d", nfields)
e.Fuse(data)
for i := range s.Fields {
if !e.p.ok() {
return
}
data = msgp.AppendString(nil, s.Fields[i].FieldTag)
e.p.printf("\n// write %q", s.Fields[i].FieldTag)
e.Fuse(data)
next(e, s.Fields[i].FieldElem)
}
}
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)
next(e, m.Value)
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(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.print(errcheck)
return
}
e.writeAndCheck(arrayHeader, literalFmt, a.Size)
e.p.rangeBlock(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 {
vname = tobaseConvert(b)
}
if b.Value == IDENT { // unknown identity
e.p.printf("\nerr = %s.EncodeMsg(en)", vname)
e.p.print(errcheck)
} else { // typical case
e.writeAndCheck(b.BaseName(), literalFmt, vname)
}
}
+198
View File
@@ -0,0 +1,198 @@
package gen
import (
"fmt"
"github.com/tinylib/msgp/msgp"
"io"
)
func marshal(w io.Writer) *marshalGen {
return &marshalGen{
p: printer{w: w},
}
}
type marshalGen struct {
passes
p printer
fuse []byte
}
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.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)
for i := range s.Fields {
if !m.p.ok() {
return
}
next(m, s.Fields[i].FieldElem)
}
}
func (m *marshalGen) mapstruct(s *Struct) {
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)
for i := range s.Fields {
if !m.p.ok() {
return
}
data = msgp.AppendString(nil, s.Fields[i].FieldTag)
m.p.printf("\n// string %q", s.Fields[i].FieldTag)
m.Fuse(data)
next(m, s.Fields[i].FieldElem)
}
}
// 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)
next(m, s.Value)
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(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, a.Size)
m.p.rangeBlock(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 {
vname = tobaseConvert(b)
}
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.print(errcheck)
}
}
+272
View File
@@ -0,0 +1,272 @@
package gen
import (
"fmt"
"github.com/tinylib/msgp/msgp"
"io"
"strconv"
)
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
}
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.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(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(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
next(s, m.Value)
s.p.closeblock()
s.p.closeblock()
s.state = add
}
func (s *sizeGen) gBase(b *BaseElem) {
if !s.p.ok() {
return
}
s.addConstant(basesizeExpr(b))
}
// 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(b *BaseElem) string {
vname := b.Varname()
if b.Convert {
vname = tobaseConvert(b)
}
switch b.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(b.BaseName())
}
}
+376
View File
@@ -0,0 +1,376 @@
package gen
import (
"fmt"
"io"
)
const (
errcheck = "\nif err != nil { return }"
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 {
err := g.Execute(e)
if err != nil {
return err
}
}
return nil
}
// 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 && size > 0 {
// 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 && %s > 0 {", vn, size)
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) 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(idx string, iter string, t traversal, inner Elem) {
p.printf("\n for %s := range %s {", idx, iter)
next(t, inner)
p.closeblock()
}
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() + ")"
}
+182
View File
@@ -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.Logf("WARNING: Msgsize() for %v is inaccurate", v)
}
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)
}
}
}
`))
}
+199
View File
@@ -0,0 +1,199 @@
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
}
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.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.print(errcheck)
}
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
}
next(u, s.Fields[i].FieldElem)
}
}
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.print(errcheck)
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)
next(u, s.Fields[i].FieldElem)
}
u.p.print("\ndefault:\nbts, err = msgp.Skip(bts)")
u.p.print(errcheck)
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())
}
if b.Convert {
// close 'tmp' block
u.p.printf("\n%s = %s(%s)\n}", b.Varname(), b.FromBase(), refname)
}
u.p.print(errcheck)
}
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.print(errcheck)
return
}
sz := randIdent()
u.p.declare(sz, u32)
u.assignAndCheck(sz, arrayHeader)
u.p.arrayCheck(a.Size, sz)
u.p.rangeBlock(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(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)
next(u, m.Value)
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()
}