forked from cerc-io/ipld-eth-server
Add Vat init transformer
This commit is contained in:
+218
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
}
|
||||
Reference in New Issue
Block a user