Compare commits

...

5 Commits

Author SHA1 Message Date
Marcellus Tavares b18db1095d
Merge 96b9d74fc6 into 3e9769d637 2024-11-22 06:54:38 +00:00
Masaaki Goshima 3e9769d637
Update go.yml 2024-11-11 12:13:39 +09:00
Andrey Grazhdankov 65c8b28ca1
Fix encode []*time.Time - check nil (#524) 2024-11-11 11:10:21 +09:00
Marcellus Tavares 96b9d74fc6
Add tests 2023-02-24 11:16:28 -08:00
Marcellus Tavares 5b019aec0e
Favor int64 precision if possible 2023-02-24 11:16:18 -08:00
6 changed files with 77 additions and 33 deletions

View File

@ -12,7 +12,7 @@ jobs:
- name: checkout
uses: actions/checkout@v3
- name: build
run: docker-compose run go-json
run: docker compose run go-json
test:
name: Test

View File

@ -208,10 +208,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))

View File

@ -426,6 +426,11 @@ func Test_Marshal(t *testing.T) {
assertErr(t, err)
assertEq(t, "[]interface{}", `[1,2.1,"hello"]`, string(bytes))
})
t.Run("[]*time.Time", func(t *testing.T) {
bytes, err := json.Marshal([]*time.Time{nil})
assertErr(t, err)
assertEq(t, "[]*time.Time", `[null]`, string(bytes))
})
})
t.Run("array", func(t *testing.T) {

View File

@ -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)

View File

@ -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)

View File

@ -406,6 +406,11 @@ func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{
rv = newV
}
}
if rv.Kind() == reflect.Ptr && rv.IsNil() {
return AppendNull(ctx, b), nil
}
v = rv.Interface()
var bb []byte
if (code.Flags & MarshalerContextFlags) != 0 {