Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: marshal struct field with "omitempty" tag as encoding/json #114

Merged
merged 1 commit into from
Oct 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 0 additions & 90 deletions encoder/assembler_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,6 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
_OP_is_zero_4 : (*_Assembler)._asm_OP_is_zero_4,
_OP_is_zero_8 : (*_Assembler)._asm_OP_is_zero_8,
_OP_is_zero_map : (*_Assembler)._asm_OP_is_zero_map,
_OP_is_zero_mem : (*_Assembler)._asm_OP_is_zero_mem,
_OP_is_zero_safe : (*_Assembler)._asm_OP_is_zero_safe,
_OP_goto : (*_Assembler)._asm_OP_goto,
_OP_map_iter : (*_Assembler)._asm_OP_map_iter,
_OP_map_stop : (*_Assembler)._asm_OP_map_stop,
Expand Down Expand Up @@ -698,79 +696,6 @@ func (self *_Assembler) encode_string(doubleQuote bool) {
}
}

/** Zero Value Check Routine **/

func (self *_Assembler) check_zero(nb int, dest int) {
i := int64(0)
e := int64(nb)

/* special case: zero-sized value, always empty */
if e == 0 {
return
}

/* 32-byte test */
for i <= e - 32 {
self.Emit("VMOVDQU", jit.Ptr(_SP_p, i), _Y0) // VMOVDQU (SP.p), Y0
self.Emit("VPTEST" , _Y0, _Y0) // VPTEST Y0, Y0
self.Sjmp("JNZ" , "_not_zero_z_{n}") // JNZ _not_zero_z_{n}
i += 32
}

/* VZEROUPPER to avoid AVX-SSE transition penalty */
if e >= 32 {
self.Emit("VZEROUPPER")
}

/* 16-byte test */
if i <= e - 16 {
self.Emit("MOVOU", jit.Ptr(_SP_p, i), _X0) // MOVOU (SP.p), X0
self.Emit("PTEST", _X0, _X0) // PTEST X0, X0
self.Sjmp("JNZ" , "_not_zero_{n}") // JNZ _not_zero_{n}
i += 16
}

/* 8-byte test */
if i <= e - 8 {
self.Emit("CMPQ", jit.Ptr(_SP_p, i), jit.Imm(0)) // CMPQ i(SP.p), $0
self.Sjmp("JNE" , "_not_zero_{n}") // JNE _not_zero_{n}
i += 8
}

/* 4 byte test */
if i <= e - 4 {
self.Emit("CMPL", jit.Ptr(_SP_p, i), jit.Imm(0)) // CMPL i(SP.p), $0
self.Sjmp("JNE" , "_not_zero_{n}") // JNE _not_zero_{n}
i += 4
}

/* 2 byte test */
if i <= e - 2 {
self.Emit("CMPW", jit.Ptr(_SP_p, i), jit.Imm(0)) // CMPW i(SP.p), $0
self.Sjmp("JNE" , "_not_zero_{n}") // JNE _not_zero_{n}
i += 2
}

/* the last byte */
if i < e {
self.Emit("CMPB", jit.Ptr(_SP_p, i), jit.Imm(0)) // CMPB i(SP.p), $0
self.Sjmp("JNE" , "_not_zero_{n}") // JNE _not_zero_{n}
}

/* value is not zero */
if e < 32 {
self.Xjmp("JMP", dest)
self.Link("_not_zero_{n}")
return
}

/* VZEROUPPER to avoid AVX-SSE transition penalty */
self.Xjmp("JMP", dest)
self.Link("_not_zero_z_{n}")
self.Emit("VZEROUPPER")
self.Link("_not_zero_{n}")
}

/** OpCode Assembler Functions **/

var (
Expand All @@ -787,7 +712,6 @@ var (

var (
_F_memmove = jit.Func(memmove)
_F_isZeroTyped = jit.Func(isZeroTyped)
_F_error_number = jit.Func(error_number)
_F_isValidNumber = jit.Func(isValidNumber)
)
Expand Down Expand Up @@ -1092,20 +1016,6 @@ func (self *_Assembler) _asm_OP_is_zero_map(p *_Instr) {
self.Xjmp("JE" , p.vi()) // JE p.vi()
}

func (self *_Assembler) _asm_OP_is_zero_mem(p *_Instr) {
self.check_zero(p.vlen(), p.vi())
}

func (self *_Assembler) _asm_OP_is_zero_safe(p *_Instr) {
self.check_zero(p.vlen(), p.vi()) // CHECKZ $p.vlen(), p.vi()
self.Emit("MOVQ", jit.Type(p.vt()), _AX) // MOVQ $p.vt(), AX
self.Emit("MOVQ", _SP_p, jit.Ptr(_SP, 0)) // MOVQ SP.p, (SP)
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 8)) // MOVQ AX, 8(SP)
self.call_go(_F_isZeroTyped) // CALL_GO isZeroTyped
self.Emit("CMPQ", jit.Ptr(_SP, 16), jit.Imm(0)) // CMPQ 16(SP), $0
self.Xjmp("JNE" , p.vi()) // JNE p.vi()
}

