gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit acc87b92070551fbd05b1ef178851952e8cf2681
parent 9d67e07523a5848a7723c933d46d8514a2c9a30a
Author: francoispqt <francois@parquet.ninja>
Date:   Tue, 19 Jun 2018 00:33:44 +0800

add gen for maps

Diffstat:
Mgojay/gen.go | 4++++
Mgojay/gen_map.go | 18++++++++++++++++++
Agojay/gen_map_marshal.go | 249+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/gen_map_marshal_tpl.go | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/gen_map_test.go | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/gen_map_unmarshal.go | 279+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/gen_map_unmarshal_tpl.go | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 792 insertions(+), 0 deletions(-)

diff --git a/gojay/gen.go b/gojay/gen.go @@ -107,6 +107,10 @@ func (g *Gen) Gen() error { // is map case *ast.MapType: // TODO: generate for map type + err = g.genMap(s.Name.String(), t) + if err != nil { + return err + } } } return nil diff --git a/gojay/gen_map.go b/gojay/gen_map.go @@ -1 +1,19 @@ package main + +import "go/ast" + +func (g *Gen) genMap(n string, s *ast.MapType) error { + err := g.mapGenUnmarshalObj(n, s) + if err != nil { + return err + } + err = g.mapGenNKeys(n, 0) + if err != nil { + return err + } + err = g.mapGenMarshalObj(n, s) + if err != nil { + return err + } + return g.mapGenIsNil(n) +} diff --git a/gojay/gen_map_marshal.go b/gojay/gen_map_marshal.go @@ -0,0 +1,249 @@ +package main + +import ( + "errors" + "go/ast" +) + +func (g *Gen) mapGenIsNil(n string) error { + err := mapMarshalTpl["isNil"].tpl.Execute(g.b, struct { + StructName string + }{ + StructName: n, + }) + return err +} + +func (g *Gen) mapGenMarshalObj(n string, s *ast.MapType) error { + err := mapMarshalTpl["def"].tpl.Execute(g.b, struct { + StructName string + }{ + StructName: n, + }) + if err != nil { + return err + } + switch t := s.Value.(type) { + case *ast.Ident: + var err error + err = g.mapGenMarshalIdent(t, false) + if err != nil { + return err + } + case *ast.StarExpr: + switch ptrExp := t.X.(type) { + case *ast.Ident: + var err error + err = g.mapGenMarshalIdent(ptrExp, true) + if err != nil { + return err + } + default: + return errors.New("Unknown type") + } + } + _, err = g.b.Write([]byte("}\n")) + if err != nil { + return err + } + return nil +} + +func (g *Gen) mapGenMarshalIdent(i *ast.Ident, ptr bool) error { + switch i.String() { + case "string": + var err = g.mapMarshalString(ptr) + if err != nil { + return err + } + case "bool": + var err = g.mapMarshalBool(ptr) + if err != nil { + return err + } + case "int": + var err = g.mapMarshalInt("", ptr) + if err != nil { + return err + } + case "int64": + var err = g.mapMarshalInt("64", ptr) + if err != nil { + return err + } + case "int32": + var err = g.mapMarshalInt("32", ptr) + if err != nil { + return err + } + case "int16": + var err = g.mapMarshalInt("16", ptr) + if err != nil { + return err + } + case "int8": + var err = g.mapMarshalInt("8", ptr) + if err != nil { + return err + } + case "uint64": + var err = g.mapMarshalUint("64", ptr) + if err != nil { + return err + } + case "uint32": + var err = g.mapMarshalUint("32", ptr) + if err != nil { + return err + } + case "uint16": + var err = g.mapMarshalUint("16", ptr) + if err != nil { + return err + } + case "uint8": + var err = g.mapMarshalUint("8", ptr) + if err != nil { + return err + } + case "float64": + var err = g.mapMarshalFloat("", ptr) + if err != nil { + return err + } + default: + // if ident is already in our spec list + if sp, ok := g.genTypes[i.Name]; ok { + err := g.mapMarshalNonPrim(sp, ptr) + if err != nil { + return err + } + + } else if i.Obj != nil { + switch t := i.Obj.Decl.(type) { + case *ast.TypeSpec: + var err = g.mapMarshalNonPrim(t, ptr) + if err != nil { + return err + } + + default: + return errors.New("could not determine what to do with type " + i.String()) + } + } else { + return errors.New("Unknown type") + } + } + return nil +} + +func (g *Gen) mapMarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { + switch sp.Type.(type) { + case *ast.StructType: + return g.mapMarshalStruct(sp, ptr) + case *ast.ArrayType: + return g.mapMarshalArr(sp, ptr) + } + return nil +} + +func (g *Gen) mapMarshalString(ptr bool) error { + ptrStr := "" + if ptr { + ptrStr = "*" + } + err := mapMarshalTpl["string"].tpl.Execute(g.b, struct { + Ptr string + }{ptrStr}) + if err != nil { + return err + } + return nil +} + +func (g *Gen) mapMarshalBool(ptr bool) error { + ptrStr := "" + if ptr { + ptrStr = "*" + } + err := mapMarshalTpl["bool"].tpl.Execute(g.b, struct { + Ptr string + }{ptrStr}) + if err != nil { + return err + } + return nil +} + +func (g *Gen) mapMarshalInt(intLen string, ptr bool) error { + ptrStr := "" + if ptr { + ptrStr = "*" + } + err := mapMarshalTpl["int"].tpl.Execute(g.b, struct { + IntLen string + Ptr string + }{intLen, ptrStr}) + if err != nil { + return err + } + return nil +} + +func (g *Gen) mapMarshalUint(intLen string, ptr bool) error { + ptrStr := "" + if ptr { + ptrStr = "*" + } + err := mapMarshalTpl["uint"].tpl.Execute(g.b, struct { + IntLen string + Ptr string + }{intLen, ptrStr}) + if err != nil { + return err + } + return nil +} + +func (g *Gen) mapMarshalFloat(intLen string, ptr bool) error { + ptrStr := "" + if ptr { + ptrStr = "*" + } + err := mapMarshalTpl["float"].tpl.Execute(g.b, struct { + IntLen string + Ptr string + }{intLen, ptrStr}) + if err != nil { + return err + } + return nil +} + +func (g *Gen) mapMarshalStruct(st *ast.TypeSpec, ptr bool) error { + ptrStr := "" + if ptr { + ptrStr = "*" + } + var err = mapMarshalTpl["struct"].tpl.Execute(g.b, struct { + Ptr string + }{ptrStr}) + if err != nil { + return err + } + return nil +} + +func (g *Gen) mapMarshalArr(st *ast.TypeSpec, ptr bool) error { + ptrStr := "" + if ptr { + ptrStr = "*" + } + var err = mapMarshalTpl["arr"].tpl.Execute(g.b, struct { + Ptr string + }{ptrStr}) + if err != nil { + return err + } + return nil +} diff --git a/gojay/gen_map_marshal_tpl.go b/gojay/gen_map_marshal_tpl.go @@ -0,0 +1,60 @@ +package main + +var mapMarshalTpl = templateList{ + "def": &genTpl{ + strTpl: "\n// MarshalJSONObject implements gojay's MarshalerJSONObject" + + "\nfunc (v {{.StructName}}) MarshalJSONObject(enc *gojay.Encoder) {\n", + }, + "isNil": &genTpl{ + strTpl: ` +// IsNil returns wether the structure is nil value or not +func (v {{.StructName}}) IsNil() bool { return v == nil || len(v) == 0 } +`, + }, + "string": &genTpl{ + strTpl: ` for k, s := range v { + enc.StringKey(k, s) + } +`, + }, + "int": &genTpl{ + strTpl: ` for k, s := range v { + enc.Int{{.IntLen}}Key(k, s) + } +`, + }, + "uint": &genTpl{ + strTpl: ` for k, s := range v { + enc.Uint{{.IntLen}}Key(k, s) + } +`, + }, + "float": &genTpl{ + strTpl: ` for k, s := range v { + enc.Float{{.IntLen}}Key(k, s) + } +`, + }, + "bool": &genTpl{ + strTpl: ` for k, s := range v { + enc.BoolKey(k, s) + } +`, + }, + "struct": &genTpl{ + strTpl: ` for k, s := range v { + enc.ObjectKey(k, s) + } +`, + }, + "arr": &genTpl{ + strTpl: ` for k, s := range v { + enc.ArrayKey(k, s) + } +`, + }, +} + +func init() { + parseTemplates(mapMarshalTpl, "mapMarshal") +} diff --git a/gojay/gen_map_test.go b/gojay/gen_map_test.go @@ -0,0 +1,100 @@ +package main + +import ( + "io" + "log" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGenMap(t *testing.T) { + testCases := map[string]struct { + input io.Reader + expectedResult string + }{ + "basicMapStringString": { + input: strings.NewReader(`package test + +//gojay:json +type StrMap map[string]string +`), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v StrMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + var str string + if err := dec.String(&str); err != nil { + return err + } + v[k] = str + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v StrMap) NKeys() int { return 0 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v StrMap) MarshalJSONObject(enc *gojay.Encoder) { + for k, s := range v { + enc.StringKey(k, s) + } +} + +// IsNil returns wether the structure is nil value or not +func (v StrMap) IsNil() bool { return v == nil || len(v) == 0 } +`, + }, + "basicMapStringInt": { + input: strings.NewReader(`package test + +//gojay:json +type IntMap map[string]int +`), + expectedResult: `package + +import "github.com/francoispqt/gojay" + +// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject +func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error { + var i int + if err := dec.Int(&i); err != nil { + return err + } + v[k] = i + return nil +} + +// NKeys returns the number of keys to unmarshal +func (v IntMap) NKeys() int { return 0 } + +// MarshalJSONObject implements gojay's MarshalerJSONObject +func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) { + for k, s := range v { + enc.IntKey(k, s) + } +} + +// IsNil returns wether the structure is nil value or not +func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 } +`, + }, + } + for n, testCase := range testCases { + t.Run(n, func(t *testing.T) { + g, err := MakeGenFromReader(testCase.input) + if err != nil { + t.Fatal(err) + } + err = g.Gen() + if err != nil { + t.Fatal(err) + } + log.Print(g.b.String()) + assert.Equal(t, testCase.expectedResult, g.b.String()) + }) + } +} diff --git a/gojay/gen_map_unmarshal.go b/gojay/gen_map_unmarshal.go @@ -0,0 +1,279 @@ +package main + +import ( + "errors" + "go/ast" +) + +func (g *Gen) mapGenNKeys(n string, count int) error { + err := mapUnmarshalTpl["nKeys"].tpl.Execute(g.b, struct { + NKeys int + StructName string + }{ + NKeys: count, + StructName: n, + }) + return err +} + +func (g *Gen) mapGenUnmarshalObj(n string, s *ast.MapType) error { + err := mapUnmarshalTpl["def"].tpl.Execute(g.b, struct { + TypeName string + }{ + TypeName: n, + }) + if err != nil { + return err + } + switch t := s.Value.(type) { + case *ast.Ident: + var err error + err = g.mapGenUnmarshalIdent(t, false) + if err != nil { + return err + } + case *ast.StarExpr: + switch ptrExp := t.X.(type) { + case *ast.Ident: + var err error + err = g.mapGenUnmarshalIdent(ptrExp, true) + if err != nil { + return err + } + default: + return errors.New("Unknown type") + } + } + _, err = g.b.Write(structUnmarshalClose) + if err != nil { + return err + } + return nil +} + +func (g *Gen) mapGenUnmarshalIdent(i *ast.Ident, ptr bool) error { + switch i.String() { + case "string": + var err = g.mapUnmarshalString(ptr) + if err != nil { + return err + } + case "bool": + var err = g.mapUnmarshalBool(ptr) + if err != nil { + return err + } + case "int": + var err = g.mapUnmarshalInt("", ptr) + if err != nil { + return err + } + case "int64": + var err = g.mapUnmarshalInt("64", ptr) + if err != nil { + return err + } + case "int32": + var err = g.mapUnmarshalInt("32", ptr) + if err != nil { + return err + } + case "int16": + var err = g.mapUnmarshalInt("16", ptr) + if err != nil { + return err + } + case "int8": + var err = g.mapUnmarshalInt("8", ptr) + if err != nil { + return err + } + case "uint64": + var err = g.mapUnmarshalUint("64", ptr) + if err != nil { + return err + } + case "uint32": + var err = g.mapUnmarshalUint("32", ptr) + if err != nil { + return err + } + case "uint16": + var err = g.mapUnmarshalUint("16", ptr) + if err != nil { + return err + } + case "uint8": + var err = g.mapUnmarshalUint("8", ptr) + if err != nil { + return err + } + case "float64": + var err = g.mapUnmarshalFloat("", ptr) + if err != nil { + return err + } + default: + // if ident is already in our spec list + if sp, ok := g.genTypes[i.Name]; ok { + err := g.mapUnmarshalNonPrim(sp, ptr) + if err != nil { + return err + } + } else if i.Obj != nil { + // else check the obj infos + switch t := i.Obj.Decl.(type) { + case *ast.TypeSpec: + err := g.mapUnmarshalNonPrim(t, ptr) + if err != nil { + return err + } + default: + return errors.New("could not determine what to do with type " + i.String()) + } + } else { + return errors.New("Unknown type") + } + } + return nil +} + +func (g *Gen) mapUnmarshalNonPrim(sp *ast.TypeSpec, ptr bool) error { + switch sp.Type.(type) { + case *ast.StructType: + return g.mapUnmarshalStruct(sp, ptr) + case *ast.ArrayType: + return g.mapUnmarshalArr(sp, ptr) + } + return errors.New("Unknown type") +} + +func (g *Gen) mapUnmarshalString(ptr bool) error { + if ptr { + err := mapUnmarshalTpl["string"].tpl.Execute(g.b, struct { + Ptr string + }{""}) + if err != nil { + return err + } + } else { + err := mapUnmarshalTpl["string"].tpl.Execute(g.b, struct { + Ptr string + }{"&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *Gen) mapUnmarshalBool(ptr bool) error { + if ptr { + err := mapUnmarshalTpl["bool"].tpl.Execute(g.b, struct { + Ptr string + }{""}) + if err != nil { + return err + } + } else { + err := mapUnmarshalTpl["bool"].tpl.Execute(g.b, struct { + Ptr string + }{"&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *Gen) mapUnmarshalInt(intLen string, ptr bool) error { + if ptr { + err := mapUnmarshalTpl["int"].tpl.Execute(g.b, struct { + IntLen string + Ptr string + }{intLen, ""}) + if err != nil { + return err + } + } else { + err := mapUnmarshalTpl["int"].tpl.Execute(g.b, struct { + IntLen string + Ptr string + }{intLen, "&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *Gen) mapUnmarshalUint(intLen string, ptr bool) error { + if ptr { + err := mapUnmarshalTpl["uint"].tpl.Execute(g.b, struct { + IntLen string + Ptr string + }{intLen, ""}) + if err != nil { + return err + } + } else { + err := mapUnmarshalTpl["uint"].tpl.Execute(g.b, struct { + IntLen string + Ptr string + }{intLen, "&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *Gen) mapUnmarshalFloat(intLen string, ptr bool) error { + if ptr { + err := mapUnmarshalTpl["float"].tpl.Execute(g.b, struct { + IntLen string + Ptr string + }{intLen, ""}) + if err != nil { + return err + } + } else { + err := mapUnmarshalTpl["float"].tpl.Execute(g.b, struct { + IntLen string + Ptr string + }{intLen, "&"}) + if err != nil { + return err + } + } + return nil +} + +func (g *Gen) mapUnmarshalStruct(st *ast.TypeSpec, ptr bool) error { + if ptr { + err := mapUnmarshalTpl["structPtr"].tpl.Execute(g.b, struct { + StructName string + }{st.Name.String()}) + if err != nil { + return err + } + } else { + err := mapUnmarshalTpl["struct"].tpl.Execute(g.b, struct { + StructName string + }{st.Name.String()}) + if err != nil { + return err + } + } + return nil +} + +func (g *Gen) mapUnmarshalArr(st *ast.TypeSpec, ptr bool) error { + err := mapUnmarshalTpl["arr"].tpl.Execute(g.b, struct { + TypeName string + }{st.Name.String()}) + if err != nil { + return err + } + return nil +} diff --git a/gojay/gen_map_unmarshal_tpl.go b/gojay/gen_map_unmarshal_tpl.go @@ -0,0 +1,82 @@ +package main + +var mapUnmarshalTpl = templateList{ + "def": &genTpl{ + strTpl: "\n// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject" + + "\nfunc (v {{.TypeName}}) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {\n", + }, + "nKeys": &genTpl{ + strTpl: ` +// NKeys returns the number of keys to unmarshal +func (v {{.StructName}}) NKeys() int { return {{.NKeys}} } +`, + }, + "string": &genTpl{ + strTpl: ` var str string + if err := dec.String(&str); err != nil { + return err + } + v[k] = str +`, + }, + "int": &genTpl{ + strTpl: ` var i int{{.IntLen}} + if err := dec.Int{{.IntLen}}(&i); err != nil { + return err + } + v[k] = i +`, + }, + "uint": &genTpl{ + strTpl: ` var i uint{{.IntLen}} + if err := dec.Uint{{.IntLen}}(&i); err != nil { + return err + } + v[k] = i +`, + }, + "float": &genTpl{ + strTpl: ` var i float{{.IntLen}} + if err := dec.Float{{.IntLen}}(&i); err != nil { + return err + } + v[k] = i +`, + }, + "bool": &genTpl{ + strTpl: ` var b bool + if err := dec.Bool(&b); err != nil { + return err + } + v[k] = b +`, + }, + "struct": &genTpl{ + strTpl: ` var s = {{.StructName}}{} + if err := dec.Object(&s); err != nil { + return err + } + v[k] = s +`, + }, + "structPtr": &genTpl{ + strTpl: ` var s = &{{.StructName}}{} + if err := dec.Object(s); err != nil { + return err + } + v[k] = s +`, + }, + "arr": &genTpl{ + strTpl: ` var s = &{{.StructName}}{} + if err := dec.Array(s); err != nil { + return err + } + v[k] = s +`, + }, +} + +func init() { + parseTemplates(mapUnmarshalTpl, "mapUnmarshal") +}