mirror of
https://github.com/json-iterator/go.git
synced 2025-06-06 22:36:25 +02:00
rewrite how eface and iface are handled
This commit is contained in:
parent
ea6403326b
commit
2fcbb23d96
@ -36,3 +36,13 @@ func Test_customize_tag_key(t *testing.T) {
|
|||||||
should.Nil(err)
|
should.Nil(err)
|
||||||
should.Equal(`{"field":"hello"}`, str)
|
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)
|
||||||
|
}
|
@ -2,7 +2,6 @@ package jsoniter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -267,11 +266,6 @@ func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error {
|
|||||||
data = data[:lastNotSpacePos(data)]
|
data = data[:lastNotSpacePos(data)]
|
||||||
iter := cfg.BorrowIterator(data)
|
iter := cfg.BorrowIterator(data)
|
||||||
defer cfg.ReturnIterator(iter)
|
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)
|
iter.ReadVal(v)
|
||||||
if iter.head == iter.tail {
|
if iter.head == iter.tail {
|
||||||
iter.loadMore()
|
iter.loadMore()
|
||||||
|
@ -49,13 +49,13 @@ func Test_read_float64_cursor(t *testing.T) {
|
|||||||
func Test_read_float_scientific(t *testing.T) {
|
func Test_read_float_scientific(t *testing.T) {
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
var obj interface{}
|
var obj interface{}
|
||||||
should.Nil(jsoniter.UnmarshalFromString(`1e1`, &obj))
|
should.NoError(jsoniter.UnmarshalFromString(`1e1`, &obj))
|
||||||
should.Equal(float64(10), 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.Equal(float64(10), obj)
|
||||||
should.Nil(jsoniter.UnmarshalFromString(`1.0e1`, &obj))
|
should.NoError(jsoniter.UnmarshalFromString(`1.0e1`, &obj))
|
||||||
should.Equal(float64(10), 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)
|
should.Equal(float64(10), obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,36 +2,15 @@ package misc_tests
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/json-iterator/go"
|
"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) {
|
func Test_nil_non_empty_interface(t *testing.T) {
|
||||||
type TestObject struct {
|
type TestObject struct {
|
||||||
Field []MyInterface
|
Field []io.Closer
|
||||||
}
|
}
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
obj := TestObject{}
|
obj := TestObject{}
|
||||||
@ -40,31 +19,6 @@ func Test_nil_non_empty_interface(t *testing.T) {
|
|||||||
should.NotNil(jsoniter.Unmarshal(b, &obj))
|
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) {
|
func Test_nil_out_null_interface(t *testing.T) {
|
||||||
type TestData struct {
|
type TestData struct {
|
||||||
Field interface{} `json:"field"`
|
Field interface{} `json:"field"`
|
||||||
@ -86,7 +40,7 @@ func Test_nil_out_null_interface(t *testing.T) {
|
|||||||
|
|
||||||
err = jsoniter.Unmarshal(data2, &obj)
|
err = jsoniter.Unmarshal(data2, &obj)
|
||||||
should.NoError(err)
|
should.NoError(err)
|
||||||
should.Equal(nil, obj.Field)
|
should.Nil(obj.Field)
|
||||||
|
|
||||||
// Checking stdlib behavior matches.
|
// Checking stdlib behavior matches.
|
||||||
obj2 := TestData{
|
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)
|
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||||
should.Equal(nil, err)
|
should.NoError(err)
|
||||||
should.Equal(&payload, wrapper.Payload)
|
should.Equal(&payload, wrapper.Payload)
|
||||||
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
|
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||||
should.Equal(nil, err)
|
should.NoError(err)
|
||||||
should.Equal(&payload, wrapper.Payload)
|
should.Equal(&payload, wrapper.Payload)
|
||||||
should.Equal((*Payload)(nil), 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)
|
should.Equal(42, (*(wrapper.Payload.(**Payload))).Value)
|
||||||
|
|
||||||
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||||
should.Equal(nil, err)
|
should.NoError(err)
|
||||||
should.Equal(&payload, wrapper.Payload)
|
should.Equal(&payload, wrapper.Payload)
|
||||||
should.Equal((*Payload)(nil), 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)
|
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||||
should.Equal(nil, err)
|
should.NoError(err)
|
||||||
should.Equal(42, (*(wrapper.Payload.(*Payload))).Value)
|
should.Equal(42, (*(wrapper.Payload.(*Payload))).Value)
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||||
should.Equal(nil, err)
|
should.NoError(err)
|
||||||
should.Equal(nil, wrapper.Payload)
|
should.Equal(nil, wrapper.Payload)
|
||||||
should.Equal(42, payload.Value)
|
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)
|
err := json.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||||
should.Nil(err)
|
should.NoError(err)
|
||||||
should.NotNil(wrapper.Payload)
|
should.NotNil(wrapper.Payload)
|
||||||
should.Nil(payload)
|
should.Nil(payload)
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
err = json.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||||
should.Nil(err)
|
should.NoError(err)
|
||||||
should.Nil(wrapper.Payload)
|
should.Nil(wrapper.Payload)
|
||||||
should.Nil(payload)
|
should.Nil(payload)
|
||||||
|
|
||||||
@ -213,12 +167,12 @@ func Test_unmarshal_into_nil(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
err = jsoniter.Unmarshal([]byte(`{"payload": {"val": 42}}`), &wrapper)
|
||||||
should.Nil(err)
|
should.NoError(err)
|
||||||
should.NotNil(wrapper.Payload)
|
should.NotNil(wrapper.Payload)
|
||||||
should.Nil(payload)
|
should.Nil(payload)
|
||||||
|
|
||||||
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
err = jsoniter.Unmarshal([]byte(`{"payload": null}`), &wrapper)
|
||||||
should.Nil(err)
|
should.NoError(err)
|
||||||
should.Nil(wrapper.Payload)
|
should.Nil(wrapper.Payload)
|
||||||
should.Nil(payload)
|
should.Nil(payload)
|
||||||
}
|
}
|
||||||
|
46
reflect.go
46
reflect.go
@ -36,14 +36,17 @@ type checkIsEmpty interface {
|
|||||||
// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
|
// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
|
||||||
func (iter *Iterator) ReadVal(obj interface{}) {
|
func (iter *Iterator) ReadVal(obj interface{}) {
|
||||||
typ := reflect.TypeOf(obj)
|
typ := reflect.TypeOf(obj)
|
||||||
cacheKey := typ.Elem()
|
if typ.Kind() != reflect.Ptr {
|
||||||
decoder := decoderOfType(iter.cfg, "", cacheKey)
|
iter.ReportError("ReadVal", "can only unmarshal into pointer")
|
||||||
e := (*emptyInterface)(unsafe.Pointer(&obj))
|
return
|
||||||
if e.word == nil {
|
}
|
||||||
|
decoder := iter.cfg.DecoderOf(typ)
|
||||||
|
ptr := reflect2.PtrOf(obj)
|
||||||
|
if ptr == nil {
|
||||||
iter.ReportError("ReadVal", "can not read into nil pointer")
|
iter.ReportError("ReadVal", "can not read into nil pointer")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
decoder.Decode(e.word, iter)
|
decoder.Decode(ptr, iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteVal copy the go interface into underlying JSON, same as json.Marshal
|
// 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 {
|
if decoder != nil {
|
||||||
return decoder
|
return decoder
|
||||||
}
|
}
|
||||||
decoder = decoderOfType(cfg, "", typ)
|
decoder = decoderOfType(cfg, "", typ.Elem())
|
||||||
cfg.addDecoderToCache(cacheKey, decoder)
|
cfg.addDecoderToCache(cacheKey, decoder)
|
||||||
return decoder
|
return decoder
|
||||||
}
|
}
|
||||||
@ -106,10 +109,11 @@ func createDecoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) Val
|
|||||||
}
|
}
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
if typ.NumMethod() == 0 {
|
if typ.NumMethod() > 0 {
|
||||||
return &emptyInterfaceCodec{}
|
ifaceType := reflect2.Type2(typ).(*reflect2.UnsafeIFaceType)
|
||||||
|
return &ifaceDecoder{valType: ifaceType}
|
||||||
}
|
}
|
||||||
return &nonEmptyInterfaceCodec{}
|
return &efaceDecoder{}
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return decoderOfStruct(cfg, prefix, typ)
|
return decoderOfStruct(cfg, prefix, typ)
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
@ -239,27 +243,3 @@ func (encoder *lazyErrorEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|||||||
func (encoder *lazyErrorEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
func (encoder *lazyErrorEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||||
return false
|
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
70
reflect_dynamic.go
Normal 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)
|
||||||
|
}
|
@ -7,6 +7,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
"github.com/v2pro/plz/reflect2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var typeDecoders = map[string]ValDecoder{}
|
var typeDecoders = map[string]ValDecoder{}
|
||||||
@ -240,7 +241,7 @@ func _getTypeDecoderFromExtension(cfg *frozenConfig, typ reflect.Type) ValDecode
|
|||||||
if typ.Kind() == reflect.Ptr {
|
if typ.Kind() == reflect.Ptr {
|
||||||
decoder := typeDecoders[typ.Elem().String()]
|
decoder := typeDecoders[typ.Elem().String()]
|
||||||
if decoder != nil {
|
if decoder != nil {
|
||||||
return &OptionalDecoder{typ.Elem(), decoder}
|
return &OptionalDecoder{reflect2.Type2(typ.Elem()), decoder}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -317,7 +318,7 @@ func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructD
|
|||||||
omitempty := binding.Encoder.(*structFieldEncoder).omitempty
|
omitempty := binding.Encoder.(*structFieldEncoder).omitempty
|
||||||
binding.Encoder = &dereferenceEncoder{binding.Encoder}
|
binding.Encoder = &dereferenceEncoder{binding.Encoder}
|
||||||
binding.Encoder = &structFieldEncoder{&field, binding.Encoder, omitempty}
|
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}
|
binding.Decoder = &structFieldDecoder{&field, binding.Decoder}
|
||||||
embeddedBindings = append(embeddedBindings, binding)
|
embeddedBindings = append(embeddedBindings, binding)
|
||||||
}
|
}
|
||||||
|
@ -435,112 +435,6 @@ func (codec *boolCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|||||||
return !(*((*bool)(ptr)))
|
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 {
|
type base64Codec struct {
|
||||||
sliceType *reflect2.UnsafeSliceType
|
sliceType *reflect2.UnsafeSliceType
|
||||||
sliceDecoder ValDecoder
|
sliceDecoder ValDecoder
|
||||||
|
@ -3,12 +3,16 @@ package jsoniter
|
|||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
"github.com/v2pro/plz/reflect2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func decoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
|
func decoderOfOptional(cfg *frozenConfig, prefix string, typ reflect.Type) ValDecoder {
|
||||||
elemType := typ.Elem()
|
elemType := typ.Elem()
|
||||||
decoder := decoderOfType(cfg, prefix, elemType)
|
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 {
|
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 {
|
type OptionalDecoder struct {
|
||||||
ValueType reflect.Type
|
ValueType reflect2.Type
|
||||||
ValueDecoder ValDecoder
|
ValueDecoder ValDecoder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,12 +32,10 @@ func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|||||||
*((*unsafe.Pointer)(ptr)) = nil
|
*((*unsafe.Pointer)(ptr)) = nil
|
||||||
} else {
|
} else {
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||||
// TODO: use reflect2 instead
|
|
||||||
//pointer to null, we have to allocate memory to hold the value
|
//pointer to null, we have to allocate memory to hold the value
|
||||||
value := reflect.New(decoder.ValueType)
|
newPtr := decoder.ValueType.UnsafeNew()
|
||||||
newPtr := extractInterface(value.Interface()).word
|
|
||||||
decoder.ValueDecoder.Decode(newPtr, iter)
|
decoder.ValueDecoder.Decode(newPtr, iter)
|
||||||
*((*uintptr)(ptr)) = uintptr(newPtr)
|
*((*unsafe.Pointer)(ptr)) = newPtr
|
||||||
} else {
|
} else {
|
||||||
//reuse existing instance
|
//reuse existing instance
|
||||||
decoder.ValueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
|
decoder.ValueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
|
||||||
@ -43,17 +45,16 @@ func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|||||||
|
|
||||||
type dereferenceDecoder struct {
|
type dereferenceDecoder struct {
|
||||||
// only to deference a pointer
|
// only to deference a pointer
|
||||||
valueType reflect.Type
|
valueType reflect2.Type
|
||||||
valueDecoder ValDecoder
|
valueDecoder ValDecoder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (decoder *dereferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
func (decoder *dereferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||||
//pointer to null, we have to allocate memory to hold the value
|
//pointer to null, we have to allocate memory to hold the value
|
||||||
value := reflect.New(decoder.valueType)
|
newPtr := decoder.valueType.UnsafeNew()
|
||||||
newPtr := extractInterface(value.Interface()).word
|
|
||||||
decoder.valueDecoder.Decode(newPtr, iter)
|
decoder.valueDecoder.Decode(newPtr, iter)
|
||||||
*((*uintptr)(ptr)) = uintptr(newPtr)
|
*((*unsafe.Pointer)(ptr)) = newPtr
|
||||||
} else {
|
} else {
|
||||||
//reuse existing instance
|
//reuse existing instance
|
||||||
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
|
decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter)
|
||||||
|
78
value_tests/eface_test.go
Normal file
78
value_tests/eface_test.go
Normal 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
45
value_tests/iface_test.go
Normal 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
|
||||||
|
}
|
@ -1,25 +1,39 @@
|
|||||||
package test
|
package test
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var pEFace = func(val interface{}) *interface{} {
|
|
||||||
return &val
|
|
||||||
}
|
|
||||||
var pInt = func(val int) *int {
|
var pInt = func(val int) *int {
|
||||||
return &val
|
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,
|
marshalCases = append(marshalCases,
|
||||||
pEFace("hello"),
|
|
||||||
(*int)(nil),
|
(*int)(nil),
|
||||||
pInt(100),
|
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",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type unmarshalCase struct {
|
type unmarshalCase struct {
|
||||||
|
obj func() interface{}
|
||||||
ptr interface{}
|
ptr interface{}
|
||||||
input string
|
input string
|
||||||
|
selected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var unmarshalCases []unmarshalCase
|
var unmarshalCases []unmarshalCase
|
||||||
@ -25,16 +27,31 @@ type selectedMarshalCase struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_unmarshal(t *testing.T) {
|
func Test_unmarshal(t *testing.T) {
|
||||||
should := require.New(t)
|
|
||||||
for _, testCase := range unmarshalCases {
|
for _, testCase := range unmarshalCases {
|
||||||
|
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()
|
valType := reflect.TypeOf(testCase.ptr).Elem()
|
||||||
ptr1Val := reflect.New(valType)
|
obj1 = reflect.New(valType).Interface()
|
||||||
err1 := json.Unmarshal([]byte(testCase.input), ptr1Val.Interface())
|
obj2 = reflect.New(valType).Interface()
|
||||||
should.NoError(err1)
|
}
|
||||||
ptr2Val := reflect.New(valType)
|
err1 := json.Unmarshal([]byte(testCase.input), obj1)
|
||||||
err2 := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(testCase.input), ptr2Val.Interface())
|
should.NoError(err1, "json")
|
||||||
should.NoError(err2)
|
err2 := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal([]byte(testCase.input), obj2)
|
||||||
should.Equal(ptr1Val.Interface(), ptr2Val.Interface())
|
should.NoError(err2, "jsoniter")
|
||||||
|
should.Equal(obj1, obj2)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user