1
0
mirror of https://github.com/json-iterator/go.git synced 2025-02-19 19:59:49 +02:00

rewrite how eface and iface are handled

This commit is contained in:
Tao Wen 2018-02-21 12:16:50 +08:00
parent ea6403326b
commit 2fcbb23d96
13 changed files with 301 additions and 243 deletions

View File

@ -35,4 +35,14 @@ func Test_customize_tag_key(t *testing.T) {
str, err := json.MarshalToString(TestObject{"hello"})
should.Nil(err)
should.Equal(`{"field":"hello"}`, str)
}
func Test_read_large_number_as_interface(t *testing.T) {
should := require.New(t)
var val interface{}
err := jsoniter.Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val)
should.Nil(err)
output, err := jsoniter.MarshalToString(val)
should.Nil(err)
should.Equal(`123456789123456789123456789`, output)
}

View File

@ -2,7 +2,6 @@ package jsoniter
import (
"encoding/json"
"errors"
"io"
"reflect"
"unsafe"
@ -267,11 +266,6 @@ func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error {
data = data[:lastNotSpacePos(data)]
iter := cfg.BorrowIterator(data)
defer cfg.ReturnIterator(iter)
typ := reflect.TypeOf(v)
if typ.Kind() != reflect.Ptr {
// return non-pointer error
return errors.New("the second param must be ptr type")
}
iter.ReadVal(v)
if iter.head == iter.tail {
iter.loadMore()

View File

@ -49,13 +49,13 @@ func Test_read_float64_cursor(t *testing.T) {
func Test_read_float_scientific(t *testing.T) {
should := require.New(t)
var obj interface{}
should.Nil(jsoniter.UnmarshalFromString(`1e1`, &obj))
should.NoError(jsoniter.UnmarshalFromString(`1e1`, &obj))
should.Equal(float64(10), obj)
should.Nil(json.Unmarshal([]byte(`1e1`), &obj))
should.NoError(json.Unmarshal([]byte(`1e1`), &obj))
should.Equal(float64(10), obj)
should.Nil(jsoniter.UnmarshalFromString(`1.0e1`, &obj))
should.NoError(jsoniter.UnmarshalFromString(`1.0e1`, &obj))
should.Equal(float64(10), obj)
should.Nil(json.Unmarshal([]byte(`1.0e1`), &obj))
should.NoError(json.Unmarshal([]byte(`1.0e1`), &obj))
should.Equal(float64(10), obj)
}

View File

@ -2,36 +2,15 @@ package misc_tests
import (
"encoding/json"
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/json-iterator/go"
"io"
)
type MyInterface interface {
Hello() string
}
type MyString string
func (ms MyString) Hello() string {
return string(ms)
}
func Test_decode_object_contain_non_empty_interface(t *testing.T) {
type TestObject struct {
Field MyInterface
}
should := require.New(t)
obj := TestObject{}
obj.Field = MyString("abc")
should.Nil(jsoniter.UnmarshalFromString(`{"Field": "hello"}`, &obj))
should.Equal(MyString("hello"), obj.Field)
}
func Test_nil_non_empty_interface(t *testing.T) {
type TestObject struct {
Field []MyInterface
Field []io.Closer
}
should := require.New(t)
obj := TestObject{}
@ -40,31 +19,6 @@ func Test_nil_non_empty_interface(t *testing.T) {
should.NotNil(jsoniter.Unmarshal(b, &obj))
}
func Test_read_large_number_as_interface(t *testing.T) {
should := require.New(t)
var val interface{}
err := jsoniter.Config{UseNumber: true}.Froze().UnmarshalFromString(`123456789123456789123456789`, &val)
should.Nil(err)
output, err := jsoniter.MarshalToString(val)
should.Nil(err)
should.Equal(`123456789123456789123456789`, output)
}
func Test_unmarshal_ptr_to_interface(t *testing.T) {
type TestData struct {
Name string `json:"name"`
}
should := require.New(t)
var obj interface{} = &TestData{}
err := json.Unmarshal([]byte(`{"name":"value"}`), &obj)
should.Nil(err)
should.Equal("&{value}", fmt.Sprintf("%v", obj))
obj = interface{}(&TestData{})
err = jsoniter.Unmarshal([]byte(`{"name":"value"}`), &obj)
should.Nil(err)
should.Equal("&{value}", fmt.Sprintf("%v", obj))
}
func Test_nil_out_null_interface(t *testing.T) {
type TestData struct {
Field interface{} `json:"field"`
@ -86,7 +40,7 @@ func Test_nil_out_null_interface(t *testing.T) {
err = jsoniter.Unmarshal(data2, &obj)
should.NoError(err)
should.Equal(nil, obj.Field)
should.Nil(obj.Field)
// Checking stdlib behavior matches.
obj2 := TestData{
@ -118,12 +72,12 @@ func Test_overwrite_interface_ptr_value_with_nil(t *testing.T) {
}
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Equal(nil, err)
should.NoError(err)
should.Equal(&payload, wrapper.Payload)
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Equal(nil, err)
should.NoError(err)
should.Equal(&payload, wrapper.Payload)
should.Equal((*Payload)(nil), payload)
@ -138,7 +92,7 @@ func Test_overwrite_interface_ptr_value_with_nil(t *testing.T) {
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Equal(nil, err)
should.NoError(err)
should.Equal(&payload, wrapper.Payload)
should.Equal((*Payload)(nil), payload)
}
@ -159,11 +113,11 @@ func Test_overwrite_interface_value_with_nil(t *testing.T) {
}
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Equal(nil, err)
should.NoError(err)
should.Equal(42, (*(wrapper.Payload.(*Payload))).Value)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Equal(nil, err)
should.NoError(err)
should.Equal(nil, wrapper.Payload)
should.Equal(42, payload.Value)
@ -198,12 +152,12 @@ func Test_unmarshal_into_nil(t *testing.T) {
}
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Nil(err)
should.NoError(err)
should.NotNil(wrapper.Payload)
should.Nil(payload)
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Nil(err)
should.NoError(err)
should.Nil(wrapper.Payload)
should.Nil(payload)
@ -213,12 +167,12 @@ func Test_unmarshal_into_nil(t *testing.T) {
}
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
should.Nil(err)
should.NoError(err)
should.NotNil(wrapper.Payload)
should.Nil(payload)
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
should.Nil(err)
should.NoError(err)
should.Nil(wrapper.Payload)
should.Nil(payload)
}

View File

@ -36,14 +36,17 @@ type checkIsEmpty interface {
// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
func (iter *Iterator) ReadVal(obj interface{}) {
typ := reflect.TypeOf(obj)
cacheKey := typ.Elem()
decoder := decoderOfType(iter.cfg, "", cacheKey)
e := (*emptyInterface)(unsafe.Pointer(&obj))
if e.word == nil {
if typ.Kind() != reflect.Ptr {
iter.ReportError("ReadVal", "can only unmarshal into pointer")
return
}
decoder := iter.cfg.DecoderOf(typ)
ptr := reflect2.PtrOf(obj)
if ptr == nil {
iter.ReportError("ReadVal", "can not read into nil pointer")
return
}
decoder.Decode(e.word, iter)
decoder.Decode(ptr, iter)
}
// WriteVal copy the go interface into underlying JSON, same as json.Marshal
@ -63,7 +66,7 @@ func (cfg *frozenConfig) DecoderOf(typ reflect.Type) ValDecoder {
if decoder != nil {
return decoder
}
decoder = decoderOfType(cfg, "", typ)
decoder = decoderOfType(cfg, "", typ.Elem())
cfg.addDecoderToCache(cacheKey, decoder)
return decoder
}
@ -106,10 +109,11 @@ func createDecoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) Val
}
switch typ.Kind() {
case reflect.Interface:
if typ.NumMethod() == 0 {
return &emptyInterfaceCodec{}
if typ.NumMethod() > 0 {
ifaceType := reflect2.Type2(typ).(*reflect2.UnsafeIFaceType)
return &ifaceDecoder{valType: ifaceType}
}
return &nonEmptyInterfaceCodec{}
return &efaceDecoder{}
case reflect.Struct:
return decoderOfStruct(cfg, prefix, typ)
case reflect.Array:
@ -239,27 +243,3 @@ func (encoder *lazyErrorEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
func (encoder *lazyErrorEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return false
}
func extractInterface(val interface{}) emptyInterface {
return *((*emptyInterface)(unsafe.Pointer(&val)))
}
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ unsafe.Pointer
word unsafe.Pointer
}
// emptyInterface is the header for an interface with method (not interface{})
type nonEmptyInterface struct {
// see ../runtime/iface.go:/Itab
itab *struct {
ityp unsafe.Pointer // static interface type
typ unsafe.Pointer // dynamic concrete type
link unsafe.Pointer
bad int32
unused int32
fun [100000]unsafe.Pointer // method table
}
word unsafe.Pointer
}

70
reflect_dynamic.go Normal file
View File

@ -0,0 +1,70 @@
package jsoniter
import (
"github.com/v2pro/plz/reflect2"
"unsafe"
"reflect"
)
type dynamicEncoder struct {
valType reflect2.Type
}
func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
obj := encoder.valType.UnsafeIndirect(ptr)
stream.WriteVal(obj)
}
func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return encoder.valType.UnsafeIndirect(ptr) == nil
}
type efaceDecoder struct {
}
func (decoder *efaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
pObj := (*interface{})(ptr)
obj := *pObj
if obj == nil {
*pObj = iter.Read()
return
}
typ := reflect2.TypeOf(obj)
if typ.Kind() != reflect.Ptr {
*pObj = iter.Read()
return
}
ptrType := typ.(*reflect2.UnsafePtrType)
ptrElemType := ptrType.Elem()
if iter.WhatIsNext() == NilValue {
if ptrElemType.Kind() != reflect.Ptr {
iter.skipFourBytes('n', 'u', 'l', 'l')
*pObj = nil
return
}
}
if reflect2.IsNil(obj) {
obj := ptrElemType.New()
iter.ReadVal(obj)
*pObj = obj
return
}
iter.ReadVal(obj)
}
type ifaceDecoder struct {
valType *reflect2.UnsafeIFaceType
}
func (decoder *ifaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.ReadNil() {
decoder.valType.UnsafeSet(ptr, decoder.valType.UnsafeNew())
return
}
obj := decoder.valType.UnsafeIndirect(ptr)
if reflect2.IsNil(obj) {
iter.ReportError("decode non empty interface", "can not unmarshal into nil")
return
}
iter.ReadVal(obj)
}

View File

@ -7,6 +7,7 @@ import (
"strings"
"unicode"
"unsafe"
"github.com/v2pro/plz/reflect2"
)
var typeDecoders = map[string]ValDecoder{}
@ -240,7 +241,7 @@ func _getTypeDecoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValDecode
if typ.Kind() == reflect.Ptr {
decoder := typeDecoders[typ.Elem().String()]
if decoder != nil {
return &OptionalDecoder{typ.Elem(), decoder}
return &OptionalDecoder{reflect2.Type2(typ.Elem()), decoder}
}
}
return nil
@ -317,7 +318,7 @@ func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructD
omitempty := binding.Encoder.(*structFieldEncoder).omitempty
binding.Encoder = &dereferenceEncoder{binding.Encoder}
binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
binding.Decoder = &dereferenceDecoder{field.Type.Elem(), binding.Decoder}
binding.Decoder = &dereferenceDecoder{reflect2.Type2(field.Type.Elem()), binding.Decoder}
binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
embeddedBindings = append(embeddedBindings, binding)
}

View File

@ -435,112 +435,6 @@ func (codec *boolCodec) IsEmpty(ptr unsafe.Pointer) bool {
return !(*((*bool)(ptr)))
}
type emptyInterfaceCodec struct {
}
func (codec *emptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
existing := *((*interface{})(ptr))
// Checking for both typed and untyped nil pointers.
if existing != nil &&
reflect.TypeOf(existing).Kind() == reflect.Ptr &&
!reflect.ValueOf(existing).IsNil() {
var ptrToExisting interface{}
for {
elem := reflect.ValueOf(existing).Elem()
if elem.Kind() != reflect.Ptr || elem.IsNil() {
break
}
ptrToExisting = existing
existing = elem.Interface()
}
if iter.ReadNil() {
if ptrToExisting != nil {
nilPtr := reflect.Zero(reflect.TypeOf(ptrToExisting).Elem())
reflect.ValueOf(ptrToExisting).Elem().Set(nilPtr)
} else {
*((*interface{})(ptr)) = nil
}
} else {
iter.ReadVal(existing)
}
return
}
if iter.ReadNil() {
*((*interface{})(ptr)) = nil
} else {
*((*interface{})(ptr)) = iter.Read()
}
}
func (codec *emptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
obj := *((*interface{})(ptr))
stream.WriteVal(obj)
}
func (codec *emptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
emptyInterface := (*emptyInterface)(ptr)
return emptyInterface.typ == nil
}
type nonEmptyInterfaceCodec struct {
}
func (codec *nonEmptyInterfaceCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
if iter.WhatIsNext() == NilValue {
iter.skipFourBytes('n', 'u', 'l', 'l')
*((*interface{})(ptr)) = nil
return
}
nonEmptyInterface := (*nonEmptyInterface)(ptr)
if nonEmptyInterface.itab == nil {
iter.ReportError("read non-empty interface", "do not know which concrete type to decode to")
return
}
var i interface{}
e := (*emptyInterface)(unsafe.Pointer(&i))
e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word
iter.ReadVal(&i)
if e.word == nil {
nonEmptyInterface.itab = nil
}
nonEmptyInterface.word = e.word
}
func (codec *nonEmptyInterfaceCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
nonEmptyInterface := (*nonEmptyInterface)(ptr)
var i interface{}
if nonEmptyInterface.itab != nil {
e := (*emptyInterface)(unsafe.Pointer(&i))
e.typ = nonEmptyInterface.itab.typ
e.word = nonEmptyInterface.word
}
stream.WriteVal(i)
}
func (codec *nonEmptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
nonEmptyInterface := (*nonEmptyInterface)(ptr)
return nonEmptyInterface.word == nil
}
type dynamicEncoder struct {
valType reflect2.Type
}
func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
obj := encoder.valType.UnsafeIndirect(ptr)
stream.WriteVal(obj)
}
func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool {
return encoder.valType.UnsafeIndirect(ptr) == nil
}
type base64Codec struct {
sliceType *reflect2.UnsafeSliceType
sliceDecoder ValDecoder

View File

@ -3,12 +3,16 @@ package jsoniter
import (
"reflect"
"unsafe"
"github.com/v2pro/plz/reflect2"
)
func decoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
elemType := typ.Elem()
decoder := decoderOfType(cfg, prefix, elemType)
return &OptionalDecoder{elemType, decoder}
if prefix == "" && elemType.Kind() == reflect.Ptr {
return &dereferenceDecoder{reflect2.Type2(elemType), decoder}
}
return &OptionalDecoder{reflect2.Type2(elemType), decoder}
}
func encoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValEncoder {
@ -19,7 +23,7 @@ func encoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValEn
}
type OptionalDecoder struct {
ValueType reflect.Type
ValueType reflect2.Type
ValueDecoder ValDecoder
}
@ -28,12 +32,10 @@ func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*unsafe.Pointer)(ptr)) = nil
} else {
if *((*unsafe.Pointer)(ptr)) == nil {
// TODO: use reflect2 instead
//pointer to null, we have to allocate memory to hold the value
value := reflect.New(decoder.ValueType)
newPtr := extractInterface(value.Interface()).word
newPtr := decoder.ValueType.UnsafeNew()
decoder.ValueDecoder.Decode(newPtr, iter)
*((*uintptr)(ptr)) = uintptr(newPtr)
*((*unsafe.Pointer)(ptr)) = newPtr
} else {
//reuse existing instance
decoder.ValueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
@ -43,17 +45,16 @@ func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
type dereferenceDecoder struct {
// only to deference a pointer
valueType reflect.Type
valueType reflect2.Type
valueDecoder ValDecoder
}
func (decoder *dereferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
if *((*unsafe.Pointer)(ptr)) == nil {
//pointer to null, we have to allocate memory to hold the value
value := reflect.New(decoder.valueType)
newPtr := extractInterface(value.Interface()).word
newPtr := decoder.valueType.UnsafeNew()
decoder.valueDecoder.Decode(newPtr, iter)
*((*uintptr)(ptr)) = uintptr(newPtr)
*((*unsafe.Pointer)(ptr)) = newPtr
} else {
//reuse existing instance
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
@ -127,4 +128,4 @@ type referenceDecoder struct {
func (decoder *referenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
decoder.decoder.Decode(unsafe.Pointer(&ptr), iter)
}
}

78
value_tests/eface_test.go Normal file
View File

@ -0,0 +1,78 @@
package test
func init() {
var pEFace = func(val interface{}) *interface{} {
return &val
}
var pInt = func(val int) *int {
return &val
}
unmarshalCases = append(unmarshalCases, unmarshalCase{
ptr: (**interface{})(nil),
input: `"hello"`,
}, unmarshalCase{
ptr: (**interface{})(nil),
input: `1e1`,
}, unmarshalCase{
ptr: (**interface{})(nil),
input: `1.0e1`,
}, unmarshalCase{
ptr: (*[]interface{})(nil),
input: `[1.0e1]`,
}, unmarshalCase{
ptr: (*struct {
Field interface{}
})(nil),
input: `{"field":"hello"}`,
}, unmarshalCase{
obj: func() interface{} {
type TestData struct {
Name string `json:"name"`
}
o := &TestData{}
return &o
},
input: `{"name":"value"}`,
}, unmarshalCase{
obj: func() interface{} {
b := true
return &struct {
Field interface{} `json:"field"`
}{&b}
},
input: `{"field": null}`,
}, unmarshalCase{
obj: func() interface{} {
var pb *bool
return &struct {
Field interface{} `json:"field"`
}{&pb}
},
input: `{"field": null}`,
}, unmarshalCase{
obj: func() interface{} {
b := true
pb := &b
return &struct {
Field interface{} `json:"field"`
}{&pb}
},
input: `{"field": null}`,
})
marshalCases = append(marshalCases,
pEFace("hello"),
struct {
Field interface{}
}{"hello"},
struct {
Field interface{}
}{struct{
field chan int
}{}},
struct {
Field interface{}
}{struct{
Field *int
}{pInt(100)}},
)
}

45
value_tests/iface_test.go Normal file
View File

@ -0,0 +1,45 @@
package test
import "io"
func init() {
var pCloser1 = func(str string) *io.Closer {
closer := io.Closer(strCloser1(str))
return &closer
}
var pCloser2 = func(str string) *io.Closer {
strCloser := strCloser2(str)
closer := io.Closer(&strCloser)
return &closer
}
marshalCases = append(marshalCases,
pCloser1("hello"),
pCloser2("hello"),
)
unmarshalCases = append(unmarshalCases, unmarshalCase{
ptr: (*[]io.Closer)(nil),
input: "[null]",
}, unmarshalCase{
obj: func() interface{} {
strCloser := strCloser2("")
return &struct {
Field io.Closer
}{
&strCloser,
}
},
input: `{"Field": "hello"}`,
})
}
type strCloser1 string
func (closer strCloser1) Close() error {
return nil
}
type strCloser2 string
func (closer *strCloser2) Close() error {
return nil
}

View File

@ -1,25 +1,39 @@
package test
func init() {
var pEFace = func(val interface{}) *interface{} {
return &val
}
var pInt = func(val int) *int {
return &val
}
unmarshalCases = append(unmarshalCases, unmarshalCase{
ptr: (**interface{})(nil),
input: `"hello"`,
}, unmarshalCase{
ptr: (**interface{})(nil),
input: `1e1`,
}, unmarshalCase{
ptr: (**interface{})(nil),
input: `1.0e1`,
})
marshalCases = append(marshalCases,
pEFace("hello"),
(*int)(nil),
pInt(100),
)
unmarshalCases = append(unmarshalCases, unmarshalCase{
obj: func() interface{} {
var i int
return &i
},
input: "null",
}, unmarshalCase{
obj: func() interface{} {
var i *int
return &i
},
input: "10",
}, unmarshalCase{
obj: func() interface{} {
var i int
pi := &i
return &pi
},
input: "null",
}, unmarshalCase{
obj: func() interface{} {
var i int
pi := &i
ppi := &pi
return &ppi
},
input: "null",
})
}

View File

@ -10,8 +10,10 @@ import (
)
type unmarshalCase struct {
obj func() interface{}
ptr interface{}
input string
selected bool
}
var unmarshalCases []unmarshalCase
@ -25,16 +27,31 @@ type selectedMarshalCase struct {
}
func Test_unmarshal(t *testing.T) {
should := require.New(t)
for _, testCase := range unmarshalCases {
valType := reflect.TypeOf(testCase.ptr).Elem()
ptr1Val := reflect.New(valType)
err1 := json.Unmarshal([]byte(testCase.input), ptr1Val.Interface())
should.NoError(err1)
ptr2Val := reflect.New(valType)
err2 := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(testCase.input), ptr2Val.Interface())
should.NoError(err2)
should.Equal(ptr1Val.Interface(), ptr2Val.Interface())
if testCase.selected {
unmarshalCases = []unmarshalCase{testCase}
break
}
}
for i, testCase := range unmarshalCases {
t.Run(fmt.Sprintf("[%v]%s", i, testCase.input), func(t *testing.T) {
should := require.New(t)
var obj1 interface{}
var obj2 interface{}
if testCase.obj != nil {
obj1 = testCase.obj()
obj2 = testCase.obj()
} else {
valType := reflect.TypeOf(testCase.ptr).Elem()
obj1 = reflect.New(valType).Interface()
obj2 = reflect.New(valType).Interface()
}
err1 := json.Unmarshal([]byte(testCase.input), obj1)
should.NoError(err1, "json")
err2 := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(testCase.input), obj2)
should.NoError(err2, "jsoniter")
should.Equal(obj1, obj2)
})
}
}