mirror of
https://github.com/netbirdio/easyjson.git
synced 2026-05-22 18:44:42 -07:00
238 lines
6.5 KiB
Go
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
|
|
}
|