From d1aa59e34e2b02811f83d1e25c8536a059e7be4c Mon Sep 17 00:00:00 2001
From: Tao Wen <taowen@gmail.com>
Date: Wed, 8 Mar 2017 07:38:25 -0800
Subject: [PATCH] #12 implement omitempty

---
 feature_reflect.go              | 30 ++++++++++++++++
 feature_reflect_array.go        |  5 +++
 feature_reflect_native.go       | 64 +++++++++++++++++++++++++++++++++
 feature_reflect_object.go       | 48 +++++++++++++++++++------
 jsoniter_reflect_struct_test.go | 13 +++++++
 5 files changed, 150 insertions(+), 10 deletions(-)

diff --git a/feature_reflect.go b/feature_reflect.go
index b446812..af4fcc5 100644
--- a/feature_reflect.go
+++ b/feature_reflect.go
@@ -22,6 +22,7 @@ type Decoder interface {
 }
 
 type Encoder interface {
+	isEmpty(ptr unsafe.Pointer) bool
 	encode(ptr unsafe.Pointer, stream *Stream)
 	encodeInterface(val interface{}, stream *Stream)
 }
@@ -59,6 +60,10 @@ func (encoder *funcEncoder) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (encoder *funcEncoder) isEmpty(ptr unsafe.Pointer) bool {
+	return false
+}
+
 var DECODERS unsafe.Pointer
 var ENCODERS unsafe.Pointer
 
@@ -187,6 +192,14 @@ func (encoder *optionalEncoder) encodeInterface(val interface{}, stream *Stream)
 	WriteToStream(val, stream, encoder)
 }
 
+func (encoder *optionalEncoder) isEmpty(ptr unsafe.Pointer) bool {
+	if *((*unsafe.Pointer)(ptr)) == nil {
+		return true
+	} else {
+		return encoder.valueEncoder.isEmpty(*((*unsafe.Pointer)(ptr)))
+	}
+}
+
 type mapDecoder struct {
 	mapType      reflect.Type
 	elemType     reflect.Type
@@ -240,6 +253,14 @@ func (encoder *mapEncoder) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (encoder *mapEncoder) isEmpty(ptr unsafe.Pointer) bool {
+	mapInterface := encoder.mapInterface
+	mapInterface.word = ptr
+	realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
+	realVal := reflect.ValueOf(*realInterface)
+	return realVal.Len() == 0
+}
+
 type mapInterfaceEncoder struct {
 	mapType      reflect.Type
 	elemType     reflect.Type
@@ -269,6 +290,15 @@ func (encoder *mapInterfaceEncoder) encodeInterface(val interface{}, stream *Str
 	WriteToStream(val, stream, encoder)
 }
 
+func (encoder *mapInterfaceEncoder) isEmpty(ptr unsafe.Pointer) bool {
+	mapInterface := encoder.mapInterface
+	mapInterface.word = ptr
+	realInterface := (*interface{})(unsafe.Pointer(&mapInterface))
+	realVal := reflect.ValueOf(*realInterface)
+
+	return realVal.Len() == 0
+}
+
 // emptyInterface is the header for an interface{} value.
 type emptyInterface struct {
 	typ  *struct{}
diff --git a/feature_reflect_array.go b/feature_reflect_array.go
index c0370f2..aa092d4 100644
--- a/feature_reflect_array.go
+++ b/feature_reflect_array.go
@@ -53,6 +53,11 @@ func (encoder *sliceEncoder) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (encoder *sliceEncoder) isEmpty(ptr unsafe.Pointer) bool {
+	slice := (*sliceHeader)(ptr)
+	return slice.Len == 0
+}
+
 type sliceDecoder struct {
 	sliceType   reflect.Type
 	elemType    reflect.Type
diff --git a/feature_reflect_native.go b/feature_reflect_native.go
index 4e56e0d..6da523f 100644
--- a/feature_reflect_native.go
+++ b/feature_reflect_native.go
@@ -19,6 +19,10 @@ func (encoder *stringCodec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *stringCodec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*string)(ptr)) == ""
+}
+
 type intCodec struct {
 }
 
@@ -34,6 +38,10 @@ func (encoder *intCodec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *intCodec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*int)(ptr)) == 0
+}
+
 type int8Codec struct {
 }
 
