diff --git a/Makefile b/Makefile index ea591b0..f877ab2 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ root: .root/src/$(PKG) clean: rm -rf .root + rm -rf tests/*_easyjson.go build: go build -i -o .root/bin/easyjson $(PKG)/easyjson diff --git a/gen/decoder.go b/gen/decoder.go index f8f43e5..021933a 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -36,16 +36,18 @@ var primitiveDecoders = map[reflect.Kind]string{ } var primitiveStringDecoders = map[reflect.Kind]string{ - reflect.Int: "in.IntStr()", - reflect.Int8: "in.Int8Str()", - reflect.Int16: "in.Int16Str()", - reflect.Int32: "in.Int32Str()", - reflect.Int64: "in.Int64Str()", - reflect.Uint: "in.UintStr()", - reflect.Uint8: "in.Uint8Str()", - reflect.Uint16: "in.Uint16Str()", - reflect.Uint32: "in.Uint32Str()", - reflect.Uint64: "in.Uint64Str()", + reflect.String: "in.String()", + reflect.Int: "in.IntStr()", + reflect.Int8: "in.Int8Str()", + reflect.Int16: "in.Int16Str()", + reflect.Int32: "in.Int32Str()", + reflect.Int64: "in.Int64Str()", + reflect.Uint: "in.UintStr()", + reflect.Uint8: "in.Uint8Str()", + reflect.Uint16: "in.Uint16Str()", + reflect.Uint32: "in.Uint32Str()", + reflect.Uint64: "in.Uint64Str()", + reflect.Uintptr: "in.UintptrStr()", } var customDecoders = map[string]string{ @@ -205,8 +207,9 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field 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) + keyDec, ok := primitiveStringDecoders[key.Kind()] + if !ok { + return fmt.Errorf("map type %v not supported: only string and integer keys are allowed", key) } elem := t.Elem() tmpVar := g.uniqueVarName() @@ -222,7 +225,7 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field fmt.Fprintln(g.out, ws+" }") fmt.Fprintln(g.out, ws+" for !in.IsDelim('}') {") - fmt.Fprintln(g.out, ws+" key := "+g.getType(t.Key())+"(in.String())") + fmt.Fprintln(g.out, ws+" key := "+g.getType(key)+"("+keyDec+")") fmt.Fprintln(g.out, ws+" in.WantColon()") fmt.Fprintln(g.out, ws+" var "+tmpVar+" "+g.getType(elem)) diff --git a/gen/encoder.go b/gen/encoder.go index e3f7c40..408ae55 100644 --- a/gen/encoder.go +++ b/gen/encoder.go @@ -33,16 +33,18 @@ var primitiveEncoders = map[reflect.Kind]string{ } var primitiveStringEncoders = map[reflect.Kind]string{ - 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.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. @@ -186,8 +188,9 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT 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) + 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() @@ -199,7 +202,7 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT fmt.Fprintln(g.out, ws+" for "+tmpVar+"Name, "+tmpVar+"Value := range "+in+" {") fmt.Fprintln(g.out, ws+" if !"+tmpVar+"First { out.RawByte(',') }") fmt.Fprintln(g.out, ws+" "+tmpVar+"First = false") - fmt.Fprintln(g.out, ws+" out.String(string("+tmpVar+"Name))") + 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); err != nil { diff --git a/jlexer/lexer.go b/jlexer/lexer.go index 563ca06..e5558ae 100644 --- a/jlexer/lexer.go +++ b/jlexer/lexer.go @@ -904,6 +904,10 @@ func (r *Lexer) UintStr() uint { return uint(r.Uint64Str()) } +func (r *Lexer) UintptrStr() uintptr { + return uintptr(r.Uint64Str()) +} + func (r *Lexer) Int8Str() int8 { s, b := r.unsafeString() if !r.Ok() { diff --git a/jwriter/writer.go b/jwriter/writer.go index 7b55293..250920d 100644 --- a/jwriter/writer.go +++ b/jwriter/writer.go @@ -196,6 +196,13 @@ func (w *Writer) Uint64Str(n uint64) { w.Buffer.Buf = append(w.Buffer.Buf, '"') } +func (w *Writer) UintptrStr(n uintptr) { + w.Buffer.EnsureSpace(20) + w.Buffer.Buf = append(w.Buffer.Buf, '"') + w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) + w.Buffer.Buf = append(w.Buffer.Buf, '"') +} + func (w *Writer) Int8Str(n int8) { w.Buffer.EnsureSpace(4) w.Buffer.Buf = append(w.Buffer.Buf, '"') diff --git a/tests/basic_test.go b/tests/basic_test.go index b727c9e..0186784 100644 --- a/tests/basic_test.go +++ b/tests/basic_test.go @@ -38,6 +38,15 @@ var testCases = []struct { {&IntsValue, IntsString}, {&mapStringStringValue, mapStringStringString}, {&namedTypeValue, namedTypeValueString}, + {&mapMyIntStringValue, mapMyIntStringValueString}, + {&mapIntStringValue, mapIntStringValueString}, + {&mapInt32StringValue, mapInt32StringValueString}, + {&mapInt64StringValue, mapInt64StringValueString}, + {&mapUintStringValue, mapUintStringValueString}, + {&mapUint32StringValue, mapUint32StringValueString}, + {&mapUint64StringValue, mapUint64StringValueString}, + {&mapUintptrStringValue, mapUintptrStringValueString}, + {&intKeyedMapStructValue, intKeyedMapStructValueString}, } func TestMarshal(t *testing.T) { diff --git a/tests/data.go b/tests/data.go index ca8676e..234e578 100644 --- a/tests/data.go +++ b/tests/data.go @@ -692,3 +692,68 @@ type EmbeddedStruct struct { var structWithInterfaceString = `{"f1":1,"f2":{"f1":11,"f2":"22"},"f3":"3"}` var structWithInterfaceValueFilled = StructWithInterface{1, &EmbeddedStruct{11, "22"}, "3"} + +//easyjson:json +type MapIntString map[int]string + +var mapIntStringValue = MapIntString{3: "hi"} +var mapIntStringValueString = `{"3":"hi"}` + +//easyjson:json +type MapInt32String map[int32]string + +var mapInt32StringValue = MapInt32String{-354634382: "life"} +var mapInt32StringValueString = `{"-354634382":"life"}` + +//easyjson:json +type MapInt64String map[int64]string + +var mapInt64StringValue = MapInt64String{-3546343826724305832: "life"} +var mapInt64StringValueString = `{"-3546343826724305832":"life"}` + +//easyjson:json +type MapUintString map[uint]string + +var mapUintStringValue = MapUintString{42: "life"} +var mapUintStringValueString = `{"42":"life"}` + +//easyjson:json +type MapUint32String map[uint32]string + +var mapUint32StringValue = MapUint32String{354634382: "life"} +var mapUint32StringValueString = `{"354634382":"life"}` + +//easyjson:json +type MapUint64String map[uint64]string + +var mapUint64StringValue = MapUint64String{3546343826724305832: "life"} +var mapUint64StringValueString = `{"3546343826724305832":"life"}` + +//easyjson:json +type MapUintptrString map[uintptr]string + +var mapUintptrStringValue = MapUintptrString{272679208: "obj"} +var mapUintptrStringValueString = `{"272679208":"obj"}` + +type MyInt int + +//easyjson:json +type MapMyIntString map[MyInt]string + +var mapMyIntStringValue = MapMyIntString{MyInt(42): "life"} +var mapMyIntStringValueString = `{"42":"life"}` + +//easyjson:json +type IntKeyedMapStruct struct { + Foo MapMyIntString `json:"foo"` + Bar map[int16]MapUint32String `json:"bar"` +} + +var intKeyedMapStructValue = IntKeyedMapStruct{ + Foo: mapMyIntStringValue, + Bar: map[int16]MapUint32String{32: mapUint32StringValue}, +} +var intKeyedMapStructValueString = `{` + + `"foo":{"42":"life"},` + + `"bar":{"32":{"354634382":"life"}}` + + `}` diff --git a/tests/errors.go b/tests/errors.go index 2ec3299..14360fc 100644 --- a/tests/errors.go +++ b/tests/errors.go @@ -21,3 +21,6 @@ type ErrorNestedStruct struct { ErrorStruct ErrorStruct `json:"error_struct"` Int int `json:"int"` } + +//easyjson:json +type ErrorIntMap map[uint32]string diff --git a/tests/errors_test.go b/tests/errors_test.go index 756f7db..40fa335 100644 --- a/tests/errors_test.go +++ b/tests/errors_test.go @@ -241,3 +241,45 @@ func TestMultipleErrorsNestedStruct(t *testing.T) { } } } + +func TestMultipleErrorsIntMap(t *testing.T) { + for i, test := range []struct { + Data []byte + Offsets []int + }{ + { + Data: []byte(`{"a":"NumErr"}`), + Offsets: []int{1}, + }, + { + Data: []byte(`{"":"ErrSyntax"}`), + Offsets: []int{1}, + }, + { + Data: []byte(`{"a":"NumErr","33147483647":"ErrRange","-1":"ErrRange"}`), + Offsets: []int{1, 14, 39}, + }, + } { + l := jlexer.Lexer{ + Data: test.Data, + UseMultipleErrors: true, + } + + var v ErrorIntMap + + v.UnmarshalEasyJSON(&l) + + errors := l.GetNonFatalErrors() + + if len(errors) != len(test.Offsets) { + t.Errorf("[%d] TestMultipleErrorsInt(): errornum: want: %d, got %d", i, len(test.Offsets), len(errors)) + return + } + + for ii, e := range errors { + if e.Offset != test.Offsets[ii] { + t.Errorf("[%d] TestMultipleErrorsInt(): offset[%d]: want %d, got %d", i, ii, test.Offsets[ii], e.Offset) + } + } + } +} diff --git a/tests/non_string_keyed_map_test.go b/tests/non_string_keyed_map_test.go deleted file mode 100644 index 392b8f3..0000000 --- a/tests/non_string_keyed_map_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package tests - -import ( - "os" - "testing" - - "github.com/mailru/easyjson/gen" -) - -type IntMap map[int]string -type IntMapSlice []IntMap -type IntMapArray [2]IntMap -type IntMapPtr *IntMap -type IntMapMap map[string]IntMap - -func TestNonStringKeyedtMapEncoder(t *testing.T) { - f := "non_string_keyed_map_easyjson.go" - for _, test := range []struct { - Data interface{} - }{ - { - Data: IntMap{}, - }, - { - Data: IntMapSlice{}, - }, - { - Data: IntMapArray{}, - }, - { - Data: IntMapPtr(nil), - }, - { - Data: IntMapMap{}, - }, - } { - g := gen.NewGenerator(f) - g.Add(test.Data) - e := g.Run(os.Stdout) - if e == nil { - t.Errorf("generation for %#v should have errored", test.Data) - } - } -}