From 707ed3b091ed40257590f3544f0a78d438ab424a Mon Sep 17 00:00:00 2001 From: Tao Wen Date: Sat, 27 May 2017 00:36:21 +0800 Subject: [PATCH] support non empty interface --- feature_reflect.go | 30 ++++++++++++++++++++----- feature_reflect_native.go | 41 ++++++++++++++++++++++++++++----- jsoniter_customize_test.go | 2 +- jsoniter_interface_test.go | 46 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 12 deletions(-) diff --git a/feature_reflect.go b/feature_reflect.go index f9ac08a..f02994b 100644 --- a/feature_reflect.go +++ b/feature_reflect.go @@ -5,7 +5,6 @@ import ( "reflect" "sync/atomic" "unsafe" - "errors" "encoding/json" ) @@ -235,7 +234,21 @@ func (decoder *placeholderDecoder) decode(ptr unsafe.Pointer, iter *Iterator) { // emptyInterface is the header for an interface{} value. type emptyInterface struct { - typ *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 } @@ -358,9 +371,9 @@ func createDecoderOfType(typ reflect.Type) (Decoder, error) { return &boolCodec{}, nil case reflect.Interface: if typ.NumMethod() == 0 { - return &interfaceCodec{}, nil + return &emptyInterfaceCodec{}, nil } else { - return nil, errors.New("unsupportd type: " + typ.String()) + return &nonEmptyInterfaceCodec{}, nil } case reflect.Struct: return prefix(fmt.Sprintf("[%s]", typ.String())).addToDecoder(decoderOfStruct(typ)) @@ -408,7 +421,8 @@ func createEncoderOfType(typ reflect.Type) (Encoder, error) { templateInterface := reflect.New(typ).Elem().Interface() return &marshalerEncoder{extractInterface(templateInterface)}, nil } - switch typ.Kind() { + kind := typ.Kind() + switch kind { case reflect.String: return &stringCodec{}, nil case reflect.Int: @@ -438,7 +452,11 @@ func createEncoderOfType(typ reflect.Type) (Encoder, error) { case reflect.Bool: return &boolCodec{}, nil case reflect.Interface: - return &interfaceCodec{}, nil + if typ.NumMethod() == 0 { + return &emptyInterfaceCodec{}, nil + } else { + return &nonEmptyInterfaceCodec{}, nil + } case reflect.Struct: return prefix(fmt.Sprintf("[%s]", typ.String())).addToEncoder(encoderOfStruct(typ)) case reflect.Slice: diff --git a/feature_reflect_native.go b/feature_reflect_native.go index 8b4dcd3..3e4e51d 100644 --- a/feature_reflect_native.go +++ b/feature_reflect_native.go @@ -272,25 +272,56 @@ func (codec *boolCodec) isEmpty(ptr unsafe.Pointer) bool { return !(*((*bool)(ptr))) } -type interfaceCodec struct { +type emptyInterfaceCodec struct { } -func (codec *interfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) { +func (codec *emptyInterfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) { *((*interface{})(ptr)) = iter.Read() } -func (codec *interfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) { +func (codec *emptyInterfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) { stream.WriteVal(*((*interface{})(ptr))) } -func (encoder *interfaceCodec) encodeInterface(val interface{}, stream *Stream) { +func (encoder *emptyInterfaceCodec) encodeInterface(val interface{}, stream *Stream) { stream.WriteVal(val) } -func (codec *interfaceCodec) isEmpty(ptr unsafe.Pointer) bool { +func (codec *emptyInterfaceCodec) isEmpty(ptr unsafe.Pointer) bool { return ptr == nil } +type nonEmptyInterfaceCodec struct { +} + +func (codec *nonEmptyInterfaceCodec) decode(ptr unsafe.Pointer, iter *Iterator) { + nonEmptyInterface := (*nonEmptyInterface)(ptr) + var i interface{} + e := (*emptyInterface)(unsafe.Pointer(&i)) + e.typ = nonEmptyInterface.itab.typ + e.word = nonEmptyInterface.word + iter.ReadVal(&i) + nonEmptyInterface.word = e.word +} + +func (codec *nonEmptyInterfaceCodec) encode(ptr unsafe.Pointer, stream *Stream) { + nonEmptyInterface := (*nonEmptyInterface)(ptr) + var i interface{} + e := (*emptyInterface)(unsafe.Pointer(&i)) + e.typ = nonEmptyInterface.itab.typ + e.word = nonEmptyInterface.word + stream.WriteVal(i) +} + +func (encoder *nonEmptyInterfaceCodec) encodeInterface(val interface{}, stream *Stream) { + stream.WriteVal(val) +} + +func (codec *nonEmptyInterfaceCodec) isEmpty(ptr unsafe.Pointer) bool { + nonEmptyInterface := (*nonEmptyInterface)(ptr) + return nonEmptyInterface.word == nil +} + type anyCodec struct { } diff --git a/jsoniter_customize_test.go b/jsoniter_customize_test.go index 617cfe1..d4d9257 100644 --- a/jsoniter_customize_test.go +++ b/jsoniter_customize_test.go @@ -212,4 +212,4 @@ func Test_unmarshaler_and_decoder(t *testing.T) { err = Unmarshal([]byte(`{"Field":"hello"}`), &obj) should.Nil(err) should.Equal(10, int(*obj.Field)) -} +} \ No newline at end of file diff --git a/jsoniter_interface_test.go b/jsoniter_interface_test.go index 76ad73c..dd754a6 100644 --- a/jsoniter_interface_test.go +++ b/jsoniter_interface_test.go @@ -91,4 +91,50 @@ func Test_read_custom_interface(t *testing.T) { err := UnmarshalFromString(`"hello"`, &val) should.Nil(err) should.Equal("hello", val.Hello()) +} + +func Test_decode_object_contain_empty_interface(t *testing.T) { + type TestObject struct { + Field interface{} + } + should := require.New(t) + obj := TestObject{} + obj.Field = 1024 + should.Nil(UnmarshalFromString(`{"Field": "hello"}`, &obj)) + should.Equal("hello", obj.Field) +} + +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(UnmarshalFromString(`{"Field": "hello"}`, &obj)) + should.Equal(MyString("hello"), obj.Field) +} + +func Test_encode_object_contain_empty_interface(t *testing.T) { + type TestObject struct { + Field interface{} + } + should := require.New(t) + obj := TestObject{} + obj.Field = 1024 + str, err := MarshalToString(obj) + should.Nil(err) + should.Equal(`{"Field":1024}`, str) +} + +func Test_encode_object_contain_non_empty_interface(t *testing.T) { + type TestObject struct { + Field MyInterface + } + should := require.New(t) + obj := TestObject{} + obj.Field = MyString("hello") + str, err := MarshalToString(obj) + should.Nil(err) + should.Equal(`{"Field":"hello"}`, str) } \ No newline at end of file