Преглед изворни кода

when embedded ptr is nil, the fields should be omitted

Tao Wen пре 7 година
родитељ
комит
9dafbc667f

+ 3 - 13
feature_reflect.go

@@ -340,14 +340,10 @@ func createEncoderOfType(cfg *frozenConfig, prefix string, typ reflect.Type) Val
 	}
 	if typ.Implements(textMarshalerType) {
 		checkIsEmpty := createCheckIsEmpty(cfg, typ)
-		templateInterface := reflect.New(typ).Elem().Interface()
 		var encoder ValEncoder = &textMarshalerEncoder{
-			templateInterface: extractInterface(templateInterface),
+			valType: reflect2.Type2(typ),
 			checkIsEmpty:      checkIsEmpty,
 		}
-		if typ.Kind() == reflect.Ptr {
-			encoder = &OptionalEncoder{encoder}
-		}
 		return encoder
 	}
 	if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
@@ -393,10 +389,7 @@ func createCheckIsEmpty(cfg *frozenConfig, typ reflect.Type) checkIsEmpty {
 	case reflect.Bool:
 		return &boolCodec{}
 	case reflect.Interface:
-		if typ.NumMethod() == 0 {
-			return &emptyInterfaceCodec{}
-		}
-		return &nonEmptyInterfaceCodec{}
+		return &dynamicEncoder{reflect2.Type2(typ)}
 	case reflect.Struct:
 		return &structEncoder{typ: typ}
 	case reflect.Array:
@@ -492,10 +485,7 @@ func createEncoderOfSimpleType(cfg *frozenConfig, prefix string, typ reflect.Typ
 		}
 		return &boolCodec{}
 	case reflect.Interface:
-		if typ.NumMethod() == 0 {
-			return &emptyInterfaceCodec{}
-		}
-		return &nonEmptyInterfaceCodec{}
+		return &dynamicEncoder{reflect2.Type2(typ)}
 	case reflect.Struct:
 		return encoderOfStruct(cfg, prefix, typ)
 	case reflect.Array:

+ 0 - 20
feature_reflect_extension.go

@@ -17,8 +17,6 @@ var extensions = []Extension{}
 
 // StructDescriptor describe how should we encode/decode the struct
 type StructDescriptor struct {
-	onePtrEmbedded     bool
-	onePtrOptimization bool
 	Type               reflect.Type
 	Fields             []*Binding
 }
@@ -297,25 +295,7 @@ func describeStruct(cfg *frozenConfig, prefix string, typ reflect.Type) *StructD
 	return createStructDescriptor(cfg, typ, bindings, embeddedBindings)
 }
 func createStructDescriptor(cfg *frozenConfig, typ reflect.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor {
-	onePtrEmbedded := false
-	onePtrOptimization := false
-	if typ.NumField() == 1 {
-		firstField := typ.Field(0)
-		switch firstField.Type.Kind() {
-		case reflect.Ptr:
-			if firstField.Anonymous && firstField.Type.Elem().Kind() == reflect.Struct {
-				onePtrEmbedded = true
-			}
-			fallthrough
-		case reflect.Map:
-			onePtrOptimization = true
-		case reflect.Struct:
-			onePtrOptimization = isStructOnePtr(firstField.Type)
-		}
-	}
 	structDescriptor := &StructDescriptor{
-		onePtrEmbedded:     onePtrEmbedded,
-		onePtrOptimization: onePtrOptimization,
 		Type:               typ,
 		Fields:             bindings,
 	}

+ 23 - 7
feature_reflect_native.go

@@ -357,6 +357,20 @@ func (codec *nonEmptyInterfaceCodec) IsEmpty(ptr unsafe.Pointer) bool {
 	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 anyCodec struct {
 }
 
@@ -390,7 +404,7 @@ func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
 func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
 	number := *((*json.Number)(ptr))
 	if len(number) == 0 {
-		stream.WriteRaw("0")
+		stream.writeByte('0')
 	} else {
 		stream.WriteRaw(string(number))
 	}
@@ -418,7 +432,7 @@ func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
 func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
 	number := *((*Number)(ptr))
 	if len(number) == 0 {
-		stream.WriteRaw("0")
+		stream.writeByte('0')
 	} else {
 		stream.WriteRaw(string(number))
 	}
@@ -603,15 +617,17 @@ func (encoder *marshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
 }
 
 type textMarshalerEncoder struct {
-	templateInterface emptyInterface
+	valType			reflect2.Type
 	checkIsEmpty      checkIsEmpty
 }
 
 func (encoder *textMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
-	templateInterface := encoder.templateInterface
-	templateInterface.word = ptr
-	realInterface := (*interface{})(unsafe.Pointer(&templateInterface))
-	marshaler := (*realInterface).(encoding.TextMarshaler)
+	obj := encoder.valType.UnsafeIndirect(ptr)
+	if obj == nil {
+		stream.WriteNil()
+		return
+	}
+	marshaler := (obj).(encoding.TextMarshaler)
 	bytes, err := marshaler.MarshalText()
 	if err != nil {
 		stream.Error = err

+ 18 - 1
feature_reflect_optional.go

@@ -88,5 +88,22 @@ func (encoder *dereferenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
 }
 
 func (encoder *dereferenceEncoder) IsEmpty(ptr unsafe.Pointer) bool {
-	return encoder.ValueEncoder.IsEmpty(*((*unsafe.Pointer)(ptr)))
+	dePtr := *((*unsafe.Pointer)(ptr))
+	if dePtr == nil {
+		return true
+	}
+	return encoder.ValueEncoder.IsEmpty(dePtr)
+}
+
+func (encoder *dereferenceEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool {
+	deReferenced := *((*unsafe.Pointer)(ptr))
+	if deReferenced == nil {
+		return true
+	}
+	isEmbeddedPtrNil, converted := encoder.ValueEncoder.(IsEmbeddedPtrNil)
+	if !converted {
+		return false
+	}
+	fieldPtr := unsafe.Pointer(deReferenced)
+	return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr)
 }

+ 19 - 6
feature_reflect_object.go → feature_reflect_struct_encoder.go

@@ -42,8 +42,7 @@ func encoderOfStruct(cfg *frozenConfig, prefix string, typ reflect.Type) ValEnco
 			})
 		}
 	}
-	return &structEncoder{typ, structDescriptor.onePtrEmbedded,
-		structDescriptor.onePtrOptimization, finalOrderedFields}
+	return &structEncoder{typ, finalOrderedFields}
 }
 
 func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