@@ -49,6 +57,10 @@ func (encoder *int8Codec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *int8Codec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*int8)(ptr)) == 0
+}
+
 type int16Codec struct {
 }
 
@@ -64,6 +76,10 @@ func (encoder *int16Codec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *int16Codec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*int16)(ptr)) == 0
+}
+
 type int32Codec struct {
 }
 
@@ -79,6 +95,10 @@ func (encoder *int32Codec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *int32Codec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*int32)(ptr)) == 0
+}
+
 type int64Codec struct {
 }
 
@@ -94,6 +114,10 @@ func (encoder *int64Codec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *int64Codec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*int64)(ptr)) == 0
+}
+
 type uintCodec struct {
 }
 
@@ -109,6 +133,10 @@ func (encoder *uintCodec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *uintCodec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*uint)(ptr)) == 0
+}
+
 type uint8Codec struct {
 }
 
@@ -124,6 +152,10 @@ func (encoder *uint8Codec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *uint8Codec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*uint8)(ptr)) == 0
+}
+
 type uint16Codec struct {
 }
 
@@ -139,6 +171,10 @@ func (encoder *uint16Codec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *uint16Codec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*uint16)(ptr)) == 0
+}
+
 type uint32Codec struct {
 }
 
@@ -154,6 +190,10 @@ func (encoder *uint32Codec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *uint32Codec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*uint32)(ptr)) == 0
+}
+
 type uint64Codec struct {
 }
 
@@ -169,6 +209,10 @@ func (encoder *uint64Codec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *uint64Codec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*uint64)(ptr)) == 0
+}
+
 type float32Codec struct {
 }
 
@@ -184,6 +228,10 @@ func (encoder *float32Codec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *float32Codec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*float32)(ptr)) == 0
+}
+
 type float64Codec struct {
 }
 
@@ -199,6 +247,10 @@ func (encoder *float64Codec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *float64Codec) isEmpty(ptr unsafe.Pointer) bool {
+	return *((*float64)(ptr)) == 0
+}
+
 type boolCodec struct {
 }
 
@@ -214,6 +266,10 @@ func (encoder *boolCodec) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (codec *boolCodec) isEmpty(ptr unsafe.Pointer) bool {
+	return !(*((*bool)(ptr)))
+}
+
 type interfaceCodec struct {
 }
 
@@ -229,6 +285,10 @@ func (encoder *interfaceCodec) encodeInterface(val interface{}, stream *Stream)
 	stream.WriteVal(val)
 }
 
+func (codec *interfaceCodec) isEmpty(ptr unsafe.Pointer) bool {
+	return ptr == nil
+}
+
 type anyCodec struct {
 }
 
@@ -244,6 +304,10 @@ func (encoder *anyCodec) encodeInterface(val interface{}, stream *Stream) {
 	(val.(Any)).WriteTo(stream)
 }
 
+func (encoder *anyCodec) isEmpty(ptr unsafe.Pointer) bool {
+	return (*((*Any)(ptr))).Size() == 0
+}
+
 type stringNumberDecoder struct {
 	elemDecoder Decoder
 }
diff --git a/feature_reflect_object.go b/feature_reflect_object.go
index be66575..b0fe97b 100644
--- a/feature_reflect_object.go
+++ b/feature_reflect_object.go
@@ -33,9 +33,16 @@ func encoderOfStruct(typ reflect.Type) (Encoder, error) {
 				fieldNames = []string{tagParts[0]}
 			}
 		}
+		omitempty := false
+		for _, tagPart := range tagParts {
+			if tagPart == "omitempty" {
+				omitempty = true
+			}
+		}
 		var encoder Encoder
