From 5b019aec0ea6dc46a8754021612f6bcf6c938710 Mon Sep 17 00:00:00 2001 From: Marcellus Tavares Date: Fri, 24 Feb 2023 11:16:04 -0800 Subject: [PATCH 1/2] Favor int64 precision if possible --- internal/decoder/int.go | 72 ++++++++++++++++++++--------------- internal/decoder/interface.go | 16 +++++++- 2 files changed, 57 insertions(+), 31 deletions(-) diff --git a/internal/decoder/int.go b/internal/decoder/int.go index 1a7f081..ef16d95 100644 --- a/internal/decoder/int.go +++ b/internal/decoder/int.go @@ -18,12 +18,20 @@ type intDecoder struct { } func newIntDecoder(typ *runtime.Type, structName, fieldName string, op func(unsafe.Pointer, int64)) *intDecoder { - return &intDecoder{ - typ: typ, - kind: typ.Kind(), - op: op, - structName: structName, - fieldName: fieldName, + if typ != nil { + return &intDecoder{ + typ: typ, + kind: typ.Kind(), + op: op, + structName: structName, + fieldName: fieldName, + } + } else { + return &intDecoder{ + op: op, + structName: structName, + fieldName: fieldName, + } } } @@ -190,18 +198,20 @@ func (d *intDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) erro if err != nil { return d.typeError(bytes, s.totalOffset()) } - switch d.kind { - case reflect.Int8: - if i64 < -1*(1<<7) || (1<<7) <= i64 { - return d.typeError(bytes, s.totalOffset()) - } - case reflect.Int16: - if i64 < -1*(1<<15) || (1<<15) <= i64 { - return d.typeError(bytes, s.totalOffset()) - } - case reflect.Int32: - if i64 < -1*(1<<31) || (1<<31) <= i64 { - return d.typeError(bytes, s.totalOffset()) + if d.typ != nil { + switch d.kind { + case reflect.Int8: + if i64 < -1*(1<<7) || (1<<7) <= i64 { + return d.typeError(bytes, s.totalOffset()) + } + case reflect.Int16: + if i64 < -1*(1<<15) || (1<<15) <= i64 { + return d.typeError(bytes, s.totalOffset()) + } + case reflect.Int32: + if i64 < -1*(1<<31) || (1<<31) <= i64 { + return d.typeError(bytes, s.totalOffset()) + } } } d.op(p, i64) @@ -223,18 +233,20 @@ func (d *intDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.P if err != nil { return 0, d.typeError(bytes, cursor) } - switch d.kind { - case reflect.Int8: - if i64 < -1*(1<<7) || (1<<7) <= i64 { - return 0, d.typeError(bytes, cursor) - } - case reflect.Int16: - if i64 < -1*(1<<15) || (1<<15) <= i64 { - return 0, d.typeError(bytes, cursor) - } - case reflect.Int32: - if i64 < -1*(1<<31) || (1<<31) <= i64 { - return 0, d.typeError(bytes, cursor) + if d.typ != nil { + switch d.kind { + case reflect.Int8: + if i64 < -1*(1<<7) || (1<<7) <= i64 { + return 0, d.typeError(bytes, cursor) + } + case reflect.Int16: + if i64 < -1*(1<<15) || (1<<15) <= i64 { + return 0, d.typeError(bytes, cursor) + } + case reflect.Int32: + if i64 < -1*(1<<31) || (1<<31) <= i64 { + return 0, d.typeError(bytes, cursor) + } } } d.op(p, i64) diff --git a/internal/decoder/interface.go b/internal/decoder/interface.go index 45c69ab..f0fefe5 100644 --- a/internal/decoder/interface.go +++ b/internal/decoder/interface.go @@ -5,6 +5,7 @@ import ( "encoding" "encoding/json" "reflect" + "strings" "unsafe" "github.com/goccy/go-json/internal/errors" @@ -18,6 +19,7 @@ type interfaceDecoder struct { sliceDecoder *sliceDecoder mapDecoder *mapDecoder floatDecoder *floatDecoder + intDecoder *intDecoder numberDecoder *numberDecoder stringDecoder *stringDecoder } @@ -30,6 +32,9 @@ func newEmptyInterfaceDecoder(structName, fieldName string) *interfaceDecoder { floatDecoder: newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) { *(*interface{})(p) = v }), + intDecoder: newIntDecoder(nil, structName, fieldName, func(p unsafe.Pointer, v int64) { + *(*interface{})(p) = v + }), numberDecoder: newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v json.Number) { *(*interface{})(p) = v }), @@ -78,6 +83,9 @@ func newInterfaceDecoder(typ *runtime.Type, structName, fieldName string) *inter floatDecoder: newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) { *(*interface{})(p) = v }), + intDecoder: newIntDecoder(nil, structName, fieldName, func(p unsafe.Pointer, v int64) { + *(*interface{})(p) = v + }), numberDecoder: newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v json.Number) { *(*interface{})(p) = v }), @@ -423,7 +431,13 @@ func (d *interfaceDecoder) decodeEmptyInterface(ctx *RuntimeContext, cursor, dep **(**interface{})(unsafe.Pointer(&p)) = v return cursor, nil case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return d.floatDecoder.Decode(ctx, cursor, depth, p) + bytes, _, _ := d.numberDecoder.decodeByte(ctx.Buf, cursor) + numberStr := *(*string)(unsafe.Pointer(&bytes)) + if strings.Contains(numberStr, ".") { + return d.floatDecoder.Decode(ctx, cursor, depth, p) + } else { + return d.intDecoder.Decode(ctx, cursor, depth, p) + } case '"': var v string ptr := unsafe.Pointer(&v) From 96b9d74fc60aace6c2495ea25578a4bf13ff8522 Mon Sep 17 00:00:00 2001 From: Marcellus Tavares Date: Fri, 24 Feb 2023 11:16:28 -0800 Subject: [PATCH 2/2] Add tests --- decode_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/decode_test.go b/decode_test.go index f139eae..02aefaf 100644 --- a/decode_test.go +++ b/decode_test.go @@ -201,10 +201,18 @@ func Test_Decoder(t *testing.T) { t.Run("interface", func(t *testing.T) { t.Run("number", func(t *testing.T) { var v interface{} - assertErr(t, json.Unmarshal([]byte(`10`), &v)) + assertErr(t, json.Unmarshal([]byte(`10.0`), &v)) assertEq(t, "interface.kind", "float64", reflect.TypeOf(v).Kind().String()) assertEq(t, "interface", `10`, fmt.Sprint(v)) }) + t.Run("int64", func(t *testing.T) { + type StructValueLayout struct { + Values []interface{} `json:"values"` + } + var structLayout StructValueLayout + assertErr(t, json.Unmarshal([]byte(`{"values":[2074546971352916989, "test"]}`), &structLayout)) + assertEq(t, "int64", int64(2074546971352916989), structLayout.Values[0].(int64)) + }) t.Run("string", func(t *testing.T) { var v interface{} assertErr(t, json.Unmarshal([]byte(`"hello"`), &v))