Files
easyjson/gen/decoder.go
T
Victor Starodub 5d497db34c Initial commit.
2016-02-28 03:16:29 +03:00

238 lines
6.5 KiB
Go

package gen
import (
"fmt"
"reflect"
"strings"
"unicode"
"github.com/mailru/easyjson"
)
// Target this byte size for initial slice allocation to reduce garbage collection.
const minSliceBytes = 64
func (g *Generator) getStructDecoderName(t reflect.Type) string {
return g.functionName("easyjson_decode_", t)
}
var primitiveDecoders = map[reflect.Kind]string{
reflect.String: "in.String()",
reflect.Bool: "in.Bool()",
reflect.Int: "in.Int()",
reflect.Int8: "in.Int8()",
reflect.Int16: "in.Int16()",
reflect.Int32: "in.Int32()",
reflect.Int64: "in.Int64()",
reflect.Uint: "in.Uint()",
reflect.Uint8: "in.Uint8()",
reflect.Uint16: "in.Uint16()",
reflect.Uint32: "in.Uint32()",
reflect.Uint64: "in.Uint64()",
reflect.Float32: "in.Float32()",
reflect.Float64: "in.Float64()",
}
func (g *Generator) genTypeDecoder(t reflect.Type, out string, indent int) error {
ws := strings.Repeat(" ", indent)
unmarshalerIface := reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem()
if reflect.PtrTo(t).Implements(unmarshalerIface) {
fmt.Fprintln(g.out, ws+"("+out+").UnmarshalEasyJSON(in)")
return nil
}
// Check whether type is primitive, needs to be done after interface check.
if dec := primitiveDecoders[t.Kind()]; dec != "" {
fmt.Fprintln(g.out, ws+out+" = "+dec)
return nil
}
switch t.Kind() {
case reflect.Slice:
tmpVar := g.uniqueVarName()
elem := t.Elem()
capacity := minSliceBytes / elem.Size()
if capacity == 0 {
capacity = 1
}
fmt.Fprintln(g.out, ws+"in.Delim('[')")
fmt.Fprintln(g.out, ws+"if !in.IsDelim(']') {")
fmt.Fprintln(g.out, ws+" "+out+" = make([]"+g.getType(elem)+", 0, "+fmt.Sprint(capacity)+")")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" "+out+" = nil")
fmt.Fprintln(g.out, ws+"}")
fmt.Fprintln(g.out, ws+"for !in.IsDelim(']') {")
fmt.Fprintln(g.out, ws+" var "+tmpVar+" "+g.getType(elem))
g.genTypeDecoder(elem, tmpVar, indent+1)
fmt.Fprintln(g.out, ws+" "+out+" = append("+out+", "+tmpVar+")")
fmt.Fprintln(g.out, ws+" in.WantComma()")
fmt.Fprintln(g.out, ws+"}")
fmt.Fprintln(g.out, ws+"in.Delim(']')")
case reflect.Struct:
dec := g.getStructDecoderName(t)
g.addType(t)
fmt.Fprintln(g.out, ws+dec+"(in, &"+out+")")
case reflect.Ptr:
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+" "+out+" = nil")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" "+out+" = new("+g.getType(t.Elem())+")")
g.genTypeDecoder(t.Elem(), "*"+out, indent+1)
fmt.Fprintln(g.out, ws+"}")
case reflect.Map:
key := t.Key()
if key.Kind() != reflect.String {
return fmt.Errorf("map type %v not supported: only string keys are allowed", key)
}
elem := t.Elem()
tmpVar := g.uniqueVarName()
fmt.Fprintln(g.out, ws+"in.Delim('{')")
fmt.Fprintln(g.out, ws+"if !in.IsDelim('}') {")
fmt.Fprintln(g.out, ws+out+" = make("+g.getType(t)+")")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+out+" = nil")
fmt.Fprintln(g.out, ws+"}")
fmt.Fprintln(g.out, ws+"for !in.IsDelim('}') {")
fmt.Fprintln(g.out, ws+" key := in.String()")
fmt.Fprintln(g.out, ws+" in.WantColon()")
fmt.Fprintln(g.out, ws+" var "+tmpVar+" "+g.getType(elem))
g.genTypeDecoder(elem, tmpVar, indent+1)
fmt.Fprintln(g.out, ws+" ("+out+")[key] = "+tmpVar)
fmt.Fprintln(g.out, ws+" in.WantComma()")
fmt.Fprintln(g.out, ws+"}")
fmt.Fprintln(g.out, ws+"in.Delim('}')")
case reflect.Interface:
if t.NumMethod() != 0 {
return fmt.Errorf("interface type %v not supported: only interface{} is allowed", t)
}
fmt.Fprintln(g.out, ws+out+" = in.Interface()")
default:
return fmt.Errorf("don't know how to decode %v", t)
}
return nil
}
func (g *Generator) genStructFieldDecoder(t reflect.Type, f reflect.StructField) error {
jsonName := g.namer.GetJSONFieldName(t, f)
fmt.Fprintf(g.out, " case %q:\n", jsonName)
return g.genTypeDecoder(f.Type, "out."+f.Name, 3)
}
func mergeStructFields(fields1, fields2 []reflect.StructField) (fields []reflect.StructField) {
used := map[string]bool{}
for _, f := range fields2 {
used[f.Name] = true
fields = append(fields, f)
}
for _, f := range fields1 {
if !used[f.Name] {
fields = append(fields, f)
}
}
return
}
func getStructFields(t reflect.Type) []reflect.StructField {
var efields []reflect.StructField
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if !f.Anonymous {
continue
}
efields = mergeStructFields(efields, getStructFields(f.Type))
}
var fields []reflect.StructField
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Anonymous {
continue
}
c := []rune(f.Name)[0]
if unicode.IsUpper(c) {
fields = append(fields, f)
}
}
return mergeStructFields(efields, fields)
}
func (g *Generator) genStructDecoder(t reflect.Type) error {
if t.Kind() != reflect.Struct {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct type", t)
}
fname := g.getStructDecoderName(t)
typ := g.getType(t)
fmt.Fprintln(g.out, "func "+fname+"(in *jlexer.Lexer, out *"+typ+") {")
fmt.Fprintln(g.out, " in.Delim('{')")
fmt.Fprintln(g.out, " for !in.IsDelim('}') {")
fmt.Fprintln(g.out, " key := in.UnsafeString()")
fmt.Fprintln(g.out, " in.WantColon()")
fmt.Fprintln(g.out, " if in.IsNull() {")
fmt.Fprintln(g.out, " in.Skip()")
fmt.Fprintln(g.out, " in.WantComma()")
fmt.Fprintln(g.out, " continue")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " switch key {")
for _, f := range getStructFields(t) {
if err := g.genStructFieldDecoder(t, f); err != nil {
return err
}
}
fmt.Fprintln(g.out, " default:")
fmt.Fprintln(g.out, " in.SkipRecursive()")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " in.WantComma()")
fmt.Fprintln(g.out, " }")
fmt.Fprintln(g.out, " in.Delim('}')")
fmt.Fprintln(g.out, "}")
return nil
}
func (g *Generator) genStructUnmarshaller(t reflect.Type) error {
if t.Kind() != reflect.Struct {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct type", t)
}
fname := g.getStructDecoderName(t)
typ := g.getType(t)
fmt.Fprintln(g.out, "func (v *"+typ+") UnmarshalJSON(data []byte) error {")
fmt.Fprintln(g.out, " r := jlexer.Lexer{Data: data}")
fmt.Fprintln(g.out, " "+fname+"(&r, v)")
fmt.Fprintln(g.out, " return r.Error()")
fmt.Fprintln(g.out, "}")
fmt.Fprintln(g.out, "func (v *"+typ+") UnmarshalEasyJSON(l *jlexer.Lexer) {")
fmt.Fprintln(g.out, " "+fname+"(l, v)")
fmt.Fprintln(g.out, "}")
return nil
}