Browse Source

support non empty interface

Tao Wen 8 years ago
parent
commit
707ed3b091
4 changed files with 107 additions and 12 deletions
  1. 24 6
      feature_reflect.go
  2. 36 5
      feature_reflect_native.go
  3. 1 1
      jsoniter_customize_test.go
  4. 46 0
      jsoniter_interface_test.go

+ 24 - 6
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:

+ 36 - 5
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 {
 }
 

+ 1 - 1
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))
-}
+}

+ 46 - 0
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)
 }