forked from mirror/go-json
Support omitempty
This commit is contained in:
parent
e50e1d4878
commit
03a21193fc
11
encode.go
11
encode.go
|
@ -23,14 +23,14 @@ type opcodeMap struct {
|
|||
sync.Map
|
||||
}
|
||||
|
||||
func (m *opcodeMap) Get(k string) *opcode {
|
||||
func (m *opcodeMap) Get(k *rtype) *opcode {
|
||||
if v, ok := m.Load(k); ok {
|
||||
return v.(*opcode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *opcodeMap) Set(k string, op *opcode) {
|
||||
func (m *opcodeMap) Set(k *rtype, op *opcode) {
|
||||
m.Store(k, op)
|
||||
}
|
||||
|
||||
|
@ -175,8 +175,7 @@ func (e *Encoder) encode(v interface{}) error {
|
|||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
name := typ.String()
|
||||
if code := cachedOpcode.Get(name); code != nil {
|
||||
if code := cachedOpcode.Get(typ); code != nil {
|
||||
p := uintptr(header.ptr)
|
||||
code.ptr = p
|
||||
if err := e.run(code); err != nil {
|
||||
|
@ -188,9 +187,7 @@ func (e *Encoder) encode(v interface{}) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if name != "" {
|
||||
cachedOpcode.Set(name, code)
|
||||
}
|
||||
cachedOpcode.Set(typ, code)
|
||||
p := uintptr(header.ptr)
|
||||
code.ptr = p
|
||||
return e.run(code)
|
||||
|
|
|
@ -89,6 +89,36 @@ func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode {
|
|||
code.op = opStructFieldPtrHeadString
|
||||
case opStructFieldHeadBool:
|
||||
code.op = opStructFieldPtrHeadBool
|
||||
case opStructFieldHeadOmitEmpty:
|
||||
code.op = opStructFieldPtrHeadOmitEmpty
|
||||
case opStructFieldHeadIntOmitEmpty:
|
||||
code.op = opStructFieldPtrHeadIntOmitEmpty
|
||||
case opStructFieldHeadInt8OmitEmpty:
|
||||
code.op = opStructFieldPtrHeadInt8OmitEmpty
|
||||
case opStructFieldHeadInt16OmitEmpty:
|
||||
code.op = opStructFieldPtrHeadInt16OmitEmpty
|
||||
case opStructFieldHeadInt32OmitEmpty:
|
||||
code.op = opStructFieldPtrHeadInt32OmitEmpty
|
||||
case opStructFieldHeadInt64OmitEmpty:
|
||||
code.op = opStructFieldPtrHeadInt64OmitEmpty
|
||||
case opStructFieldHeadUintOmitEmpty:
|
||||
code.op = opStructFieldPtrHeadUintOmitEmpty
|
||||
case opStructFieldHeadUint8OmitEmpty:
|
||||
code.op = opStructFieldPtrHeadUint8OmitEmpty
|
||||
case opStructFieldHeadUint16OmitEmpty:
|
||||
code.op = opStructFieldPtrHeadUint16OmitEmpty
|
||||
case opStructFieldHeadUint32OmitEmpty:
|
||||
code.op = opStructFieldPtrHeadUint32OmitEmpty
|
||||
case opStructFieldHeadUint64OmitEmpty:
|
||||
code.op = opStructFieldPtrHeadUint64OmitEmpty
|
||||
case opStructFieldHeadFloat32OmitEmpty:
|
||||
code.op = opStructFieldPtrHeadFloat32OmitEmpty
|
||||
case opStructFieldHeadFloat64OmitEmpty:
|
||||
code.op = opStructFieldPtrHeadFloat64OmitEmpty
|
||||
case opStructFieldHeadStringOmitEmpty:
|
||||
code.op = opStructFieldPtrHeadStringOmitEmpty
|
||||
case opStructFieldHeadBoolOmitEmpty:
|
||||
code.op = opStructFieldPtrHeadBoolOmitEmpty
|
||||
default:
|
||||
return newOpCode(opPtr, typ, code)
|
||||
}
|
||||
|
@ -308,6 +338,10 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
|
|||
keyName = opts[0]
|
||||
}
|
||||
}
|
||||
isOmitEmpty := false
|
||||
if len(opts) > 1 {
|
||||
isOmitEmpty = opts[1] == "omitempty"
|
||||
}
|
||||
fieldType := type2rtype(field.Type)
|
||||
valueCode, err := e.compile(fieldType)
|
||||
if err != nil {
|
||||
|
@ -323,10 +357,45 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
|
|||
offset: field.Offset,
|
||||
}
|
||||
if fieldIdx == 0 {
|
||||
fieldCode.op = opStructFieldHead
|
||||
head = fieldCode
|
||||
code = (*opcode)(unsafe.Pointer(fieldCode))
|
||||
prevField = fieldCode
|
||||
if isOmitEmpty {
|
||||
fieldCode.op = opStructFieldHeadOmitEmpty
|
||||
switch valueCode.op {
|
||||
case opInt:
|
||||
fieldCode.op = opStructFieldHeadIntOmitEmpty
|
||||
case opInt8:
|
||||
fieldCode.op = opStructFieldHeadInt8OmitEmpty
|
||||
case opInt16:
|
||||
fieldCode.op = opStructFieldHeadInt16OmitEmpty
|
||||
case opInt32:
|
||||
fieldCode.op = opStructFieldHeadInt32OmitEmpty
|
||||
case opInt64:
|
||||
fieldCode.op = opStructFieldHeadInt64OmitEmpty
|
||||
case opUint:
|
||||
fieldCode.op = opStructFieldHeadUintOmitEmpty
|
||||
case opUint8:
|
||||
fieldCode.op = opStructFieldHeadUint8OmitEmpty
|
||||
case opUint16:
|
||||
fieldCode.op = opStructFieldHeadUint16OmitEmpty
|
||||
case opUint32:
|
||||
fieldCode.op = opStructFieldHeadUint32OmitEmpty
|
||||
case opUint64:
|
||||
fieldCode.op = opStructFieldHeadUint64OmitEmpty
|
||||
case opFloat32:
|
||||
fieldCode.op = opStructFieldHeadFloat32OmitEmpty
|
||||
case opFloat64:
|
||||
fieldCode.op = opStructFieldHeadFloat64OmitEmpty
|
||||
case opString:
|
||||
fieldCode.op = opStructFieldHeadStringOmitEmpty
|
||||
case opBool:
|
||||
fieldCode.op = opStructFieldHeadBoolOmitEmpty
|
||||
default:
|
||||
code = valueCode.beforeLastCode()
|
||||
}
|
||||
} else {
|
||||
fieldCode.op = opStructFieldHead
|
||||
switch valueCode.op {
|
||||
case opInt:
|
||||
fieldCode.op = opStructFieldHeadInt
|
||||
|
@ -359,12 +428,48 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
|
|||
default:
|
||||
code = valueCode.beforeLastCode()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fieldCode.op = opStructField
|
||||
code.next = (*opcode)(unsafe.Pointer(fieldCode))
|
||||
prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode))
|
||||
prevField = fieldCode
|
||||
code = (*opcode)(unsafe.Pointer(fieldCode))
|
||||
if isOmitEmpty {
|
||||
fieldCode.op = opStructFieldOmitEmpty
|
||||
switch valueCode.op {
|
||||
case opInt:
|
||||
fieldCode.op = opStructFieldIntOmitEmpty
|
||||
case opInt8:
|
||||
fieldCode.op = opStructFieldInt8OmitEmpty
|
||||
case opInt16:
|
||||
fieldCode.op = opStructFieldInt16OmitEmpty
|
||||
case opInt32:
|
||||
fieldCode.op = opStructFieldInt32OmitEmpty
|
||||
case opInt64:
|
||||
fieldCode.op = opStructFieldInt64OmitEmpty
|
||||
case opUint:
|
||||
fieldCode.op = opStructFieldUintOmitEmpty
|
||||
case opUint8:
|
||||
fieldCode.op = opStructFieldUint8OmitEmpty
|
||||
case opUint16:
|
||||
fieldCode.op = opStructFieldUint16OmitEmpty
|
||||
case opUint32:
|
||||
fieldCode.op = opStructFieldUint32OmitEmpty
|
||||
case opUint64:
|
||||
fieldCode.op = opStructFieldUint64OmitEmpty
|
||||
case opFloat32:
|
||||
fieldCode.op = opStructFieldFloat32OmitEmpty
|
||||
case opFloat64:
|
||||
fieldCode.op = opStructFieldFloat64OmitEmpty
|
||||
case opString:
|
||||
fieldCode.op = opStructFieldStringOmitEmpty
|
||||
case opBool:
|
||||
fieldCode.op = opStructFieldBoolOmitEmpty
|
||||
default:
|
||||
code = valueCode.beforeLastCode()
|
||||
}
|
||||
} else {
|
||||
switch valueCode.op {
|
||||
case opInt:
|
||||
fieldCode.op = opStructFieldInt
|
||||
|
@ -398,10 +503,27 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
|
|||
code = valueCode.beforeLastCode()
|
||||
}
|
||||
}
|
||||
prevField.nextField = newEndOp()
|
||||
}
|
||||
fieldIdx++
|
||||
}
|
||||
|
||||
structEndCode := newOpCode(opStructEnd, nil, nil)
|
||||
|
||||
if prevField != nil && prevField.nextField == nil {
|
||||
prevField.nextField = structEndCode
|
||||
}
|
||||
|
||||
// no struct field
|
||||
if head == nil {
|
||||
head = &structFieldCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
op: opStructFieldHead,
|
||||
typ: typ,
|
||||
},
|
||||
nextField: structEndCode,
|
||||
}
|
||||
code = (*opcode)(unsafe.Pointer(head))
|
||||
}
|
||||
head.end = structEndCode
|
||||
code.next = structEndCode
|
||||
structEndCode.next = newEndOp()
|
||||
|
|
155
encode_opcode.go
155
encode_opcode.go
|
@ -37,6 +37,8 @@ const (
|
|||
opMapKey
|
||||
opMapValue
|
||||
opMapEnd
|
||||
|
||||
// StructFieldHead
|
||||
opStructFieldHead
|
||||
opStructFieldHeadInt
|
||||
opStructFieldHeadInt8
|
||||
|
@ -52,6 +54,25 @@ const (
|
|||
opStructFieldHeadFloat64
|
||||
opStructFieldHeadString
|
||||
opStructFieldHeadBool
|
||||
|
||||
// StructFieldHead with omitempty
|
||||
opStructFieldHeadOmitEmpty
|
||||
opStructFieldHeadIntOmitEmpty
|
||||
opStructFieldHeadInt8OmitEmpty
|
||||
opStructFieldHeadInt16OmitEmpty
|
||||
opStructFieldHeadInt32OmitEmpty
|
||||
opStructFieldHeadInt64OmitEmpty
|
||||
opStructFieldHeadUintOmitEmpty
|
||||
opStructFieldHeadUint8OmitEmpty
|
||||
opStructFieldHeadUint16OmitEmpty
|
||||
opStructFieldHeadUint32OmitEmpty
|
||||
opStructFieldHeadUint64OmitEmpty
|
||||
opStructFieldHeadFloat32OmitEmpty
|
||||
opStructFieldHeadFloat64OmitEmpty
|
||||
opStructFieldHeadStringOmitEmpty
|
||||
opStructFieldHeadBoolOmitEmpty
|
||||
|
||||
// StructFieldHead for pointer structure
|
||||
opStructFieldPtrHead
|
||||
opStructFieldPtrHeadInt
|
||||
opStructFieldPtrHeadInt8
|
||||
|
@ -67,6 +88,25 @@ const (
|
|||
opStructFieldPtrHeadFloat64
|
||||
opStructFieldPtrHeadString
|
||||
opStructFieldPtrHeadBool
|
||||
|
||||
// StructFieldPtrHead with omitempty
|
||||
opStructFieldPtrHeadOmitEmpty
|
||||
opStructFieldPtrHeadIntOmitEmpty
|
||||
opStructFieldPtrHeadInt8OmitEmpty
|
||||
opStructFieldPtrHeadInt16OmitEmpty
|
||||
opStructFieldPtrHeadInt32OmitEmpty
|
||||
opStructFieldPtrHeadInt64OmitEmpty
|
||||
opStructFieldPtrHeadUintOmitEmpty
|
||||
opStructFieldPtrHeadUint8OmitEmpty
|
||||
opStructFieldPtrHeadUint16OmitEmpty
|
||||
opStructFieldPtrHeadUint32OmitEmpty
|
||||
opStructFieldPtrHeadUint64OmitEmpty
|
||||
opStructFieldPtrHeadFloat32OmitEmpty
|
||||
opStructFieldPtrHeadFloat64OmitEmpty
|
||||
opStructFieldPtrHeadStringOmitEmpty
|
||||
opStructFieldPtrHeadBoolOmitEmpty
|
||||
|
||||
// StructField
|
||||
opStructField
|
||||
opStructFieldInt
|
||||
opStructFieldInt8
|
||||
|
@ -82,6 +122,24 @@ const (
|
|||
opStructFieldFloat64
|
||||
opStructFieldString
|
||||
opStructFieldBool
|
||||
|
||||
// StructField with omitempty
|
||||
opStructFieldOmitEmpty
|
||||
opStructFieldIntOmitEmpty
|
||||
opStructFieldInt8OmitEmpty
|
||||
opStructFieldInt16OmitEmpty
|
||||
opStructFieldInt32OmitEmpty
|
||||
opStructFieldInt64OmitEmpty
|
||||
opStructFieldUintOmitEmpty
|
||||
opStructFieldUint8OmitEmpty
|
||||
opStructFieldUint16OmitEmpty
|
||||
opStructFieldUint32OmitEmpty
|
||||
opStructFieldUint64OmitEmpty
|
||||
opStructFieldFloat32OmitEmpty
|
||||
opStructFieldFloat64OmitEmpty
|
||||
opStructFieldStringOmitEmpty
|
||||
opStructFieldBoolOmitEmpty
|
||||
|
||||
opStructEnd
|
||||
)
|
||||
|
||||
|
@ -141,6 +199,7 @@ func (t opType) String() string {
|
|||
return "MAP_VALUE"
|
||||
case opMapEnd:
|
||||
return "MAP_END"
|
||||
|
||||
case opStructFieldHead:
|
||||
return "STRUCT_FIELD_HEAD"
|
||||
case opStructFieldHeadInt:
|
||||
|
@ -171,6 +230,38 @@ func (t opType) String() string {
|
|||
return "STRUCT_FIELD_HEAD_STRING"
|
||||
case opStructFieldHeadBool:
|
||||
return "STRUCT_FIELD_HEAD_BOOL"
|
||||
|
||||
case opStructFieldHeadOmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_OMIT_EMPTY"
|
||||
case opStructFieldHeadIntOmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_INT_OMIT_EMPTY"
|
||||
case opStructFieldHeadInt8OmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_INT8_OMIT_EMPTY"
|
||||
case opStructFieldHeadInt16OmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_INT16_OMIT_EMPTY"
|
||||
case opStructFieldHeadInt32OmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_INT32_OMIT_EMPTY"
|
||||
case opStructFieldHeadInt64OmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_INT64_OMIT_EMPTY"
|
||||
case opStructFieldHeadUintOmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_UINT_OMIT_EMPTY"
|
||||
case opStructFieldHeadUint8OmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_UINT8_OMIT_EMPTY"
|
||||
case opStructFieldHeadUint16OmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_UINT16_OMIT_EMPTY"
|
||||
case opStructFieldHeadUint32OmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_UINT32_OMIT_EMPTY"
|
||||
case opStructFieldHeadUint64OmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_UINT64_OMIT_EMPTY"
|
||||
case opStructFieldHeadFloat32OmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_FLOAT32_OMIT_EMPTY"
|
||||
case opStructFieldHeadFloat64OmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_FLOAT64_OMIT_EMPTY"
|
||||
case opStructFieldHeadStringOmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_STRING_OMIT_EMPTY"
|
||||
case opStructFieldHeadBoolOmitEmpty:
|
||||
return "STRUCT_FIELD_HEAD_BOOL_OMIT_EMPTY"
|
||||
|
||||
case opStructFieldPtrHead:
|
||||
return "STRUCT_FIELD_PTR_HEAD"
|
||||
case opStructFieldPtrHeadInt:
|
||||
|
@ -201,6 +292,38 @@ func (t opType) String() string {
|
|||
return "STRUCT_FIELD_PTR_HEAD_STRING"
|
||||
case opStructFieldPtrHeadBool:
|
||||
return "STRUCT_FIELD_PTR_HEAD_BOOL"
|
||||
|
||||
case opStructFieldPtrHeadOmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadIntOmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_INT_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadInt8OmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_INT8_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadInt16OmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_INT16_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadInt32OmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_INT32_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadInt64OmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_INT64_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadUintOmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_UINT_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadUint8OmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_UINT8_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadUint16OmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_UINT16_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadUint32OmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_UINT32_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadUint64OmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_UINT64_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadFloat32OmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_FLOAT32_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadFloat64OmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_FLOAT64_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadStringOmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_STRING_OMIT_EMPTY"
|
||||
case opStructFieldPtrHeadBoolOmitEmpty:
|
||||
return "STRUCT_FIELD_PTR_HEAD_BOOL_OMIT_EMPTY"
|
||||
|
||||
case opStructField:
|
||||
return "STRUCT_FIELD"
|
||||
case opStructFieldInt:
|
||||
|
@ -231,6 +354,38 @@ func (t opType) String() string {
|
|||
return "STRUCT_FIELD_STRING"
|
||||
case opStructFieldBool:
|
||||
return "STRUCT_FIELD_BOOL"
|
||||
|
||||
case opStructFieldOmitEmpty:
|
||||
return "STRUCT_FIELD_OMIT_EMPTY"
|
||||
case opStructFieldIntOmitEmpty:
|
||||
return "STRUCT_FIELD_INT_OMIT_EMPTY"
|
||||
case opStructFieldInt8OmitEmpty:
|
||||
return "STRUCT_FIELD_INT8_OMIT_EMPTY"
|
||||
case opStructFieldInt16OmitEmpty:
|
||||
return "STRUCT_FIELD_INT16_OMIT_EMPTY"
|
||||
case opStructFieldInt32OmitEmpty:
|
||||
return "STRUCT_FIELD_INT32_OMIT_EMPTY"
|
||||
case opStructFieldInt64OmitEmpty:
|
||||
return "STRUCT_FIELD_INT64_OMIT_EMPTY"
|
||||
case opStructFieldUintOmitEmpty:
|
||||
return "STRUCT_FIELD_UINT_OMIT_EMPTY"
|
||||
case opStructFieldUint8OmitEmpty:
|
||||
return "STRUCT_FIELD_UINT8_OMIT_EMPTY"
|
||||
case opStructFieldUint16OmitEmpty:
|
||||
return "STRUCT_FIELD_UINT16_OMIT_EMPTY"
|
||||
case opStructFieldUint32OmitEmpty:
|
||||
return "STRUCT_FIELD_UINT32_OMIT_EMPTY"
|
||||
case opStructFieldUint64OmitEmpty:
|
||||
return "STRUCT_FIELD_UINT64_OMIT_EMPTY"
|
||||
case opStructFieldFloat32OmitEmpty:
|
||||
return "STRUCT_FIELD_FLOAT32_OMIT_EMPTY"
|
||||
case opStructFieldFloat64OmitEmpty:
|
||||
return "STRUCT_FIELD_FLOAT64_OMIT_EMPTY"
|
||||
case opStructFieldStringOmitEmpty:
|
||||
return "STRUCT_FIELD_STRING_OMIT_EMPTY"
|
||||
case opStructFieldBoolOmitEmpty:
|
||||
return "STRUCT_FIELD_BOOL_OMIT_EMPTY"
|
||||
|
||||
case opStructEnd:
|
||||
return "STRUCT_END"
|
||||
}
|
||||
|
|
|
@ -91,6 +91,72 @@ func Test_Encoder(t *testing.T) {
|
|||
})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "struct", `{"a":-1,"b":1,"c":"hello world"}`, string(bytes))
|
||||
t.Run("null", func(t *testing.T) {
|
||||
type T struct {
|
||||
A *struct{} `json:"a"`
|
||||
}
|
||||
var v T
|
||||
bytes, err := json.Marshal(&v)
|
||||
assertErr(t, err)
|
||||
assertEq(t, "struct", `{"a":null}`, string(bytes))
|
||||
})
|
||||
t.Run("omitempty", func(t *testing.T) {
|
||||
type T struct {
|
||||
A int `json:",omitempty"`
|
||||
B int8 `json:",omitempty"`
|
||||
C int16 `json:",omitempty"`
|
||||
D int32 `json:",omitempty"`
|
||||
E int64 `json:",omitempty"`
|
||||
F uint `json:",omitempty"`
|
||||
G uint8 `json:",omitempty"`
|
||||
H uint16 `json:",omitempty"`
|
||||
I uint32 `json:",omitempty"`
|
||||
J uint64 `json:",omitempty"`
|
||||
K float32 `json:",omitempty"`
|
||||
L float64 `json:",omitempty"`
|
||||
O string `json:",omitempty"`
|
||||
P bool `json:",omitempty"`
|
||||
Q []int `json:",omitempty"`
|
||||
R map[string]interface{} `json:",omitempty"`
|
||||
S *struct{} `json:",omitempty"`
|
||||
T int `json:"t,omitempty"`
|
||||
}
|
||||
var v T
|
||||
v.T = 1
|
||||
bytes, err := json.Marshal(&v)
|
||||
assertErr(t, err)
|
||||
assertEq(t, "struct", `{"t":1}`, string(bytes))
|
||||
})
|
||||
t.Run("head_omitempty", func(t *testing.T) {
|
||||
type T struct {
|
||||
A *struct{} `json:"a,omitempty"`
|
||||
}
|
||||
var v T
|
||||
bytes, err := json.Marshal(&v)
|
||||
assertErr(t, err)
|
||||
assertEq(t, "struct", `{}`, string(bytes))
|
||||
})
|
||||
t.Run("pointer_head_omitempty", func(t *testing.T) {
|
||||
type V struct{}
|
||||
type U struct {
|
||||
B *V `json:"b,omitempty"`
|
||||
}
|
||||
type T struct {
|
||||
A *U `json:"a"`
|
||||
}
|
||||
bytes, err := json.Marshal(&T{A: &U{}})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "struct", `{"a":{}}`, string(bytes))
|
||||
})
|
||||
t.Run("head_int_omitempty", func(t *testing.T) {
|
||||
type T struct {
|
||||
A int `json:"a,omitempty"`
|
||||
}
|
||||
var v T
|
||||
bytes, err := json.Marshal(&v)
|
||||
assertErr(t, err)
|
||||
assertEq(t, "struct", `{}`, string(bytes))
|
||||
})
|
||||
})
|
||||
t.Run("slice", func(t *testing.T) {
|
||||
t.Run("[]int", func(t *testing.T) {
|
||||
|
|
532
encode_vm.go
532
encode_vm.go
|
@ -171,14 +171,16 @@ func (e *Encoder) run(code *opcode) error {
|
|||
mapiternext(c.iter)
|
||||
code = c.next
|
||||
case opStructFieldPtrHead:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHead:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
e.encodeBytes(field.key)
|
||||
|
@ -410,6 +412,352 @@ func (e *Encoder) run(code *opcode) error {
|
|||
field.nextField.ptr = field.ptr
|
||||
code = field.next
|
||||
}
|
||||
|
||||
case opStructFieldPtrHeadOmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadOmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
p := ptr + field.offset
|
||||
if p == 0 || *(*uintptr)(unsafe.Pointer(p)) == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
code = field.next
|
||||
code.ptr = p
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadIntOmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadIntOmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToInt(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeInt(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadInt8OmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadInt8OmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToInt8(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeInt8(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadInt16OmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadInt16OmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToInt16(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeInt16(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadInt32OmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadInt32OmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToInt32(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeInt32(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadInt64OmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadInt64OmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToInt64(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeInt64(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadUintOmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadUintOmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToUint(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeUint(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadUint8OmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadUint8OmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToUint8(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeUint8(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadUint16OmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadUint16OmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToUint16(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeUint16(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadUint32OmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadUint32OmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToUint32(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeUint32(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadUint64OmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadUint64OmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToUint64(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeUint64(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadFloat32OmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadFloat32OmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToFloat32(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeFloat32(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadFloat64OmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadFloat64OmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToFloat64(ptr + field.offset)
|
||||
if v == 0 {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeFloat64(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadStringOmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadStringOmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToString(ptr + field.offset)
|
||||
if v == "" {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeEscapedString(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructFieldPtrHeadBoolOmitEmpty:
|
||||
if code.ptr != 0 {
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
}
|
||||
fallthrough
|
||||
case opStructFieldHeadBoolOmitEmpty:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = field.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
v := e.ptrToBool(ptr + field.offset)
|
||||
if !v {
|
||||
code = field.nextField
|
||||
} else {
|
||||
e.encodeBytes(field.key)
|
||||
e.encodeBool(v)
|
||||
code = field.next
|
||||
}
|
||||
field.nextField.ptr = field.ptr
|
||||
}
|
||||
case opStructField:
|
||||
e.encodeByte(',')
|
||||
c := code.toStructFieldCode()
|
||||
|
@ -515,6 +863,188 @@ func (e *Encoder) run(code *opcode) error {
|
|||
e.encodeBytes(c.key)
|
||||
e.encodeBool(e.ptrToBool(c.ptr + c.offset))
|
||||
code = code.next
|
||||
case opStructFieldOmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
p := c.ptr + c.offset
|
||||
if p == 0 || *(*uintptr)(unsafe.Pointer(p)) == 0 {
|
||||
code = c.nextField
|
||||
} else {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
code = code.next
|
||||
code.ptr = p
|
||||
}
|
||||
c.nextField.ptr = c.ptr
|
||||
case opStructFieldIntOmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToInt(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeInt(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldInt8OmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToInt8(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeInt8(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldInt16OmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToInt16(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeInt16(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldInt32OmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToInt32(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeInt32(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldInt64OmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToInt64(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeInt64(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldUintOmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToUint(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeUint(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldUint8OmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToUint8(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeUint8(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldUint16OmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToUint16(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeUint16(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldUint32OmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToUint32(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeUint32(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldUint64OmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToUint64(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeUint64(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldFloat32OmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToFloat32(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeFloat32(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldFloat64OmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToFloat64(c.ptr + c.offset)
|
||||
if v != 0 {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeFloat64(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldStringOmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToString(c.ptr + c.offset)
|
||||
if v != "" {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeEscapedString(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructFieldBoolOmitEmpty:
|
||||
c := code.toStructFieldCode()
|
||||
v := e.ptrToBool(c.ptr + c.offset)
|
||||
if v {
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(c.key)
|
||||
e.encodeBool(v)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr
|
||||
case opStructEnd:
|
||||
e.encodeByte('}')
|
||||
code = code.next
|
||||
|
|
Loading…
Reference in New Issue