From 0ee35918971027a4e4efaeaab70e812eaf1bfa99 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Fri, 28 Jul 2017 15:13:56 -0500 Subject: [PATCH 1/8] Detect GOPATH if not specified in $GOPATH It is no longer required to have the GOPATH environment variable set - it now defaults to ~/go, but irrespective of whether the value is the default or not it can be found in the standard output of `go env GOPATH`. This commit uses that value if the GOPATH environment variable is not set. --- parser/parser.go | 6 ++++++ parser/parser_unix.go | 9 +++++++++ parser/parser_windows.go | 9 +++++++++ 3 files changed, 24 insertions(+) diff --git a/parser/parser.go b/parser/parser.go index 1c0b94c..5bd06e9 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -4,6 +4,7 @@ import ( "go/ast" "go/parser" "go/token" + "os/exec" "strings" ) @@ -89,3 +90,8 @@ func (p *Parser) Parse(fname string, isDir bool) error { } return nil } + +func getDefaultGoPath() (string, error) { + output, err := exec.Command("go", "env", "GOPATH").Output() + return string(output), err +} diff --git a/parser/parser_unix.go b/parser/parser_unix.go index a1b9d84..09b20a2 100644 --- a/parser/parser_unix.go +++ b/parser/parser_unix.go @@ -18,6 +18,15 @@ func getPkgPath(fname string, isDir bool) (string, error) { fname = path.Join(pwd, fname) } + gopath := os.Getenv("GOPATH") + if gopath == "" { + var err error + gopath, err = getDefaultGoPath() + if err != nil { + return "", fmt.Errorf("cannot determine GOPATH: %s", err) + } + } + for _, p := range strings.Split(os.Getenv("GOPATH"), ":") { prefix := path.Join(p, "src") + "/" if rel := strings.TrimPrefix(fname, prefix); rel != fname { diff --git a/parser/parser_windows.go b/parser/parser_windows.go index 64974aa..2d741a5 100644 --- a/parser/parser_windows.go +++ b/parser/parser_windows.go @@ -22,6 +22,15 @@ func getPkgPath(fname string, isDir bool) (string, error) { fname = normalizePath(fname) + gopath := os.Getenv("GOPATH") + if gopath == "" { + var err error + gopath, err = getDefaultGoPath() + if err != nil { + return "", fmt.Errorf("cannot determine GOPATH: %s", err) + } + } + for _, p := range strings.Split(os.Getenv("GOPATH"), ";") { prefix := path.Join(normalizePath(p), "src") + "/" if rel := strings.TrimPrefix(fname, prefix); rel != fname { From 06d7c00bd4904b8ae8f55ac3acce23425fc02106 Mon Sep 17 00:00:00 2001 From: Chuntao Lu Date: Thu, 19 Oct 2017 19:06:37 -0700 Subject: [PATCH 2/8] Support integer type as map key --- gen/decoder.go | 29 ++++++++++++++++------------- gen/encoder.go | 29 ++++++++++++++++------------- jlexer/lexer.go | 4 ++++ jwriter/writer.go | 7 +++++++ 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/gen/decoder.go b/gen/decoder.go index 600bb32..1cf7257 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()", } // genTypeDecoder generates decoding code for the type t, but uses unmarshaler interface if implemented by t. @@ -198,8 +200,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() @@ -215,7 +218,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 e81f103..ab94657 100644 --- a/jlexer/lexer.go +++ b/jlexer/lexer.go @@ -903,6 +903,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, '"') From 86f5f60c4e8857efcb0fc4451e68218d4f862ec8 Mon Sep 17 00:00:00 2001 From: Chuntao Lu Date: Thu, 19 Oct 2017 19:07:55 -0700 Subject: [PATCH 3/8] Add tests for integer keyed maps --- tests/basic_test.go | 9 +++++ tests/data.go | 65 ++++++++++++++++++++++++++++++ tests/errors.go | 3 ++ tests/errors_test.go | 42 +++++++++++++++++++ tests/non_string_keyed_map_test.go | 44 -------------------- 5 files changed, 119 insertions(+), 44 deletions(-) delete mode 100644 tests/non_string_keyed_map_test.go 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) - } - } -} From 3588232891edac2367280a57732f631d3e8d3930 Mon Sep 17 00:00:00 2001 From: Chuntao Lu Date: Thu, 19 Oct 2017 19:18:13 -0700 Subject: [PATCH 4/8] Remove generated easyjson files in tests dir when make clean --- Makefile | 1 + 1 file changed, 1 insertion(+) 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 From 8b2f92253d67af5ee8ba1731c214f9458ca1f08e Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 4 Nov 2017 21:24:12 +0300 Subject: [PATCH 5/8] fix problem with installation on 32-bit system --- benchmark/data.go | 4 ++-- tests/data.go | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/benchmark/data.go b/benchmark/data.go index d2c689c..71eb91a 100644 --- a/benchmark/data.go +++ b/benchmark/data.go @@ -25,12 +25,12 @@ var smallStructData = Entities{ type SearchMetadata struct { CompletedIn float64 `json:"completed_in"` Count int `json:"count"` - MaxID int `json:"max_id"` + MaxID int64 `json:"max_id"` MaxIDStr string `json:"max_id_str"` NextResults string `json:"next_results"` Query string `json:"query"` RefreshURL string `json:"refresh_url"` - SinceID int `json:"since_id"` + SinceID int64 `json:"since_id"` SinceIDStr string `json:"since_id_str"` } diff --git a/tests/data.go b/tests/data.go index 234e578..b45ad73 100644 --- a/tests/data.go +++ b/tests/data.go @@ -89,10 +89,10 @@ var primitiveTypesString = "{" + `"Int32":` + fmt.Sprint(math.MinInt32) + `,` + `"Int64":` + fmt.Sprint(int64(math.MinInt64)) + `,` + - `"Uint":` + fmt.Sprint(math.MaxUint32) + `,` + + `"Uint":` + fmt.Sprint(uint32(math.MaxUint32)) + `,` + `"Uint8":` + fmt.Sprint(math.MaxUint8) + `,` + `"Uint16":` + fmt.Sprint(math.MaxUint16) + `,` + - `"Uint32":` + fmt.Sprint(math.MaxUint32) + `,` + + `"Uint32":` + fmt.Sprint(uint32(math.MaxUint32)) + `,` + `"Uint64":` + fmt.Sprint(uint64(math.MaxUint64)) + `,` + `"IntString":"` + fmt.Sprint(math.MinInt32) + `",` + @@ -101,10 +101,10 @@ var primitiveTypesString = "{" + `"Int32String":"` + fmt.Sprint(math.MinInt32) + `",` + `"Int64String":"` + fmt.Sprint(int64(math.MinInt64)) + `",` + - `"UintString":"` + fmt.Sprint(math.MaxUint32) + `",` + + `"UintString":"` + fmt.Sprint(uint32(math.MaxUint32)) + `",` + `"Uint8String":"` + fmt.Sprint(math.MaxUint8) + `",` + `"Uint16String":"` + fmt.Sprint(math.MaxUint16) + `",` + - `"Uint32String":"` + fmt.Sprint(math.MaxUint32) + `",` + + `"Uint32String":"` + fmt.Sprint(uint32(math.MaxUint32)) + `",` + `"Uint64String":"` + fmt.Sprint(uint64(math.MaxUint64)) + `",` + `"Float32":` + fmt.Sprint(1.5) + `,` + @@ -192,10 +192,10 @@ var namedPrimitiveTypesString = "{" + `"Int32":` + fmt.Sprint(math.MinInt32) + `,` + `"Int64":` + fmt.Sprint(int64(math.MinInt64)) + `,` + - `"Uint":` + fmt.Sprint(math.MaxUint32) + `,` + + `"Uint":` + fmt.Sprint(uint32(math.MaxUint32)) + `,` + `"Uint8":` + fmt.Sprint(math.MaxUint8) + `,` + `"Uint16":` + fmt.Sprint(math.MaxUint16) + `,` + - `"Uint32":` + fmt.Sprint(math.MaxUint32) + `,` + + `"Uint32":` + fmt.Sprint(uint32(math.MaxUint32)) + `,` + `"Uint64":` + fmt.Sprint(uint64(math.MaxUint64)) + `,` + `"Float32":` + fmt.Sprint(1.5) + `,` + @@ -570,7 +570,7 @@ type DeepNest struct { var deepNestValue = DeepNest{ SliceMap: map[Str][]Str{ - "testSliceMap": []Str{ + "testSliceMap": { "0", "1", }, @@ -579,39 +579,39 @@ var deepNestValue = DeepNest{ "testSliceMap1": []Str(nil), }, SliceMap2: map[Str][]Str{ - "testSliceMap2": []Str{}, + "testSliceMap2": {}, }, NamedSliceMap: map[Str]NamedSlice{ - "testNamedSliceMap": NamedSlice{ + "testNamedSliceMap": { "2", "3", }, }, NamedMapMap: map[Str]NamedMap{ - "testNamedMapMap": NamedMap{ + "testNamedMapMap": { "key1": "value1", }, }, MapSlice: []map[Str]Str{ - map[Str]Str{ + { "testMapSlice": "someValue", }, }, NamedSliceSlice: []NamedSlice{ - NamedSlice{ + { "someValue1", "someValue2", }, - NamedSlice{ + { "someValue3", "someValue4", }, }, NamedMapSlice: []NamedMap{ - NamedMap{ + { "key2": "value2", }, - NamedMap{ + { "key3": "value3", }, }, From a2a98adabc72c7e0536426ed116e16d04f018b73 Mon Sep 17 00:00:00 2001 From: Kwisatz Date: Sat, 4 Nov 2017 22:01:21 +0300 Subject: [PATCH 6/8] fix problem with installation on 32-bit system --- tests/data.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/data.go b/tests/data.go index b45ad73..145f093 100644 --- a/tests/data.go +++ b/tests/data.go @@ -570,7 +570,7 @@ type DeepNest struct { var deepNestValue = DeepNest{ SliceMap: map[Str][]Str{ - "testSliceMap": { + "testSliceMap": []Str{ "0", "1", }, @@ -579,39 +579,39 @@ var deepNestValue = DeepNest{ "testSliceMap1": []Str(nil), }, SliceMap2: map[Str][]Str{ - "testSliceMap2": {}, + "testSliceMap2": []Str{}, }, NamedSliceMap: map[Str]NamedSlice{ - "testNamedSliceMap": { + "testNamedSliceMap": NamedSlice{ "2", "3", }, }, NamedMapMap: map[Str]NamedMap{ - "testNamedMapMap": { + "testNamedMapMap": NamedMap{ "key1": "value1", }, }, MapSlice: []map[Str]Str{ - { + map[Str]Str{ "testMapSlice": "someValue", }, }, NamedSliceSlice: []NamedSlice{ - { + NamedSlice{ "someValue1", "someValue2", }, - { + NamedSlice{ "someValue3", "someValue4", }, }, NamedMapSlice: []NamedMap{ - { + NamedMap{ "key2": "value2", }, - { + NamedMap{ "key3": "value3", }, }, From 00e42d3ff019999ce04498985a4c96725e64f6ec Mon Sep 17 00:00:00 2001 From: Michael Monashev Date: Sun, 5 Nov 2017 22:09:23 +0300 Subject: [PATCH 7/8] avoid memory allocation while []byte encoding --- jwriter/writer.go | 50 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/jwriter/writer.go b/jwriter/writer.go index 250920d..e5a5ddf 100644 --- a/jwriter/writer.go +++ b/jwriter/writer.go @@ -2,7 +2,6 @@ package jwriter import ( - "encoding/base64" "io" "strconv" "unicode/utf8" @@ -105,9 +104,7 @@ func (w *Writer) Base64Bytes(data []byte) { return } w.Buffer.AppendByte('"') - dst := make([]byte, base64.StdEncoding.EncodedLen(len(data))) - base64.StdEncoding.Encode(dst, data) - w.Buffer.AppendBytes(dst) + w.base64(data) w.Buffer.AppendByte('"') } @@ -333,3 +330,48 @@ func (w *Writer) String(s string) { w.Buffer.AppendString(s[p:]) w.Buffer.AppendByte('"') } + +const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +const padChar = '=' + +func (w *Writer) base64(in []byte) { + + if len(in) == 0 { + return + } + + w.Buffer.EnsureSpace(((len(in) - 1) / 3 + 1) * 4) + + si := 0 + n := (len(in) / 3) * 3 + + + for si < n { + // Convert 3x 8bit source bytes into 4 bytes + val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2]) + + w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F], encode[val>>6&0x3F], encode[val&0x3F]) + + si += 3 + } + + remain := len(in) - si + if remain == 0 { + return + } + + // Add the remaining small block + val := uint(in[si+0]) << 16 + if remain == 2 { + val |= uint(in[si+1]) << 8 + } + + w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F]) + + switch remain { + case 2: + w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>6&0x3F], byte(padChar)) + case 1: + w.Buffer.Buf = append(w.Buffer.Buf, byte(padChar), byte(padChar)) + } +} From 21691f66439c5d8fff695491a429e1e91d6ca5ed Mon Sep 17 00:00:00 2001 From: Kaur Kuut Date: Tue, 14 Nov 2017 21:57:56 +0200 Subject: [PATCH 8/8] Fixed optional type wrapper's standard marshaler. --- opt/gotemplate_Bool.go | 4 +-- opt/gotemplate_Float32.go | 4 +-- opt/gotemplate_Float64.go | 4 +-- opt/gotemplate_Int.go | 4 +-- opt/gotemplate_Int16.go | 4 +-- opt/gotemplate_Int32.go | 4 +-- opt/gotemplate_Int64.go | 4 +-- opt/gotemplate_Int8.go | 4 +-- opt/gotemplate_String.go | 4 +-- opt/gotemplate_Uint.go | 4 +-- opt/gotemplate_Uint16.go | 4 +-- opt/gotemplate_Uint32.go | 4 +-- opt/gotemplate_Uint64.go | 4 +-- opt/gotemplate_Uint8.go | 4 +-- opt/optional/opt.go | 4 +-- tests/opt_test.go | 70 +++++++++++++++++++++++++++++++++++++++ 16 files changed, 100 insertions(+), 30 deletions(-) create mode 100644 tests/opt_test.go diff --git a/opt/gotemplate_Bool.go b/opt/gotemplate_Bool.go index 927d239..6978ee9 100644 --- a/opt/gotemplate_Bool.go +++ b/opt/gotemplate_Bool.go @@ -51,7 +51,7 @@ func (v *Bool) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Bool) MarshalJSON() ([]byte, error) { +func (v Bool) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Bool) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Bool) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Float32.go b/opt/gotemplate_Float32.go index 3d5f157..643cea3 100644 --- a/opt/gotemplate_Float32.go +++ b/opt/gotemplate_Float32.go @@ -51,7 +51,7 @@ func (v *Float32) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Float32) MarshalJSON() ([]byte, error) { +func (v Float32) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Float32) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Float32) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Float64.go b/opt/gotemplate_Float64.go index 9719657..75ae727 100644 --- a/opt/gotemplate_Float64.go +++ b/opt/gotemplate_Float64.go @@ -51,7 +51,7 @@ func (v *Float64) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Float64) MarshalJSON() ([]byte, error) { +func (v Float64) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Float64) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Float64) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Int.go b/opt/gotemplate_Int.go index bfe6eff..469742f 100644 --- a/opt/gotemplate_Int.go +++ b/opt/gotemplate_Int.go @@ -51,7 +51,7 @@ func (v *Int) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Int) MarshalJSON() ([]byte, error) { +func (v Int) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Int) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Int) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Int16.go b/opt/gotemplate_Int16.go index e2f5daa..b7723e2 100644 --- a/opt/gotemplate_Int16.go +++ b/opt/gotemplate_Int16.go @@ -51,7 +51,7 @@ func (v *Int16) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Int16) MarshalJSON() ([]byte, error) { +func (v Int16) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Int16) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Int16) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Int32.go b/opt/gotemplate_Int32.go index e0d6975..7c7637a 100644 --- a/opt/gotemplate_Int32.go +++ b/opt/gotemplate_Int32.go @@ -51,7 +51,7 @@ func (v *Int32) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Int32) MarshalJSON() ([]byte, error) { +func (v Int32) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Int32) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Int32) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Int64.go b/opt/gotemplate_Int64.go index 641fccc..e6ea6dc 100644 --- a/opt/gotemplate_Int64.go +++ b/opt/gotemplate_Int64.go @@ -51,7 +51,7 @@ func (v *Int64) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Int64) MarshalJSON() ([]byte, error) { +func (v Int64) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Int64) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Int64) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Int8.go b/opt/gotemplate_Int8.go index 9d6745c..ddc6665 100644 --- a/opt/gotemplate_Int8.go +++ b/opt/gotemplate_Int8.go @@ -51,7 +51,7 @@ func (v *Int8) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Int8) MarshalJSON() ([]byte, error) { +func (v Int8) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Int8) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Int8) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_String.go b/opt/gotemplate_String.go index d2799de..11c90b4 100644 --- a/opt/gotemplate_String.go +++ b/opt/gotemplate_String.go @@ -51,7 +51,7 @@ func (v *String) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *String) MarshalJSON() ([]byte, error) { +func (v String) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *String) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *String) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Uint.go b/opt/gotemplate_Uint.go index b41405b..57efd31 100644 --- a/opt/gotemplate_Uint.go +++ b/opt/gotemplate_Uint.go @@ -51,7 +51,7 @@ func (v *Uint) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Uint) MarshalJSON() ([]byte, error) { +func (v Uint) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Uint) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Uint) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Uint16.go b/opt/gotemplate_Uint16.go index 35c38fd..f28e1d2 100644 --- a/opt/gotemplate_Uint16.go +++ b/opt/gotemplate_Uint16.go @@ -51,7 +51,7 @@ func (v *Uint16) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Uint16) MarshalJSON() ([]byte, error) { +func (v Uint16) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Uint16) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Uint16) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Uint32.go b/opt/gotemplate_Uint32.go index 118b794..9fb95c0 100644 --- a/opt/gotemplate_Uint32.go +++ b/opt/gotemplate_Uint32.go @@ -51,7 +51,7 @@ func (v *Uint32) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Uint32) MarshalJSON() ([]byte, error) { +func (v Uint32) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Uint32) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Uint32) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Uint64.go b/opt/gotemplate_Uint64.go index e540c9d..0e623c6 100644 --- a/opt/gotemplate_Uint64.go +++ b/opt/gotemplate_Uint64.go @@ -51,7 +51,7 @@ func (v *Uint64) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Uint64) MarshalJSON() ([]byte, error) { +func (v Uint64) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Uint64) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Uint64) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/gotemplate_Uint8.go b/opt/gotemplate_Uint8.go index f184569..c629e44 100644 --- a/opt/gotemplate_Uint8.go +++ b/opt/gotemplate_Uint8.go @@ -51,7 +51,7 @@ func (v *Uint8) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Uint8) MarshalJSON() ([]byte, error) { +func (v Uint8) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -59,7 +59,7 @@ func (v *Uint8) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Uint8) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/opt/optional/opt.go b/opt/optional/opt.go index 29c4afa..277dd1a 100644 --- a/opt/optional/opt.go +++ b/opt/optional/opt.go @@ -52,7 +52,7 @@ func (v *Optional) UnmarshalEasyJSON(l *jlexer.Lexer) { } // MarshalJSON implements a standard json marshaler interface. -func (v *Optional) MarshalJSON() ([]byte, error) { +func (v Optional) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} v.MarshalEasyJSON(&w) return w.Buffer.BuildBytes(), w.Error @@ -60,7 +60,7 @@ func (v *Optional) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements a standard json unmarshaler interface. func (v *Optional) UnmarshalJSON(data []byte) error { - l := jlexer.Lexer{} + l := jlexer.Lexer{Data: data} v.UnmarshalEasyJSON(&l) return l.Error() } diff --git a/tests/opt_test.go b/tests/opt_test.go new file mode 100644 index 0000000..bdd32aa --- /dev/null +++ b/tests/opt_test.go @@ -0,0 +1,70 @@ +package tests + +import ( + "math" + "reflect" + "testing" + + "encoding/json" + + "github.com/mailru/easyjson/opt" +) + +// This struct type must NOT have a generated marshaler +type OptsVanilla struct { + Int opt.Int + Uint opt.Uint + + Int8 opt.Int8 + Int16 opt.Int16 + Int32 opt.Int32 + Int64 opt.Int64 + + Uint8 opt.Uint8 + Uint16 opt.Uint16 + Uint32 opt.Uint32 + Uint64 opt.Uint64 + + Float32 opt.Float32 + Float64 opt.Float64 + + Bool opt.Bool + String opt.String +} + +var optsVanillaValue = OptsVanilla{ + Int: opt.OInt(-123), + Uint: opt.OUint(123), + + Int8: opt.OInt8(math.MaxInt8), + Int16: opt.OInt16(math.MaxInt16), + Int32: opt.OInt32(math.MaxInt32), + Int64: opt.OInt64(math.MaxInt64), + + Uint8: opt.OUint8(math.MaxUint8), + Uint16: opt.OUint16(math.MaxUint16), + Uint32: opt.OUint32(math.MaxUint32), + Uint64: opt.OUint64(math.MaxUint64), + + Float32: opt.OFloat32(math.MaxFloat32), + Float64: opt.OFloat64(math.MaxFloat64), + + Bool: opt.OBool(true), + String: opt.OString("foo"), +} + +func TestOptsVanilla(t *testing.T) { + data, err := json.Marshal(optsVanillaValue) + if err != nil { + t.Errorf("Failed to marshal vanilla opts: %v", err) + } + + var ov OptsVanilla + if err := json.Unmarshal(data, &ov); err != nil { + t.Errorf("Failed to unmarshal vanilla opts: %v", err) + } + + if !reflect.DeepEqual(optsVanillaValue, ov) { + t.Errorf("Vanilla opts unmarshal returned invalid value %+v, want %+v", ov, optsVanillaValue) + } +}