summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mailru/easyjson/gen/encoder.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mailru/easyjson/gen/encoder.go')
-rw-r--r--vendor/github.com/mailru/easyjson/gen/encoder.go382
1 files changed, 382 insertions, 0 deletions
diff --git a/vendor/github.com/mailru/easyjson/gen/encoder.go b/vendor/github.com/mailru/easyjson/gen/encoder.go
new file mode 100644
index 000000000..48cba15d4
--- /dev/null
+++ b/vendor/github.com/mailru/easyjson/gen/encoder.go
@@ -0,0 +1,382 @@
+package gen
+
+import (
+ "encoding"
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+
+ "github.com/mailru/easyjson"
+)
+
+func (g *Generator) getEncoderName(t reflect.Type) string {
+ return g.functionName("encode", t)
+}
+
+var primitiveEncoders = map[reflect.Kind]string{
+ reflect.String: "out.String(string(%v))",
+ reflect.Bool: "out.Bool(bool(%v))",
+ reflect.Int: "out.Int(int(%v))",
+ reflect.Int8: "out.Int8(int8(%v))",
+ reflect.Int16: "out.Int16(int16(%v))",
+ reflect.Int32: "out.Int32(int32(%v))",
+ reflect.Int64: "out.Int64(int64(%v))",
+ reflect.Uint: "out.Uint(uint(%v))",
+ reflect.Uint8: "out.Uint8(uint8(%v))",
+ reflect.Uint16: "out.Uint16(uint16(%v))",
+ reflect.Uint32: "out.Uint32(uint32(%v))",
+ reflect.Uint64: "out.Uint64(uint64(%v))",
+ reflect.Float32: "out.Float32(float32(%v))",
+ reflect.Float64: "out.Float64(float64(%v))",
+}
+
+var primitiveStringEncoders = map[reflect.Kind]string{
+ reflect.String: "out.String(string(%v))",
+ reflect.Int: "out.IntStr(int(%v))",
+ reflect.Int8: "out.Int8Str(int8(%v))",
+ reflect.Int16: "out.Int16Str(int16(%v))",
+ reflect.Int32: "out.Int32Str(int32(%v))",
+ reflect.Int64: "out.Int64Str(int64(%v))",
+ reflect.Uint: "out.UintStr(uint(%v))",
+ reflect.Uint8: "out.Uint8Str(uint8(%v))",
+ reflect.Uint16: "out.Uint16Str(uint16(%v))",
+ reflect.Uint32: "out.Uint32Str(uint32(%v))",
+ reflect.Uint64: "out.Uint64Str(uint64(%v))",
+ reflect.Uintptr: "out.UintptrStr(uintptr(%v))",
+}
+
+// fieldTags contains parsed version of json struct field tags.
+type fieldTags struct {
+ name string
+
+ omit bool
+ omitEmpty bool
+ noOmitEmpty bool
+ asString bool
+ required bool
+}
+
+// parseFieldTags parses the json field tag into a structure.
+func parseFieldTags(f reflect.StructField) fieldTags {
+ var ret fieldTags
+
+ for i, s := range strings.Split(f.Tag.Get("json"), ",") {
+ switch {
+ case i == 0 && s == "-":
+ ret.omit = true
+ case i == 0:
+ ret.name = s
+ case s == "omitempty":
+ ret.omitEmpty = true
+ case s == "!omitempty":
+ ret.noOmitEmpty = true
+ case s == "string":
+ ret.asString = true
+ case s == "required":
+ ret.required = true
+ }
+ }
+
+ return ret
+}
+
+// genTypeEncoder generates code that encodes in of type t into the writer, but uses marshaler interface if implemented by t.
+func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, indent int, assumeNonEmpty bool) error {
+ ws := strings.Repeat(" ", indent)
+
+ marshalerIface := reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem()
+ if reflect.PtrTo(t).Implements(marshalerIface) {
+ fmt.Fprintln(g.out, ws+"("+in+").MarshalEasyJSON(out)")
+ return nil
+ }
+
+ marshalerIface = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
+ if reflect.PtrTo(t).Implements(marshalerIface) {
+ fmt.Fprintln(g.out, ws+"out.Raw( ("+in+").MarshalJSON() )")
+ return nil
+ }
+
+ marshalerIface = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
+ if reflect.PtrTo(t).Implements(marshalerIface) {
+ fmt.Fprintln(g.out, ws+"out.RawText( ("+in+").MarshalText() )")
+ return nil
+ }
+
+ err := g.genTypeEncoderNoCheck(t, in, tags, indent, assumeNonEmpty)
+ return err
+}
+
+// genTypeEncoderNoCheck generates code that encodes in of type t into the writer.
+func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldTags, indent int, assumeNonEmpty bool) error {
+ ws := strings.Repeat(" ", indent)
+
+ // Check whether type is primitive, needs to be done after interface check.
+ if enc := primitiveStringEncoders[t.Kind()]; enc != "" && tags.asString {
+ fmt.Fprintf(g.out, ws+enc+"\n", in)
+ return nil
+ } else if enc := primitiveEncoders[t.Kind()]; enc != "" {
+ fmt.Fprintf(g.out, ws+enc+"\n", in)
+ return nil
+ }
+
+ switch t.Kind() {
+ case reflect.Slice:
+ elem := t.Elem()
+ iVar := g.uniqueVarName()
+ vVar := g.uniqueVarName()
+
+ if t.Elem().Kind() == reflect.Uint8 {
+ fmt.Fprintln(g.out, ws+"out.Base64Bytes("+in+")")
+ } else {
+ if !assumeNonEmpty {
+ fmt.Fprintln(g.out, ws+"if "+in+" == nil && (out.Flags & jwriter.NilSliceAsEmpty) == 0 {")
+ fmt.Fprintln(g.out, ws+` out.RawString("null")`)
+ fmt.Fprintln(g.out, ws+"} else {")
+ } else {
+ fmt.Fprintln(g.out, ws+"{")
+ }
+ fmt.Fprintln(g.out, ws+" out.RawByte('[')")
+ fmt.Fprintln(g.out, ws+" for "+iVar+", "+vVar+" := range "+in+" {")
+ fmt.Fprintln(g.out, ws+" if "+iVar+" > 0 {")
+ fmt.Fprintln(g.out, ws+" out.RawByte(',')")
+ fmt.Fprintln(g.out, ws+" }")
+
+ if err := g.genTypeEncoder(elem, vVar, tags, indent+2, false); err != nil {
+ return err
+ }
+
+ fmt.Fprintln(g.out, ws+" }")
+ fmt.Fprintln(g.out, ws+" out.RawByte(']')")
+ fmt.Fprintln(g.out, ws+"}")
+ }
+
+ case reflect.Array:
+ elem := t.Elem()
+ iVar := g.uniqueVarName()
+
+ if t.Elem().Kind() == reflect.Uint8 {
+ fmt.Fprintln(g.out, ws+"out.Base64Bytes("+in+"[:])")
+ } else {
+ fmt.Fprintln(g.out, ws+"out.RawByte('[')")
+ fmt.Fprintln(g.out, ws+"for "+iVar+" := range "+in+" {")
+ fmt.Fprintln(g.out, ws+" if "+iVar+" > 0 {")
+ fmt.Fprintln(g.out, ws+" out.RawByte(',')")
+ fmt.Fprintln(g.out, ws+" }")
+
+ if err := g.genTypeEncoder(elem, in+"["+iVar+"]", tags, indent+1, false); err != nil {
+ return err
+ }
+
+ fmt.Fprintln(g.out, ws+"}")
+ fmt.Fprintln(g.out, ws+"out.RawByte(']')")
+ }
+
+ case reflect.Struct:
+ enc := g.getEncoderName(t)
+ g.addType(t)
+
+ fmt.Fprintln(g.out, ws+enc+"(out, "+in+")")
+
+ case reflect.Ptr:
+ if !assumeNonEmpty {
+ fmt.Fprintln(g.out, ws+"if "+in+" == nil {")
+ fmt.Fprintln(g.out, ws+` out.RawString("null")`)
+ fmt.Fprintln(g.out, ws+"} else {")
+ }
+
+ if err := g.genTypeEncoder(t.Elem(), "*"+in, tags, indent+1, false); err != nil {
+ return err
+ }
+
+ if !assumeNonEmpty {
+ fmt.Fprintln(g.out, ws+"}")
+ }
+
+ case reflect.Map:
+ key := t.Key()
+ keyEnc, ok := primitiveStringEncoders[key.Kind()]
+ if !ok {
+ return fmt.Errorf("map key type %v not supported: only string and integer keys are allowed", key)
+ }
+ tmpVar := g.uniqueVarName()
+
+ if !assumeNonEmpty {
+ fmt.Fprintln(g.out, ws+"if "+in+" == nil && (out.Flags & jwriter.NilMapAsEmpty) == 0 {")
+ fmt.Fprintln(g.out, ws+" out.RawString(`null`)")
+ fmt.Fprintln(g.out, ws+"} else {")
+ } else {
+ fmt.Fprintln(g.out, ws+"{")
+ }
+ fmt.Fprintln(g.out, ws+" out.RawByte('{')")
+ fmt.Fprintln(g.out, ws+" "+tmpVar+"First := true")
+ fmt.Fprintln(g.out, ws+" for "+tmpVar+"Name, "+tmpVar+"Value := range "+in+" {")
+ fmt.Fprintln(g.out, ws+" if "+tmpVar+"First { "+tmpVar+"First = false } else { out.RawByte(',') }")
+ fmt.Fprintln(g.out, ws+" "+fmt.Sprintf(keyEnc, tmpVar+"Name"))
+ fmt.Fprintln(g.out, ws+" out.RawByte(':')")
+
+ if err := g.genTypeEncoder(t.Elem(), tmpVar+"Value", tags, indent+2, false); err != nil {
+ return err
+ }
+
+ fmt.Fprintln(g.out, ws+" }")
+ fmt.Fprintln(g.out, ws+" out.RawByte('}')")
+ fmt.Fprintln(g.out, ws+"}")
+
+ 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+"if m, ok := "+in+".(easyjson.Marshaler); ok {")
+ fmt.Fprintln(g.out, ws+" m.MarshalEasyJSON(out)")
+ fmt.Fprintln(g.out, ws+"} else if m, ok := "+in+".(json.Marshaler); ok {")
+ fmt.Fprintln(g.out, ws+" out.Raw(m.MarshalJSON())")
+ fmt.Fprintln(g.out, ws+"} else {")
+ fmt.Fprintln(g.out, ws+" out.Raw(json.Marshal("+in+"))")
+ fmt.Fprintln(g.out, ws+"}")
+
+ default:
+ return fmt.Errorf("don't know how to encode %v", t)
+ }
+ return nil
+}
+
+func (g *Generator) notEmptyCheck(t reflect.Type, v string) string {
+ optionalIface := reflect.TypeOf((*easyjson.Optional)(nil)).Elem()
+ if reflect.PtrTo(t).Implements(optionalIface) {
+ return "(" + v + ").IsDefined()"
+ }
+
+ switch t.Kind() {
+ case reflect.Slice, reflect.Map:
+ return "len(" + v + ") != 0"
+ case reflect.Interface, reflect.Ptr:
+ return v + " != nil"
+ case reflect.Bool:
+ return v
+ case reflect.String:
+ return v + ` != ""`
+ case reflect.Float32, reflect.Float64,
+ reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+
+ return v + " != 0"
+
+ default:
+ // note: Array types don't have a useful empty value
+ return "true"
+ }
+}
+
+func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField) error {
+ jsonName := g.fieldNamer.GetJSONFieldName(t, f)
+ tags := parseFieldTags(f)
+
+ if tags.omit {
+ return nil
+ }
+ noOmitEmpty := (!tags.omitEmpty && !g.omitEmpty) || tags.noOmitEmpty
+ if noOmitEmpty {
+ fmt.Fprintln(g.out, " {")
+ } else {
+ fmt.Fprintln(g.out, " if", g.notEmptyCheck(f.Type, "in."+f.Name), "{")
+ }
+ fmt.Fprintf(g.out, " const prefix string = %q\n", ","+strconv.Quote(jsonName)+":")
+ fmt.Fprintln(g.out, " if first {")
+ fmt.Fprintln(g.out, " first = false")
+ fmt.Fprintln(g.out, " out.RawString(prefix[1:])")
+ fmt.Fprintln(g.out, " } else {")
+ fmt.Fprintln(g.out, " out.RawString(prefix)")
+ fmt.Fprintln(g.out, " }")
+
+ if err := g.genTypeEncoder(f.Type, "in."+f.Name, tags, 2, !noOmitEmpty); err != nil {
+ return err
+ }
+ fmt.Fprintln(g.out, " }")
+ return nil
+}
+
+func (g *Generator) genEncoder(t reflect.Type) error {
+ switch t.Kind() {
+ case reflect.Slice, reflect.Array, reflect.Map:
+ return g.genSliceArrayMapEncoder(t)
+ default:
+ return g.genStructEncoder(t)
+ }
+}
+
+func (g *Generator) genSliceArrayMapEncoder(t reflect.Type) error {
+ switch t.Kind() {
+ case reflect.Slice, reflect.Array, reflect.Map:
+ default:
+ return fmt.Errorf("cannot generate encoder/decoder for %v, not a slice/array/map type", t)
+ }
+
+ fname := g.getEncoderName(t)
+ typ := g.getType(t)
+
+ fmt.Fprintln(g.out, "func "+fname+"(out *jwriter.Writer, in "+typ+") {")
+ err := g.genTypeEncoderNoCheck(t, "in", fieldTags{}, 1, false)
+ if err != nil {
+ return err
+ }
+ fmt.Fprintln(g.out, "}")
+ return nil
+}
+
+func (g *Generator) genStructEncoder(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.getEncoderName(t)
+ typ := g.getType(t)
+
+ fmt.Fprintln(g.out, "func "+fname+"(out *jwriter.Writer, in "+typ+") {")
+ fmt.Fprintln(g.out, " out.RawByte('{')")
+ fmt.Fprintln(g.out, " first := true")
+ fmt.Fprintln(g.out, " _ = first")
+
+ fs, err := getStructFields(t)
+ if err != nil {
+ return fmt.Errorf("cannot generate encoder for %v: %v", t, err)
+ }
+ for _, f := range fs {
+ if err := g.genStructFieldEncoder(t, f); err != nil {
+ return err
+ }
+ }
+
+ fmt.Fprintln(g.out, " out.RawByte('}')")
+ fmt.Fprintln(g.out, "}")
+
+ return nil
+}
+
+func (g *Generator) genStructMarshaler(t reflect.Type) error {
+ switch t.Kind() {
+ case reflect.Slice, reflect.Array, reflect.Map, reflect.Struct:
+ default:
+ return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct/slice/array/map type", t)
+ }
+
+ fname := g.getEncoderName(t)
+ typ := g.getType(t)
+
+ if !g.noStdMarshalers {
+ fmt.Fprintln(g.out, "// MarshalJSON supports json.Marshaler interface")
+ fmt.Fprintln(g.out, "func (v "+typ+") MarshalJSON() ([]byte, error) {")
+ fmt.Fprintln(g.out, " w := jwriter.Writer{}")
+ fmt.Fprintln(g.out, " "+fname+"(&w, v)")
+ fmt.Fprintln(g.out, " return w.Buffer.BuildBytes(), w.Error")
+ fmt.Fprintln(g.out, "}")
+ }
+
+ fmt.Fprintln(g.out, "// MarshalEasyJSON supports easyjson.Marshaler interface")
+ fmt.Fprintln(g.out, "func (v "+typ+") MarshalEasyJSON(w *jwriter.Writer) {")
+ fmt.Fprintln(g.out, " "+fname+"(w, v)")
+ fmt.Fprintln(g.out, "}")
+
+ return nil
+}