func (self *_Assembler) _asm_OP_goto(p *_Instr) {
self.Xjmp("JMP", p.vi())
}
Expand Down
19 changes: 1 addition & 18 deletions encoder/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ const (
_OP_is_zero_4
_OP_is_zero_8
_OP_is_zero_map
_OP_is_zero_mem
_OP_is_zero_safe
_OP_goto
_OP_map_iter
_OP_map_stop
Expand Down Expand Up @@ -127,8 +125,6 @@ var _OpNames = [256]string {
_OP_is_zero_4 : "is_zero_4",
_OP_is_zero_8 : "is_zero_8",
_OP_is_zero_map : "is_zero_map",
_OP_is_zero_mem : "is_zero_mem",
_OP_is_zero_safe : "is_zero_safe",
_OP_goto : "goto",
_OP_map_iter : "map_iter",
_OP_map_stop : "map_stop",
Expand Down Expand Up @@ -259,8 +255,6 @@ func (self _Instr) isBranch() bool {
case _OP_is_zero_2 : fallthrough
case _OP_is_zero_4 : fallthrough
case _OP_is_zero_8 : fallthrough
case _OP_is_zero_mem : fallthrough
case _OP_is_zero_safe : fallthrough
case _OP_map_check_key : fallthrough
case _OP_map_write_key : fallthrough
case _OP_slice_next : fallthrough
Expand Down Expand Up @@ -291,8 +285,6 @@ func (self _Instr) disassemble() string {
case _OP_cond_testc : fallthrough
case _OP_map_check_key : fallthrough
case _OP_map_write_key : return fmt.Sprintf("%-18sL_%d", self.op().String(), self.vi())
case _OP_is_zero_mem : fallthrough
case _OP_is_zero_safe : fallthrough
case _OP_slice_next : return fmt.Sprintf("%-18sL_%d, %s", self.op().String(), self.vi(), self.vt())
default : return self.op().String()
}
Expand Down Expand Up @@ -686,7 +678,7 @@ func (self *_Compiler) compileStructBody(p *_Program, sp int, vt reflect.Type) {
}

/* check for "omitempty" option */
if (fv.Opts & resolver.F_omitempty) != 0 {
if fv.Type.Kind() != reflect.Struct && fv.Type.Kind() != reflect.Array && (fv.Opts & resolver.F_omitempty) != 0 {
s = append(s, p.pc())
self.compileStructFieldZero(p, fv.Type)
}
Expand Down Expand Up @@ -799,7 +791,6 @@ func (self *_Compiler) compileStructFieldZero(p *_Program, vt reflect.Type) {
case reflect.Map : p.add(_OP_is_zero_map)
case reflect.Ptr : p.add(_OP_is_nil)
case reflect.Slice : p.add(_OP_is_nil_p1)
case reflect.Struct : self.compileStructFieldNonTrivialZero(p, vt)
default : panic(error_type(vt))
}
}
Expand All @@ -810,14 +801,6 @@ func (self *_Compiler) compileStructFieldQuoted(p *_Program, sp int, vt reflect.
p.int(_OP_byte, '"')
}

func (self *_Compiler) compileStructFieldNonTrivialZero(p *_Program, vt reflect.Type) {
if isTrivialZeroable(vt) {
p.rtt(_OP_is_zero_mem, vt)
} else {
p.rtt(_OP_is_zero_safe, vt)
}
}

func (self *_Compiler) compileInterface(p *_Program, vt reflect.Type) {
x := p.pc()
p.add(_OP_is_nil_p1)
Expand Down
73 changes: 1 addition & 72 deletions encoder/primitives.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package encoder
import (
`encoding`
`encoding/json`
`reflect`
`unsafe`

`github.com/bytedance/sonic/internal/native`
Expand Down Expand Up @@ -92,74 +91,4 @@ func encodeTextMarshaler(buf *[]byte, val encoding.TextMarshaler) error {
} else {
return encodeString(buf, rt.Mem2Str(ret))
}
}

func isZeroSafe(p unsafe.Pointer, vt *rt.GoType) bool {
if native.Lzero(p, int(vt.Size)) == 0 {
return true
} else {
return isZeroTyped(p, vt)
}
}

func isZeroTyped(p unsafe.Pointer, vt *rt.GoType) bool {
switch vt.Kind() {
case reflect.Map : return (*(**rt.GoMap)(p)).Count == 0
case reflect.Slice : return (*rt.GoSlice)(p).Len == 0
case reflect.String : return (*rt.GoString)(p).Len == 0
case reflect.Struct : return isZeroStruct(p, vt)
case reflect.Interface : return (*rt.GoEface)(p).Value == nil
default : return false
}
}

func isZeroStruct(p unsafe.Pointer, vt *rt.GoType) bool {
var dp uintptr
var fp unsafe.Pointer

/* check for each field */
for _, fv := range (*rt.GoStructType)(unsafe.Pointer(vt)).Fields {
dp = fv.OffEmbed >> 1
fp = unsafe.Pointer(uintptr(p) + dp)

/* check for the field */
if !isZeroSafe(fp, fv.Type) {
return false
}
}

/* all tests are passed */
return true
}

func isTrivialZeroable(vt reflect.Type) bool {
switch vt.Kind() {
case reflect.Bool : return true
case reflect.Int : return true
case reflect.Int8 : return true
case reflect.Int16 : return true
case reflect.Int32 : return true
case reflect.Int64 : return true
case reflect.Uint : return true
case reflect.Uint8 : return true
case reflect.Uint16 : return true
case reflect.Uint32 : return true
case reflect.Uint64 : return true
case reflect.Uintptr : return true
case reflect.Float32 : return true
case reflect.Float64 : return true
case reflect.String : return false
case reflect.Array : return true
case reflect.Interface : return false
case reflect.Map : return false
case reflect.Ptr : return true
case reflect.Slice : return false
case reflect.Struct : return isStructTrivialZeroable(vt)
default : return false
}
}

func isStructTrivialZeroable(vt reflect.Type) bool {
for i := 0; i < vt.NumField(); i++ { if !isTrivialZeroable(vt.Field(i).Type) { return false } }
return true
}
}
Loading