@@ -120,11 +119,22 @@ func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
 	return encoder.fieldEncoder.IsEmpty(fieldPtr)
 }
 
+func (encoder *structFieldEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool {
+	isEmbeddedPtrNil, converted := encoder.fieldEncoder.(IsEmbeddedPtrNil)
+	if !converted {
+		return false
+	}
+	fieldPtr := unsafe.Pointer(uintptr(ptr) + encoder.field.Offset)
+	return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr)
+}
+
+type IsEmbeddedPtrNil interface {
+	IsEmbeddedPtrNil(ptr unsafe.Pointer) bool
+}
+
 type structEncoder struct {
-	typ                reflect.Type
-	onePtrEmbedded     bool
-	onePtrOptimization bool
-	fields             []structFieldTo
+	typ    reflect.Type
+	fields []structFieldTo
 }
 
 type structFieldTo struct {
@@ -139,6 +149,9 @@ func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
 		if field.encoder.omitempty && field.encoder.IsEmpty(ptr) {
 			continue
 		}
+		if field.encoder.IsEmbeddedPtrNil(ptr) {
+			continue
+		}
 		if isNotFirst {
 			stream.WriteMore()
 		}

+ 3 - 3
value_tests/struct_test.go

@@ -92,7 +92,8 @@ func init() {
 		}{&StructVarious{}},
 		struct {
 			*StructVarious
-		}{},
+			Field int
+		}{nil, 10},
 		struct {
 			Field1 int
 			Field2 [1]*float64
@@ -172,7 +173,6 @@ type CacheItem struct {
 	MaxAge int    `json:"cacheAge"`
 }
 
-
 type orderA struct {
 	Field2 string
 }
@@ -193,4 +193,4 @@ type structOrder struct {
 	Field3 string
 	orderB
 	Field7 string
-}
+}