+		var err error
 		if len(fieldNames) > 0 {
-			encoder, err := encoderOfType(field.Type)
+			encoder, err = encoderOfType(field.Type)
 			if err != nil {
 				return prefix(fmt.Sprintf("{%s}", field.Name)).addToEncoder(encoder, err)
 			}
@@ -46,14 +53,11 @@ func encoderOfStruct(typ reflect.Type) (Encoder, error) {
 			}
 		}
 		for _, fieldName := range fieldNames {
-			if structEncoder_.firstField == nil {
-				structEncoder_.firstField = &structFieldEncoder{&field, fieldName, encoder}
-			} else {
-				structEncoder_.fields = append(structEncoder_.fields, &structFieldEncoder{&field, fieldName, encoder})
-			}
+			structEncoder_.fields = append(structEncoder_.fields,
+				&structFieldEncoder{&field, fieldName, encoder, omitempty})
 		}
 	}
-	if structEncoder_.firstField == nil {
+	if len(structEncoder_.fields) == 0 {
 		return &emptyStructEncoder{}, nil
 	}
 	return structEncoder_, nil
@@ -1024,6 +1028,7 @@ type structFieldEncoder struct {
 	field        *reflect.StructField
 	fieldName    string
 	fieldEncoder Encoder
+	omitempty    bool
 }
 
 func (encoder *structFieldEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
@@ -1039,18 +1044,28 @@ func (encoder *structFieldEncoder) encodeInterface(val interface{}, stream *Stre
 	WriteToStream(val, stream, encoder)
 }
 
+func (encoder *structFieldEncoder) isEmpty(ptr unsafe.Pointer) bool {
+	fieldPtr := uintptr(ptr) + encoder.field.Offset
+	return encoder.fieldEncoder.isEmpty(unsafe.Pointer(fieldPtr))
+}
+
 
 type structEncoder struct {
-	firstField *structFieldEncoder
 	fields []*structFieldEncoder
 }
 
 func (encoder *structEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
 	stream.WriteObjectStart()
-	encoder.firstField.encode(ptr, stream)
+	isNotFirst := false
 	for _, field := range encoder.fields {
-		stream.WriteMore()
+		if isNotFirst {
+			stream.WriteMore()
+		}
+		if field.omitempty && field.isEmpty(ptr) {
+			continue
+		}
 		field.encode(ptr, stream)
+		isNotFirst = true
 	}
 	stream.WriteObjectEnd()
 }
@@ -1059,6 +1074,15 @@ func (encoder *structEncoder) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
 }
 
+func (encoder *structEncoder) isEmpty(ptr unsafe.Pointer) bool {
+	for _, field := range encoder.fields {
+		if !field.isEmpty(ptr) {
+			return false
+		}
+	}
+	return true
+}
+
 
 type emptyStructEncoder struct {
 }
@@ -1069,4 +1093,8 @@ func (encoder *emptyStructEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
 
 func (encoder *emptyStructEncoder) encodeInterface(val interface{}, stream *Stream) {
 	WriteToStream(val, stream, encoder)
+}
+
+func (encoder *emptyStructEncoder) isEmpty(ptr unsafe.Pointer) bool {
+	return true
 }
\ No newline at end of file
diff --git a/jsoniter_reflect_struct_test.go b/jsoniter_reflect_struct_test.go
index d55edf8..78459f3 100644
--- a/jsoniter_reflect_struct_test.go
+++ b/jsoniter_reflect_struct_test.go
@@ -158,4 +158,17 @@ func Test_mixed(t *testing.T) {
 	should.Nil(err)
 	should.Equal(1, aa.ID)
 	should.Equal("123", aa.Payload["account"])
+}
+
+func Test_omit_empty(t *testing.T) {
+	should := require.New(t)
+	type TestObject struct {
+		Field1 string `json:"field-1,omitempty"`
+		Field2 string `json:"field-2,omitempty"`
+	}
+	obj := TestObject{}
+	obj.Field2 = "hello"
+	str, err := MarshalToString(&obj)
+	should.Nil(err)
+	should.Equal(`{"field-2":"hello"}`, str)
 }
\ No newline at end of file