From b7038ed2e0be8e2c5d79d6189990f24f0c0d046b Mon Sep 17 00:00:00 2001 From: zhangming12 Date: Mon, 16 Dec 2024 20:57:55 +0800 Subject: [PATCH 1/8] opt: encoder support Uint64ToString --- api.go | 3 + encode_test.go | 116 ++++++++++++++++++ encoder/encoder_native.go | 3 + internal/encoder/alg/opts.go | 3 +- internal/encoder/compiler.go | 8 +- internal/encoder/encoder.go | 3 + internal/encoder/ir/op.go | 38 +++++- internal/encoder/vm/vm.go | 10 ++ .../encoder/x86/assembler_regabi_amd64.go | 15 ++- sonic.go | 3 + 10 files changed, 190 insertions(+), 12 deletions(-) diff --git a/api.go b/api.go index af6be70a4..26cdbaba7 100644 --- a/api.go +++ b/api.go @@ -94,6 +94,9 @@ type Config struct { // Encode Infinity or Nan float into `null`, instead of returning an error. EncodeNullForInfOrNan bool + + // Uint64 into strings on Marshal + Uint64ToString bool } var ( diff --git a/encode_test.go b/encode_test.go index f97244a2c..119085f46 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1224,4 +1224,120 @@ func TestMarshalInfOrNan(t *testing.T) { assert.NotNil(t, err) assert.True(t, strings.Contains(err.Error(), "json: unsupported value: NaN or ±Infinite")) } +} + +func TestUint64ToString(t *testing.T) { + int64ptr := int64(432556670863027541) + uint64ptr := uint64(12372850276778298372) + cases := []struct { + name string + val any + exceptTrue string + exceptFalse string + }{ + { + name: "normal_map", + val: map[string]any{ + "int": int(12), + "int64": int64(34), + "uint64": uint64(56), + }, + exceptTrue: `{"int":12,"int64":34,"uint64":"56"}`, + exceptFalse: `{"int":12,"int64":34,"uint64":56}`, + }, + { + name: "int_key_map", + val: map[int64]any{ + int64(12): int(12), + int64(34): int64(34), + int64(56): uint64(56), + }, + exceptTrue: `{"12":12,"34":34,"56":"56"}`, + exceptFalse: `{"12":12,"34":34,"56":56}`, + }, + { + name: "uint_key_map", + val: map[uint64]any{ + uint64(12): int(12), + uint64(34): int64(34), + uint64(56): uint64(56), + }, + exceptTrue: `{"12":12,"34":34,"56":"56"}`, + exceptFalse: `{"12":12,"34":34,"56":56}`, + }, + { + name: "normal_struct", + val: struct { + Int int `json:"int"` + Int64 int64 `json:"int64"` + Uint64 uint64 `json:"uint64"` + }{ + Int: int(12), + Int64: int64(34), + Uint64: uint64(56), + }, + exceptTrue: `{"int":12,"int64":34,"uint64":"56"}`, + exceptFalse: `{"int":12,"int64":34,"uint64":56}`, + }, + { + name: "normal_slice", + val: []any{ + int(12), int64(34), uint64(56), + }, + exceptTrue: `[12,34,"56"]`, + exceptFalse: `[12,34,56]`, + }, + { + name: "single_int64", + val: int64(34), + exceptTrue: `34`, + exceptFalse: `34`, + }, + { + name: "single_uint64", + val: uint64(56), + exceptTrue: `"56"`, + exceptFalse: `56`, + }, + { + name: "int64ptr", + val: struct { + Map map[string]any + }{map[string]any{"val": struct { + Int64Ptr any + Uint64Ptr any + Int64 any + Uint64 any + }{ + Int64Ptr: &int64ptr, + Uint64Ptr: &uint64ptr, + Int64: int64(123), + Uint64: uint64(456), + }}}, + exceptTrue: `{"Map":{"val":{"Int64Ptr":432556670863027541,` + + `"Uint64Ptr":"12372850276778298372","Int64":123,"Uint64":"456"}}}`, + exceptFalse: `{"Map":{"val":{"Int64Ptr":432556670863027541,` + + `"Uint64Ptr":12372850276778298372,"Int64":123,"Uint64":456}}}`, + }, + } + + check := func(t *testing.T, except string, testRes []byte) { + var tmp1 any + assert.Nil(t, Unmarshal([]byte(testRes), &tmp1)) + var tmp2 any + assert.Nil(t, Unmarshal([]byte(except), &tmp2)) + assert.Equal(t, tmp2, tmp1) + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + b, e := Config{Uint64ToString: true}.Froze().Marshal(c.val) + assert.Nil(t, e) + check(t, c.exceptTrue, b) + + b, e = Config{Uint64ToString: false}.Froze().Marshal(c.val) + assert.Nil(t, e) + check(t, c.exceptFalse, b) + }) + } } \ No newline at end of file diff --git a/encoder/encoder_native.go b/encoder/encoder_native.go index b300ebf08..9eb159e3c 100644 --- a/encoder/encoder_native.go +++ b/encoder/encoder_native.go @@ -73,6 +73,9 @@ const ( // Encode Infinity or Nan float into `null`, instead of returning an error. EncodeNullForInfOrNan Options = encoder.EncodeNullForInfOrNan + + // Uint64 into strings on Marshal + Uint64ToString Options = encoder.Uint64ToString ) diff --git a/internal/encoder/alg/opts.go b/internal/encoder/alg/opts.go index c19e2de4e..8fc94f721 100644 --- a/internal/encoder/alg/opts.go +++ b/internal/encoder/alg/opts.go @@ -25,7 +25,8 @@ const ( BitValidateString BitNoValidateJSONMarshaler BitNoEncoderNewline - BitEncodeNullForInfOrNan + BitEncodeNullForInfOrNan + BitUint64ToString BitPointerValue = 63 ) diff --git a/internal/encoder/compiler.go b/internal/encoder/compiler.go index 902fbc98b..f19894f0d 100644 --- a/internal/encoder/compiler.go +++ b/internal/encoder/compiler.go @@ -179,7 +179,7 @@ func (self *Compiler) compileOps(p *ir.Program, sp int, vt reflect.Type) { case reflect.Bool: p.Add(ir.OP_bool) case reflect.Int: - p.Add(ir.OP_int()) + p.Add(ir.OP_int(), ir.OP_i) case reflect.Int8: p.Add(ir.OP_i8) case reflect.Int16: @@ -189,7 +189,7 @@ func (self *Compiler) compileOps(p *ir.Program, sp int, vt reflect.Type) { case reflect.Int64: p.Add(ir.OP_i64) case reflect.Uint: - p.Add(ir.OP_uint()) + p.Add(ir.OP_uint(), ir.OP_ui) case reflect.Uint8: p.Add(ir.OP_u8) case reflect.Uint16: @@ -301,7 +301,7 @@ func (self *Compiler) compileMapBodyTextKey(p *ir.Program, vk reflect.Type) { case reflect.Bool: p.Key(ir.OP_bool) case reflect.Int: - p.Key(ir.OP_int()) + p.Key(ir.OP_int(), ir.OP_i) case reflect.Int8: p.Key(ir.OP_i8) case reflect.Int16: @@ -311,7 +311,7 @@ func (self *Compiler) compileMapBodyTextKey(p *ir.Program, vk reflect.Type) { case reflect.Int64: p.Key(ir.OP_i64) case reflect.Uint: - p.Key(ir.OP_uint()) + p.Key(ir.OP_uint(), ir.OP_ui) case reflect.Uint8: p.Key(ir.OP_u8) case reflect.Uint16: diff --git a/internal/encoder/encoder.go b/internal/encoder/encoder.go index 4cba1a168..72862215b 100644 --- a/internal/encoder/encoder.go +++ b/internal/encoder/encoder.go @@ -73,6 +73,9 @@ const ( // Encode Infinity or Nan float into `null`, instead of returning an error. EncodeNullForInfOrNan Options = 1 << alg.BitEncodeNullForInfOrNan + + // Uint64 into strings on Marshal + Uint64ToString Options = 1 << alg.BitUint64ToString ) // Encoder represents a specific set of encoder configurations. diff --git a/internal/encoder/ir/op.go b/internal/encoder/ir/op.go index a0c693f00..3762d20da 100644 --- a/internal/encoder/ir/op.go +++ b/internal/encoder/ir/op.go @@ -38,10 +38,12 @@ const ( OP_i16 OP_i32 OP_i64 + OP_i OP_u8 OP_u16 OP_u32 OP_u64 + OP_ui OP_f32 OP_f64 OP_str @@ -99,10 +101,12 @@ var OpNames = [256]string{ OP_i16: "i16", OP_i32: "i32", OP_i64: "i64", + OP_i: "i", OP_u8: "u8", OP_u16: "u16", OP_u32: "u32", OP_u64: "u64", + OP_ui: "ui", OP_f32: "f32", OP_f64: "f64", OP_str: "str", @@ -197,12 +201,26 @@ func OP_is_zero_ints() Op { type Instr struct { o Op + co Op + mapKey bool u int // union {op: 8, _: 8, vi: 48}, vi maybe int or len(str) p unsafe.Pointer // maybe GoString.Ptr, or *GoType } -func NewInsOp(op Op) Instr { - return Instr{o: op} +func NewInsOp(op Op, compatOp ...Op) Instr { + i := Instr{o: op, co: op} + if len(compatOp) == 1 { + i.co = compatOp[0] + } + return i +} + +func NewInsKeyOp(op Op, compatOp ...Op) Instr { + i := Instr{o: op, co: op, mapKey: true} + if len(compatOp) == 1 { + i.co = compatOp[0] + } + return i } func NewInsVi(op Op, vi int) Instr { @@ -255,6 +273,14 @@ func (self Instr) Op() Op { return Op(self.o) } +func (self Instr) CompatOp() Op { + return Op(self.co) +} + +func (self Instr) IsMapKey() bool { + return self.mapKey +} + func (self Instr) Vi() int { return self.u } @@ -410,14 +436,14 @@ func (self Program) Rel(v []int) { } } -func (self *Program) Add(op Op) { - *self = append(*self, NewInsOp(op)) +func (self *Program) Add(op Op, co ...Op) { + *self = append(*self, NewInsOp(op, co...)) } -func (self *Program) Key(op Op) { +func (self *Program) Key(op Op, co ...Op) { *self = append(*self, NewInsVi(OP_byte, '"'), - NewInsOp(op), + NewInsKeyOp(op, co...), NewInsVi(OP_byte, '"'), ) } diff --git a/internal/encoder/vm/vm.go b/internal/encoder/vm/vm.go index b75ba807a..ea1ca767a 100644 --- a/internal/encoder/vm/vm.go +++ b/internal/encoder/vm/vm.go @@ -152,8 +152,18 @@ func Execute(b *[]byte, p unsafe.Pointer, s *vars.Stack, flags uint64, prog *ir. v := *(*uint32)(p) buf = alg.U64toa(buf, uint64(v)) case ir.OP_u64: + quote := false + if ins.CompatOp() == ir.OP_u64 && + !ins.IsMapKey() && + flags&(1< Date: Tue, 17 Dec 2024 10:34:24 +0800 Subject: [PATCH 2/8] opt: encoder add Uint64ToString (fix any => interface{}) --- encode_test.go | 1803 ++++++++++++++++++++++++------------------------ 1 file changed, 902 insertions(+), 901 deletions(-) diff --git a/encode_test.go b/encode_test.go index 119085f46..b02a96606 100644 --- a/encode_test.go +++ b/encode_test.go @@ -20,70 +20,71 @@ package sonic import ( - `bytes` - `encoding` - `encoding/json` - `fmt` - `log` - `math` - `os` - `reflect` - `regexp` - `runtime` - `runtime/debug` - `strconv` - `testing` - `time` - `unsafe` - `strings` - - `github.com/bytedance/sonic/encoder` - `github.com/stretchr/testify/assert` + "bytes" + "encoding" + "encoding/json" + "fmt" + "log" + "math" + "os" + "reflect" + "regexp" + "runtime" + "runtime/debug" + "strconv" + "strings" + "testing" + "time" + "unsafe" + + "github.com/bytedance/sonic/encoder" + "github.com/stretchr/testify/assert" ) var ( - debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == "" + debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == "" ) + func TestMain(m *testing.M) { - go func () { - if !debugAsyncGC { - return - } - println("Begin GC looping...") - for { - runtime.GC() - debug.FreeOSMemory() - } - }() - time.Sleep(time.Millisecond) - m.Run() + go func() { + if !debugAsyncGC { + return + } + println("Begin GC looping...") + for { + runtime.GC() + debug.FreeOSMemory() + } + }() + time.Sleep(time.Millisecond) + m.Run() } type Optionals struct { - Sr string `json:"sr"` - So string `json:"so,omitempty"` - Sw string `json:"-"` + Sr string `json:"sr"` + So string `json:"so,omitempty"` + Sw string `json:"-"` - Ir int `json:"omitempty"` // actually named omitempty, not an option - Io int `json:"io,omitempty"` + Ir int `json:"omitempty"` // actually named omitempty, not an option + Io int `json:"io,omitempty"` - Slr []string `json:"slr,random"` - Slo []string `json:"slo,omitempty"` + Slr []string `json:"slr,random"` + Slo []string `json:"slo,omitempty"` - Mr map[string]interface{} `json:"mr"` - Mo map[string]interface{} `json:",omitempty"` + Mr map[string]interface{} `json:"mr"` + Mo map[string]interface{} `json:",omitempty"` - Fr float64 `json:"fr"` - Fo float64 `json:"fo,omitempty"` + Fr float64 `json:"fr"` + Fo float64 `json:"fo,omitempty"` - Br bool `json:"br"` - Bo bool `json:"bo,omitempty"` + Br bool `json:"br"` + Bo bool `json:"bo,omitempty"` - Ur uint `json:"ur"` - Uo uint `json:"uo,omitempty"` + Ur uint `json:"ur"` + Uo uint `json:"uo,omitempty"` - Str struct{} `json:"str"` - Sto struct{} `json:"sto,omitempty"` + Str struct{} `json:"str"` + Sto struct{} `json:"sto,omitempty"` } var optionalsExpected = `{ @@ -99,89 +100,89 @@ var optionalsExpected = `{ }` func TestOmitEmpty(t *testing.T) { - var o Optionals - o.Sw = "something" - o.Mr = map[string]interface{}{} - o.Mo = map[string]interface{}{} - - got, err := encoder.EncodeIndented(&o, "", " ", 0) - if err != nil { - t.Fatal(err) - } - if got := string(got); got != optionalsExpected { - t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected) - } + var o Optionals + o.Sw = "something" + o.Mr = map[string]interface{}{} + o.Mo = map[string]interface{}{} + + got, err := encoder.EncodeIndented(&o, "", " ", 0) + if err != nil { + t.Fatal(err) + } + if got := string(got); got != optionalsExpected { + t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected) + } } type StringTag struct { - BoolStr bool `json:",string"` - IntStr int64 `json:",string"` - UintptrStr uintptr `json:",string"` - StrStr string `json:",string"` - NumberStr json.Number `json:",string"` + BoolStr bool `json:",string"` + IntStr int64 `json:",string"` + UintptrStr uintptr `json:",string"` + StrStr string `json:",string"` + NumberStr json.Number `json:",string"` } func TestRoundtripStringTag(t *testing.T) { - tests := []struct { - name string - in StringTag - want string // empty to just test that we roundtrip - }{ - { - name: "AllTypes", - in: StringTag{ - BoolStr: true, - IntStr: 42, - UintptrStr: 44, - StrStr: "xzbit", - NumberStr: "46", - }, - want: `{ + tests := []struct { + name string + in StringTag + want string // empty to just test that we roundtrip + }{ + { + name: "AllTypes", + in: StringTag{ + BoolStr: true, + IntStr: 42, + UintptrStr: 44, + StrStr: "xzbit", + NumberStr: "46", + }, + want: `{ "BoolStr": "true", "IntStr": "42", "UintptrStr": "44", "StrStr": "\"xzbit\"", "NumberStr": "46" }`, - }, - { - // See golang.org/issues/38173. - name: "StringDoubleEscapes", - in: StringTag{ - StrStr: "\b\f\n\r\t\"\\", - NumberStr: "0", // just to satisfy the roundtrip - }, - want: `{ + }, + { + // See golang.org/issues/38173. + name: "StringDoubleEscapes", + in: StringTag{ + StrStr: "\b\f\n\r\t\"\\", + NumberStr: "0", // just to satisfy the roundtrip + }, + want: `{ "BoolStr": "false", "IntStr": "0", "UintptrStr": "0", "StrStr": "\"\\u0008\\u000c\\n\\r\\t\\\"\\\\\"", "NumberStr": "0" }`, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // Indent with a tab prefix to make the multi-line string - // literals in the table nicer to read. - got, err := encoder.EncodeIndented(&test.in, " ", " ", 0) - if err != nil { - t.Fatal(err) - } - if got := string(got); got != test.want { - t.Fatalf(" got: %s\nwant: %s\n", got, test.want) - } - - // Verify that it round-trips. - var s2 StringTag - if err := Unmarshal(got, &s2); err != nil { - t.Fatalf("Decode: %v", err) - } - if !reflect.DeepEqual(test.in, s2) { - t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2) - } - }) - } + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + // Indent with a tab prefix to make the multi-line string + // literals in the table nicer to read. + got, err := encoder.EncodeIndented(&test.in, " ", " ", 0) + if err != nil { + t.Fatal(err) + } + if got := string(got); got != test.want { + t.Fatalf(" got: %s\nwant: %s\n", got, test.want) + } + + // Verify that it round-trips. + var s2 StringTag + if err := Unmarshal(got, &s2); err != nil { + t.Fatalf("Decode: %v", err) + } + if !reflect.DeepEqual(test.in, s2) { + t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2) + } + }) + } } // byte slices are special even if they're renamed types. @@ -190,169 +191,169 @@ type renamedByteSlice []byte type renamedRenamedByteSlice []renamedByte func TestEncodeRenamedByteSlice(t *testing.T) { - s := renamedByteSlice("abc") - result, err := Marshal(s) - if err != nil { - t.Fatal(err) - } - expect := `"YWJj"` - if string(result) != expect { - t.Errorf(" got %s want %s", result, expect) - } - r := renamedRenamedByteSlice("abc") - result, err = Marshal(r) - if err != nil { - t.Fatal(err) - } - if string(result) != expect { - t.Errorf(" got %s want %s", result, expect) - } + s := renamedByteSlice("abc") + result, err := Marshal(s) + if err != nil { + t.Fatal(err) + } + expect := `"YWJj"` + if string(result) != expect { + t.Errorf(" got %s want %s", result, expect) + } + r := renamedRenamedByteSlice("abc") + result, err = Marshal(r) + if err != nil { + t.Fatal(err) + } + if string(result) != expect { + t.Errorf(" got %s want %s", result, expect) + } } type SamePointerNoCycle struct { - Ptr1, Ptr2 *SamePointerNoCycle + Ptr1, Ptr2 *SamePointerNoCycle } var samePointerNoCycle = &SamePointerNoCycle{} type PointerCycle struct { - Ptr *PointerCycle + Ptr *PointerCycle } var pointerCycle = &PointerCycle{} type PointerCycleIndirect struct { - Ptrs []interface{} + Ptrs []interface{} } type RecursiveSlice []RecursiveSlice var ( - pointerCycleIndirect = &PointerCycleIndirect{} - mapCycle = make(map[string]interface{}) - sliceCycle = []interface{}{nil} - sliceNoCycle = []interface{}{nil, nil} - recursiveSliceCycle = []RecursiveSlice{nil} + pointerCycleIndirect = &PointerCycleIndirect{} + mapCycle = make(map[string]interface{}) + sliceCycle = []interface{}{nil} + sliceNoCycle = []interface{}{nil, nil} + recursiveSliceCycle = []RecursiveSlice{nil} ) func init() { - ptr := &SamePointerNoCycle{} - samePointerNoCycle.Ptr1 = ptr - samePointerNoCycle.Ptr2 = ptr - - pointerCycle.Ptr = pointerCycle - pointerCycleIndirect.Ptrs = []interface{}{pointerCycleIndirect} - - mapCycle["x"] = mapCycle - sliceCycle[0] = sliceCycle - sliceNoCycle[1] = sliceNoCycle[:1] - for i := 3; i > 0; i-- { - sliceNoCycle = []interface{}{sliceNoCycle} - } - recursiveSliceCycle[0] = recursiveSliceCycle + ptr := &SamePointerNoCycle{} + samePointerNoCycle.Ptr1 = ptr + samePointerNoCycle.Ptr2 = ptr + + pointerCycle.Ptr = pointerCycle + pointerCycleIndirect.Ptrs = []interface{}{pointerCycleIndirect} + + mapCycle["x"] = mapCycle + sliceCycle[0] = sliceCycle + sliceNoCycle[1] = sliceNoCycle[:1] + for i := 3; i > 0; i-- { + sliceNoCycle = []interface{}{sliceNoCycle} + } + recursiveSliceCycle[0] = recursiveSliceCycle } func TestSamePointerNoCycle(t *testing.T) { - if _, err := Marshal(samePointerNoCycle); err != nil { - t.Fatalf("unexpected error: %v", err) - } + if _, err := Marshal(samePointerNoCycle); err != nil { + t.Fatalf("unexpected error: %v", err) + } } func TestSliceNoCycle(t *testing.T) { - if _, err := Marshal(sliceNoCycle); err != nil { - t.Fatalf("unexpected error: %v", err) - } + if _, err := Marshal(sliceNoCycle); err != nil { + t.Fatalf("unexpected error: %v", err) + } } var unsupportedValues = []interface{}{ - math.NaN(), - math.Inf(-1), - math.Inf(1), - pointerCycle, - pointerCycleIndirect, - mapCycle, - sliceCycle, - recursiveSliceCycle, + math.NaN(), + math.Inf(-1), + math.Inf(1), + pointerCycle, + pointerCycleIndirect, + mapCycle, + sliceCycle, + recursiveSliceCycle, } func TestUnsupportedValues(t *testing.T) { - for _, v := range unsupportedValues { - if _, err := Marshal(v); err != nil { - if _, ok := err.(*json.UnsupportedValueError); !ok { - t.Errorf("for %v, got %T want UnsupportedValueError", v, err) - } - } else { - t.Errorf("for %v, expected error", v) - } - } + for _, v := range unsupportedValues { + if _, err := Marshal(v); err != nil { + if _, ok := err.(*json.UnsupportedValueError); !ok { + t.Errorf("for %v, got %T want UnsupportedValueError", v, err) + } + } else { + t.Errorf("for %v, expected error", v) + } + } } // Ref has Marshaler and Unmarshaler methods with pointer receiver. type Ref int func (*Ref) MarshalJSON() ([]byte, error) { - return []byte(`"ref"`), nil + return []byte(`"ref"`), nil } func (r *Ref) UnmarshalJSON([]byte) error { - *r = 12 - return nil + *r = 12 + return nil } // Val has Marshaler methods with value receiver. type Val int func (Val) MarshalJSON() ([]byte, error) { - return []byte(`"val"`), nil + return []byte(`"val"`), nil } // RefText has Marshaler and Unmarshaler methods with pointer receiver. type RefText int func (*RefText) MarshalText() ([]byte, error) { - return []byte(`"ref"`), nil + return []byte(`"ref"`), nil } func (r *RefText) UnmarshalText([]byte) error { - *r = 13 - return nil + *r = 13 + return nil } // ValText has Marshaler methods with value receiver. type ValText int func (ValText) MarshalText() ([]byte, error) { - return []byte(`"val"`), nil + return []byte(`"val"`), nil } func TestRefValMarshal(t *testing.T) { - var s = struct { - R0 Ref - R1 *Ref - R2 RefText - R3 *RefText - V0 Val - V1 *Val - V2 ValText - V3 *ValText - }{ - R0: 12, - R1: new(Ref), - R2: 14, - R3: new(RefText), - V0: 13, - V1: new(Val), - V2: 15, - V3: new(ValText), - } - const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}` - b, err := Marshal(&s) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if got := string(b); got != want { - t.Errorf("got %q, want %q", got, want) - } + var s = struct { + R0 Ref + R1 *Ref + R2 RefText + R3 *RefText + V0 Val + V1 *Val + V2 ValText + V3 *ValText + }{ + R0: 12, + R1: new(Ref), + R2: 14, + R3: new(RefText), + V0: 13, + V1: new(Val), + V2: 15, + V3: new(ValText), + } + const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}` + b, err := Marshal(&s) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if got := string(b); got != want { + t.Errorf("got %q, want %q", got, want) + } } /* @@ -397,200 +398,200 @@ func TestMarshalerEscaping(t *testing.T) { */ func TestAnonymousFields(t *testing.T) { - tests := []struct { - label string // Test name - makeInput func() interface{} // Function to create input value - want string // Expected JSON output - }{{ - // Both S1 and S2 have a field named X. From the perspective of S, - // it is ambiguous which one X refers to. - // This should not serialize either field. - label: "AmbiguousField", - makeInput: func() interface{} { - type ( - S1 struct{ x, X int } - S2 struct{ x, X int } - S struct { - S1 - S2 - } - ) - return S{S1{1, 2}, S2{3, 4}} - }, - want: `{}`, - }, { - label: "DominantField", - // Both S1 and S2 have a field named X, but since S has an X field as - // well, it takes precedence over S1.X and S2.X. - makeInput: func() interface{} { - type ( - S1 struct{ x, X int } - S2 struct{ x, X int } - S struct { - S1 - S2 - x, X int - } - ) - return S{S1{1, 2}, S2{3, 4}, 5, 6} - }, - want: `{"X":6}`, - }, { - // Unexported embedded field of non-struct type should not be serialized. - label: "UnexportedEmbeddedInt", - makeInput: func() interface{} { - type ( - myInt int - S struct{ myInt } - ) - return S{5} - }, - want: `{}`, - }, { - // Exported embedded field of non-struct type should be serialized. - label: "ExportedEmbeddedInt", - makeInput: func() interface{} { - type ( - MyInt int - S struct{ MyInt } - ) - return S{5} - }, - want: `{"MyInt":5}`, - }, { - // Unexported embedded field of pointer to non-struct type - // should not be serialized. - label: "UnexportedEmbeddedIntPointer", - makeInput: func() interface{} { - type ( - myInt int - S struct{ *myInt } - ) - s := S{new(myInt)} - *s.myInt = 5 - return s - }, - want: `{}`, - }, { - // Exported embedded field of pointer to non-struct type - // should be serialized. - label: "ExportedEmbeddedIntPointer", - makeInput: func() interface{} { - type ( - MyInt int - S struct{ *MyInt } - ) - s := S{new(MyInt)} - *s.MyInt = 5 - return s - }, - want: `{"MyInt":5}`, - }, { - // Exported fields of embedded structs should have their - // exported fields be serialized regardless of whether the struct types - // themselves are exported. - label: "EmbeddedStruct", - makeInput: func() interface{} { - type ( - s1 struct{ x, X int } - S2 struct{ y, Y int } - S struct { - s1 - S2 - } - ) - return S{s1{1, 2}, S2{3, 4}} - }, - want: `{"X":2,"Y":4}`, - }, { - // Exported fields of pointers to embedded structs should have their - // exported fields be serialized regardless of whether the struct types - // themselves are exported. - label: "EmbeddedStructPointer", - makeInput: func() interface{} { - type ( - s1 struct{ x, X int } - S2 struct{ y, Y int } - S struct { - *s1 - *S2 - } - ) - return S{&s1{1, 2}, &S2{3, 4}} - }, - want: `{"X":2,"Y":4}`, - }, { - // Exported fields on embedded unexported structs at multiple levels - // of nesting should still be serialized. - label: "NestedStructAndInts", - makeInput: func() interface{} { - type ( - MyInt1 int - MyInt2 int - myInt int - s2 struct { - MyInt2 - myInt - } - s1 struct { - MyInt1 - myInt - s2 - } - S struct { - s1 - myInt - } - ) - return S{s1{1, 2, s2{3, 4}}, 6} - }, - want: `{"MyInt1":1,"MyInt2":3}`, - }, { - // If an anonymous struct pointer field is nil, we should ignore - // the embedded fields behind it. Not properly doing so may - // result in the wrong output or reflect panics. - label: "EmbeddedFieldBehindNilPointer", - makeInput: func() interface{} { - type ( - S2 struct{ Field string } - S struct{ *S2 } - ) - return S{} - }, - want: `{}`, - }} - - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - b, err := Marshal(tt.makeInput()) - if err != nil { - t.Fatalf("Marshal() = %v, want nil error", err) - } - if string(b) != tt.want { - t.Fatalf("Marshal() = %q, want %q", b, tt.want) - } - }) - } + tests := []struct { + label string // Test name + makeInput func() interface{} // Function to create input value + want string // Expected JSON output + }{{ + // Both S1 and S2 have a field named X. From the perspective of S, + // it is ambiguous which one X refers to. + // This should not serialize either field. + label: "AmbiguousField", + makeInput: func() interface{} { + type ( + S1 struct{ x, X int } + S2 struct{ x, X int } + S struct { + S1 + S2 + } + ) + return S{S1{1, 2}, S2{3, 4}} + }, + want: `{}`, + }, { + label: "DominantField", + // Both S1 and S2 have a field named X, but since S has an X field as + // well, it takes precedence over S1.X and S2.X. + makeInput: func() interface{} { + type ( + S1 struct{ x, X int } + S2 struct{ x, X int } + S struct { + S1 + S2 + x, X int + } + ) + return S{S1{1, 2}, S2{3, 4}, 5, 6} + }, + want: `{"X":6}`, + }, { + // Unexported embedded field of non-struct type should not be serialized. + label: "UnexportedEmbeddedInt", + makeInput: func() interface{} { + type ( + myInt int + S struct{ myInt } + ) + return S{5} + }, + want: `{}`, + }, { + // Exported embedded field of non-struct type should be serialized. + label: "ExportedEmbeddedInt", + makeInput: func() interface{} { + type ( + MyInt int + S struct{ MyInt } + ) + return S{5} + }, + want: `{"MyInt":5}`, + }, { + // Unexported embedded field of pointer to non-struct type + // should not be serialized. + label: "UnexportedEmbeddedIntPointer", + makeInput: func() interface{} { + type ( + myInt int + S struct{ *myInt } + ) + s := S{new(myInt)} + *s.myInt = 5 + return s + }, + want: `{}`, + }, { + // Exported embedded field of pointer to non-struct type + // should be serialized. + label: "ExportedEmbeddedIntPointer", + makeInput: func() interface{} { + type ( + MyInt int + S struct{ *MyInt } + ) + s := S{new(MyInt)} + *s.MyInt = 5 + return s + }, + want: `{"MyInt":5}`, + }, { + // Exported fields of embedded structs should have their + // exported fields be serialized regardless of whether the struct types + // themselves are exported. + label: "EmbeddedStruct", + makeInput: func() interface{} { + type ( + s1 struct{ x, X int } + S2 struct{ y, Y int } + S struct { + s1 + S2 + } + ) + return S{s1{1, 2}, S2{3, 4}} + }, + want: `{"X":2,"Y":4}`, + }, { + // Exported fields of pointers to embedded structs should have their + // exported fields be serialized regardless of whether the struct types + // themselves are exported. + label: "EmbeddedStructPointer", + makeInput: func() interface{} { + type ( + s1 struct{ x, X int } + S2 struct{ y, Y int } + S struct { + *s1 + *S2 + } + ) + return S{&s1{1, 2}, &S2{3, 4}} + }, + want: `{"X":2,"Y":4}`, + }, { + // Exported fields on embedded unexported structs at multiple levels + // of nesting should still be serialized. + label: "NestedStructAndInts", + makeInput: func() interface{} { + type ( + MyInt1 int + MyInt2 int + myInt int + s2 struct { + MyInt2 + myInt + } + s1 struct { + MyInt1 + myInt + s2 + } + S struct { + s1 + myInt + } + ) + return S{s1{1, 2, s2{3, 4}}, 6} + }, + want: `{"MyInt1":1,"MyInt2":3}`, + }, { + // If an anonymous struct pointer field is nil, we should ignore + // the embedded fields behind it. Not properly doing so may + // result in the wrong output or reflect panics. + label: "EmbeddedFieldBehindNilPointer", + makeInput: func() interface{} { + type ( + S2 struct{ Field string } + S struct{ *S2 } + ) + return S{} + }, + want: `{}`, + }} + + for _, tt := range tests { + t.Run(tt.label, func(t *testing.T) { + b, err := Marshal(tt.makeInput()) + if err != nil { + t.Fatalf("Marshal() = %v, want nil error", err) + } + if string(b) != tt.want { + t.Fatalf("Marshal() = %q, want %q", b, tt.want) + } + }) + } } type BugA struct { - S string + S string } type BugB struct { - BugA - S string + BugA + S string } type BugC struct { - S string + S string } // Legal Go: We never use the repeated embedded field (S). type BugX struct { - A int - BugA - BugB + A int + BugA + BugB } // golang.org/issue/16042. @@ -599,10 +600,10 @@ type BugX struct { type nilJSONMarshaler string func (nm *nilJSONMarshaler) MarshalJSON() ([]byte, error) { - if nm == nil { - return Marshal("0zenil0") - } - return Marshal("zenil:" + string(*nm)) + if nm == nil { + return Marshal("0zenil0") + } + return Marshal("zenil:" + string(*nm)) } // golang.org/issue/34235. @@ -611,214 +612,214 @@ func (nm *nilJSONMarshaler) MarshalJSON() ([]byte, error) { type nilTextMarshaler string func (nm *nilTextMarshaler) MarshalText() ([]byte, error) { - if nm == nil { - return []byte("0zenil0"), nil - } - return []byte("zenil:" + string(*nm)), nil + if nm == nil { + return []byte("0zenil0"), nil + } + return []byte("zenil:" + string(*nm)), nil } // See golang.org/issue/16042 and golang.org/issue/34235. func TestNilMarshal(t *testing.T) { - testCases := []struct { - v interface{} - want string - }{ - {v: nil, want: `null`}, - {v: new(float64), want: `0`}, - {v: []interface{}(nil), want: `null`}, - {v: []string(nil), want: `null`}, - {v: map[string]string(nil), want: `null`}, - {v: []byte(nil), want: `null`}, - {v: struct{ M string }{"gopher"}, want: `{"M":"gopher"}`}, - {v: struct{ M json.Marshaler }{}, want: `{"M":null}`}, - {v: struct{ M json.Marshaler }{(*nilJSONMarshaler)(nil)}, want: `{"M":"0zenil0"}`}, - {v: struct{ M interface{} }{(*nilJSONMarshaler)(nil)}, want: `{"M":null}`}, - {v: struct{ M encoding.TextMarshaler }{}, want: `{"M":null}`}, - {v: struct{ M encoding.TextMarshaler }{(*nilTextMarshaler)(nil)}, want: `{"M":"0zenil0"}`}, - {v: struct{ M interface{} }{(*nilTextMarshaler)(nil)}, want: `{"M":null}`}, - } + testCases := []struct { + v interface{} + want string + }{ + {v: nil, want: `null`}, + {v: new(float64), want: `0`}, + {v: []interface{}(nil), want: `null`}, + {v: []string(nil), want: `null`}, + {v: map[string]string(nil), want: `null`}, + {v: []byte(nil), want: `null`}, + {v: struct{ M string }{"gopher"}, want: `{"M":"gopher"}`}, + {v: struct{ M json.Marshaler }{}, want: `{"M":null}`}, + {v: struct{ M json.Marshaler }{(*nilJSONMarshaler)(nil)}, want: `{"M":"0zenil0"}`}, + {v: struct{ M interface{} }{(*nilJSONMarshaler)(nil)}, want: `{"M":null}`}, + {v: struct{ M encoding.TextMarshaler }{}, want: `{"M":null}`}, + {v: struct{ M encoding.TextMarshaler }{(*nilTextMarshaler)(nil)}, want: `{"M":"0zenil0"}`}, + {v: struct{ M interface{} }{(*nilTextMarshaler)(nil)}, want: `{"M":null}`}, + } - for _, tt := range testCases { - out, err := Marshal(tt.v) - if err != nil || string(out) != tt.want { - t.Errorf("Marshal(%#v) = %#q, %#v, want %#q, nil", tt.v, out, err, tt.want) - continue - } - } + for _, tt := range testCases { + out, err := Marshal(tt.v) + if err != nil || string(out) != tt.want { + t.Errorf("Marshal(%#v) = %#q, %#v, want %#q, nil", tt.v, out, err, tt.want) + continue + } + } } // Issue 5245. func TestEmbeddedBug(t *testing.T) { - v := BugB{ - BugA{"A"}, - "B", - } - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{"S":"B"}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } - // Now check that the duplicate field, S, does not appear. - x := BugX{ - A: 23, - } - b, err = Marshal(x) - if err != nil { - t.Fatal("Marshal:", err) - } - want = `{"A":23}` - got = string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } + v := BugB{ + BugA{"A"}, + "B", + } + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{"S":"B"}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } + // Now check that the duplicate field, S, does not appear. + x := BugX{ + A: 23, + } + b, err = Marshal(x) + if err != nil { + t.Fatal("Marshal:", err) + } + want = `{"A":23}` + got = string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } } type BugD struct { // Same as BugA after tagging. - XXX string `json:"S"` + XXX string `json:"S"` } // BugD's tagged S field should dominate BugA's. type BugY struct { - BugA - BugD + BugA + BugD } // Test that a field with a tag dominates untagged fields. func TestTaggedFieldDominates(t *testing.T) { - v := BugY{ - BugA{"BugA"}, - BugD{"BugD"}, - } - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{"S":"BugD"}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } + v := BugY{ + BugA{"BugA"}, + BugD{"BugD"}, + } + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{"S":"BugD"}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } } // There are no tags here, so S should not appear. type BugZ struct { - BugA - BugC - BugY // Contains a tagged S field through BugD; should not dominate. + BugA + BugC + BugY // Contains a tagged S field through BugD; should not dominate. } func TestDuplicatedFieldDisappears(t *testing.T) { - v := BugZ{ - BugA{"BugA"}, - BugC{"BugC"}, - BugY{ - BugA{"nested BugA"}, - BugD{"nested BugD"}, - }, - } - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } + v := BugZ{ + BugA{"BugA"}, + BugC{"BugC"}, + BugY{ + BugA{"nested BugA"}, + BugD{"nested BugD"}, + }, + } + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } } func TestStdLibIssue10281(t *testing.T) { - type Foo struct { - N json.Number - } - x := Foo{json.Number(`invalid`)} + type Foo struct { + N json.Number + } + x := Foo{json.Number(`invalid`)} - b, err := Marshal(&x) - if err == nil { - t.Errorf("Marshal(&x) = %#q; want error", b) - } + b, err := Marshal(&x) + if err == nil { + t.Errorf("Marshal(&x) = %#q; want error", b) + } } // golang.org/issue/8582 func TestEncodePointerString(t *testing.T) { - type stringPointer struct { - N *int64 `json:"n,string"` - } - var n int64 = 42 - b, err := Marshal(stringPointer{N: &n}) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if got, want := string(b), `{"n":"42"}`; got != want { - t.Errorf("Marshal = %s, want %s", got, want) - } - var back stringPointer - err = Unmarshal(b, &back) - if err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if back.N == nil { - t.Fatalf("Unmarshaled nil N field") - } - if *back.N != 42 { - t.Fatalf("*N = %d; want 42", *back.N) - } + type stringPointer struct { + N *int64 `json:"n,string"` + } + var n int64 = 42 + b, err := Marshal(stringPointer{N: &n}) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if got, want := string(b), `{"n":"42"}`; got != want { + t.Errorf("Marshal = %s, want %s", got, want) + } + var back stringPointer + err = Unmarshal(b, &back) + if err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if back.N == nil { + t.Fatalf("Unmarshaled nil N field") + } + if *back.N != 42 { + t.Fatalf("*N = %d; want 42", *back.N) + } } var encodeStringTests = []struct { - in string - out string + in string + out string }{ - {"\x00", `"\u0000"`}, - {"\x01", `"\u0001"`}, - {"\x02", `"\u0002"`}, - {"\x03", `"\u0003"`}, - {"\x04", `"\u0004"`}, - {"\x05", `"\u0005"`}, - {"\x06", `"\u0006"`}, - {"\x07", `"\u0007"`}, - {"\x08", `"\u0008"`}, - {"\x09", `"\t"`}, - {"\x0a", `"\n"`}, - {"\x0b", `"\u000b"`}, - {"\x0c", `"\u000c"`}, - {"\x0d", `"\r"`}, - {"\x0e", `"\u000e"`}, - {"\x0f", `"\u000f"`}, - {"\x10", `"\u0010"`}, - {"\x11", `"\u0011"`}, - {"\x12", `"\u0012"`}, - {"\x13", `"\u0013"`}, - {"\x14", `"\u0014"`}, - {"\x15", `"\u0015"`}, - {"\x16", `"\u0016"`}, - {"\x17", `"\u0017"`}, - {"\x18", `"\u0018"`}, - {"\x19", `"\u0019"`}, - {"\x1a", `"\u001a"`}, - {"\x1b", `"\u001b"`}, - {"\x1c", `"\u001c"`}, - {"\x1d", `"\u001d"`}, - {"\x1e", `"\u001e"`}, - {"\x1f", `"\u001f"`}, + {"\x00", `"\u0000"`}, + {"\x01", `"\u0001"`}, + {"\x02", `"\u0002"`}, + {"\x03", `"\u0003"`}, + {"\x04", `"\u0004"`}, + {"\x05", `"\u0005"`}, + {"\x06", `"\u0006"`}, + {"\x07", `"\u0007"`}, + {"\x08", `"\u0008"`}, + {"\x09", `"\t"`}, + {"\x0a", `"\n"`}, + {"\x0b", `"\u000b"`}, + {"\x0c", `"\u000c"`}, + {"\x0d", `"\r"`}, + {"\x0e", `"\u000e"`}, + {"\x0f", `"\u000f"`}, + {"\x10", `"\u0010"`}, + {"\x11", `"\u0011"`}, + {"\x12", `"\u0012"`}, + {"\x13", `"\u0013"`}, + {"\x14", `"\u0014"`}, + {"\x15", `"\u0015"`}, + {"\x16", `"\u0016"`}, + {"\x17", `"\u0017"`}, + {"\x18", `"\u0018"`}, + {"\x19", `"\u0019"`}, + {"\x1a", `"\u001a"`}, + {"\x1b", `"\u001b"`}, + {"\x1c", `"\u001c"`}, + {"\x1d", `"\u001d"`}, + {"\x1e", `"\u001e"`}, + {"\x1f", `"\u001f"`}, } func TestEncodeString(t *testing.T) { - for _, tt := range encodeStringTests { - b, err := Marshal(tt.in) - if err != nil { - t.Errorf("Marshal(%q): %v", tt.in, err) - continue - } - out := string(b) - if out != tt.out { - t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out) - } - } + for _, tt := range encodeStringTests { + b, err := Marshal(tt.in) + if err != nil { + t.Errorf("Marshal(%q): %v", tt.in, err) + continue + } + out := string(b) + if out != tt.out { + t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out) + } + } } type jsonbyte byte @@ -838,255 +839,255 @@ type textint int func (i textint) MarshalText() ([]byte, error) { return tenc(`TI:%d`, i) } func tenc(format string, a ...interface{}) ([]byte, error) { - var buf bytes.Buffer - _, _ = fmt.Fprintf(&buf, format, a...) - return buf.Bytes(), nil + var buf bytes.Buffer + _, _ = fmt.Fprintf(&buf, format, a...) + return buf.Bytes(), nil } // Issue 13783 func TestEncodeBytekind(t *testing.T) { - testdata := []struct { - data interface{} - want string - }{ - {byte(7), "7"}, - {jsonbyte(7), `{"JB":7}`}, - {textbyte(4), `"TB:4"`}, - {jsonint(5), `{"JI":5}`}, - {textint(1), `"TI:1"`}, - {[]byte{0, 1}, `"AAE="`}, - {[]jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`}, - {[][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`}, - {[]textbyte{2, 3}, `["TB:2","TB:3"]`}, - {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`}, - {[]textint{9, 3}, `["TI:9","TI:3"]`}, - {[]int{9, 3}, `[9,3]`}, - } - for _, d := range testdata { - js, err := Marshal(d.data) - if err != nil { - t.Error(err) - continue - } - got, want := string(js), d.want - if got != want { - t.Errorf("got %s, want %s", got, want) - } - } + testdata := []struct { + data interface{} + want string + }{ + {byte(7), "7"}, + {jsonbyte(7), `{"JB":7}`}, + {textbyte(4), `"TB:4"`}, + {jsonint(5), `{"JI":5}`}, + {textint(1), `"TI:1"`}, + {[]byte{0, 1}, `"AAE="`}, + {[]jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`}, + {[][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`}, + {[]textbyte{2, 3}, `["TB:2","TB:3"]`}, + {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`}, + {[]textint{9, 3}, `["TI:9","TI:3"]`}, + {[]int{9, 3}, `[9,3]`}, + } + for _, d := range testdata { + js, err := Marshal(d.data) + if err != nil { + t.Error(err) + continue + } + got, want := string(js), d.want + if got != want { + t.Errorf("got %s, want %s", got, want) + } + } } // https://golang.org/issue/33675 func TestNilMarshalerTextMapKey(t *testing.T) { - b, err := Marshal(map[*unmarshalerText]int{ - (*unmarshalerText)(nil): 1, - }) - if err != nil { - t.Fatalf("Failed to Marshal *text.Marshaler: %v", err) - } - const want = `{"":1}` - if string(b) != want { - t.Errorf("Marshal map with *text.Marshaler keys: got %#q, want %#q", b, want) - } + b, err := Marshal(map[*unmarshalerText]int{ + (*unmarshalerText)(nil): 1, + }) + if err != nil { + t.Fatalf("Failed to Marshal *text.Marshaler: %v", err) + } + const want = `{"":1}` + if string(b) != want { + t.Errorf("Marshal map with *text.Marshaler keys: got %#q, want %#q", b, want) + } } var re = regexp.MustCompile // syntactic checks on form of marshaled floating point numbers. var badFloatREs = []*regexp.Regexp{ - re(`p`), // no binary exponential notation - re(`^\+`), // no leading + sign - re(`^-?0[^.]`), // no unnecessary leading zeros - re(`^-?\.`), // leading zero required before decimal point - re(`\.(e|$)`), // no trailing decimal - re(`\.[0-9]+0(e|$)`), // no trailing zero in fraction - re(`^-?(0|[0-9]{2,})\..*e`), // exponential notation must have normalized mantissa - re(`e[+-]0`), // exponent must not have leading zeros - re(`e-[1-6]$`), // not tiny enough for exponential notation - re(`e+(.|1.|20)$`), // not big enough for exponential notation - re(`^-?0\.0000000`), // too tiny, should use exponential notation - re(`^-?[0-9]{22}`), // too big, should use exponential notation - re(`[1-9][0-9]{16}[1-9]`), // too many significant digits in integer - re(`[1-9][0-9.]{17}[1-9]`), // too many significant digits in decimal + re(`p`), // no binary exponential notation + re(`^\+`), // no leading + sign + re(`^-?0[^.]`), // no unnecessary leading zeros + re(`^-?\.`), // leading zero required before decimal point + re(`\.(e|$)`), // no trailing decimal + re(`\.[0-9]+0(e|$)`), // no trailing zero in fraction + re(`^-?(0|[0-9]{2,})\..*e`), // exponential notation must have normalized mantissa + re(`e[+-]0`), // exponent must not have leading zeros + re(`e-[1-6]$`), // not tiny enough for exponential notation + re(`e+(.|1.|20)$`), // not big enough for exponential notation + re(`^-?0\.0000000`), // too tiny, should use exponential notation + re(`^-?[0-9]{22}`), // too big, should use exponential notation + re(`[1-9][0-9]{16}[1-9]`), // too many significant digits in integer + re(`[1-9][0-9.]{17}[1-9]`), // too many significant digits in decimal } func TestMarshalFloat(t *testing.T) { - t.Parallel() - nfail := 0 - test := func(f float64, bits int) { - vf := interface{}(f) - if bits == 32 { - f = float64(float32(f)) // round - vf = float32(f) - } - bout, err := Marshal(vf) - if err != nil { - t.Errorf("Marshal(%T(%g)): %v", vf, vf, err) - nfail++ - return - } - out := string(bout) - - // result must convert back to the same float - g, err := strconv.ParseFloat(out, bits) - if err != nil { - t.Errorf("Marshal(%T(%g)) = %q, cannot parse back: %v", vf, vf, out, err) - nfail++ - return - } - if f != g { - t.Errorf("Marshal(%T(%g)) = %q (is %g, not %g)", vf, vf, out, float32(g), vf) - nfail++ - return - } - - for _, re := range badFloatREs { - if re.MatchString(out) { - t.Errorf("Marshal(%T(%g)) = %q, must not match /%s/", vf, vf, out, re) - nfail++ - return - } - } - } + t.Parallel() + nfail := 0 + test := func(f float64, bits int) { + vf := interface{}(f) + if bits == 32 { + f = float64(float32(f)) // round + vf = float32(f) + } + bout, err := Marshal(vf) + if err != nil { + t.Errorf("Marshal(%T(%g)): %v", vf, vf, err) + nfail++ + return + } + out := string(bout) + + // result must convert back to the same float + g, err := strconv.ParseFloat(out, bits) + if err != nil { + t.Errorf("Marshal(%T(%g)) = %q, cannot parse back: %v", vf, vf, out, err) + nfail++ + return + } + if f != g { + t.Errorf("Marshal(%T(%g)) = %q (is %g, not %g)", vf, vf, out, float32(g), vf) + nfail++ + return + } + + for _, re := range badFloatREs { + if re.MatchString(out) { + t.Errorf("Marshal(%T(%g)) = %q, must not match /%s/", vf, vf, out, re) + nfail++ + return + } + } + } - var ( - bigger = math.Inf(+1) - smaller = math.Inf(-1) - ) - - var digits = "1.2345678901234567890123" - for i := len(digits); i >= 2; i-- { - if testing.Short() && i < len(digits)-4 { - break - } - for exp := -30; exp <= 30; exp++ { - for _, sign := range "+-" { - for bits := 32; bits <= 64; bits += 32 { - s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp) - f, err := strconv.ParseFloat(s, bits) - if err != nil { - log.Fatal(err) - } - next := math.Nextafter - if bits == 32 { - next = func(g, h float64) float64 { - return float64(math.Nextafter32(float32(g), float32(h))) - } - } - test(f, bits) - test(next(f, bigger), bits) - test(next(f, smaller), bits) - if nfail > 50 { - t.Fatalf("stopping test early") - } - } - } - } - } - test(0, 64) - test(math.Copysign(0, -1), 64) - test(0, 32) - test(math.Copysign(0, -1), 32) + var ( + bigger = math.Inf(+1) + smaller = math.Inf(-1) + ) + + var digits = "1.2345678901234567890123" + for i := len(digits); i >= 2; i-- { + if testing.Short() && i < len(digits)-4 { + break + } + for exp := -30; exp <= 30; exp++ { + for _, sign := range "+-" { + for bits := 32; bits <= 64; bits += 32 { + s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp) + f, err := strconv.ParseFloat(s, bits) + if err != nil { + log.Fatal(err) + } + next := math.Nextafter + if bits == 32 { + next = func(g, h float64) float64 { + return float64(math.Nextafter32(float32(g), float32(h))) + } + } + test(f, bits) + test(next(f, bigger), bits) + test(next(f, smaller), bits) + if nfail > 50 { + t.Fatalf("stopping test early") + } + } + } + } + } + test(0, 64) + test(math.Copysign(0, -1), 64) + test(0, 32) + test(math.Copysign(0, -1), 32) } func TestMarshalRawMessageValue(t *testing.T) { - type ( - T1 struct { - M json.RawMessage `json:",omitempty"` - } - T2 struct { - M *json.RawMessage `json:",omitempty"` - } - ) - - var ( - rawNil = json.RawMessage(nil) - rawEmpty = json.RawMessage([]byte{}) - rawText = json.RawMessage(`"foo"`) - ) - - tests := []struct { - in interface{} - want string - ok bool - }{ - // Test with nil RawMessage. - {rawNil, "null", true}, - {&rawNil, "null", true}, - {[]interface{}{rawNil}, "[null]", true}, - {&[]interface{}{rawNil}, "[null]", true}, - {[]interface{}{&rawNil}, "[null]", true}, - {&[]interface{}{&rawNil}, "[null]", true}, - {struct{ M json.RawMessage }{rawNil}, `{"M":null}`, true}, - {&struct{ M json.RawMessage }{rawNil}, `{"M":null}`, true}, - {struct{ M *json.RawMessage }{&rawNil}, `{"M":null}`, true}, - {&struct{ M *json.RawMessage }{&rawNil}, `{"M":null}`, true}, - {map[string]interface{}{"M": rawNil}, `{"M":null}`, true}, - {&map[string]interface{}{"M": rawNil}, `{"M":null}`, true}, - {map[string]interface{}{"M": &rawNil}, `{"M":null}`, true}, - {&map[string]interface{}{"M": &rawNil}, `{"M":null}`, true}, - {T1{rawNil}, "{}", true}, - {T2{&rawNil}, `{"M":null}`, true}, - {&T1{rawNil}, "{}", true}, - {&T2{&rawNil}, `{"M":null}`, true}, - - // Test with empty, but non-nil, RawMessage. - {rawEmpty, "", false}, - {&rawEmpty, "", false}, - {[]interface{}{rawEmpty}, "", false}, - {&[]interface{}{rawEmpty}, "", false}, - {[]interface{}{&rawEmpty}, "", false}, - {&[]interface{}{&rawEmpty}, "", false}, - {struct{ X json.RawMessage }{rawEmpty}, "", false}, - {&struct{ X json.RawMessage }{rawEmpty}, "", false}, - {struct{ X *json.RawMessage }{&rawEmpty}, "", false}, - {&struct{ X *json.RawMessage }{&rawEmpty}, "", false}, - {map[string]interface{}{"nil": rawEmpty}, "", false}, - {&map[string]interface{}{"nil": rawEmpty}, "", false}, - {map[string]interface{}{"nil": &rawEmpty}, "", false}, - {&map[string]interface{}{"nil": &rawEmpty}, "", false}, - {T1{rawEmpty}, "{}", true}, - {T2{&rawEmpty}, "", false}, - {&T1{rawEmpty}, "{}", true}, - {&T2{&rawEmpty}, "", false}, - - // Test with RawMessage with some text. - // - // The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo". - // This behavior was intentionally changed in Go 1.8. - // See https://golang.org/issues/14493#issuecomment-255857318 - {rawText, `"foo"`, true}, // Issue6458 - {&rawText, `"foo"`, true}, - {[]interface{}{rawText}, `["foo"]`, true}, // Issue6458 - {&[]interface{}{rawText}, `["foo"]`, true}, // Issue6458 - {[]interface{}{&rawText}, `["foo"]`, true}, - {&[]interface{}{&rawText}, `["foo"]`, true}, - {struct{ M json.RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458 - {&struct{ M json.RawMessage }{rawText}, `{"M":"foo"}`, true}, - {struct{ M *json.RawMessage }{&rawText}, `{"M":"foo"}`, true}, - {&struct{ M *json.RawMessage }{&rawText}, `{"M":"foo"}`, true}, - {map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 - {&map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 - {map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true}, - {&map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true}, - {T1{rawText}, `{"M":"foo"}`, true}, // Issue6458 - {T2{&rawText}, `{"M":"foo"}`, true}, - {&T1{rawText}, `{"M":"foo"}`, true}, - {&T2{&rawText}, `{"M":"foo"}`, true}, - } + type ( + T1 struct { + M json.RawMessage `json:",omitempty"` + } + T2 struct { + M *json.RawMessage `json:",omitempty"` + } + ) + + var ( + rawNil = json.RawMessage(nil) + rawEmpty = json.RawMessage([]byte{}) + rawText = json.RawMessage(`"foo"`) + ) + + tests := []struct { + in interface{} + want string + ok bool + }{ + // Test with nil RawMessage. + {rawNil, "null", true}, + {&rawNil, "null", true}, + {[]interface{}{rawNil}, "[null]", true}, + {&[]interface{}{rawNil}, "[null]", true}, + {[]interface{}{&rawNil}, "[null]", true}, + {&[]interface{}{&rawNil}, "[null]", true}, + {struct{ M json.RawMessage }{rawNil}, `{"M":null}`, true}, + {&struct{ M json.RawMessage }{rawNil}, `{"M":null}`, true}, + {struct{ M *json.RawMessage }{&rawNil}, `{"M":null}`, true}, + {&struct{ M *json.RawMessage }{&rawNil}, `{"M":null}`, true}, + {map[string]interface{}{"M": rawNil}, `{"M":null}`, true}, + {&map[string]interface{}{"M": rawNil}, `{"M":null}`, true}, + {map[string]interface{}{"M": &rawNil}, `{"M":null}`, true}, + {&map[string]interface{}{"M": &rawNil}, `{"M":null}`, true}, + {T1{rawNil}, "{}", true}, + {T2{&rawNil}, `{"M":null}`, true}, + {&T1{rawNil}, "{}", true}, + {&T2{&rawNil}, `{"M":null}`, true}, + + // Test with empty, but non-nil, RawMessage. + {rawEmpty, "", false}, + {&rawEmpty, "", false}, + {[]interface{}{rawEmpty}, "", false}, + {&[]interface{}{rawEmpty}, "", false}, + {[]interface{}{&rawEmpty}, "", false}, + {&[]interface{}{&rawEmpty}, "", false}, + {struct{ X json.RawMessage }{rawEmpty}, "", false}, + {&struct{ X json.RawMessage }{rawEmpty}, "", false}, + {struct{ X *json.RawMessage }{&rawEmpty}, "", false}, + {&struct{ X *json.RawMessage }{&rawEmpty}, "", false}, + {map[string]interface{}{"nil": rawEmpty}, "", false}, + {&map[string]interface{}{"nil": rawEmpty}, "", false}, + {map[string]interface{}{"nil": &rawEmpty}, "", false}, + {&map[string]interface{}{"nil": &rawEmpty}, "", false}, + {T1{rawEmpty}, "{}", true}, + {T2{&rawEmpty}, "", false}, + {&T1{rawEmpty}, "{}", true}, + {&T2{&rawEmpty}, "", false}, + + // Test with RawMessage with some text. + // + // The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo". + // This behavior was intentionally changed in Go 1.8. + // See https://golang.org/issues/14493#issuecomment-255857318 + {rawText, `"foo"`, true}, // Issue6458 + {&rawText, `"foo"`, true}, + {[]interface{}{rawText}, `["foo"]`, true}, // Issue6458 + {&[]interface{}{rawText}, `["foo"]`, true}, // Issue6458 + {[]interface{}{&rawText}, `["foo"]`, true}, + {&[]interface{}{&rawText}, `["foo"]`, true}, + {struct{ M json.RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458 + {&struct{ M json.RawMessage }{rawText}, `{"M":"foo"}`, true}, + {struct{ M *json.RawMessage }{&rawText}, `{"M":"foo"}`, true}, + {&struct{ M *json.RawMessage }{&rawText}, `{"M":"foo"}`, true}, + {map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 + {&map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 + {map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true}, + {&map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true}, + {T1{rawText}, `{"M":"foo"}`, true}, // Issue6458 + {T2{&rawText}, `{"M":"foo"}`, true}, + {&T1{rawText}, `{"M":"foo"}`, true}, + {&T2{&rawText}, `{"M":"foo"}`, true}, + } - for i, tt := range tests { - b, err := Marshal(tt.in) - if ok := err == nil; ok != tt.ok { - if err != nil { - t.Errorf("test %d, unexpected failure: %v", i, err) - } else { - t.Errorf("test %d, unexpected success", i) - } - } - if got := string(b); got != tt.want { - t.Errorf("test %d, Marshal(%#v) = %q, want %q", i, tt.in, got, tt.want) - } - } + for i, tt := range tests { + b, err := Marshal(tt.in) + if ok := err == nil; ok != tt.ok { + if err != nil { + t.Errorf("test %d, unexpected failure: %v", i, err) + } else { + t.Errorf("test %d, unexpected success", i) + } + } + if got := string(b); got != tt.want { + t.Errorf("test %d, Marshal(%#v) = %q, want %q", i, tt.in, got, tt.want) + } + } } type marshalPanic struct{} @@ -1094,136 +1095,136 @@ type marshalPanic struct{} func (marshalPanic) MarshalJSON() ([]byte, error) { panic(0xdead) } func TestMarshalPanic(t *testing.T) { - defer func() { - if got := recover(); !reflect.DeepEqual(got, 0xdead) { - t.Errorf("panic() = (%T)(%v), want 0xdead", got, got) - } - }() - _, _ = Marshal(&marshalPanic{}) - t.Error("Marshal should have panicked") + defer func() { + if got := recover(); !reflect.DeepEqual(got, 0xdead) { + t.Errorf("panic() = (%T)(%v), want 0xdead", got, got) + } + }() + _, _ = Marshal(&marshalPanic{}) + t.Error("Marshal should have panicked") } //goland:noinspection NonAsciiCharacters func TestMarshalUncommonFieldNames(t *testing.T) { - v := struct { - A0, À, Aβ int - }{} - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{"A0":0,"À":0,"Aβ":0}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } + v := struct { + A0, À, Aβ int + }{} + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{"A0":0,"À":0,"Aβ":0}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } } type DummyMarshalerError struct { - Type reflect.Type - Err error - SourceFunc string + Type reflect.Type + Err error + SourceFunc string } func (self *DummyMarshalerError) err() *json.MarshalerError { - return (*json.MarshalerError)(unsafe.Pointer(self)) + return (*json.MarshalerError)(unsafe.Pointer(self)) } func TestMarshalerError(t *testing.T) { - s := "test variable" - st := reflect.TypeOf(s) - errText := "json: test error" - - tests := []struct { - err *json.MarshalerError - want string - }{ - { - (&DummyMarshalerError{st, fmt.Errorf(errText), ""}).err(), - "json: error calling MarshalJSON for type " + st.String() + ": " + errText, - }, - { - (&DummyMarshalerError{st, fmt.Errorf(errText), "TestMarshalerError"}).err(), - "json: error calling TestMarshalerError for type " + st.String() + ": " + errText, - }, - } + s := "test variable" + st := reflect.TypeOf(s) + errText := "json: test error" - for i, tt := range tests { - got := tt.err.Error() - if got != tt.want { - t.Errorf("MarshalerError test %d, got: %s, want: %s", i, got, tt.want) - } - } + tests := []struct { + err *json.MarshalerError + want string + }{ + { + (&DummyMarshalerError{st, fmt.Errorf(errText), ""}).err(), + "json: error calling MarshalJSON for type " + st.String() + ": " + errText, + }, + { + (&DummyMarshalerError{st, fmt.Errorf(errText), "TestMarshalerError"}).err(), + "json: error calling TestMarshalerError for type " + st.String() + ": " + errText, + }, + } + + for i, tt := range tests { + got := tt.err.Error() + if got != tt.want { + t.Errorf("MarshalerError test %d, got: %s, want: %s", i, got, tt.want) + } + } } func TestMarshalNullNil(t *testing.T) { - var v = struct { - A []int - B map[string]int - }{} - o, e := Marshal(v) - assert.Nil(t, e) - assert.Equal(t, `{"A":null,"B":null}`, string(o)) - o, e = Config{ - NoNullSliceOrMap: true, - }.Froze().Marshal(v) - assert.Nil(t, e) - assert.Equal(t, `{"A":[],"B":{}}`, string(o)) + var v = struct { + A []int + B map[string]int + }{} + o, e := Marshal(v) + assert.Nil(t, e) + assert.Equal(t, `{"A":null,"B":null}`, string(o)) + o, e = Config{ + NoNullSliceOrMap: true, + }.Froze().Marshal(v) + assert.Nil(t, e) + assert.Equal(t, `{"A":[],"B":{}}`, string(o)) } func TestEncoder_LongestInvalidUtf8(t *testing.T) { - for _, data := range([]string{ - "\"" + strings.Repeat("\x80", 4096) + "\"", - "\"" + strings.Repeat("\x80", 4095) + "\"", - "\"" + strings.Repeat("\x80", 4097) + "\"", - "\"" + strings.Repeat("\x80", 12345) + "\"", - }) { - testEncodeInvalidUtf8(t, []byte(data)) - } + for _, data := range []string{ + "\"" + strings.Repeat("\x80", 4096) + "\"", + "\"" + strings.Repeat("\x80", 4095) + "\"", + "\"" + strings.Repeat("\x80", 4097) + "\"", + "\"" + strings.Repeat("\x80", 12345) + "\"", + } { + testEncodeInvalidUtf8(t, []byte(data)) + } } func testEncodeInvalidUtf8(t *testing.T, data []byte) { - jgot, jerr := json.Marshal(data) - sgot, serr := ConfigStd.Marshal(data) - assert.Equal(t, serr != nil, jerr != nil) - if jerr == nil { - assert.Equal(t, sgot, jgot) - } + jgot, jerr := json.Marshal(data) + sgot, serr := ConfigStd.Marshal(data) + assert.Equal(t, serr != nil, jerr != nil) + if jerr == nil { + assert.Equal(t, sgot, jgot) + } } func TestEncoder_RandomInvalidUtf8(t *testing.T) { - nums := 1000 - maxLen := 1000 - for i := 0; i < nums; i++ { - testEncodeInvalidUtf8(t, genRandJsonBytes(maxLen)) - testEncodeInvalidUtf8(t, genRandJsonRune(maxLen)) - } + nums := 1000 + maxLen := 1000 + for i := 0; i < nums; i++ { + testEncodeInvalidUtf8(t, genRandJsonBytes(maxLen)) + testEncodeInvalidUtf8(t, genRandJsonRune(maxLen)) + } } func TestMarshalInfOrNan(t *testing.T) { - tests := [] interface{}{ - math.Inf(1), math.Inf(-1), math.NaN(), float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), []interface{}{math.Inf(1), math.Inf(-1), math.NaN(), float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN())}, + tests := []interface{}{ + math.Inf(1), math.Inf(-1), math.NaN(), float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), []interface{}{math.Inf(1), math.Inf(-1), math.NaN(), float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN())}, []float64{math.Inf(1), math.Inf(-1), math.NaN()}, []float32{float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN())}, - } + } - allowNanInf := Config { - EncodeNullForInfOrNan: true, - }.Froze() - for _, tt := range tests { - b, err := allowNanInf.Marshal(tt) - assert.Nil(t, err) - if len(b) == 4 { - assert.Equal(t, string(b), "null") - } else { - println(string(b)) - } - - b, err = Marshal(tt) - assert.NotNil(t, err) - assert.True(t, strings.Contains(err.Error(), "json: unsupported value: NaN or ±Infinite")) - } + allowNanInf := Config{ + EncodeNullForInfOrNan: true, + }.Froze() + for _, tt := range tests { + b, err := allowNanInf.Marshal(tt) + assert.Nil(t, err) + if len(b) == 4 { + assert.Equal(t, string(b), "null") + } else { + println(string(b)) + } + + b, err = Marshal(tt) + assert.NotNil(t, err) + assert.True(t, strings.Contains(err.Error(), "json: unsupported value: NaN or ±Infinite")) + } } func TestUint64ToString(t *testing.T) { @@ -1231,13 +1232,13 @@ func TestUint64ToString(t *testing.T) { uint64ptr := uint64(12372850276778298372) cases := []struct { name string - val any + val interface{} exceptTrue string exceptFalse string }{ { name: "normal_map", - val: map[string]any{ + val: map[string]interface{}{ "int": int(12), "int64": int64(34), "uint64": uint64(56), @@ -1247,7 +1248,7 @@ func TestUint64ToString(t *testing.T) { }, { name: "int_key_map", - val: map[int64]any{ + val: map[int64]interface{}{ int64(12): int(12), int64(34): int64(34), int64(56): uint64(56), @@ -1255,9 +1256,9 @@ func TestUint64ToString(t *testing.T) { exceptTrue: `{"12":12,"34":34,"56":"56"}`, exceptFalse: `{"12":12,"34":34,"56":56}`, }, - { + { name: "uint_key_map", - val: map[uint64]any{ + val: map[uint64]interface{}{ uint64(12): int(12), uint64(34): int64(34), uint64(56): uint64(56), @@ -1281,7 +1282,7 @@ func TestUint64ToString(t *testing.T) { }, { name: "normal_slice", - val: []any{ + val: []interface{}{ int(12), int64(34), uint64(56), }, exceptTrue: `[12,34,"56"]`, @@ -1302,12 +1303,12 @@ func TestUint64ToString(t *testing.T) { { name: "int64ptr", val: struct { - Map map[string]any - }{map[string]any{"val": struct { - Int64Ptr any - Uint64Ptr any - Int64 any - Uint64 any + Map map[string]interface{} + }{map[string]interface{}{"val": struct { + Int64Ptr interface{} + Uint64Ptr interface{} + Int64 interface{} + Uint64 interface{} }{ Int64Ptr: &int64ptr, Uint64Ptr: &uint64ptr, @@ -1322,9 +1323,9 @@ func TestUint64ToString(t *testing.T) { } check := func(t *testing.T, except string, testRes []byte) { - var tmp1 any + var tmp1 interface{} assert.Nil(t, Unmarshal([]byte(testRes), &tmp1)) - var tmp2 any + var tmp2 interface{} assert.Nil(t, Unmarshal([]byte(except), &tmp2)) assert.Equal(t, tmp2, tmp1) } @@ -1340,4 +1341,4 @@ func TestUint64ToString(t *testing.T) { check(t, c.exceptFalse, b) }) } -} \ No newline at end of file +} From 1db3dd061ca5c4107ba3e4656ced39c7c4242818 Mon Sep 17 00:00:00 2001 From: zhangming12 Date: Thu, 19 Dec 2024 16:12:59 +0800 Subject: [PATCH 3/8] reset encode_test.go code style and vm.go --- encode_test.go | 1775 ++++++++++++++++++------------------- internal/encoder/vm/vm.go | 17 +- 2 files changed, 895 insertions(+), 897 deletions(-) diff --git a/encode_test.go b/encode_test.go index b02a96606..683a03fe6 100644 --- a/encode_test.go +++ b/encode_test.go @@ -20,71 +20,70 @@ package sonic import ( - "bytes" - "encoding" - "encoding/json" - "fmt" - "log" - "math" - "os" - "reflect" - "regexp" - "runtime" - "runtime/debug" - "strconv" - "strings" - "testing" - "time" - "unsafe" - - "github.com/bytedance/sonic/encoder" - "github.com/stretchr/testify/assert" + `bytes` + `encoding` + `encoding/json` + `fmt` + `log` + `math` + `os` + `reflect` + `regexp` + `runtime` + `runtime/debug` + `strconv` + `testing` + `time` + `unsafe` + `strings` + + `github.com/bytedance/sonic/encoder` + `github.com/stretchr/testify/assert` ) var ( - debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == "" + debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == "" ) - func TestMain(m *testing.M) { - go func() { - if !debugAsyncGC { - return - } - println("Begin GC looping...") - for { - runtime.GC() - debug.FreeOSMemory() - } - }() - time.Sleep(time.Millisecond) - m.Run() + go func () { + if !debugAsyncGC { + return + } + println("Begin GC looping...") + for { + runtime.GC() + debug.FreeOSMemory() + } + }() + time.Sleep(time.Millisecond) + m.Run() } type Optionals struct { - Sr string `json:"sr"` - So string `json:"so,omitempty"` - Sw string `json:"-"` + Sr string `json:"sr"` + So string `json:"so,omitempty"` + Sw string `json:"-"` - Ir int `json:"omitempty"` // actually named omitempty, not an option - Io int `json:"io,omitempty"` + Ir int `json:"omitempty"` // actually named omitempty, not an option + Io int `json:"io,omitempty"` - Slr []string `json:"slr,random"` - Slo []string `json:"slo,omitempty"` + Slr []string `json:"slr,random"` + Slo []string `json:"slo,omitempty"` - Mr map[string]interface{} `json:"mr"` - Mo map[string]interface{} `json:",omitempty"` + Mr map[string]interface{} `json:"mr"` + Mo map[string]interface{} `json:",omitempty"` - Fr float64 `json:"fr"` - Fo float64 `json:"fo,omitempty"` + Fr float64 `json:"fr"` + Fo float64 `json:"fo,omitempty"` - Br bool `json:"br"` - Bo bool `json:"bo,omitempty"` + Br bool `json:"br"` + Bo bool `json:"bo,omitempty"` - Ur uint `json:"ur"` - Uo uint `json:"uo,omitempty"` + Ur uint `json:"ur"` + Uo uint `json:"uo,omitempty"` - Str struct{} `json:"str"` - Sto struct{} `json:"sto,omitempty"` + Str struct{} `json:"str"` + Sto struct{} `json:"sto,omitempty"` } var optionalsExpected = `{ @@ -100,89 +99,89 @@ var optionalsExpected = `{ }` func TestOmitEmpty(t *testing.T) { - var o Optionals - o.Sw = "something" - o.Mr = map[string]interface{}{} - o.Mo = map[string]interface{}{} - - got, err := encoder.EncodeIndented(&o, "", " ", 0) - if err != nil { - t.Fatal(err) - } - if got := string(got); got != optionalsExpected { - t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected) - } + var o Optionals + o.Sw = "something" + o.Mr = map[string]interface{}{} + o.Mo = map[string]interface{}{} + + got, err := encoder.EncodeIndented(&o, "", " ", 0) + if err != nil { + t.Fatal(err) + } + if got := string(got); got != optionalsExpected { + t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected) + } } type StringTag struct { - BoolStr bool `json:",string"` - IntStr int64 `json:",string"` - UintptrStr uintptr `json:",string"` - StrStr string `json:",string"` - NumberStr json.Number `json:",string"` + BoolStr bool `json:",string"` + IntStr int64 `json:",string"` + UintptrStr uintptr `json:",string"` + StrStr string `json:",string"` + NumberStr json.Number `json:",string"` } func TestRoundtripStringTag(t *testing.T) { - tests := []struct { - name string - in StringTag - want string // empty to just test that we roundtrip - }{ - { - name: "AllTypes", - in: StringTag{ - BoolStr: true, - IntStr: 42, - UintptrStr: 44, - StrStr: "xzbit", - NumberStr: "46", - }, - want: `{ + tests := []struct { + name string + in StringTag + want string // empty to just test that we roundtrip + }{ + { + name: "AllTypes", + in: StringTag{ + BoolStr: true, + IntStr: 42, + UintptrStr: 44, + StrStr: "xzbit", + NumberStr: "46", + }, + want: `{ "BoolStr": "true", "IntStr": "42", "UintptrStr": "44", "StrStr": "\"xzbit\"", "NumberStr": "46" }`, - }, - { - // See golang.org/issues/38173. - name: "StringDoubleEscapes", - in: StringTag{ - StrStr: "\b\f\n\r\t\"\\", - NumberStr: "0", // just to satisfy the roundtrip - }, - want: `{ + }, + { + // See golang.org/issues/38173. + name: "StringDoubleEscapes", + in: StringTag{ + StrStr: "\b\f\n\r\t\"\\", + NumberStr: "0", // just to satisfy the roundtrip + }, + want: `{ "BoolStr": "false", "IntStr": "0", "UintptrStr": "0", "StrStr": "\"\\u0008\\u000c\\n\\r\\t\\\"\\\\\"", "NumberStr": "0" }`, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // Indent with a tab prefix to make the multi-line string - // literals in the table nicer to read. - got, err := encoder.EncodeIndented(&test.in, " ", " ", 0) - if err != nil { - t.Fatal(err) - } - if got := string(got); got != test.want { - t.Fatalf(" got: %s\nwant: %s\n", got, test.want) - } - - // Verify that it round-trips. - var s2 StringTag - if err := Unmarshal(got, &s2); err != nil { - t.Fatalf("Decode: %v", err) - } - if !reflect.DeepEqual(test.in, s2) { - t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2) - } - }) - } + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + // Indent with a tab prefix to make the multi-line string + // literals in the table nicer to read. + got, err := encoder.EncodeIndented(&test.in, " ", " ", 0) + if err != nil { + t.Fatal(err) + } + if got := string(got); got != test.want { + t.Fatalf(" got: %s\nwant: %s\n", got, test.want) + } + + // Verify that it round-trips. + var s2 StringTag + if err := Unmarshal(got, &s2); err != nil { + t.Fatalf("Decode: %v", err) + } + if !reflect.DeepEqual(test.in, s2) { + t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2) + } + }) + } } // byte slices are special even if they're renamed types. @@ -191,169 +190,169 @@ type renamedByteSlice []byte type renamedRenamedByteSlice []renamedByte func TestEncodeRenamedByteSlice(t *testing.T) { - s := renamedByteSlice("abc") - result, err := Marshal(s) - if err != nil { - t.Fatal(err) - } - expect := `"YWJj"` - if string(result) != expect { - t.Errorf(" got %s want %s", result, expect) - } - r := renamedRenamedByteSlice("abc") - result, err = Marshal(r) - if err != nil { - t.Fatal(err) - } - if string(result) != expect { - t.Errorf(" got %s want %s", result, expect) - } + s := renamedByteSlice("abc") + result, err := Marshal(s) + if err != nil { + t.Fatal(err) + } + expect := `"YWJj"` + if string(result) != expect { + t.Errorf(" got %s want %s", result, expect) + } + r := renamedRenamedByteSlice("abc") + result, err = Marshal(r) + if err != nil { + t.Fatal(err) + } + if string(result) != expect { + t.Errorf(" got %s want %s", result, expect) + } } type SamePointerNoCycle struct { - Ptr1, Ptr2 *SamePointerNoCycle + Ptr1, Ptr2 *SamePointerNoCycle } var samePointerNoCycle = &SamePointerNoCycle{} type PointerCycle struct { - Ptr *PointerCycle + Ptr *PointerCycle } var pointerCycle = &PointerCycle{} type PointerCycleIndirect struct { - Ptrs []interface{} + Ptrs []interface{} } type RecursiveSlice []RecursiveSlice var ( - pointerCycleIndirect = &PointerCycleIndirect{} - mapCycle = make(map[string]interface{}) - sliceCycle = []interface{}{nil} - sliceNoCycle = []interface{}{nil, nil} - recursiveSliceCycle = []RecursiveSlice{nil} + pointerCycleIndirect = &PointerCycleIndirect{} + mapCycle = make(map[string]interface{}) + sliceCycle = []interface{}{nil} + sliceNoCycle = []interface{}{nil, nil} + recursiveSliceCycle = []RecursiveSlice{nil} ) func init() { - ptr := &SamePointerNoCycle{} - samePointerNoCycle.Ptr1 = ptr - samePointerNoCycle.Ptr2 = ptr - - pointerCycle.Ptr = pointerCycle - pointerCycleIndirect.Ptrs = []interface{}{pointerCycleIndirect} - - mapCycle["x"] = mapCycle - sliceCycle[0] = sliceCycle - sliceNoCycle[1] = sliceNoCycle[:1] - for i := 3; i > 0; i-- { - sliceNoCycle = []interface{}{sliceNoCycle} - } - recursiveSliceCycle[0] = recursiveSliceCycle + ptr := &SamePointerNoCycle{} + samePointerNoCycle.Ptr1 = ptr + samePointerNoCycle.Ptr2 = ptr + + pointerCycle.Ptr = pointerCycle + pointerCycleIndirect.Ptrs = []interface{}{pointerCycleIndirect} + + mapCycle["x"] = mapCycle + sliceCycle[0] = sliceCycle + sliceNoCycle[1] = sliceNoCycle[:1] + for i := 3; i > 0; i-- { + sliceNoCycle = []interface{}{sliceNoCycle} + } + recursiveSliceCycle[0] = recursiveSliceCycle } func TestSamePointerNoCycle(t *testing.T) { - if _, err := Marshal(samePointerNoCycle); err != nil { - t.Fatalf("unexpected error: %v", err) - } + if _, err := Marshal(samePointerNoCycle); err != nil { + t.Fatalf("unexpected error: %v", err) + } } func TestSliceNoCycle(t *testing.T) { - if _, err := Marshal(sliceNoCycle); err != nil { - t.Fatalf("unexpected error: %v", err) - } + if _, err := Marshal(sliceNoCycle); err != nil { + t.Fatalf("unexpected error: %v", err) + } } var unsupportedValues = []interface{}{ - math.NaN(), - math.Inf(-1), - math.Inf(1), - pointerCycle, - pointerCycleIndirect, - mapCycle, - sliceCycle, - recursiveSliceCycle, + math.NaN(), + math.Inf(-1), + math.Inf(1), + pointerCycle, + pointerCycleIndirect, + mapCycle, + sliceCycle, + recursiveSliceCycle, } func TestUnsupportedValues(t *testing.T) { - for _, v := range unsupportedValues { - if _, err := Marshal(v); err != nil { - if _, ok := err.(*json.UnsupportedValueError); !ok { - t.Errorf("for %v, got %T want UnsupportedValueError", v, err) - } - } else { - t.Errorf("for %v, expected error", v) - } - } + for _, v := range unsupportedValues { + if _, err := Marshal(v); err != nil { + if _, ok := err.(*json.UnsupportedValueError); !ok { + t.Errorf("for %v, got %T want UnsupportedValueError", v, err) + } + } else { + t.Errorf("for %v, expected error", v) + } + } } // Ref has Marshaler and Unmarshaler methods with pointer receiver. type Ref int func (*Ref) MarshalJSON() ([]byte, error) { - return []byte(`"ref"`), nil + return []byte(`"ref"`), nil } func (r *Ref) UnmarshalJSON([]byte) error { - *r = 12 - return nil + *r = 12 + return nil } // Val has Marshaler methods with value receiver. type Val int func (Val) MarshalJSON() ([]byte, error) { - return []byte(`"val"`), nil + return []byte(`"val"`), nil } // RefText has Marshaler and Unmarshaler methods with pointer receiver. type RefText int func (*RefText) MarshalText() ([]byte, error) { - return []byte(`"ref"`), nil + return []byte(`"ref"`), nil } func (r *RefText) UnmarshalText([]byte) error { - *r = 13 - return nil + *r = 13 + return nil } // ValText has Marshaler methods with value receiver. type ValText int func (ValText) MarshalText() ([]byte, error) { - return []byte(`"val"`), nil + return []byte(`"val"`), nil } func TestRefValMarshal(t *testing.T) { - var s = struct { - R0 Ref - R1 *Ref - R2 RefText - R3 *RefText - V0 Val - V1 *Val - V2 ValText - V3 *ValText - }{ - R0: 12, - R1: new(Ref), - R2: 14, - R3: new(RefText), - V0: 13, - V1: new(Val), - V2: 15, - V3: new(ValText), - } - const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}` - b, err := Marshal(&s) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if got := string(b); got != want { - t.Errorf("got %q, want %q", got, want) - } + var s = struct { + R0 Ref + R1 *Ref + R2 RefText + R3 *RefText + V0 Val + V1 *Val + V2 ValText + V3 *ValText + }{ + R0: 12, + R1: new(Ref), + R2: 14, + R3: new(RefText), + V0: 13, + V1: new(Val), + V2: 15, + V3: new(ValText), + } + const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}` + b, err := Marshal(&s) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if got := string(b); got != want { + t.Errorf("got %q, want %q", got, want) + } } /* @@ -398,200 +397,200 @@ func TestMarshalerEscaping(t *testing.T) { */ func TestAnonymousFields(t *testing.T) { - tests := []struct { - label string // Test name - makeInput func() interface{} // Function to create input value - want string // Expected JSON output - }{{ - // Both S1 and S2 have a field named X. From the perspective of S, - // it is ambiguous which one X refers to. - // This should not serialize either field. - label: "AmbiguousField", - makeInput: func() interface{} { - type ( - S1 struct{ x, X int } - S2 struct{ x, X int } - S struct { - S1 - S2 - } - ) - return S{S1{1, 2}, S2{3, 4}} - }, - want: `{}`, - }, { - label: "DominantField", - // Both S1 and S2 have a field named X, but since S has an X field as - // well, it takes precedence over S1.X and S2.X. - makeInput: func() interface{} { - type ( - S1 struct{ x, X int } - S2 struct{ x, X int } - S struct { - S1 - S2 - x, X int - } - ) - return S{S1{1, 2}, S2{3, 4}, 5, 6} - }, - want: `{"X":6}`, - }, { - // Unexported embedded field of non-struct type should not be serialized. - label: "UnexportedEmbeddedInt", - makeInput: func() interface{} { - type ( - myInt int - S struct{ myInt } - ) - return S{5} - }, - want: `{}`, - }, { - // Exported embedded field of non-struct type should be serialized. - label: "ExportedEmbeddedInt", - makeInput: func() interface{} { - type ( - MyInt int - S struct{ MyInt } - ) - return S{5} - }, - want: `{"MyInt":5}`, - }, { - // Unexported embedded field of pointer to non-struct type - // should not be serialized. - label: "UnexportedEmbeddedIntPointer", - makeInput: func() interface{} { - type ( - myInt int - S struct{ *myInt } - ) - s := S{new(myInt)} - *s.myInt = 5 - return s - }, - want: `{}`, - }, { - // Exported embedded field of pointer to non-struct type - // should be serialized. - label: "ExportedEmbeddedIntPointer", - makeInput: func() interface{} { - type ( - MyInt int - S struct{ *MyInt } - ) - s := S{new(MyInt)} - *s.MyInt = 5 - return s - }, - want: `{"MyInt":5}`, - }, { - // Exported fields of embedded structs should have their - // exported fields be serialized regardless of whether the struct types - // themselves are exported. - label: "EmbeddedStruct", - makeInput: func() interface{} { - type ( - s1 struct{ x, X int } - S2 struct{ y, Y int } - S struct { - s1 - S2 - } - ) - return S{s1{1, 2}, S2{3, 4}} - }, - want: `{"X":2,"Y":4}`, - }, { - // Exported fields of pointers to embedded structs should have their - // exported fields be serialized regardless of whether the struct types - // themselves are exported. - label: "EmbeddedStructPointer", - makeInput: func() interface{} { - type ( - s1 struct{ x, X int } - S2 struct{ y, Y int } - S struct { - *s1 - *S2 - } - ) - return S{&s1{1, 2}, &S2{3, 4}} - }, - want: `{"X":2,"Y":4}`, - }, { - // Exported fields on embedded unexported structs at multiple levels - // of nesting should still be serialized. - label: "NestedStructAndInts", - makeInput: func() interface{} { - type ( - MyInt1 int - MyInt2 int - myInt int - s2 struct { - MyInt2 - myInt - } - s1 struct { - MyInt1 - myInt - s2 - } - S struct { - s1 - myInt - } - ) - return S{s1{1, 2, s2{3, 4}}, 6} - }, - want: `{"MyInt1":1,"MyInt2":3}`, - }, { - // If an anonymous struct pointer field is nil, we should ignore - // the embedded fields behind it. Not properly doing so may - // result in the wrong output or reflect panics. - label: "EmbeddedFieldBehindNilPointer", - makeInput: func() interface{} { - type ( - S2 struct{ Field string } - S struct{ *S2 } - ) - return S{} - }, - want: `{}`, - }} - - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - b, err := Marshal(tt.makeInput()) - if err != nil { - t.Fatalf("Marshal() = %v, want nil error", err) - } - if string(b) != tt.want { - t.Fatalf("Marshal() = %q, want %q", b, tt.want) - } - }) - } + tests := []struct { + label string // Test name + makeInput func() interface{} // Function to create input value + want string // Expected JSON output + }{{ + // Both S1 and S2 have a field named X. From the perspective of S, + // it is ambiguous which one X refers to. + // This should not serialize either field. + label: "AmbiguousField", + makeInput: func() interface{} { + type ( + S1 struct{ x, X int } + S2 struct{ x, X int } + S struct { + S1 + S2 + } + ) + return S{S1{1, 2}, S2{3, 4}} + }, + want: `{}`, + }, { + label: "DominantField", + // Both S1 and S2 have a field named X, but since S has an X field as + // well, it takes precedence over S1.X and S2.X. + makeInput: func() interface{} { + type ( + S1 struct{ x, X int } + S2 struct{ x, X int } + S struct { + S1 + S2 + x, X int + } + ) + return S{S1{1, 2}, S2{3, 4}, 5, 6} + }, + want: `{"X":6}`, + }, { + // Unexported embedded field of non-struct type should not be serialized. + label: "UnexportedEmbeddedInt", + makeInput: func() interface{} { + type ( + myInt int + S struct{ myInt } + ) + return S{5} + }, + want: `{}`, + }, { + // Exported embedded field of non-struct type should be serialized. + label: "ExportedEmbeddedInt", + makeInput: func() interface{} { + type ( + MyInt int + S struct{ MyInt } + ) + return S{5} + }, + want: `{"MyInt":5}`, + }, { + // Unexported embedded field of pointer to non-struct type + // should not be serialized. + label: "UnexportedEmbeddedIntPointer", + makeInput: func() interface{} { + type ( + myInt int + S struct{ *myInt } + ) + s := S{new(myInt)} + *s.myInt = 5 + return s + }, + want: `{}`, + }, { + // Exported embedded field of pointer to non-struct type + // should be serialized. + label: "ExportedEmbeddedIntPointer", + makeInput: func() interface{} { + type ( + MyInt int + S struct{ *MyInt } + ) + s := S{new(MyInt)} + *s.MyInt = 5 + return s + }, + want: `{"MyInt":5}`, + }, { + // Exported fields of embedded structs should have their + // exported fields be serialized regardless of whether the struct types + // themselves are exported. + label: "EmbeddedStruct", + makeInput: func() interface{} { + type ( + s1 struct{ x, X int } + S2 struct{ y, Y int } + S struct { + s1 + S2 + } + ) + return S{s1{1, 2}, S2{3, 4}} + }, + want: `{"X":2,"Y":4}`, + }, { + // Exported fields of pointers to embedded structs should have their + // exported fields be serialized regardless of whether the struct types + // themselves are exported. + label: "EmbeddedStructPointer", + makeInput: func() interface{} { + type ( + s1 struct{ x, X int } + S2 struct{ y, Y int } + S struct { + *s1 + *S2 + } + ) + return S{&s1{1, 2}, &S2{3, 4}} + }, + want: `{"X":2,"Y":4}`, + }, { + // Exported fields on embedded unexported structs at multiple levels + // of nesting should still be serialized. + label: "NestedStructAndInts", + makeInput: func() interface{} { + type ( + MyInt1 int + MyInt2 int + myInt int + s2 struct { + MyInt2 + myInt + } + s1 struct { + MyInt1 + myInt + s2 + } + S struct { + s1 + myInt + } + ) + return S{s1{1, 2, s2{3, 4}}, 6} + }, + want: `{"MyInt1":1,"MyInt2":3}`, + }, { + // If an anonymous struct pointer field is nil, we should ignore + // the embedded fields behind it. Not properly doing so may + // result in the wrong output or reflect panics. + label: "EmbeddedFieldBehindNilPointer", + makeInput: func() interface{} { + type ( + S2 struct{ Field string } + S struct{ *S2 } + ) + return S{} + }, + want: `{}`, + }} + + for _, tt := range tests { + t.Run(tt.label, func(t *testing.T) { + b, err := Marshal(tt.makeInput()) + if err != nil { + t.Fatalf("Marshal() = %v, want nil error", err) + } + if string(b) != tt.want { + t.Fatalf("Marshal() = %q, want %q", b, tt.want) + } + }) + } } type BugA struct { - S string + S string } type BugB struct { - BugA - S string + BugA + S string } type BugC struct { - S string + S string } // Legal Go: We never use the repeated embedded field (S). type BugX struct { - A int - BugA - BugB + A int + BugA + BugB } // golang.org/issue/16042. @@ -600,10 +599,10 @@ type BugX struct { type nilJSONMarshaler string func (nm *nilJSONMarshaler) MarshalJSON() ([]byte, error) { - if nm == nil { - return Marshal("0zenil0") - } - return Marshal("zenil:" + string(*nm)) + if nm == nil { + return Marshal("0zenil0") + } + return Marshal("zenil:" + string(*nm)) } // golang.org/issue/34235. @@ -612,214 +611,214 @@ func (nm *nilJSONMarshaler) MarshalJSON() ([]byte, error) { type nilTextMarshaler string func (nm *nilTextMarshaler) MarshalText() ([]byte, error) { - if nm == nil { - return []byte("0zenil0"), nil - } - return []byte("zenil:" + string(*nm)), nil + if nm == nil { + return []byte("0zenil0"), nil + } + return []byte("zenil:" + string(*nm)), nil } // See golang.org/issue/16042 and golang.org/issue/34235. func TestNilMarshal(t *testing.T) { - testCases := []struct { - v interface{} - want string - }{ - {v: nil, want: `null`}, - {v: new(float64), want: `0`}, - {v: []interface{}(nil), want: `null`}, - {v: []string(nil), want: `null`}, - {v: map[string]string(nil), want: `null`}, - {v: []byte(nil), want: `null`}, - {v: struct{ M string }{"gopher"}, want: `{"M":"gopher"}`}, - {v: struct{ M json.Marshaler }{}, want: `{"M":null}`}, - {v: struct{ M json.Marshaler }{(*nilJSONMarshaler)(nil)}, want: `{"M":"0zenil0"}`}, - {v: struct{ M interface{} }{(*nilJSONMarshaler)(nil)}, want: `{"M":null}`}, - {v: struct{ M encoding.TextMarshaler }{}, want: `{"M":null}`}, - {v: struct{ M encoding.TextMarshaler }{(*nilTextMarshaler)(nil)}, want: `{"M":"0zenil0"}`}, - {v: struct{ M interface{} }{(*nilTextMarshaler)(nil)}, want: `{"M":null}`}, - } + testCases := []struct { + v interface{} + want string + }{ + {v: nil, want: `null`}, + {v: new(float64), want: `0`}, + {v: []interface{}(nil), want: `null`}, + {v: []string(nil), want: `null`}, + {v: map[string]string(nil), want: `null`}, + {v: []byte(nil), want: `null`}, + {v: struct{ M string }{"gopher"}, want: `{"M":"gopher"}`}, + {v: struct{ M json.Marshaler }{}, want: `{"M":null}`}, + {v: struct{ M json.Marshaler }{(*nilJSONMarshaler)(nil)}, want: `{"M":"0zenil0"}`}, + {v: struct{ M interface{} }{(*nilJSONMarshaler)(nil)}, want: `{"M":null}`}, + {v: struct{ M encoding.TextMarshaler }{}, want: `{"M":null}`}, + {v: struct{ M encoding.TextMarshaler }{(*nilTextMarshaler)(nil)}, want: `{"M":"0zenil0"}`}, + {v: struct{ M interface{} }{(*nilTextMarshaler)(nil)}, want: `{"M":null}`}, + } - for _, tt := range testCases { - out, err := Marshal(tt.v) - if err != nil || string(out) != tt.want { - t.Errorf("Marshal(%#v) = %#q, %#v, want %#q, nil", tt.v, out, err, tt.want) - continue - } - } + for _, tt := range testCases { + out, err := Marshal(tt.v) + if err != nil || string(out) != tt.want { + t.Errorf("Marshal(%#v) = %#q, %#v, want %#q, nil", tt.v, out, err, tt.want) + continue + } + } } // Issue 5245. func TestEmbeddedBug(t *testing.T) { - v := BugB{ - BugA{"A"}, - "B", - } - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{"S":"B"}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } - // Now check that the duplicate field, S, does not appear. - x := BugX{ - A: 23, - } - b, err = Marshal(x) - if err != nil { - t.Fatal("Marshal:", err) - } - want = `{"A":23}` - got = string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } + v := BugB{ + BugA{"A"}, + "B", + } + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{"S":"B"}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } + // Now check that the duplicate field, S, does not appear. + x := BugX{ + A: 23, + } + b, err = Marshal(x) + if err != nil { + t.Fatal("Marshal:", err) + } + want = `{"A":23}` + got = string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } } type BugD struct { // Same as BugA after tagging. - XXX string `json:"S"` + XXX string `json:"S"` } // BugD's tagged S field should dominate BugA's. type BugY struct { - BugA - BugD + BugA + BugD } // Test that a field with a tag dominates untagged fields. func TestTaggedFieldDominates(t *testing.T) { - v := BugY{ - BugA{"BugA"}, - BugD{"BugD"}, - } - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{"S":"BugD"}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } + v := BugY{ + BugA{"BugA"}, + BugD{"BugD"}, + } + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{"S":"BugD"}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } } // There are no tags here, so S should not appear. type BugZ struct { - BugA - BugC - BugY // Contains a tagged S field through BugD; should not dominate. + BugA + BugC + BugY // Contains a tagged S field through BugD; should not dominate. } func TestDuplicatedFieldDisappears(t *testing.T) { - v := BugZ{ - BugA{"BugA"}, - BugC{"BugC"}, - BugY{ - BugA{"nested BugA"}, - BugD{"nested BugD"}, - }, - } - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } + v := BugZ{ + BugA{"BugA"}, + BugC{"BugC"}, + BugY{ + BugA{"nested BugA"}, + BugD{"nested BugD"}, + }, + } + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } } func TestStdLibIssue10281(t *testing.T) { - type Foo struct { - N json.Number - } - x := Foo{json.Number(`invalid`)} + type Foo struct { + N json.Number + } + x := Foo{json.Number(`invalid`)} - b, err := Marshal(&x) - if err == nil { - t.Errorf("Marshal(&x) = %#q; want error", b) - } + b, err := Marshal(&x) + if err == nil { + t.Errorf("Marshal(&x) = %#q; want error", b) + } } // golang.org/issue/8582 func TestEncodePointerString(t *testing.T) { - type stringPointer struct { - N *int64 `json:"n,string"` - } - var n int64 = 42 - b, err := Marshal(stringPointer{N: &n}) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - if got, want := string(b), `{"n":"42"}`; got != want { - t.Errorf("Marshal = %s, want %s", got, want) - } - var back stringPointer - err = Unmarshal(b, &back) - if err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if back.N == nil { - t.Fatalf("Unmarshaled nil N field") - } - if *back.N != 42 { - t.Fatalf("*N = %d; want 42", *back.N) - } + type stringPointer struct { + N *int64 `json:"n,string"` + } + var n int64 = 42 + b, err := Marshal(stringPointer{N: &n}) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if got, want := string(b), `{"n":"42"}`; got != want { + t.Errorf("Marshal = %s, want %s", got, want) + } + var back stringPointer + err = Unmarshal(b, &back) + if err != nil { + t.Fatalf("Unmarshal: %v", err) + } + if back.N == nil { + t.Fatalf("Unmarshaled nil N field") + } + if *back.N != 42 { + t.Fatalf("*N = %d; want 42", *back.N) + } } var encodeStringTests = []struct { - in string - out string + in string + out string }{ - {"\x00", `"\u0000"`}, - {"\x01", `"\u0001"`}, - {"\x02", `"\u0002"`}, - {"\x03", `"\u0003"`}, - {"\x04", `"\u0004"`}, - {"\x05", `"\u0005"`}, - {"\x06", `"\u0006"`}, - {"\x07", `"\u0007"`}, - {"\x08", `"\u0008"`}, - {"\x09", `"\t"`}, - {"\x0a", `"\n"`}, - {"\x0b", `"\u000b"`}, - {"\x0c", `"\u000c"`}, - {"\x0d", `"\r"`}, - {"\x0e", `"\u000e"`}, - {"\x0f", `"\u000f"`}, - {"\x10", `"\u0010"`}, - {"\x11", `"\u0011"`}, - {"\x12", `"\u0012"`}, - {"\x13", `"\u0013"`}, - {"\x14", `"\u0014"`}, - {"\x15", `"\u0015"`}, - {"\x16", `"\u0016"`}, - {"\x17", `"\u0017"`}, - {"\x18", `"\u0018"`}, - {"\x19", `"\u0019"`}, - {"\x1a", `"\u001a"`}, - {"\x1b", `"\u001b"`}, - {"\x1c", `"\u001c"`}, - {"\x1d", `"\u001d"`}, - {"\x1e", `"\u001e"`}, - {"\x1f", `"\u001f"`}, + {"\x00", `"\u0000"`}, + {"\x01", `"\u0001"`}, + {"\x02", `"\u0002"`}, + {"\x03", `"\u0003"`}, + {"\x04", `"\u0004"`}, + {"\x05", `"\u0005"`}, + {"\x06", `"\u0006"`}, + {"\x07", `"\u0007"`}, + {"\x08", `"\u0008"`}, + {"\x09", `"\t"`}, + {"\x0a", `"\n"`}, + {"\x0b", `"\u000b"`}, + {"\x0c", `"\u000c"`}, + {"\x0d", `"\r"`}, + {"\x0e", `"\u000e"`}, + {"\x0f", `"\u000f"`}, + {"\x10", `"\u0010"`}, + {"\x11", `"\u0011"`}, + {"\x12", `"\u0012"`}, + {"\x13", `"\u0013"`}, + {"\x14", `"\u0014"`}, + {"\x15", `"\u0015"`}, + {"\x16", `"\u0016"`}, + {"\x17", `"\u0017"`}, + {"\x18", `"\u0018"`}, + {"\x19", `"\u0019"`}, + {"\x1a", `"\u001a"`}, + {"\x1b", `"\u001b"`}, + {"\x1c", `"\u001c"`}, + {"\x1d", `"\u001d"`}, + {"\x1e", `"\u001e"`}, + {"\x1f", `"\u001f"`}, } func TestEncodeString(t *testing.T) { - for _, tt := range encodeStringTests { - b, err := Marshal(tt.in) - if err != nil { - t.Errorf("Marshal(%q): %v", tt.in, err) - continue - } - out := string(b) - if out != tt.out { - t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out) - } - } + for _, tt := range encodeStringTests { + b, err := Marshal(tt.in) + if err != nil { + t.Errorf("Marshal(%q): %v", tt.in, err) + continue + } + out := string(b) + if out != tt.out { + t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out) + } + } } type jsonbyte byte @@ -839,255 +838,255 @@ type textint int func (i textint) MarshalText() ([]byte, error) { return tenc(`TI:%d`, i) } func tenc(format string, a ...interface{}) ([]byte, error) { - var buf bytes.Buffer - _, _ = fmt.Fprintf(&buf, format, a...) - return buf.Bytes(), nil + var buf bytes.Buffer + _, _ = fmt.Fprintf(&buf, format, a...) + return buf.Bytes(), nil } // Issue 13783 func TestEncodeBytekind(t *testing.T) { - testdata := []struct { - data interface{} - want string - }{ - {byte(7), "7"}, - {jsonbyte(7), `{"JB":7}`}, - {textbyte(4), `"TB:4"`}, - {jsonint(5), `{"JI":5}`}, - {textint(1), `"TI:1"`}, - {[]byte{0, 1}, `"AAE="`}, - {[]jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`}, - {[][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`}, - {[]textbyte{2, 3}, `["TB:2","TB:3"]`}, - {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`}, - {[]textint{9, 3}, `["TI:9","TI:3"]`}, - {[]int{9, 3}, `[9,3]`}, - } - for _, d := range testdata { - js, err := Marshal(d.data) - if err != nil { - t.Error(err) - continue - } - got, want := string(js), d.want - if got != want { - t.Errorf("got %s, want %s", got, want) - } - } + testdata := []struct { + data interface{} + want string + }{ + {byte(7), "7"}, + {jsonbyte(7), `{"JB":7}`}, + {textbyte(4), `"TB:4"`}, + {jsonint(5), `{"JI":5}`}, + {textint(1), `"TI:1"`}, + {[]byte{0, 1}, `"AAE="`}, + {[]jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`}, + {[][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`}, + {[]textbyte{2, 3}, `["TB:2","TB:3"]`}, + {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`}, + {[]textint{9, 3}, `["TI:9","TI:3"]`}, + {[]int{9, 3}, `[9,3]`}, + } + for _, d := range testdata { + js, err := Marshal(d.data) + if err != nil { + t.Error(err) + continue + } + got, want := string(js), d.want + if got != want { + t.Errorf("got %s, want %s", got, want) + } + } } // https://golang.org/issue/33675 func TestNilMarshalerTextMapKey(t *testing.T) { - b, err := Marshal(map[*unmarshalerText]int{ - (*unmarshalerText)(nil): 1, - }) - if err != nil { - t.Fatalf("Failed to Marshal *text.Marshaler: %v", err) - } - const want = `{"":1}` - if string(b) != want { - t.Errorf("Marshal map with *text.Marshaler keys: got %#q, want %#q", b, want) - } + b, err := Marshal(map[*unmarshalerText]int{ + (*unmarshalerText)(nil): 1, + }) + if err != nil { + t.Fatalf("Failed to Marshal *text.Marshaler: %v", err) + } + const want = `{"":1}` + if string(b) != want { + t.Errorf("Marshal map with *text.Marshaler keys: got %#q, want %#q", b, want) + } } var re = regexp.MustCompile // syntactic checks on form of marshaled floating point numbers. var badFloatREs = []*regexp.Regexp{ - re(`p`), // no binary exponential notation - re(`^\+`), // no leading + sign - re(`^-?0[^.]`), // no unnecessary leading zeros - re(`^-?\.`), // leading zero required before decimal point - re(`\.(e|$)`), // no trailing decimal - re(`\.[0-9]+0(e|$)`), // no trailing zero in fraction - re(`^-?(0|[0-9]{2,})\..*e`), // exponential notation must have normalized mantissa - re(`e[+-]0`), // exponent must not have leading zeros - re(`e-[1-6]$`), // not tiny enough for exponential notation - re(`e+(.|1.|20)$`), // not big enough for exponential notation - re(`^-?0\.0000000`), // too tiny, should use exponential notation - re(`^-?[0-9]{22}`), // too big, should use exponential notation - re(`[1-9][0-9]{16}[1-9]`), // too many significant digits in integer - re(`[1-9][0-9.]{17}[1-9]`), // too many significant digits in decimal + re(`p`), // no binary exponential notation + re(`^\+`), // no leading + sign + re(`^-?0[^.]`), // no unnecessary leading zeros + re(`^-?\.`), // leading zero required before decimal point + re(`\.(e|$)`), // no trailing decimal + re(`\.[0-9]+0(e|$)`), // no trailing zero in fraction + re(`^-?(0|[0-9]{2,})\..*e`), // exponential notation must have normalized mantissa + re(`e[+-]0`), // exponent must not have leading zeros + re(`e-[1-6]$`), // not tiny enough for exponential notation + re(`e+(.|1.|20)$`), // not big enough for exponential notation + re(`^-?0\.0000000`), // too tiny, should use exponential notation + re(`^-?[0-9]{22}`), // too big, should use exponential notation + re(`[1-9][0-9]{16}[1-9]`), // too many significant digits in integer + re(`[1-9][0-9.]{17}[1-9]`), // too many significant digits in decimal } func TestMarshalFloat(t *testing.T) { - t.Parallel() - nfail := 0 - test := func(f float64, bits int) { - vf := interface{}(f) - if bits == 32 { - f = float64(float32(f)) // round - vf = float32(f) - } - bout, err := Marshal(vf) - if err != nil { - t.Errorf("Marshal(%T(%g)): %v", vf, vf, err) - nfail++ - return - } - out := string(bout) - - // result must convert back to the same float - g, err := strconv.ParseFloat(out, bits) - if err != nil { - t.Errorf("Marshal(%T(%g)) = %q, cannot parse back: %v", vf, vf, out, err) - nfail++ - return - } - if f != g { - t.Errorf("Marshal(%T(%g)) = %q (is %g, not %g)", vf, vf, out, float32(g), vf) - nfail++ - return - } - - for _, re := range badFloatREs { - if re.MatchString(out) { - t.Errorf("Marshal(%T(%g)) = %q, must not match /%s/", vf, vf, out, re) - nfail++ - return - } - } - } + t.Parallel() + nfail := 0 + test := func(f float64, bits int) { + vf := interface{}(f) + if bits == 32 { + f = float64(float32(f)) // round + vf = float32(f) + } + bout, err := Marshal(vf) + if err != nil { + t.Errorf("Marshal(%T(%g)): %v", vf, vf, err) + nfail++ + return + } + out := string(bout) + + // result must convert back to the same float + g, err := strconv.ParseFloat(out, bits) + if err != nil { + t.Errorf("Marshal(%T(%g)) = %q, cannot parse back: %v", vf, vf, out, err) + nfail++ + return + } + if f != g { + t.Errorf("Marshal(%T(%g)) = %q (is %g, not %g)", vf, vf, out, float32(g), vf) + nfail++ + return + } + + for _, re := range badFloatREs { + if re.MatchString(out) { + t.Errorf("Marshal(%T(%g)) = %q, must not match /%s/", vf, vf, out, re) + nfail++ + return + } + } + } - var ( - bigger = math.Inf(+1) - smaller = math.Inf(-1) - ) - - var digits = "1.2345678901234567890123" - for i := len(digits); i >= 2; i-- { - if testing.Short() && i < len(digits)-4 { - break - } - for exp := -30; exp <= 30; exp++ { - for _, sign := range "+-" { - for bits := 32; bits <= 64; bits += 32 { - s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp) - f, err := strconv.ParseFloat(s, bits) - if err != nil { - log.Fatal(err) - } - next := math.Nextafter - if bits == 32 { - next = func(g, h float64) float64 { - return float64(math.Nextafter32(float32(g), float32(h))) - } - } - test(f, bits) - test(next(f, bigger), bits) - test(next(f, smaller), bits) - if nfail > 50 { - t.Fatalf("stopping test early") - } - } - } - } - } - test(0, 64) - test(math.Copysign(0, -1), 64) - test(0, 32) - test(math.Copysign(0, -1), 32) + var ( + bigger = math.Inf(+1) + smaller = math.Inf(-1) + ) + + var digits = "1.2345678901234567890123" + for i := len(digits); i >= 2; i-- { + if testing.Short() && i < len(digits)-4 { + break + } + for exp := -30; exp <= 30; exp++ { + for _, sign := range "+-" { + for bits := 32; bits <= 64; bits += 32 { + s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp) + f, err := strconv.ParseFloat(s, bits) + if err != nil { + log.Fatal(err) + } + next := math.Nextafter + if bits == 32 { + next = func(g, h float64) float64 { + return float64(math.Nextafter32(float32(g), float32(h))) + } + } + test(f, bits) + test(next(f, bigger), bits) + test(next(f, smaller), bits) + if nfail > 50 { + t.Fatalf("stopping test early") + } + } + } + } + } + test(0, 64) + test(math.Copysign(0, -1), 64) + test(0, 32) + test(math.Copysign(0, -1), 32) } func TestMarshalRawMessageValue(t *testing.T) { - type ( - T1 struct { - M json.RawMessage `json:",omitempty"` - } - T2 struct { - M *json.RawMessage `json:",omitempty"` - } - ) - - var ( - rawNil = json.RawMessage(nil) - rawEmpty = json.RawMessage([]byte{}) - rawText = json.RawMessage(`"foo"`) - ) - - tests := []struct { - in interface{} - want string - ok bool - }{ - // Test with nil RawMessage. - {rawNil, "null", true}, - {&rawNil, "null", true}, - {[]interface{}{rawNil}, "[null]", true}, - {&[]interface{}{rawNil}, "[null]", true}, - {[]interface{}{&rawNil}, "[null]", true}, - {&[]interface{}{&rawNil}, "[null]", true}, - {struct{ M json.RawMessage }{rawNil}, `{"M":null}`, true}, - {&struct{ M json.RawMessage }{rawNil}, `{"M":null}`, true}, - {struct{ M *json.RawMessage }{&rawNil}, `{"M":null}`, true}, - {&struct{ M *json.RawMessage }{&rawNil}, `{"M":null}`, true}, - {map[string]interface{}{"M": rawNil}, `{"M":null}`, true}, - {&map[string]interface{}{"M": rawNil}, `{"M":null}`, true}, - {map[string]interface{}{"M": &rawNil}, `{"M":null}`, true}, - {&map[string]interface{}{"M": &rawNil}, `{"M":null}`, true}, - {T1{rawNil}, "{}", true}, - {T2{&rawNil}, `{"M":null}`, true}, - {&T1{rawNil}, "{}", true}, - {&T2{&rawNil}, `{"M":null}`, true}, - - // Test with empty, but non-nil, RawMessage. - {rawEmpty, "", false}, - {&rawEmpty, "", false}, - {[]interface{}{rawEmpty}, "", false}, - {&[]interface{}{rawEmpty}, "", false}, - {[]interface{}{&rawEmpty}, "", false}, - {&[]interface{}{&rawEmpty}, "", false}, - {struct{ X json.RawMessage }{rawEmpty}, "", false}, - {&struct{ X json.RawMessage }{rawEmpty}, "", false}, - {struct{ X *json.RawMessage }{&rawEmpty}, "", false}, - {&struct{ X *json.RawMessage }{&rawEmpty}, "", false}, - {map[string]interface{}{"nil": rawEmpty}, "", false}, - {&map[string]interface{}{"nil": rawEmpty}, "", false}, - {map[string]interface{}{"nil": &rawEmpty}, "", false}, - {&map[string]interface{}{"nil": &rawEmpty}, "", false}, - {T1{rawEmpty}, "{}", true}, - {T2{&rawEmpty}, "", false}, - {&T1{rawEmpty}, "{}", true}, - {&T2{&rawEmpty}, "", false}, - - // Test with RawMessage with some text. - // - // The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo". - // This behavior was intentionally changed in Go 1.8. - // See https://golang.org/issues/14493#issuecomment-255857318 - {rawText, `"foo"`, true}, // Issue6458 - {&rawText, `"foo"`, true}, - {[]interface{}{rawText}, `["foo"]`, true}, // Issue6458 - {&[]interface{}{rawText}, `["foo"]`, true}, // Issue6458 - {[]interface{}{&rawText}, `["foo"]`, true}, - {&[]interface{}{&rawText}, `["foo"]`, true}, - {struct{ M json.RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458 - {&struct{ M json.RawMessage }{rawText}, `{"M":"foo"}`, true}, - {struct{ M *json.RawMessage }{&rawText}, `{"M":"foo"}`, true}, - {&struct{ M *json.RawMessage }{&rawText}, `{"M":"foo"}`, true}, - {map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 - {&map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 - {map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true}, - {&map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true}, - {T1{rawText}, `{"M":"foo"}`, true}, // Issue6458 - {T2{&rawText}, `{"M":"foo"}`, true}, - {&T1{rawText}, `{"M":"foo"}`, true}, - {&T2{&rawText}, `{"M":"foo"}`, true}, - } + type ( + T1 struct { + M json.RawMessage `json:",omitempty"` + } + T2 struct { + M *json.RawMessage `json:",omitempty"` + } + ) + + var ( + rawNil = json.RawMessage(nil) + rawEmpty = json.RawMessage([]byte{}) + rawText = json.RawMessage(`"foo"`) + ) + + tests := []struct { + in interface{} + want string + ok bool + }{ + // Test with nil RawMessage. + {rawNil, "null", true}, + {&rawNil, "null", true}, + {[]interface{}{rawNil}, "[null]", true}, + {&[]interface{}{rawNil}, "[null]", true}, + {[]interface{}{&rawNil}, "[null]", true}, + {&[]interface{}{&rawNil}, "[null]", true}, + {struct{ M json.RawMessage }{rawNil}, `{"M":null}`, true}, + {&struct{ M json.RawMessage }{rawNil}, `{"M":null}`, true}, + {struct{ M *json.RawMessage }{&rawNil}, `{"M":null}`, true}, + {&struct{ M *json.RawMessage }{&rawNil}, `{"M":null}`, true}, + {map[string]interface{}{"M": rawNil}, `{"M":null}`, true}, + {&map[string]interface{}{"M": rawNil}, `{"M":null}`, true}, + {map[string]interface{}{"M": &rawNil}, `{"M":null}`, true}, + {&map[string]interface{}{"M": &rawNil}, `{"M":null}`, true}, + {T1{rawNil}, "{}", true}, + {T2{&rawNil}, `{"M":null}`, true}, + {&T1{rawNil}, "{}", true}, + {&T2{&rawNil}, `{"M":null}`, true}, + + // Test with empty, but non-nil, RawMessage. + {rawEmpty, "", false}, + {&rawEmpty, "", false}, + {[]interface{}{rawEmpty}, "", false}, + {&[]interface{}{rawEmpty}, "", false}, + {[]interface{}{&rawEmpty}, "", false}, + {&[]interface{}{&rawEmpty}, "", false}, + {struct{ X json.RawMessage }{rawEmpty}, "", false}, + {&struct{ X json.RawMessage }{rawEmpty}, "", false}, + {struct{ X *json.RawMessage }{&rawEmpty}, "", false}, + {&struct{ X *json.RawMessage }{&rawEmpty}, "", false}, + {map[string]interface{}{"nil": rawEmpty}, "", false}, + {&map[string]interface{}{"nil": rawEmpty}, "", false}, + {map[string]interface{}{"nil": &rawEmpty}, "", false}, + {&map[string]interface{}{"nil": &rawEmpty}, "", false}, + {T1{rawEmpty}, "{}", true}, + {T2{&rawEmpty}, "", false}, + {&T1{rawEmpty}, "{}", true}, + {&T2{&rawEmpty}, "", false}, + + // Test with RawMessage with some text. + // + // The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo". + // This behavior was intentionally changed in Go 1.8. + // See https://golang.org/issues/14493#issuecomment-255857318 + {rawText, `"foo"`, true}, // Issue6458 + {&rawText, `"foo"`, true}, + {[]interface{}{rawText}, `["foo"]`, true}, // Issue6458 + {&[]interface{}{rawText}, `["foo"]`, true}, // Issue6458 + {[]interface{}{&rawText}, `["foo"]`, true}, + {&[]interface{}{&rawText}, `["foo"]`, true}, + {struct{ M json.RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458 + {&struct{ M json.RawMessage }{rawText}, `{"M":"foo"}`, true}, + {struct{ M *json.RawMessage }{&rawText}, `{"M":"foo"}`, true}, + {&struct{ M *json.RawMessage }{&rawText}, `{"M":"foo"}`, true}, + {map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 + {&map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 + {map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true}, + {&map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true}, + {T1{rawText}, `{"M":"foo"}`, true}, // Issue6458 + {T2{&rawText}, `{"M":"foo"}`, true}, + {&T1{rawText}, `{"M":"foo"}`, true}, + {&T2{&rawText}, `{"M":"foo"}`, true}, + } - for i, tt := range tests { - b, err := Marshal(tt.in) - if ok := err == nil; ok != tt.ok { - if err != nil { - t.Errorf("test %d, unexpected failure: %v", i, err) - } else { - t.Errorf("test %d, unexpected success", i) - } - } - if got := string(b); got != tt.want { - t.Errorf("test %d, Marshal(%#v) = %q, want %q", i, tt.in, got, tt.want) - } - } + for i, tt := range tests { + b, err := Marshal(tt.in) + if ok := err == nil; ok != tt.ok { + if err != nil { + t.Errorf("test %d, unexpected failure: %v", i, err) + } else { + t.Errorf("test %d, unexpected success", i) + } + } + if got := string(b); got != tt.want { + t.Errorf("test %d, Marshal(%#v) = %q, want %q", i, tt.in, got, tt.want) + } + } } type marshalPanic struct{} @@ -1095,136 +1094,136 @@ type marshalPanic struct{} func (marshalPanic) MarshalJSON() ([]byte, error) { panic(0xdead) } func TestMarshalPanic(t *testing.T) { - defer func() { - if got := recover(); !reflect.DeepEqual(got, 0xdead) { - t.Errorf("panic() = (%T)(%v), want 0xdead", got, got) - } - }() - _, _ = Marshal(&marshalPanic{}) - t.Error("Marshal should have panicked") + defer func() { + if got := recover(); !reflect.DeepEqual(got, 0xdead) { + t.Errorf("panic() = (%T)(%v), want 0xdead", got, got) + } + }() + _, _ = Marshal(&marshalPanic{}) + t.Error("Marshal should have panicked") } //goland:noinspection NonAsciiCharacters func TestMarshalUncommonFieldNames(t *testing.T) { - v := struct { - A0, À, Aβ int - }{} - b, err := Marshal(v) - if err != nil { - t.Fatal("Marshal:", err) - } - want := `{"A0":0,"À":0,"Aβ":0}` - got := string(b) - if got != want { - t.Fatalf("Marshal: got %s want %s", got, want) - } + v := struct { + A0, À, Aβ int + }{} + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{"A0":0,"À":0,"Aβ":0}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } } type DummyMarshalerError struct { - Type reflect.Type - Err error - SourceFunc string + Type reflect.Type + Err error + SourceFunc string } func (self *DummyMarshalerError) err() *json.MarshalerError { - return (*json.MarshalerError)(unsafe.Pointer(self)) + return (*json.MarshalerError)(unsafe.Pointer(self)) } func TestMarshalerError(t *testing.T) { - s := "test variable" - st := reflect.TypeOf(s) - errText := "json: test error" - - tests := []struct { - err *json.MarshalerError - want string - }{ - { - (&DummyMarshalerError{st, fmt.Errorf(errText), ""}).err(), - "json: error calling MarshalJSON for type " + st.String() + ": " + errText, - }, - { - (&DummyMarshalerError{st, fmt.Errorf(errText), "TestMarshalerError"}).err(), - "json: error calling TestMarshalerError for type " + st.String() + ": " + errText, - }, - } + s := "test variable" + st := reflect.TypeOf(s) + errText := "json: test error" + + tests := []struct { + err *json.MarshalerError + want string + }{ + { + (&DummyMarshalerError{st, fmt.Errorf(errText), ""}).err(), + "json: error calling MarshalJSON for type " + st.String() + ": " + errText, + }, + { + (&DummyMarshalerError{st, fmt.Errorf(errText), "TestMarshalerError"}).err(), + "json: error calling TestMarshalerError for type " + st.String() + ": " + errText, + }, + } - for i, tt := range tests { - got := tt.err.Error() - if got != tt.want { - t.Errorf("MarshalerError test %d, got: %s, want: %s", i, got, tt.want) - } - } + for i, tt := range tests { + got := tt.err.Error() + if got != tt.want { + t.Errorf("MarshalerError test %d, got: %s, want: %s", i, got, tt.want) + } + } } func TestMarshalNullNil(t *testing.T) { - var v = struct { - A []int - B map[string]int - }{} - o, e := Marshal(v) - assert.Nil(t, e) - assert.Equal(t, `{"A":null,"B":null}`, string(o)) - o, e = Config{ - NoNullSliceOrMap: true, - }.Froze().Marshal(v) - assert.Nil(t, e) - assert.Equal(t, `{"A":[],"B":{}}`, string(o)) + var v = struct { + A []int + B map[string]int + }{} + o, e := Marshal(v) + assert.Nil(t, e) + assert.Equal(t, `{"A":null,"B":null}`, string(o)) + o, e = Config{ + NoNullSliceOrMap: true, + }.Froze().Marshal(v) + assert.Nil(t, e) + assert.Equal(t, `{"A":[],"B":{}}`, string(o)) } func TestEncoder_LongestInvalidUtf8(t *testing.T) { - for _, data := range []string{ - "\"" + strings.Repeat("\x80", 4096) + "\"", - "\"" + strings.Repeat("\x80", 4095) + "\"", - "\"" + strings.Repeat("\x80", 4097) + "\"", - "\"" + strings.Repeat("\x80", 12345) + "\"", - } { - testEncodeInvalidUtf8(t, []byte(data)) - } + for _, data := range([]string{ + "\"" + strings.Repeat("\x80", 4096) + "\"", + "\"" + strings.Repeat("\x80", 4095) + "\"", + "\"" + strings.Repeat("\x80", 4097) + "\"", + "\"" + strings.Repeat("\x80", 12345) + "\"", + }) { + testEncodeInvalidUtf8(t, []byte(data)) + } } func testEncodeInvalidUtf8(t *testing.T, data []byte) { - jgot, jerr := json.Marshal(data) - sgot, serr := ConfigStd.Marshal(data) - assert.Equal(t, serr != nil, jerr != nil) - if jerr == nil { - assert.Equal(t, sgot, jgot) - } + jgot, jerr := json.Marshal(data) + sgot, serr := ConfigStd.Marshal(data) + assert.Equal(t, serr != nil, jerr != nil) + if jerr == nil { + assert.Equal(t, sgot, jgot) + } } func TestEncoder_RandomInvalidUtf8(t *testing.T) { - nums := 1000 - maxLen := 1000 - for i := 0; i < nums; i++ { - testEncodeInvalidUtf8(t, genRandJsonBytes(maxLen)) - testEncodeInvalidUtf8(t, genRandJsonRune(maxLen)) - } + nums := 1000 + maxLen := 1000 + for i := 0; i < nums; i++ { + testEncodeInvalidUtf8(t, genRandJsonBytes(maxLen)) + testEncodeInvalidUtf8(t, genRandJsonRune(maxLen)) + } } func TestMarshalInfOrNan(t *testing.T) { - tests := []interface{}{ - math.Inf(1), math.Inf(-1), math.NaN(), float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), []interface{}{math.Inf(1), math.Inf(-1), math.NaN(), float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN())}, + tests := [] interface{}{ + math.Inf(1), math.Inf(-1), math.NaN(), float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()), []interface{}{math.Inf(1), math.Inf(-1), math.NaN(), float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN())}, []float64{math.Inf(1), math.Inf(-1), math.NaN()}, []float32{float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN())}, - } + } - allowNanInf := Config{ - EncodeNullForInfOrNan: true, - }.Froze() - for _, tt := range tests { - b, err := allowNanInf.Marshal(tt) - assert.Nil(t, err) - if len(b) == 4 { - assert.Equal(t, string(b), "null") - } else { - println(string(b)) - } - - b, err = Marshal(tt) - assert.NotNil(t, err) - assert.True(t, strings.Contains(err.Error(), "json: unsupported value: NaN or ±Infinite")) - } + allowNanInf := Config { + EncodeNullForInfOrNan: true, + }.Froze() + for _, tt := range tests { + b, err := allowNanInf.Marshal(tt) + assert.Nil(t, err) + if len(b) == 4 { + assert.Equal(t, string(b), "null") + } else { + println(string(b)) + } + + b, err = Marshal(tt) + assert.NotNil(t, err) + assert.True(t, strings.Contains(err.Error(), "json: unsupported value: NaN or ±Infinite")) + } } func TestUint64ToString(t *testing.T) { @@ -1341,4 +1340,4 @@ func TestUint64ToString(t *testing.T) { check(t, c.exceptFalse, b) }) } -} +} \ No newline at end of file diff --git a/internal/encoder/vm/vm.go b/internal/encoder/vm/vm.go index ea1ca767a..973792a3e 100644 --- a/internal/encoder/vm/vm.go +++ b/internal/encoder/vm/vm.go @@ -152,18 +152,17 @@ func Execute(b *[]byte, p unsafe.Pointer, s *vars.Stack, flags uint64, prog *ir. v := *(*uint32)(p) buf = alg.U64toa(buf, uint64(v)) case ir.OP_u64: - quote := false - if ins.CompatOp() == ir.OP_u64 && - !ins.IsMapKey() && - flags&(1< Date: Wed, 25 Dec 2024 17:41:59 +0800 Subject: [PATCH 4/8] add encoder support Int64ToString --- api.go | 3 +- encode_test.go | 38 +++++++++++++------ encoder/encoder_native.go | 3 +- internal/encoder/alg/opts.go | 1 + internal/encoder/compiler.go | 4 +- internal/encoder/encoder.go | 3 +- internal/encoder/ir/op.go | 4 +- internal/encoder/vm/vm.go | 15 ++++++-- .../encoder/x86/assembler_regabi_amd64.go | 18 ++++++++- sonic.go | 3 ++ 10 files changed, 68 insertions(+), 24 deletions(-) diff --git a/api.go b/api.go index 26cdbaba7..5e8859a95 100644 --- a/api.go +++ b/api.go @@ -95,8 +95,9 @@ type Config struct { // Encode Infinity or Nan float into `null`, instead of returning an error. EncodeNullForInfOrNan bool - // Uint64 into strings on Marshal + // Uint64 or Int64 into strings on Marshal Uint64ToString bool + Int64ToString bool } var ( diff --git a/encode_test.go b/encode_test.go index 683a03fe6..bbf20470f 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1226,13 +1226,14 @@ func TestMarshalInfOrNan(t *testing.T) { } } -func TestUint64ToString(t *testing.T) { +func TestUint64OrInt64ToString(t *testing.T) { int64ptr := int64(432556670863027541) uint64ptr := uint64(12372850276778298372) cases := []struct { name string val interface{} - exceptTrue string + exceptUint64ToStr string + exceptInt64ToStr string exceptFalse string }{ { @@ -1242,7 +1243,8 @@ func TestUint64ToString(t *testing.T) { "int64": int64(34), "uint64": uint64(56), }, - exceptTrue: `{"int":12,"int64":34,"uint64":"56"}`, + exceptUint64ToStr: `{"int":12,"int64":34,"uint64":"56"}`, + exceptInt64ToStr: `{"int":12,"int64":"34","uint64":56}`, exceptFalse: `{"int":12,"int64":34,"uint64":56}`, }, { @@ -1252,7 +1254,8 @@ func TestUint64ToString(t *testing.T) { int64(34): int64(34), int64(56): uint64(56), }, - exceptTrue: `{"12":12,"34":34,"56":"56"}`, + exceptUint64ToStr: `{"12":12,"34":34,"56":"56"}`, + exceptInt64ToStr: `{"12":12,"34":"34","56":56}`, exceptFalse: `{"12":12,"34":34,"56":56}`, }, { @@ -1262,7 +1265,8 @@ func TestUint64ToString(t *testing.T) { uint64(34): int64(34), uint64(56): uint64(56), }, - exceptTrue: `{"12":12,"34":34,"56":"56"}`, + exceptUint64ToStr: `{"12":12,"34":34,"56":"56"}`, + exceptInt64ToStr: `{"12":12,"34":"34","56":56}`, exceptFalse: `{"12":12,"34":34,"56":56}`, }, { @@ -1276,7 +1280,8 @@ func TestUint64ToString(t *testing.T) { Int64: int64(34), Uint64: uint64(56), }, - exceptTrue: `{"int":12,"int64":34,"uint64":"56"}`, + exceptUint64ToStr: `{"int":12,"int64":34,"uint64":"56"}`, + exceptInt64ToStr: `{"int":12,"int64":"34","uint64":56}`, exceptFalse: `{"int":12,"int64":34,"uint64":56}`, }, { @@ -1284,19 +1289,22 @@ func TestUint64ToString(t *testing.T) { val: []interface{}{ int(12), int64(34), uint64(56), }, - exceptTrue: `[12,34,"56"]`, + exceptUint64ToStr: `[12,34,"56"]`, + exceptInt64ToStr: `[12,"34",56]`, exceptFalse: `[12,34,56]`, }, { name: "single_int64", val: int64(34), - exceptTrue: `34`, + exceptUint64ToStr: `34`, + exceptInt64ToStr: `"34"`, exceptFalse: `34`, }, { name: "single_uint64", val: uint64(56), - exceptTrue: `"56"`, + exceptUint64ToStr: `"56"`, + exceptInt64ToStr: `56`, exceptFalse: `56`, }, { @@ -1314,8 +1322,10 @@ func TestUint64ToString(t *testing.T) { Int64: int64(123), Uint64: uint64(456), }}}, - exceptTrue: `{"Map":{"val":{"Int64Ptr":432556670863027541,` + + exceptUint64ToStr: `{"Map":{"val":{"Int64Ptr":432556670863027541,` + `"Uint64Ptr":"12372850276778298372","Int64":123,"Uint64":"456"}}}`, + exceptInt64ToStr: `{"Map":{"val":{"Int64Ptr":"432556670863027541",` + + `"Uint64Ptr":12372850276778298372,"Int64":"123","Uint64":456}}}`, exceptFalse: `{"Map":{"val":{"Int64Ptr":432556670863027541,` + `"Uint64Ptr":12372850276778298372,"Int64":123,"Uint64":456}}}`, }, @@ -1333,9 +1343,13 @@ func TestUint64ToString(t *testing.T) { t.Run(c.name, func(t *testing.T) { b, e := Config{Uint64ToString: true}.Froze().Marshal(c.val) assert.Nil(t, e) - check(t, c.exceptTrue, b) + check(t, c.exceptUint64ToStr, b) - b, e = Config{Uint64ToString: false}.Froze().Marshal(c.val) + b, e = Config{Int64ToString: true}.Froze().Marshal(c.val) + assert.Nil(t, e) + check(t, c.exceptInt64ToStr, b) + + b, e = Config{}.Froze().Marshal(c.val) assert.Nil(t, e) check(t, c.exceptFalse, b) }) diff --git a/encoder/encoder_native.go b/encoder/encoder_native.go index 9eb159e3c..3e2e577fd 100644 --- a/encoder/encoder_native.go +++ b/encoder/encoder_native.go @@ -74,8 +74,9 @@ const ( // Encode Infinity or Nan float into `null`, instead of returning an error. EncodeNullForInfOrNan Options = encoder.EncodeNullForInfOrNan - // Uint64 into strings on Marshal + // Uint64 or Int64 into strings on Marshal Uint64ToString Options = encoder.Uint64ToString + Int64ToString Options = encoder.Int64ToString ) diff --git a/internal/encoder/alg/opts.go b/internal/encoder/alg/opts.go index 8fc94f721..a32e11f5e 100644 --- a/internal/encoder/alg/opts.go +++ b/internal/encoder/alg/opts.go @@ -27,6 +27,7 @@ const ( BitNoEncoderNewline BitEncodeNullForInfOrNan BitUint64ToString + BitInt64ToString BitPointerValue = 63 ) diff --git a/internal/encoder/compiler.go b/internal/encoder/compiler.go index f19894f0d..031e94ae4 100644 --- a/internal/encoder/compiler.go +++ b/internal/encoder/compiler.go @@ -189,7 +189,7 @@ func (self *Compiler) compileOps(p *ir.Program, sp int, vt reflect.Type) { case reflect.Int64: p.Add(ir.OP_i64) case reflect.Uint: - p.Add(ir.OP_uint(), ir.OP_ui) + p.Add(ir.OP_uint(), ir.OP_u) case reflect.Uint8: p.Add(ir.OP_u8) case reflect.Uint16: @@ -311,7 +311,7 @@ func (self *Compiler) compileMapBodyTextKey(p *ir.Program, vk reflect.Type) { case reflect.Int64: p.Key(ir.OP_i64) case reflect.Uint: - p.Key(ir.OP_uint(), ir.OP_ui) + p.Key(ir.OP_uint(), ir.OP_u) case reflect.Uint8: p.Key(ir.OP_u8) case reflect.Uint16: diff --git a/internal/encoder/encoder.go b/internal/encoder/encoder.go index 72862215b..d668a9676 100644 --- a/internal/encoder/encoder.go +++ b/internal/encoder/encoder.go @@ -74,8 +74,9 @@ const ( // Encode Infinity or Nan float into `null`, instead of returning an error. EncodeNullForInfOrNan Options = 1 << alg.BitEncodeNullForInfOrNan - // Uint64 into strings on Marshal + // Uint64 or Int64 into strings on Marshal Uint64ToString Options = 1 << alg.BitUint64ToString + Int64ToString Options = 1 << alg.BitInt64ToString ) // Encoder represents a specific set of encoder configurations. diff --git a/internal/encoder/ir/op.go b/internal/encoder/ir/op.go index 3762d20da..fdc4161db 100644 --- a/internal/encoder/ir/op.go +++ b/internal/encoder/ir/op.go @@ -43,7 +43,7 @@ const ( OP_u16 OP_u32 OP_u64 - OP_ui + OP_u OP_f32 OP_f64 OP_str @@ -106,7 +106,7 @@ var OpNames = [256]string{ OP_u16: "u16", OP_u32: "u32", OP_u64: "u64", - OP_ui: "ui", + OP_u: "u", OP_f32: "f32", OP_f64: "f64", OP_str: "str", diff --git a/internal/encoder/vm/vm.go b/internal/encoder/vm/vm.go index 973792a3e..316c8c0fd 100644 --- a/internal/encoder/vm/vm.go +++ b/internal/encoder/vm/vm.go @@ -140,8 +140,17 @@ func Execute(b *[]byte, p unsafe.Pointer, s *vars.Stack, flags uint64, prog *ir. v := *(*int32)(p) buf = alg.I64toa(buf, int64(v)) case ir.OP_i64: - v := *(*int64)(p) - buf = alg.I64toa(buf, int64(v)) + if ins.CompatOp() == ir.OP_i || + ins.IsMapKey() || + flags&(1< Date: Wed, 25 Dec 2024 17:48:34 +0800 Subject: [PATCH 5/8] add encoder support Int64ToString --- encode_test.go | 37 +++++++++++++++++++++---------------- internal/encoder/vm/vm.go | 4 ++-- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/encode_test.go b/encode_test.go index bbf20470f..51280518e 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1240,58 +1240,63 @@ func TestUint64OrInt64ToString(t *testing.T) { name: "normal_map", val: map[string]interface{}{ "int": int(12), + "uint": uint(99), "int64": int64(34), "uint64": uint64(56), }, - exceptUint64ToStr: `{"int":12,"int64":34,"uint64":"56"}`, - exceptInt64ToStr: `{"int":12,"int64":"34","uint64":56}`, - exceptFalse: `{"int":12,"int64":34,"uint64":56}`, + exceptUint64ToStr: `{"int":12,"uint":99,"int64":34,"uint64":"56"}`, + exceptInt64ToStr: `{"int":12,"uint":99,"int64":"34","uint64":56}`, + exceptFalse: `{"int":12,"uint":99,"int64":34,"uint64":56}`, }, { name: "int_key_map", val: map[int64]interface{}{ int64(12): int(12), + int64(99): uint(99), int64(34): int64(34), int64(56): uint64(56), }, - exceptUint64ToStr: `{"12":12,"34":34,"56":"56"}`, - exceptInt64ToStr: `{"12":12,"34":"34","56":56}`, - exceptFalse: `{"12":12,"34":34,"56":56}`, + exceptUint64ToStr: `{"12":12,"99":99,"34":34,"56":"56"}`, + exceptInt64ToStr: `{"12":12,"99":99,"34":"34","56":56}`, + exceptFalse: `{"12":12,"99":99,"34":34,"56":56}`, }, { name: "uint_key_map", val: map[uint64]interface{}{ uint64(12): int(12), + uint64(99): uint(99), uint64(34): int64(34), uint64(56): uint64(56), }, - exceptUint64ToStr: `{"12":12,"34":34,"56":"56"}`, - exceptInt64ToStr: `{"12":12,"34":"34","56":56}`, - exceptFalse: `{"12":12,"34":34,"56":56}`, + exceptUint64ToStr: `{"12":12,"99":99,"34":34,"56":"56"}`, + exceptInt64ToStr: `{"12":12,"99":99,"34":"34","56":56}`, + exceptFalse: `{"12":12,"99":99,"34":34,"56":56}`, }, { name: "normal_struct", val: struct { Int int `json:"int"` + Uint uint `json:"uint"` Int64 int64 `json:"int64"` Uint64 uint64 `json:"uint64"` }{ Int: int(12), + Uint: uint(99), Int64: int64(34), Uint64: uint64(56), }, - exceptUint64ToStr: `{"int":12,"int64":34,"uint64":"56"}`, - exceptInt64ToStr: `{"int":12,"int64":"34","uint64":56}`, - exceptFalse: `{"int":12,"int64":34,"uint64":56}`, + exceptUint64ToStr: `{"int":12,"uint":99,"int64":34,"uint64":"56"}`, + exceptInt64ToStr: `{"int":12,"uint":99,"int64":"34","uint64":56}`, + exceptFalse: `{"int":12,"uint":99,"int64":34,"uint64":56}`, }, { name: "normal_slice", val: []interface{}{ - int(12), int64(34), uint64(56), + int(12), uint(99), int64(34), uint64(56), }, - exceptUint64ToStr: `[12,34,"56"]`, - exceptInt64ToStr: `[12,"34",56]`, - exceptFalse: `[12,34,56]`, + exceptUint64ToStr: `[12,99,34,"56"]`, + exceptInt64ToStr: `[12,99,"34",56]`, + exceptFalse: `[12,99,34,56]`, }, { name: "single_int64", diff --git a/internal/encoder/vm/vm.go b/internal/encoder/vm/vm.go index 316c8c0fd..2678b8546 100644 --- a/internal/encoder/vm/vm.go +++ b/internal/encoder/vm/vm.go @@ -148,8 +148,8 @@ func Execute(b *[]byte, p unsafe.Pointer, s *vars.Stack, flags uint64, prog *ir. continue } buf = append(buf, '"') - v := *(*uint64)(p) - buf = alg.U64toa(buf, uint64(v)) + v := *(*int64)(p) + buf = alg.I64toa(buf, int64(v)) buf = append(buf, '"') case ir.OP_u8: v := *(*uint8)(p) From fcd87788e7f2c78af486f511873c24fa7c672eca Mon Sep 17 00:00:00 2001 From: zhangming12 Date: Thu, 2 Jan 2025 11:43:24 +0800 Subject: [PATCH 6/8] add encoder support UintExceedToString --- api.go | 9 +- encode_test.go | 398 ++++++++++++------ encoder/encoder_native.go | 3 + internal/encoder/alg/opts.go | 1 + internal/encoder/encoder.go | 3 + internal/encoder/vm/vm.go | 20 +- .../encoder/x86/assembler_regabi_amd64.go | 33 +- sonic.go | 3 + 8 files changed, 323 insertions(+), 147 deletions(-) diff --git a/api.go b/api.go index 5e8859a95..485136f67 100644 --- a/api.go +++ b/api.go @@ -95,9 +95,12 @@ type Config struct { // Encode Infinity or Nan float into `null`, instead of returning an error. EncodeNullForInfOrNan bool - // Uint64 or Int64 into strings on Marshal - Uint64ToString bool - Int64ToString bool + // Uint64 or Int64 into strings on Marshal + Uint64ToString bool + Int64ToString bool + // UintExceedToString when intSize == 64, if the value of type uint or uint64 exceeds MaxInt64, + // automatically convert it to a string. + UintExceedToString bool } var ( diff --git a/encode_test.go b/encode_test.go index 51280518e..5e6c12c6f 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1227,136 +1227,270 @@ func TestMarshalInfOrNan(t *testing.T) { } func TestUint64OrInt64ToString(t *testing.T) { - int64ptr := int64(432556670863027541) - uint64ptr := uint64(12372850276778298372) - cases := []struct { - name string - val interface{} - exceptUint64ToStr string - exceptInt64ToStr string - exceptFalse string - }{ - { - name: "normal_map", - val: map[string]interface{}{ - "int": int(12), - "uint": uint(99), - "int64": int64(34), - "uint64": uint64(56), - }, - exceptUint64ToStr: `{"int":12,"uint":99,"int64":34,"uint64":"56"}`, - exceptInt64ToStr: `{"int":12,"uint":99,"int64":"34","uint64":56}`, - exceptFalse: `{"int":12,"uint":99,"int64":34,"uint64":56}`, - }, - { - name: "int_key_map", - val: map[int64]interface{}{ - int64(12): int(12), - int64(99): uint(99), - int64(34): int64(34), - int64(56): uint64(56), - }, - exceptUint64ToStr: `{"12":12,"99":99,"34":34,"56":"56"}`, - exceptInt64ToStr: `{"12":12,"99":99,"34":"34","56":56}`, - exceptFalse: `{"12":12,"99":99,"34":34,"56":56}`, - }, - { - name: "uint_key_map", - val: map[uint64]interface{}{ - uint64(12): int(12), - uint64(99): uint(99), - uint64(34): int64(34), - uint64(56): uint64(56), - }, - exceptUint64ToStr: `{"12":12,"99":99,"34":34,"56":"56"}`, - exceptInt64ToStr: `{"12":12,"99":99,"34":"34","56":56}`, - exceptFalse: `{"12":12,"99":99,"34":34,"56":56}`, - }, - { - name: "normal_struct", - val: struct { - Int int `json:"int"` - Uint uint `json:"uint"` - Int64 int64 `json:"int64"` - Uint64 uint64 `json:"uint64"` - }{ - Int: int(12), - Uint: uint(99), - Int64: int64(34), - Uint64: uint64(56), - }, - exceptUint64ToStr: `{"int":12,"uint":99,"int64":34,"uint64":"56"}`, - exceptInt64ToStr: `{"int":12,"uint":99,"int64":"34","uint64":56}`, - exceptFalse: `{"int":12,"uint":99,"int64":34,"uint64":56}`, - }, - { - name: "normal_slice", - val: []interface{}{ - int(12), uint(99), int64(34), uint64(56), - }, - exceptUint64ToStr: `[12,99,34,"56"]`, - exceptInt64ToStr: `[12,99,"34",56]`, - exceptFalse: `[12,99,34,56]`, - }, - { - name: "single_int64", - val: int64(34), - exceptUint64ToStr: `34`, - exceptInt64ToStr: `"34"`, - exceptFalse: `34`, - }, - { - name: "single_uint64", - val: uint64(56), - exceptUint64ToStr: `"56"`, - exceptInt64ToStr: `56`, - exceptFalse: `56`, - }, - { - name: "int64ptr", - val: struct { - Map map[string]interface{} - }{map[string]interface{}{"val": struct { - Int64Ptr interface{} - Uint64Ptr interface{} - Int64 interface{} - Uint64 interface{} - }{ - Int64Ptr: &int64ptr, - Uint64Ptr: &uint64ptr, - Int64: int64(123), - Uint64: uint64(456), - }}}, - exceptUint64ToStr: `{"Map":{"val":{"Int64Ptr":432556670863027541,` + - `"Uint64Ptr":"12372850276778298372","Int64":123,"Uint64":"456"}}}`, - exceptInt64ToStr: `{"Map":{"val":{"Int64Ptr":"432556670863027541",` + - `"Uint64Ptr":12372850276778298372,"Int64":"123","Uint64":456}}}`, - exceptFalse: `{"Map":{"val":{"Int64Ptr":432556670863027541,` + - `"Uint64Ptr":12372850276778298372,"Int64":123,"Uint64":456}}}`, - }, - } - - check := func(t *testing.T, except string, testRes []byte) { - var tmp1 interface{} - assert.Nil(t, Unmarshal([]byte(testRes), &tmp1)) - var tmp2 interface{} - assert.Nil(t, Unmarshal([]byte(except), &tmp2)) - assert.Equal(t, tmp2, tmp1) - } - - for _, c := range cases { - t.Run(c.name, func(t *testing.T) { - b, e := Config{Uint64ToString: true}.Froze().Marshal(c.val) - assert.Nil(t, e) - check(t, c.exceptUint64ToStr, b) - - b, e = Config{Int64ToString: true}.Froze().Marshal(c.val) - assert.Nil(t, e) - check(t, c.exceptInt64ToStr, b) - - b, e = Config{}.Froze().Marshal(c.val) - assert.Nil(t, e) - check(t, c.exceptFalse, b) - }) - } + int64ptr := int64(1) + intptr := int(1) + uintPtr := uint(1) + + uint64Lptr := uint64(9223372036854775806) + uint64Eptr := uint64(9223372036854775807) + uint64Gptr := uint64(9223372036854775808) + uintLptr := uint(9223372036854775806) + uintEptr := uint(9223372036854775807) + uintGptr := uint(9223372036854775808) + cases := []struct { + name string + val interface{} + exceptUint64ToStr string + exceptInt64ToStr string + exceptUIntExToStr string + exceptFalse string + }{ + { + name: "normal_map", + val: map[string]interface{}{ + "int": int(12), + "uint": uint(99), + "int64": int64(34), + "uint64": uint64(56), + "exceedUint": uint(9223372036854775808), + "exceedUint64": uint64(9223372036854775808), + "eqMaxInt64Uint64": uint64(9223372036854775807), + "eqMaxInt64Uint": uint(9223372036854775807), + "lsMaxInt64Uint64": uint64(9223372036854775806), + "lsMaxInt64Uint": uint(9223372036854775806), + }, + exceptUint64ToStr: `{"eqMaxInt64Uint":9223372036854775807,"eqMaxInt64Uint64":"9223372036854775807",`+ + `"exceedUint":9223372036854775808,"exceedUint64":"9223372036854775808",`+ + `"int":12,"int64":34,`+ + `"lsMaxInt64Uint":9223372036854775806,"lsMaxInt64Uint64":"9223372036854775806",`+ + `"uint":99,"uint64":"56"}`, + exceptInt64ToStr: `{"eqMaxInt64Uint":9223372036854775807,"eqMaxInt64Uint64":9223372036854775807,`+ + `"exceedUint":9223372036854775808,"exceedUint64":9223372036854775808,`+ + `"int":12,"int64":"34",`+ + `"lsMaxInt64Uint":9223372036854775806,"lsMaxInt64Uint64":9223372036854775806,`+ + `"uint":99,"uint64":56}`, + exceptUIntExToStr: `{"eqMaxInt64Uint":9223372036854775807,"eqMaxInt64Uint64":9223372036854775807,`+ + `"exceedUint":"9223372036854775808","exceedUint64":"9223372036854775808",`+ + `"int":12,"int64":34,`+ + `"lsMaxInt64Uint":9223372036854775806,"lsMaxInt64Uint64":9223372036854775806,`+ + `"uint":99,"uint64":56}`, + exceptFalse: `{"eqMaxInt64Uint":9223372036854775807,"eqMaxInt64Uint64":9223372036854775807,`+ + `"exceedUint":9223372036854775808,"exceedUint64":9223372036854775808,`+ + `"int":12,"int64":34,`+ + `"lsMaxInt64Uint":9223372036854775806,"lsMaxInt64Uint64":9223372036854775806,`+ + `"uint":99,"uint64":56}`, + }, + { + name: "int64_key_map", + val: map[int64]interface{}{ + int64(1): 1, + int64(2): 2, + int64(3): 3, + int64(4): 4, + }, + exceptUint64ToStr: `{"1":1,"2":2,"3":3,"4":4}`, + exceptInt64ToStr: `{"1":1,"2":2,"3":3,"4":4}`, + exceptUIntExToStr: `{"1":1,"2":2,"3":3,"4":4}`, + exceptFalse: `{"1":1,"2":2,"3":3,"4":4}`, + }, + { + name: "uint64_key_map", + val: map[uint64]interface{}{ + uint64(12): 1, + uint64(99): 1, + uint64(34): 1, + uint64(9223372036854775806): 1, + uint64(9223372036854775807): 1, + uint64(9223372036854775808): 1, + }, + exceptUint64ToStr: `{"12":1,"34":1,"9223372036854775806":1,"9223372036854775807":1,"9223372036854775808":1,"99":1}`, + exceptInt64ToStr: `{"12":1,"34":1,"9223372036854775806":1,"9223372036854775807":1,"9223372036854775808":1,"99":1}`, + exceptUIntExToStr: `{"12":1,"34":1,"9223372036854775806":1,"9223372036854775807":1,"9223372036854775808":1,"99":1}`, + exceptFalse: `{"12":1,"34":1,"9223372036854775806":1,"9223372036854775807":1,"9223372036854775808":1,"99":1}`, + }, + { + name: "uint_key_map", + val: map[uint]interface{}{ + uint(12): int(12), + uint(99): uint(99), + uint(34): int64(34), + uint(56): uint64(56), + uint(9223372036854775806): uint64(10), + uint(9223372036854775807): uint64(10), + uint(9223372036854775808): uint64(10), + }, + exceptUint64ToStr: `{"12":12,"34":34,"56":"56","9223372036854775806":"10","9223372036854775807":"10","9223372036854775808":"10","99":99}`, + exceptInt64ToStr: `{"12":12,"34":"34","56":56,"9223372036854775806":10,"9223372036854775807":10,"9223372036854775808":10,"99":99}`, + exceptUIntExToStr: `{"12":12,"34":34,"56":56,"9223372036854775806":10,"9223372036854775807":10,"9223372036854775808":10,"99":99}`, + exceptFalse: `{"12":12,"34":34,"56":56,"9223372036854775806":10,"9223372036854775807":10,"9223372036854775808":10,"99":99}`, + }, + { + name: "normal_struct", + val: struct { + Int int `json:"int"` + Int64 int64 `json:"int64"` + Uint uint `json:"uint"` + UintL uint `json:"uintl"` + UintE uint `json:"uinte"` + UintG uint `json:"uintg"` + Uint64 uint64 `json:"uint64"` + Uint64L uint64 `json:"uint64l"` + Uint64E uint64 `json:"uint64e"` + Uint64G uint64 `json:"uint64g"` + }{ + Int: int(1), + Int64: int64(2), + Uint: uint(1), + UintL: uint(9223372036854775806), + UintE: uint(9223372036854775807), + UintG: uint(9223372036854775808), + Uint64: uint64(1), + Uint64L: uint64(9223372036854775806), + Uint64E: uint64(9223372036854775807), + Uint64G: uint64(9223372036854775808), + }, + exceptUint64ToStr: `{"int":1,"int64":2,`+ + `"uint":1,"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ + `"uint64":"1","uint64l":"9223372036854775806","uint64e":"9223372036854775807","uint64g":"9223372036854775808"}`, + exceptInt64ToStr: `{"int":1,"int64":"2",`+ + `"uint":1,"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ + `"uint64":1,"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":9223372036854775808}`, + exceptUIntExToStr: `{"int":1,"int64":2,`+ + `"uint":1,"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":"9223372036854775808",`+ + `"uint64":1,"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":"9223372036854775808"}`, + exceptFalse: `{"int":1,"int64":2,`+ + `"uint":1,"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ + `"uint64":1,"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":9223372036854775808}`, + }, + { + name: "normal_slice", + val: []interface{}{ + int(12), uint(99), int64(34), uint64(56), uint(9223372036854775806), uint(9223372036854775807), uint(9223372036854775808), + uint64(9223372036854775806), uint64(9223372036854775807), uint64(9223372036854775808), + }, + exceptUint64ToStr: `[12,99,34,"56",`+ + `9223372036854775806,9223372036854775807,9223372036854775808,`+ + `"9223372036854775806","9223372036854775807","9223372036854775808"]`, + exceptInt64ToStr: `[12,99,"34",56,`+ + `9223372036854775806,9223372036854775807,9223372036854775808,`+ + `9223372036854775806,9223372036854775807,9223372036854775808]`, + exceptUIntExToStr: `[12,99,34,56,`+ + `9223372036854775806,9223372036854775807,"9223372036854775808",`+ + `9223372036854775806,9223372036854775807,"9223372036854775808"]`, + exceptFalse: `[12,99,34,56,`+ + `9223372036854775806,9223372036854775807,9223372036854775808,`+ + `9223372036854775806,9223372036854775807,9223372036854775808]`, + }, + { + name: "single_int64", + val: int64(34), + exceptUint64ToStr: `34`, + exceptInt64ToStr: `"34"`, + exceptUIntExToStr: `34`, + exceptFalse: `34`, + }, + { + name: "single_uint64", + val: uint64(56), + exceptUint64ToStr: `"56"`, + exceptInt64ToStr: `56`, + exceptUIntExToStr: `56`, + exceptFalse: `56`, + }, + { + name: "single_uint64g", + val: uint64(9223372036854775808), + exceptUint64ToStr: `"9223372036854775808"`, + exceptInt64ToStr: `9223372036854775808`, + exceptUIntExToStr: `"9223372036854775808"`, + exceptFalse: `9223372036854775808`, + }, + { + name: "single_uintg", + val: uint64(9223372036854775808), + exceptUint64ToStr: `"9223372036854775808"`, + exceptInt64ToStr: `9223372036854775808`, + exceptUIntExToStr: `"9223372036854775808"`, + exceptFalse: `9223372036854775808`, + }, + { + name: "normal_struct_ptr_val", + val: struct { + Int *int `json:"int"` + Int64 *int64 `json:"int64"` + Uint *uint `json:"uint"` + UintL *uint `json:"uintl"` + UintE *uint `json:"uinte"` + UintG *uint `json:"uintg"` + Uint64L *uint64 `json:"uint64l"` + Uint64E *uint64 `json:"uint64e"` + Uint64G *uint64 `json:"uint64g"` + }{ + Int: &intptr, + Int64: &int64ptr, + Uint: &uintPtr, + UintL: &uintLptr, + UintE: &uintEptr, + UintG: &uintGptr, + Uint64L: &uint64Lptr, + Uint64E: &uint64Eptr, + Uint64G: &uint64Gptr, + }, + exceptUint64ToStr: `{"int":1,"int64":1,"uint":1,`+ + `"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ + `"uint64l":"9223372036854775806","uint64e":"9223372036854775807","uint64g":"9223372036854775808"}`, + exceptInt64ToStr: `{"int":1,"int64":"1","uint":1,`+ + `"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ + `"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":9223372036854775808}`, + exceptUIntExToStr: `{"int":1,"int64":1,"uint":1,`+ + `"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":"9223372036854775808",`+ + `"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":"9223372036854775808"}`, + exceptFalse: `{"int":1,"int64":1,"uint":1,`+ + `"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ + `"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":9223372036854775808}`, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + t.Run("uint64ToStr", func(t *testing.T) { + b, e := Config{Uint64ToString: true, SortMapKeys: true}.Froze().Marshal(c.val) + assert.Nil(t, e) + assert.Equal(t, c.exceptUint64ToStr, string(b)) + }) + + t.Run("int64ToStr", func(t *testing.T) { + b, e := Config{Int64ToString: true, SortMapKeys: true}.Froze().Marshal(c.val) + assert.Nil(t, e) + assert.Equal(t, c.exceptInt64ToStr, string(b)) + }) + t.Run("uintExceedToStr", func(t *testing.T) { + b, e := Config{UintExceedToString: true, SortMapKeys: true}.Froze().Marshal(c.val) + assert.Nil(t, e) + assert.Equal(t, c.exceptUIntExToStr, string(b)) + }) + + t.Run("noIntegerToStr", func(t *testing.T) { + b, e := Config{SortMapKeys: true}.Froze().Marshal(c.val) + assert.Nil(t, e) + assert.Equal(t, c.exceptFalse, string(b)) + }) + }) + } +} + +func TestUintExceedToString(t *testing.T) { + val := map[string]interface{}{ + "a": uint(18446744073709551611), + "b": uint(1), + "l": uint(9223372036854775806), // maxInt64 - 1 + "e": uint(9223372036854775807), // maxInt64 + "g": uint(9223372036854775808), // maxInt64 + 1 + "l1": uint64(9223372036854775806), // maxInt64 - 1 + "e1": uint64(9223372036854775807), // maxInt64 + "g1": uint64(9223372036854775808), // maxInt64 + 1 + } + fmt.Println(val) + b, e := Config{UintExceedToString: true}.Froze().Marshal(val) + fmt.Println(string(b), e) } \ No newline at end of file diff --git a/encoder/encoder_native.go b/encoder/encoder_native.go index 3e2e577fd..ab6e2b389 100644 --- a/encoder/encoder_native.go +++ b/encoder/encoder_native.go @@ -77,6 +77,9 @@ const ( // Uint64 or Int64 into strings on Marshal Uint64ToString Options = encoder.Uint64ToString Int64ToString Options = encoder.Int64ToString + // UintExceedToString when intSize == 64, if the value of type uint or uint64 exceeds MaxInt64, + // automatically convert it to a string. + UintExceedToString Options = encoder.UintExceedToString ) diff --git a/internal/encoder/alg/opts.go b/internal/encoder/alg/opts.go index a32e11f5e..22fa649a9 100644 --- a/internal/encoder/alg/opts.go +++ b/internal/encoder/alg/opts.go @@ -28,6 +28,7 @@ const ( BitEncodeNullForInfOrNan BitUint64ToString BitInt64ToString + BitUintExceedToString BitPointerValue = 63 ) diff --git a/internal/encoder/encoder.go b/internal/encoder/encoder.go index d668a9676..f63015ecc 100644 --- a/internal/encoder/encoder.go +++ b/internal/encoder/encoder.go @@ -77,6 +77,9 @@ const ( // Uint64 or Int64 into strings on Marshal Uint64ToString Options = 1 << alg.BitUint64ToString Int64ToString Options = 1 << alg.BitInt64ToString + // UintExceedToString when intSize == 64, if the value of type uint or uint64 exceeds MaxInt64, + // automatically convert it to a string. + UintExceedToString Options = 1 << alg.BitUintExceedToString ) // Encoder represents a specific set of encoder configurations. diff --git a/internal/encoder/vm/vm.go b/internal/encoder/vm/vm.go index 2678b8546..ad4b36dae 100644 --- a/internal/encoder/vm/vm.go +++ b/internal/encoder/vm/vm.go @@ -161,17 +161,27 @@ func Execute(b *[]byte, p unsafe.Pointer, s *vars.Stack, flags uint64, prog *ir. v := *(*uint32)(p) buf = alg.U64toa(buf, uint64(v)) case ir.OP_u64: - if ins.CompatOp() == ir.OP_u || - ins.IsMapKey() || - flags&(1< uint64(math.MaxInt64) && + flags&(1< Date: Thu, 2 Jan 2025 14:17:48 +0800 Subject: [PATCH 7/8] add encoder support UintExceedToString(Remove redundant tests) --- encode_test.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/encode_test.go b/encode_test.go index 5e6c12c6f..1eb1ed8f7 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1478,19 +1478,3 @@ func TestUint64OrInt64ToString(t *testing.T) { }) } } - -func TestUintExceedToString(t *testing.T) { - val := map[string]interface{}{ - "a": uint(18446744073709551611), - "b": uint(1), - "l": uint(9223372036854775806), // maxInt64 - 1 - "e": uint(9223372036854775807), // maxInt64 - "g": uint(9223372036854775808), // maxInt64 + 1 - "l1": uint64(9223372036854775806), // maxInt64 - 1 - "e1": uint64(9223372036854775807), // maxInt64 - "g1": uint64(9223372036854775808), // maxInt64 + 1 - } - fmt.Println(val) - b, e := Config{UintExceedToString: true}.Froze().Marshal(val) - fmt.Println(string(b), e) -} \ No newline at end of file From 90ab8e9459f8632cfcc4b83bb681bcbf1e8c2c25 Mon Sep 17 00:00:00 2001 From: zhangming12 Date: Mon, 6 Jan 2025 20:44:11 +0800 Subject: [PATCH 8/8] support IntegerExceed53BitToString --- api.go | 4 +- encode_test.go | 320 +++++++++++++----- encoder/encoder_native.go | 4 +- internal/encoder/alg/opts.go | 2 +- internal/encoder/encoder.go | 4 +- internal/encoder/vm/vm.go | 30 +- .../encoder/x86/assembler_regabi_amd64.go | 45 ++- sonic.go | 4 +- 8 files changed, 296 insertions(+), 117 deletions(-) diff --git a/api.go b/api.go index 30a254147..986e7aa05 100644 --- a/api.go +++ b/api.go @@ -98,9 +98,9 @@ type Config struct { // Uint64 or Int64 into strings on Marshal Uint64ToString bool Int64ToString bool - // UintExceedToString when intSize == 64, if the value of type uint or uint64 exceeds MaxInt64, + // IntegerExceed53BitToString when intSize == 64, if the value of type integer exceeds 1<<53, // automatically convert it to a string. - UintExceedToString bool + IntegerExceed53BitToString bool } var ( diff --git a/encode_test.go b/encode_test.go index 1eb1ed8f7..2cb1ae10f 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1226,23 +1226,30 @@ func TestMarshalInfOrNan(t *testing.T) { } } -func TestUint64OrInt64ToString(t *testing.T) { +func TestIntegerToString(t *testing.T) { int64ptr := int64(1) intptr := int(1) uintPtr := uint(1) - uint64Lptr := uint64(9223372036854775806) - uint64Eptr := uint64(9223372036854775807) - uint64Gptr := uint64(9223372036854775808) - uintLptr := uint(9223372036854775806) - uintEptr := uint(9223372036854775807) - uintGptr := uint(9223372036854775808) + uint64Lptr := uint64(9007199254740991) + uint64Eptr := uint64(9007199254740992) + uint64Gptr := uint64(9007199254740993) + uintLptr := uint(9007199254740991) + uintEptr := uint(9007199254740992) + uintGptr := uint(9007199254740993) + + int64Lptr := int64(-9007199254740991) + int64Eptr := int64(-9007199254740992) + int64Gptr := int64(-9007199254740993) + intLptr := int(-9007199254740991) + intEptr := int(-9007199254740992) + intGptr := int(-9007199254740993) cases := []struct { name string val interface{} exceptUint64ToStr string exceptInt64ToStr string - exceptUIntExToStr string + exceptExceedToStr string exceptFalse string }{ { @@ -1252,34 +1259,63 @@ func TestUint64OrInt64ToString(t *testing.T) { "uint": uint(99), "int64": int64(34), "uint64": uint64(56), - "exceedUint": uint(9223372036854775808), - "exceedUint64": uint64(9223372036854775808), - "eqMaxInt64Uint64": uint64(9223372036854775807), - "eqMaxInt64Uint": uint(9223372036854775807), - "lsMaxInt64Uint64": uint64(9223372036854775806), - "lsMaxInt64Uint": uint(9223372036854775806), + "exceedUint": uint(9007199254740993), + "exceedUint64": uint64(9007199254740993), + "eqMaxUint64": uint64(9007199254740992), + "eqMaxUint": uint(9007199254740992), + "lsMaxUint64": uint64(9007199254740991), + "lsMaxUint": uint(9007199254740991), }, - exceptUint64ToStr: `{"eqMaxInt64Uint":9223372036854775807,"eqMaxInt64Uint64":"9223372036854775807",`+ - `"exceedUint":9223372036854775808,"exceedUint64":"9223372036854775808",`+ + exceptUint64ToStr: `{"eqMaxUint":9007199254740992,"eqMaxUint64":"9007199254740992",`+ + `"exceedUint":9007199254740993,"exceedUint64":"9007199254740993",`+ `"int":12,"int64":34,`+ - `"lsMaxInt64Uint":9223372036854775806,"lsMaxInt64Uint64":"9223372036854775806",`+ + `"lsMaxUint":9007199254740991,"lsMaxUint64":"9007199254740991",`+ `"uint":99,"uint64":"56"}`, - exceptInt64ToStr: `{"eqMaxInt64Uint":9223372036854775807,"eqMaxInt64Uint64":9223372036854775807,`+ - `"exceedUint":9223372036854775808,"exceedUint64":9223372036854775808,`+ + exceptInt64ToStr: `{"eqMaxUint":9007199254740992,"eqMaxUint64":9007199254740992,`+ + `"exceedUint":9007199254740993,"exceedUint64":9007199254740993,`+ `"int":12,"int64":"34",`+ - `"lsMaxInt64Uint":9223372036854775806,"lsMaxInt64Uint64":9223372036854775806,`+ + `"lsMaxUint":9007199254740991,"lsMaxUint64":9007199254740991,`+ `"uint":99,"uint64":56}`, - exceptUIntExToStr: `{"eqMaxInt64Uint":9223372036854775807,"eqMaxInt64Uint64":9223372036854775807,`+ - `"exceedUint":"9223372036854775808","exceedUint64":"9223372036854775808",`+ + exceptExceedToStr: `{"eqMaxUint":9007199254740992,"eqMaxUint64":9007199254740992,`+ + `"exceedUint":"9007199254740993","exceedUint64":"9007199254740993",`+ `"int":12,"int64":34,`+ - `"lsMaxInt64Uint":9223372036854775806,"lsMaxInt64Uint64":9223372036854775806,`+ + `"lsMaxUint":9007199254740991,"lsMaxUint64":9007199254740991,`+ `"uint":99,"uint64":56}`, - exceptFalse: `{"eqMaxInt64Uint":9223372036854775807,"eqMaxInt64Uint64":9223372036854775807,`+ - `"exceedUint":9223372036854775808,"exceedUint64":9223372036854775808,`+ + exceptFalse: `{"eqMaxUint":9007199254740992,"eqMaxUint64":9007199254740992,`+ + `"exceedUint":9007199254740993,"exceedUint64":9007199254740993,`+ `"int":12,"int64":34,`+ - `"lsMaxInt64Uint":9223372036854775806,"lsMaxInt64Uint64":9223372036854775806,`+ + `"lsMaxUint":9007199254740991,"lsMaxUint64":9007199254740991,`+ `"uint":99,"uint64":56}`, }, + { + name: "normal_map_minus", + val: map[string]interface{}{ + "int": int(12), + "int64": int64(34), + "exceedInt": int(-9007199254740993), + "exceedInt64": int64(-9007199254740993), + "eqMaxInt": int(-9007199254740992), + "eqMaxInt64": int64(-9007199254740992), + "lsMaxInt64": int64(-9007199254740991), + "lsMaxInt": int(-9007199254740991), + }, + exceptUint64ToStr: `{"eqMaxInt":-9007199254740992,"eqMaxInt64":-9007199254740992,`+ + `"exceedInt":-9007199254740993,"exceedInt64":-9007199254740993,`+ + `"int":12,"int64":34,`+ + `"lsMaxInt":-9007199254740991,"lsMaxInt64":-9007199254740991}`, + exceptInt64ToStr: `{"eqMaxInt":-9007199254740992,"eqMaxInt64":"-9007199254740992",`+ + `"exceedInt":-9007199254740993,"exceedInt64":"-9007199254740993",`+ + `"int":12,"int64":"34",`+ + `"lsMaxInt":-9007199254740991,"lsMaxInt64":"-9007199254740991"}`, + exceptExceedToStr: `{"eqMaxInt":-9007199254740992,"eqMaxInt64":-9007199254740992,`+ + `"exceedInt":"-9007199254740993","exceedInt64":"-9007199254740993",`+ + `"int":12,"int64":34,`+ + `"lsMaxInt":-9007199254740991,"lsMaxInt64":-9007199254740991}`, + exceptFalse: `{"eqMaxInt":-9007199254740992,"eqMaxInt64":-9007199254740992,`+ + `"exceedInt":-9007199254740993,"exceedInt64":-9007199254740993,`+ + `"int":12,"int64":34,`+ + `"lsMaxInt":-9007199254740991,"lsMaxInt64":-9007199254740991}`, + }, { name: "int64_key_map", val: map[int64]interface{}{ @@ -1290,23 +1326,35 @@ func TestUint64OrInt64ToString(t *testing.T) { }, exceptUint64ToStr: `{"1":1,"2":2,"3":3,"4":4}`, exceptInt64ToStr: `{"1":1,"2":2,"3":3,"4":4}`, - exceptUIntExToStr: `{"1":1,"2":2,"3":3,"4":4}`, + exceptExceedToStr: `{"1":1,"2":2,"3":3,"4":4}`, exceptFalse: `{"1":1,"2":2,"3":3,"4":4}`, }, + { + name: "minus_int64_key_map", + val: map[int64]interface{}{ + int64(-9007199254740991): 1, + int64(-9007199254740992): 2, + int64(-9007199254740993): 3, + }, + exceptUint64ToStr: `{"-9007199254740991":1,"-9007199254740992":2,"-9007199254740993":3}`, + exceptInt64ToStr: `{"-9007199254740991":1,"-9007199254740992":2,"-9007199254740993":3}`, + exceptExceedToStr: `{"-9007199254740991":1,"-9007199254740992":2,"-9007199254740993":3}`, + exceptFalse: `{"-9007199254740991":1,"-9007199254740992":2,"-9007199254740993":3}`, + }, { name: "uint64_key_map", val: map[uint64]interface{}{ uint64(12): 1, uint64(99): 1, uint64(34): 1, - uint64(9223372036854775806): 1, - uint64(9223372036854775807): 1, - uint64(9223372036854775808): 1, + uint64(9007199254740991): 1, + uint64(9007199254740992): 1, + uint64(9007199254740993): 1, }, - exceptUint64ToStr: `{"12":1,"34":1,"9223372036854775806":1,"9223372036854775807":1,"9223372036854775808":1,"99":1}`, - exceptInt64ToStr: `{"12":1,"34":1,"9223372036854775806":1,"9223372036854775807":1,"9223372036854775808":1,"99":1}`, - exceptUIntExToStr: `{"12":1,"34":1,"9223372036854775806":1,"9223372036854775807":1,"9223372036854775808":1,"99":1}`, - exceptFalse: `{"12":1,"34":1,"9223372036854775806":1,"9223372036854775807":1,"9223372036854775808":1,"99":1}`, + exceptUint64ToStr: `{"12":1,"34":1,"9007199254740991":1,"9007199254740992":1,"9007199254740993":1,"99":1}`, + exceptInt64ToStr: `{"12":1,"34":1,"9007199254740991":1,"9007199254740992":1,"9007199254740993":1,"99":1}`, + exceptExceedToStr: `{"12":1,"34":1,"9007199254740991":1,"9007199254740992":1,"9007199254740993":1,"99":1}`, + exceptFalse: `{"12":1,"34":1,"9007199254740991":1,"9007199254740992":1,"9007199254740993":1,"99":1}`, }, { name: "uint_key_map", @@ -1315,14 +1363,14 @@ func TestUint64OrInt64ToString(t *testing.T) { uint(99): uint(99), uint(34): int64(34), uint(56): uint64(56), - uint(9223372036854775806): uint64(10), - uint(9223372036854775807): uint64(10), - uint(9223372036854775808): uint64(10), + uint(9007199254740991): uint64(10), + uint(9007199254740992): uint64(10), + uint(9007199254740993): uint64(10), }, - exceptUint64ToStr: `{"12":12,"34":34,"56":"56","9223372036854775806":"10","9223372036854775807":"10","9223372036854775808":"10","99":99}`, - exceptInt64ToStr: `{"12":12,"34":"34","56":56,"9223372036854775806":10,"9223372036854775807":10,"9223372036854775808":10,"99":99}`, - exceptUIntExToStr: `{"12":12,"34":34,"56":56,"9223372036854775806":10,"9223372036854775807":10,"9223372036854775808":10,"99":99}`, - exceptFalse: `{"12":12,"34":34,"56":56,"9223372036854775806":10,"9223372036854775807":10,"9223372036854775808":10,"99":99}`, + exceptUint64ToStr: `{"12":12,"34":34,"56":"56","9007199254740991":"10","9007199254740992":"10","9007199254740993":"10","99":99}`, + exceptInt64ToStr: `{"12":12,"34":"34","56":56,"9007199254740991":10,"9007199254740992":10,"9007199254740993":10,"99":99}`, + exceptExceedToStr: `{"12":12,"34":34,"56":56,"9007199254740991":10,"9007199254740992":10,"9007199254740993":10,"99":99}`, + exceptFalse: `{"12":12,"34":34,"56":56,"9007199254740991":10,"9007199254740992":10,"9007199254740993":10,"99":99}`, }, { name: "normal_struct", @@ -1341,77 +1389,167 @@ func TestUint64OrInt64ToString(t *testing.T) { Int: int(1), Int64: int64(2), Uint: uint(1), - UintL: uint(9223372036854775806), - UintE: uint(9223372036854775807), - UintG: uint(9223372036854775808), + UintL: uint(9007199254740991), + UintE: uint(9007199254740992), + UintG: uint(9007199254740993), Uint64: uint64(1), - Uint64L: uint64(9223372036854775806), - Uint64E: uint64(9223372036854775807), - Uint64G: uint64(9223372036854775808), + Uint64L: uint64(9007199254740991), + Uint64E: uint64(9007199254740992), + Uint64G: uint64(9007199254740993), }, exceptUint64ToStr: `{"int":1,"int64":2,`+ - `"uint":1,"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ - `"uint64":"1","uint64l":"9223372036854775806","uint64e":"9223372036854775807","uint64g":"9223372036854775808"}`, + `"uint":1,"uintl":9007199254740991,"uinte":9007199254740992,"uintg":9007199254740993,`+ + `"uint64":"1","uint64l":"9007199254740991","uint64e":"9007199254740992","uint64g":"9007199254740993"}`, exceptInt64ToStr: `{"int":1,"int64":"2",`+ - `"uint":1,"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ - `"uint64":1,"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":9223372036854775808}`, - exceptUIntExToStr: `{"int":1,"int64":2,`+ - `"uint":1,"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":"9223372036854775808",`+ - `"uint64":1,"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":"9223372036854775808"}`, + `"uint":1,"uintl":9007199254740991,"uinte":9007199254740992,"uintg":9007199254740993,`+ + `"uint64":1,"uint64l":9007199254740991,"uint64e":9007199254740992,"uint64g":9007199254740993}`, + exceptExceedToStr: `{"int":1,"int64":2,`+ + `"uint":1,"uintl":9007199254740991,"uinte":9007199254740992,"uintg":"9007199254740993",`+ + `"uint64":1,"uint64l":9007199254740991,"uint64e":9007199254740992,"uint64g":"9007199254740993"}`, exceptFalse: `{"int":1,"int64":2,`+ - `"uint":1,"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ - `"uint64":1,"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":9223372036854775808}`, + `"uint":1,"uintl":9007199254740991,"uinte":9007199254740992,"uintg":9007199254740993,`+ + `"uint64":1,"uint64l":9007199254740991,"uint64e":9007199254740992,"uint64g":9007199254740993}`, + }, + { + name: "minus_struct", + val: struct { + Int int `json:"int"` + IntL int `json:"intl"` + IntE int `json:"inte"` + IntG int `json:"intg"` + Int64 int64 `json:"int64"` + Int64L int64 `json:"int64l"` + Int64E int64 `json:"int64e"` + Int64G int64 `json:"int64g"` + }{ + Int: 10, + IntL: int(-9007199254740991), + IntE: int(-9007199254740992), + IntG: int(-9007199254740993), + Int64: int64(1), + Int64L: int64(-9007199254740991), + Int64E: int64(-9007199254740992), + Int64G: int64(-9007199254740993), + }, + exceptUint64ToStr: `{"int":10,"intl":-9007199254740991,"inte":-9007199254740992,"intg":-9007199254740993,`+ + `"int64":1,"int64l":-9007199254740991,"int64e":-9007199254740992,"int64g":-9007199254740993}`, + exceptInt64ToStr: `{"int":10,"intl":-9007199254740991,"inte":-9007199254740992,"intg":-9007199254740993,`+ + `"int64":"1","int64l":"-9007199254740991","int64e":"-9007199254740992","int64g":"-9007199254740993"}`, + exceptExceedToStr: `{"int":10,"intl":-9007199254740991,"inte":-9007199254740992,"intg":"-9007199254740993",`+ + `"int64":1,"int64l":-9007199254740991,"int64e":-9007199254740992,"int64g":"-9007199254740993"}`, + exceptFalse: `{"int":10,"intl":-9007199254740991,"inte":-9007199254740992,"intg":-9007199254740993,`+ + `"int64":1,"int64l":-9007199254740991,"int64e":-9007199254740992,"int64g":-9007199254740993}`, }, { name: "normal_slice", val: []interface{}{ - int(12), uint(99), int64(34), uint64(56), uint(9223372036854775806), uint(9223372036854775807), uint(9223372036854775808), - uint64(9223372036854775806), uint64(9223372036854775807), uint64(9223372036854775808), + int(12), uint(99), int64(34), uint64(56), uint(9007199254740991), uint(9007199254740992), uint(9007199254740993), + uint64(9007199254740991), uint64(9007199254740992), uint64(9007199254740993), }, exceptUint64ToStr: `[12,99,34,"56",`+ - `9223372036854775806,9223372036854775807,9223372036854775808,`+ - `"9223372036854775806","9223372036854775807","9223372036854775808"]`, + `9007199254740991,9007199254740992,9007199254740993,`+ + `"9007199254740991","9007199254740992","9007199254740993"]`, exceptInt64ToStr: `[12,99,"34",56,`+ - `9223372036854775806,9223372036854775807,9223372036854775808,`+ - `9223372036854775806,9223372036854775807,9223372036854775808]`, - exceptUIntExToStr: `[12,99,34,56,`+ - `9223372036854775806,9223372036854775807,"9223372036854775808",`+ - `9223372036854775806,9223372036854775807,"9223372036854775808"]`, + `9007199254740991,9007199254740992,9007199254740993,`+ + `9007199254740991,9007199254740992,9007199254740993]`, + exceptExceedToStr: `[12,99,34,56,`+ + `9007199254740991,9007199254740992,"9007199254740993",`+ + `9007199254740991,9007199254740992,"9007199254740993"]`, exceptFalse: `[12,99,34,56,`+ - `9223372036854775806,9223372036854775807,9223372036854775808,`+ - `9223372036854775806,9223372036854775807,9223372036854775808]`, + `9007199254740991,9007199254740992,9007199254740993,`+ + `9007199254740991,9007199254740992,9007199254740993]`, + }, + { + name: "minus_normal_slice", + val: []interface{}{ + int(12), int64(99), + int(-9007199254740991), int(-9007199254740992), int(-9007199254740993), + int64(-9007199254740991), int64(-9007199254740992), int64(-9007199254740993), + }, + exceptUint64ToStr: `[12,99,`+ + `-9007199254740991,-9007199254740992,-9007199254740993,`+ + `-9007199254740991,-9007199254740992,-9007199254740993]`, + exceptInt64ToStr: `[12,"99",`+ + `-9007199254740991,-9007199254740992,-9007199254740993,`+ + `"-9007199254740991","-9007199254740992","-9007199254740993"]`, + exceptExceedToStr: `[12,99,`+ + `-9007199254740991,-9007199254740992,"-9007199254740993",`+ + `-9007199254740991,-9007199254740992,"-9007199254740993"]`, + exceptFalse: `[12,99,`+ + `-9007199254740991,-9007199254740992,-9007199254740993,`+ + `-9007199254740991,-9007199254740992,-9007199254740993]`, }, { name: "single_int64", val: int64(34), exceptUint64ToStr: `34`, exceptInt64ToStr: `"34"`, - exceptUIntExToStr: `34`, + exceptExceedToStr: `34`, exceptFalse: `34`, }, + { + name: "minus_single_int64", + val: int64(-9007199254740993), + exceptUint64ToStr: `-9007199254740993`, + exceptInt64ToStr: `"-9007199254740993"`, + exceptExceedToStr: `"-9007199254740993"`, + exceptFalse: `-9007199254740993`, + }, { name: "single_uint64", val: uint64(56), exceptUint64ToStr: `"56"`, exceptInt64ToStr: `56`, - exceptUIntExToStr: `56`, + exceptExceedToStr: `56`, exceptFalse: `56`, }, { name: "single_uint64g", - val: uint64(9223372036854775808), - exceptUint64ToStr: `"9223372036854775808"`, - exceptInt64ToStr: `9223372036854775808`, - exceptUIntExToStr: `"9223372036854775808"`, - exceptFalse: `9223372036854775808`, + val: uint64(9007199254740993), + exceptUint64ToStr: `"9007199254740993"`, + exceptInt64ToStr: `9007199254740993`, + exceptExceedToStr: `"9007199254740993"`, + exceptFalse: `9007199254740993`, }, { name: "single_uintg", - val: uint64(9223372036854775808), - exceptUint64ToStr: `"9223372036854775808"`, - exceptInt64ToStr: `9223372036854775808`, - exceptUIntExToStr: `"9223372036854775808"`, - exceptFalse: `9223372036854775808`, + val: uint64(9007199254740993), + exceptUint64ToStr: `"9007199254740993"`, + exceptInt64ToStr: `9007199254740993`, + exceptExceedToStr: `"9007199254740993"`, + exceptFalse: `9007199254740993`, + }, + { + name: "minus_struct_ptr_val", + val: struct { + Int *int `json:"int"` + IntL *int `json:"intl"` + IntE *int `json:"inte"` + IntG *int `json:"intg"` + Int64L *int64 `json:"int64l"` + Int64E *int64 `json:"int64e"` + Int64G *int64 `json:"int64g"` + }{ + Int: &intptr, + IntL: &intLptr, + IntE: &intEptr, + IntG: &intGptr, + Int64L: &int64Lptr, + Int64E: &int64Eptr, + Int64G: &int64Gptr, + }, + exceptUint64ToStr: `{"int":1,`+ + `"intl":-9007199254740991,"inte":-9007199254740992,"intg":-9007199254740993,`+ + `"int64l":-9007199254740991,"int64e":-9007199254740992,"int64g":-9007199254740993}`, + exceptInt64ToStr: `{"int":1,`+ + `"intl":-9007199254740991,"inte":-9007199254740992,"intg":-9007199254740993,`+ + `"int64l":"-9007199254740991","int64e":"-9007199254740992","int64g":"-9007199254740993"}`, + exceptExceedToStr: `{"int":1,`+ + `"intl":-9007199254740991,"inte":-9007199254740992,"intg":"-9007199254740993",`+ + `"int64l":-9007199254740991,"int64e":-9007199254740992,"int64g":"-9007199254740993"}`, + exceptFalse: `{"int":1,`+ + `"intl":-9007199254740991,"inte":-9007199254740992,"intg":-9007199254740993,`+ + `"int64l":-9007199254740991,"int64e":-9007199254740992,"int64g":-9007199254740993}`, }, { name: "normal_struct_ptr_val", @@ -1437,17 +1575,17 @@ func TestUint64OrInt64ToString(t *testing.T) { Uint64G: &uint64Gptr, }, exceptUint64ToStr: `{"int":1,"int64":1,"uint":1,`+ - `"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ - `"uint64l":"9223372036854775806","uint64e":"9223372036854775807","uint64g":"9223372036854775808"}`, + `"uintl":9007199254740991,"uinte":9007199254740992,"uintg":9007199254740993,`+ + `"uint64l":"9007199254740991","uint64e":"9007199254740992","uint64g":"9007199254740993"}`, exceptInt64ToStr: `{"int":1,"int64":"1","uint":1,`+ - `"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ - `"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":9223372036854775808}`, - exceptUIntExToStr: `{"int":1,"int64":1,"uint":1,`+ - `"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":"9223372036854775808",`+ - `"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":"9223372036854775808"}`, + `"uintl":9007199254740991,"uinte":9007199254740992,"uintg":9007199254740993,`+ + `"uint64l":9007199254740991,"uint64e":9007199254740992,"uint64g":9007199254740993}`, + exceptExceedToStr: `{"int":1,"int64":1,"uint":1,`+ + `"uintl":9007199254740991,"uinte":9007199254740992,"uintg":"9007199254740993",`+ + `"uint64l":9007199254740991,"uint64e":9007199254740992,"uint64g":"9007199254740993"}`, exceptFalse: `{"int":1,"int64":1,"uint":1,`+ - `"uintl":9223372036854775806,"uinte":9223372036854775807,"uintg":9223372036854775808,`+ - `"uint64l":9223372036854775806,"uint64e":9223372036854775807,"uint64g":9223372036854775808}`, + `"uintl":9007199254740991,"uinte":9007199254740992,"uintg":9007199254740993,`+ + `"uint64l":9007199254740991,"uint64e":9007199254740992,"uint64g":9007199254740993}`, }, } @@ -1465,9 +1603,9 @@ func TestUint64OrInt64ToString(t *testing.T) { assert.Equal(t, c.exceptInt64ToStr, string(b)) }) t.Run("uintExceedToStr", func(t *testing.T) { - b, e := Config{UintExceedToString: true, SortMapKeys: true}.Froze().Marshal(c.val) + b, e := Config{IntegerExceed53BitToString: true, SortMapKeys: true}.Froze().Marshal(c.val) assert.Nil(t, e) - assert.Equal(t, c.exceptUIntExToStr, string(b)) + assert.Equal(t, c.exceptExceedToStr, string(b)) }) t.Run("noIntegerToStr", func(t *testing.T) { @@ -1477,4 +1615,4 @@ func TestUint64OrInt64ToString(t *testing.T) { }) }) } -} +} \ No newline at end of file diff --git a/encoder/encoder_native.go b/encoder/encoder_native.go index ab6e2b389..861ec36bb 100644 --- a/encoder/encoder_native.go +++ b/encoder/encoder_native.go @@ -77,9 +77,9 @@ const ( // Uint64 or Int64 into strings on Marshal Uint64ToString Options = encoder.Uint64ToString Int64ToString Options = encoder.Int64ToString - // UintExceedToString when intSize == 64, if the value of type uint or uint64 exceeds MaxInt64, + // IntegerExceed53BitToString when intSize == 64, if the value of type integer exceeds 1<<53, // automatically convert it to a string. - UintExceedToString Options = encoder.UintExceedToString + IntegerExceed53BitToString Options = encoder.IntegerExceed53BitToString ) diff --git a/internal/encoder/alg/opts.go b/internal/encoder/alg/opts.go index 22fa649a9..a6b8aecf6 100644 --- a/internal/encoder/alg/opts.go +++ b/internal/encoder/alg/opts.go @@ -28,7 +28,7 @@ const ( BitEncodeNullForInfOrNan BitUint64ToString BitInt64ToString - BitUintExceedToString + BitIntegerExceed53BitToString BitPointerValue = 63 ) diff --git a/internal/encoder/encoder.go b/internal/encoder/encoder.go index f63015ecc..06b39c082 100644 --- a/internal/encoder/encoder.go +++ b/internal/encoder/encoder.go @@ -77,9 +77,9 @@ const ( // Uint64 or Int64 into strings on Marshal Uint64ToString Options = 1 << alg.BitUint64ToString Int64ToString Options = 1 << alg.BitInt64ToString - // UintExceedToString when intSize == 64, if the value of type uint or uint64 exceeds MaxInt64, + // IntegerExceed53BitToString when intSize == 64, if the value of type integer exceeds 1<<53, // automatically convert it to a string. - UintExceedToString Options = 1 << alg.BitUintExceedToString + IntegerExceed53BitToString Options = 1 << alg.BitIntegerExceed53BitToString ) // Encoder represents a specific set of encoder configurations. diff --git a/internal/encoder/vm/vm.go b/internal/encoder/vm/vm.go index ad4b36dae..4320f6d90 100644 --- a/internal/encoder/vm/vm.go +++ b/internal/encoder/vm/vm.go @@ -34,6 +34,11 @@ const ( _S_init ) +const ( + _IMax_53 = 1<<53 + _IMin_53 = -1<<53 +) + var ( _T_json_Marshaler = rt.UnpackType(vars.JsonMarshalerType) _T_encoding_TextMarshaler = rt.UnpackType(vars.EncodingTextMarshalerType) @@ -140,17 +145,28 @@ func Execute(b *[]byte, p unsafe.Pointer, s *vars.Stack, flags uint64, prog *ir. v := *(*int32)(p) buf = alg.I64toa(buf, int64(v)) case ir.OP_i64: - if ins.CompatOp() == ir.OP_i || - ins.IsMapKey() || - flags&(1< int64(_IMax_53) || v < int64(_IMin_53)) && + flags&(1< uint64(math.MaxInt64) && - flags&(1< uint64(_IMax_53) && + flags&(1<