mirror of
https://github.com/ii64/sonic.git
synced 2026-06-24 02:16:45 +08:00
feat: supports map key-sorting via encoder options
This commit is contained in:
parent
dd73e36cf4
commit
8383178c89
17 changed files with 811 additions and 139 deletions
|
|
@ -77,7 +77,7 @@ func TestOmitEmpty(t *testing.T) {
|
||||||
o.Mr = map[string]interface{}{}
|
o.Mr = map[string]interface{}{}
|
||||||
o.Mo = map[string]interface{}{}
|
o.Mo = map[string]interface{}{}
|
||||||
|
|
||||||
got, err := encoder.EncodeIndented(&o, "", " ")
|
got, err := encoder.EncodeIndented(&o, "", " ", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +137,7 @@ func TestRoundtripStringTag(t *testing.T) {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
// Indent with a tab prefix to make the multi-line string
|
// Indent with a tab prefix to make the multi-line string
|
||||||
// literals in the table nicer to read.
|
// literals in the table nicer to read.
|
||||||
got, err := encoder.EncodeIndented(&test.in, "\t\t\t", "\t")
|
got, err := encoder.EncodeIndented(&test.in, "\t\t\t", "\t", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
`fmt`
|
`fmt`
|
||||||
`reflect`
|
`reflect`
|
||||||
`strconv`
|
`strconv`
|
||||||
`sync`
|
|
||||||
`unsafe`
|
`unsafe`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/jit`
|
`github.com/bytedance/sonic/internal/jit`
|
||||||
|
|
@ -53,13 +52,14 @@ import (
|
||||||
|
|
||||||
/** Function Prototype & Stack Map
|
/** Function Prototype & Stack Map
|
||||||
*
|
*
|
||||||
* func (buf *[]byte, p unsafe.Pointer, sb *_Stack) (err error)
|
* func (buf *[]byte, p unsafe.Pointer, sb *_Stack, fv uint64) (err error)
|
||||||
*
|
*
|
||||||
* buf : (FP)
|
* buf : (FP)
|
||||||
* p : 8(FP)
|
* p : 8(FP)
|
||||||
* sb : 16(FP)
|
* sb : 16(FP)
|
||||||
* err.vt : 24(FP)
|
* fv : 24(FP)
|
||||||
* err.vp : 32(FP)
|
* err.vt : 32(FP)
|
||||||
|
* err.vp : 40(FP)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -68,7 +68,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_FP_args = 40 // 40 bytes for passing arguments to this function
|
_FP_args = 48 // 48 bytes for passing arguments to this function
|
||||||
_FP_fargs = 64 // 64 bytes for passing arguments to other Go functions
|
_FP_fargs = 64 // 64 bytes for passing arguments to other Go functions
|
||||||
_FP_saves = 64 // 64 bytes for saving the registers before CALL instructions
|
_FP_saves = 64 // 64 bytes for saving the registers before CALL instructions
|
||||||
_FP_locals = 16 // 16 bytes for local variables
|
_FP_locals = 16 // 16 bytes for local variables
|
||||||
|
|
@ -145,11 +145,12 @@ var (
|
||||||
_ARG_rb = jit.Ptr(_SP, _FP_base)
|
_ARG_rb = jit.Ptr(_SP, _FP_base)
|
||||||
_ARG_vp = jit.Ptr(_SP, _FP_base + 8)
|
_ARG_vp = jit.Ptr(_SP, _FP_base + 8)
|
||||||
_ARG_sb = jit.Ptr(_SP, _FP_base + 16)
|
_ARG_sb = jit.Ptr(_SP, _FP_base + 16)
|
||||||
|
_ARG_fv = jit.Ptr(_SP, _FP_base + 24)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_RET_et = jit.Ptr(_SP, _FP_base + 24)
|
_RET_et = jit.Ptr(_SP, _FP_base + 32)
|
||||||
_RET_ep = jit.Ptr(_SP, _FP_base + 32)
|
_RET_ep = jit.Ptr(_SP, _FP_base + 40)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -234,7 +235,9 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
|
||||||
_OP_is_zero_safe : (*_Assembler)._asm_OP_is_zero_safe,
|
_OP_is_zero_safe : (*_Assembler)._asm_OP_is_zero_safe,
|
||||||
_OP_goto : (*_Assembler)._asm_OP_goto,
|
_OP_goto : (*_Assembler)._asm_OP_goto,
|
||||||
_OP_map_iter : (*_Assembler)._asm_OP_map_iter,
|
_OP_map_iter : (*_Assembler)._asm_OP_map_iter,
|
||||||
|
_OP_map_stop : (*_Assembler)._asm_OP_map_stop,
|
||||||
_OP_map_check_key : (*_Assembler)._asm_OP_map_check_key,
|
_OP_map_check_key : (*_Assembler)._asm_OP_map_check_key,
|
||||||
|
_OP_map_write_key : (*_Assembler)._asm_OP_map_write_key,
|
||||||
_OP_map_value_next : (*_Assembler)._asm_OP_map_value_next,
|
_OP_map_value_next : (*_Assembler)._asm_OP_map_value_next,
|
||||||
_OP_slice_len : (*_Assembler)._asm_OP_slice_len,
|
_OP_slice_len : (*_Assembler)._asm_OP_slice_len,
|
||||||
_OP_slice_next : (*_Assembler)._asm_OP_slice_next,
|
_OP_slice_next : (*_Assembler)._asm_OP_slice_next,
|
||||||
|
|
@ -770,18 +773,10 @@ func (self *_Assembler) check_zero(nb int, dest int) {
|
||||||
/** OpCode Assembler Functions **/
|
/** OpCode Assembler Functions **/
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_T_map_Iterator = rt.UnpackType(mapIteratorType)
|
|
||||||
_T_map_PIterator = rt.UnpackType(mapPIteratorType)
|
|
||||||
_T_json_Marshaler = rt.UnpackType(jsonMarshalerType)
|
_T_json_Marshaler = rt.UnpackType(jsonMarshalerType)
|
||||||
_T_encoding_TextMarshaler = rt.UnpackType(encodingTextMarshalerType)
|
_T_encoding_TextMarshaler = rt.UnpackType(encodingTextMarshalerType)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
_P_iteratorPool = new(sync.Pool)
|
|
||||||
_N_iteratorPool = jit.Imm(int64(unsafe.Sizeof(rt.GoMapIterator{})))
|
|
||||||
_V_iteratorPool = jit.Imm(int64(uintptr(unsafe.Pointer(_P_iteratorPool))))
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_F_f64toa = jit.Imm(int64(native.S_f64toa))
|
_F_f64toa = jit.Imm(int64(native.S_f64toa))
|
||||||
_F_i64toa = jit.Imm(int64(native.S_i64toa))
|
_F_i64toa = jit.Imm(int64(native.S_i64toa))
|
||||||
|
|
@ -790,19 +785,16 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_F_memmove = jit.Func(memmove)
|
_F_memmove = jit.Func(memmove)
|
||||||
_F_newobject = jit.Func(newobject)
|
_F_isZeroTyped = jit.Func(isZeroTyped)
|
||||||
_F_isZeroTyped = jit.Func(isZeroTyped)
|
_F_error_number = jit.Func(error_number)
|
||||||
_F_mapiternext = jit.Func(mapiternext)
|
_F_isValidNumber = jit.Func(isValidNumber)
|
||||||
_F_mapiterinit = jit.Func(mapiterinit)
|
|
||||||
_F_error_number = jit.Func(error_number)
|
|
||||||
_F_isValidNumber = jit.Func(isValidNumber)
|
|
||||||
_F_memclrNoHeapPointers = jit.Func(memclrNoHeapPointers)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_F_sync_Pool_Get = jit.Func((*sync.Pool).Get)
|
_F_iteratorStop = jit.Func(iteratorStop)
|
||||||
_F_sync_Pool_Put = jit.Func((*sync.Pool).Put)
|
_F_iteratorNext = jit.Func(iteratorNext)
|
||||||
|
_F_iteratorStart = jit.Func(iteratorStart)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -958,9 +950,11 @@ func (self *_Assembler) _asm_OP_eface(_ *_Instr) {
|
||||||
self.Emit("LEAQ" , jit.Ptr(_SP_p, 8), _AX) // LEAQ 8(SP.p), AX
|
self.Emit("LEAQ" , jit.Ptr(_SP_p, 8), _AX) // LEAQ 8(SP.p), AX
|
||||||
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 16)) // MOVQ AX, 16(SP)
|
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 16)) // MOVQ AX, 16(SP)
|
||||||
self.Emit("MOVQ" , _ST, jit.Ptr(_SP, 24)) // MOVQ ST, 24(SP)
|
self.Emit("MOVQ" , _ST, jit.Ptr(_SP, 24)) // MOVQ ST, 24(SP)
|
||||||
|
self.Emit("MOVQ" , _ARG_fv, _AX) // MOVQ fv, AX
|
||||||
|
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 32)) // MOVQ AX, 32(SP)
|
||||||
self.call_encoder(_F_encodeTypedPointer) // CALL encodeTypedPointer
|
self.call_encoder(_F_encodeTypedPointer) // CALL encodeTypedPointer
|
||||||
self.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET) // MOVQ 32(SP), ET
|
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _ET) // MOVQ 40(SP), ET
|
||||||
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP) // MOVQ 40(SP), EP
|
self.Emit("MOVQ" , jit.Ptr(_SP, 48), _EP) // MOVQ 48(SP), EP
|
||||||
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
||||||
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
||||||
}
|
}
|
||||||
|
|
@ -973,9 +967,11 @@ func (self *_Assembler) _asm_OP_iface(_ *_Instr) {
|
||||||
self.Emit("LEAQ" , jit.Ptr(_SP_p, 8), _AX) // LEAQ 8(SP.p), AX
|
self.Emit("LEAQ" , jit.Ptr(_SP_p, 8), _AX) // LEAQ 8(SP.p), AX
|
||||||
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 16)) // MOVQ AX, 16(SP)
|
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 16)) // MOVQ AX, 16(SP)
|
||||||
self.Emit("MOVQ" , _ST, jit.Ptr(_SP, 24)) // MOVQ ST, 24(SP)
|
self.Emit("MOVQ" , _ST, jit.Ptr(_SP, 24)) // MOVQ ST, 24(SP)
|
||||||
|
self.Emit("MOVQ" , _ARG_fv, _AX) // MOVQ fv, AX
|
||||||
|
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 32)) // MOVQ AX, 32(SP)
|
||||||
self.call_encoder(_F_encodeTypedPointer) // CALL encodeTypedPointer
|
self.call_encoder(_F_encodeTypedPointer) // CALL encodeTypedPointer
|
||||||
self.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET) // MOVQ 32(SP), ET
|
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _ET) // MOVQ 40(SP), ET
|
||||||
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP) // MOVQ 40(SP), EP
|
self.Emit("MOVQ" , jit.Ptr(_SP, 48), _EP) // MOVQ 48(SP), EP
|
||||||
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
||||||
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
||||||
}
|
}
|
||||||
|
|
@ -1036,9 +1032,11 @@ func (self *_Assembler) _asm_OP_recurse(p *_Instr) {
|
||||||
/* call the encoder */
|
/* call the encoder */
|
||||||
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 16)) // MOVQ AX, 16(SP)
|
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 16)) // MOVQ AX, 16(SP)
|
||||||
self.Emit("MOVQ" , _ST, jit.Ptr(_SP, 24)) // MOVQ ST, 24(SP)
|
self.Emit("MOVQ" , _ST, jit.Ptr(_SP, 24)) // MOVQ ST, 24(SP)
|
||||||
|
self.Emit("MOVQ" , _ARG_fv, _AX) // MOVQ fv, AX
|
||||||
|
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 32)) // MOVQ AX, 32(SP)
|
||||||
self.call_encoder(_F_encodeTypedPointer) // CALL encodeTypedPointer
|
self.call_encoder(_F_encodeTypedPointer) // CALL encodeTypedPointer
|
||||||
self.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET) // MOVQ 32(SP), ET
|
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _ET) // MOVQ 40(SP), ET
|
||||||
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP) // MOVQ 40(SP), EP
|
self.Emit("MOVQ" , jit.Ptr(_SP, 48), _EP) // MOVQ 48(SP), EP
|
||||||
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
||||||
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
||||||
}
|
}
|
||||||
|
|
@ -1100,50 +1098,44 @@ func (self *_Assembler) _asm_OP_goto(p *_Instr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_iter(p *_Instr) {
|
func (self *_Assembler) _asm_OP_map_iter(p *_Instr) {
|
||||||
self.Emit("MOVQ" , _V_iteratorPool, _AX) // MOVQ $&iteratorPool, AX
|
self.Emit("MOVQ" , jit.Type(p.vt()), _AX) // MOVQ $p.vt(), AX
|
||||||
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
self.Emit("MOVQ" , jit.Ptr(_SP_p, 0), _CX) // MOVQ (SP.p), CX
|
||||||
self.call_go(_F_sync_Pool_Get) // CALL_GO (*sync.Pool).Get
|
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
||||||
self.Emit("MOVQ" , jit.Ptr(_SP, 16), _SP_q) // MOVQ 16(SP), SP.q
|
self.Emit("MOVQ" , _CX, jit.Ptr(_SP, 8)) // MOVQ CX, 8(SP)
|
||||||
self.Emit("TESTQ", _SP_q, _SP_q) // TESTQ SP.q, SP.q
|
self.Emit("MOVQ" , _ARG_fv, _AX) // MOVQ fv, AX
|
||||||
self.Sjmp("JZ" , "_new_iter_{n}") // JZ _new_iter_{n}
|
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 16)) // MOVQ AX, 16(SP)
|
||||||
self.Emit("MOVL" , _N_iteratorPool, _AX) // MOVL ${size(GoMapIterator)}, AX
|
self.call_go(_F_iteratorStart) // CALL_GO iteratorStart
|
||||||
self.Emit("MOVQ" , _SP_q, jit.Ptr(_SP, 0)) // MOVQ SP.q, (SP)
|
self.Emit("MOVQ" , jit.Ptr(_SP, 24), _SP_q) // MOVQ 24(SP), SP.q
|
||||||
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 8)) // MOVQ AX, 8(SP)
|
self.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET) // MOVQ 32(SP), ET
|
||||||
self.call_go(_F_memclrNoHeapPointers) // CALL_GO memclrNoHeapPointers
|
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP) // MOVQ 40(SP), EP
|
||||||
self.Sjmp("JMP" , "_init_iter_{n}") // JMP _init_iter_{n}
|
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
||||||
self.Link("_new_iter_{n}") // _new_iter_{n}:
|
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
||||||
self.Emit("MOVQ" , jit.Gtype(_T_map_Iterator), _AX) // MOVQ ${type(GoMapIterator)}, AX
|
}
|
||||||
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
|
||||||
self.call_go(_F_newobject) // CALL_GO newobject
|
func (self *_Assembler) _asm_OP_map_stop(_ *_Instr) {
|
||||||
self.Emit("MOVQ" , jit.Ptr(_SP, 8), _SP_q) // MOVQ 8(SP), SP.q
|
self.Emit("MOVQ", _SP_q, jit.Ptr(_SP, 0)) // MOVQ SP.q, 0(SP)
|
||||||
self.Link("_init_iter_{n}") // _init_iter_{n}:
|
self.call_go(_F_iteratorStop) // CALL_GO iteratorStop
|
||||||
self.Emit("MOVQ" , jit.Type(p.vt()), _AX) // MOVQ $p.vt(), AX
|
self.Emit("XORL", _SP_q, _SP_q) // XORL SP.q, SP.q
|
||||||
self.Emit("MOVQ" , jit.Ptr(_SP_p, 0), _CX) // MOVQ (SP.p), CX
|
|
||||||
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
|
||||||
self.Emit("MOVQ" , _CX, jit.Ptr(_SP, 8)) // MOVQ CX, 8(SP)
|
|
||||||
self.Emit("MOVQ" , _SP_q, jit.Ptr(_SP, 16)) // MOVQ SP.q, 16(SP)
|
|
||||||
self.call_go(_F_mapiterinit) // CALL_GO mapiterinit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_check_key(p *_Instr) {
|
func (self *_Assembler) _asm_OP_map_check_key(p *_Instr) {
|
||||||
self.Emit("MOVQ" , jit.Ptr(_SP_q, 0), _SP_p) // MOVQ (SP.q), SP.p
|
self.Emit("MOVQ" , jit.Ptr(_SP_q, 0), _SP_p) // MOVQ (SP.q), SP.p
|
||||||
self.Emit("TESTQ", _SP_p, _SP_p) // TESTQ SP.p, SP.p
|
self.Emit("TESTQ", _SP_p, _SP_p) // TESTQ SP.p, SP.p
|
||||||
self.Sjmp("JNZ" , "_map_next_{n}") // JNZ _map_next_{n}
|
self.Xjmp("JZ" , p.vi()) // JNZ p.vi()
|
||||||
self.Emit("MOVQ" , _V_iteratorPool, _AX) // MOVQ $&iteratorPool, AX
|
}
|
||||||
self.Emit("MOVQ" , jit.Gtype(_T_map_PIterator), _CX) // MOVQ ${type(*GoMapIterator)}, CX
|
|
||||||
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
func (self *_Assembler) _asm_OP_map_write_key(p *_Instr) {
|
||||||
self.Emit("MOVQ" , _CX, jit.Ptr(_SP, 8)) // MOVQ CX, 8(SP)
|
self.Emit("BTQ", jit.Imm(bitSortMapKeys), _ARG_fv) // BTQ ${SortMapKeys}, fv
|
||||||
self.Emit("MOVQ" , _SP_q, jit.Ptr(_SP, 16)) // MOVQ SP.q, 16(SP)
|
self.Sjmp("JNC", "_unordered_key_{n}") // JNC _unordered_key_{n}
|
||||||
self.call_go(_F_sync_Pool_Put) // CALL_GO (*sync.Pool).Put
|
self.encode_string(false) // STR $false
|
||||||
self.Emit("XORL" , _SP_q, _SP_q) // XORL SP.q, SP.q
|
self.Xjmp("JMP", p.vi()) // JMP ${p.vi()}
|
||||||
self.Xjmp("JMP" , p.vi()) // JMP p.vi()
|
self.Link("_unordered_key_{n}") // _unordered_key_{n}:
|
||||||
self.Link("_map_next_{n}") // _map_next_{n}:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_value_next(_ *_Instr) {
|
func (self *_Assembler) _asm_OP_map_value_next(_ *_Instr) {
|
||||||
self.Emit("MOVQ", jit.Ptr(_SP_q, 8), _SP_p) // MOVQ 8(SP.q), SP.p
|
self.Emit("MOVQ", jit.Ptr(_SP_q, 8), _SP_p) // MOVQ 8(SP.q), SP.p
|
||||||
self.Emit("MOVQ", _SP_q, jit.Ptr(_SP, 0)) // MOVQ SP.q, (SP)
|
self.Emit("MOVQ", _SP_q, jit.Ptr(_SP, 0)) // MOVQ SP.q, (SP)
|
||||||
self.call_go(_F_mapiternext) // CALL_GO mapiternext
|
self.call_go(_F_iteratorNext) // CALL_GO iteratorNext
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_slice_len(_ *_Instr) {
|
func (self *_Assembler) _asm_OP_slice_len(_ *_Instr) {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ func TestAssembler_CompileAndLoad(t *testing.T) {
|
||||||
/* true */
|
/* true */
|
||||||
v := true
|
v := true
|
||||||
u := &v
|
u := &v
|
||||||
e := f(&b, unsafe.Pointer(&u), s)
|
e := f(&b, unsafe.Pointer(&u), s, 0)
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
println(cap(b))
|
println(cap(b))
|
||||||
println(hex.Dump(b))
|
println(hex.Dump(b))
|
||||||
|
|
@ -50,7 +50,7 @@ func TestAssembler_CompileAndLoad(t *testing.T) {
|
||||||
v = false
|
v = false
|
||||||
u = &v
|
u = &v
|
||||||
b = b[:0]
|
b = b[:0]
|
||||||
e = f(&b, unsafe.Pointer(&u), s)
|
e = f(&b, unsafe.Pointer(&u), s, 0)
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
println(cap(b))
|
println(cap(b))
|
||||||
println(hex.Dump(b))
|
println(hex.Dump(b))
|
||||||
|
|
@ -58,7 +58,7 @@ func TestAssembler_CompileAndLoad(t *testing.T) {
|
||||||
/* nil */
|
/* nil */
|
||||||
u = nil
|
u = nil
|
||||||
b = b[:0]
|
b = b[:0]
|
||||||
e = f(&b, unsafe.Pointer(&u), s)
|
e = f(&b, unsafe.Pointer(&u), s, 0)
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
println(cap(b))
|
println(cap(b))
|
||||||
println(hex.Dump(b))
|
println(hex.Dump(b))
|
||||||
|
|
@ -78,7 +78,7 @@ func testOpCode(t *testing.T, v interface{}, ex string, err error, ins []_Instr)
|
||||||
s := new(_Stack)
|
s := new(_Stack)
|
||||||
a := newAssembler(p)
|
a := newAssembler(p)
|
||||||
f := a.Load()
|
f := a.Load()
|
||||||
e := f(&m, rt.UnpackEface(v).Value, s)
|
e := f(&m, rt.UnpackEface(v).Value, s, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
assert.EqualError(t, e, err.Error())
|
assert.EqualError(t, e, err.Error())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -347,7 +347,7 @@ func TestAssembler_StringMoreSpace(t *testing.T) {
|
||||||
a := newAssembler(p)
|
a := newAssembler(p)
|
||||||
f := a.Load()
|
f := a.Load()
|
||||||
v := "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f\u0010"
|
v := "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f\u0010"
|
||||||
e := f(&m, unsafe.Pointer(&v), s)
|
e := f(&m, unsafe.Pointer(&v), s, 0)
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
spew.Dump(m)
|
spew.Dump(m)
|
||||||
}
|
}
|
||||||
|
|
@ -359,7 +359,7 @@ func TestAssembler_TwitterJSON_Generic(t *testing.T) {
|
||||||
a := newAssembler(p)
|
a := newAssembler(p)
|
||||||
f := a.Load()
|
f := a.Load()
|
||||||
v := &_GenericValue
|
v := &_GenericValue
|
||||||
e := f(&m, unsafe.Pointer(&v), s)
|
e := f(&m, unsafe.Pointer(&v), s, 0)
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
println(string(m))
|
println(string(m))
|
||||||
}
|
}
|
||||||
|
|
@ -370,7 +370,7 @@ func TestAssembler_TwitterJSON_Structure(t *testing.T) {
|
||||||
s := new(_Stack)
|
s := new(_Stack)
|
||||||
a := newAssembler(p)
|
a := newAssembler(p)
|
||||||
f := a.Load()
|
f := a.Load()
|
||||||
e := f(&m, unsafe.Pointer(&_BindingValue), s)
|
e := f(&m, unsafe.Pointer(&_BindingValue), s, 0)
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
println(string(m))
|
println(string(m))
|
||||||
runtime.KeepAlive(s)
|
runtime.KeepAlive(s)
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,9 @@ const (
|
||||||
_OP_is_zero_safe
|
_OP_is_zero_safe
|
||||||
_OP_goto
|
_OP_goto
|
||||||
_OP_map_iter
|
_OP_map_iter
|
||||||
|
_OP_map_stop
|
||||||
_OP_map_check_key
|
_OP_map_check_key
|
||||||
|
_OP_map_write_key
|
||||||
_OP_map_value_next
|
_OP_map_value_next
|
||||||
_OP_slice_len
|
_OP_slice_len
|
||||||
_OP_slice_next
|
_OP_slice_next
|
||||||
|
|
@ -129,7 +131,9 @@ var _OpNames = [256]string {
|
||||||
_OP_is_zero_safe : "is_zero_safe",
|
_OP_is_zero_safe : "is_zero_safe",
|
||||||
_OP_goto : "goto",
|
_OP_goto : "goto",
|
||||||
_OP_map_iter : "map_iter",
|
_OP_map_iter : "map_iter",
|
||||||
|
_OP_map_stop : "map_stop",
|
||||||
_OP_map_check_key : "map_check_key",
|
_OP_map_check_key : "map_check_key",
|
||||||
|
_OP_map_write_key : "map_write_key",
|
||||||
_OP_map_value_next : "map_value_next",
|
_OP_map_value_next : "map_value_next",
|
||||||
_OP_slice_len : "slice_len",
|
_OP_slice_len : "slice_len",
|
||||||
_OP_slice_next : "slice_next",
|
_OP_slice_next : "slice_next",
|
||||||
|
|
@ -254,6 +258,7 @@ func (self _Instr) isBranch() bool {
|
||||||
case _OP_is_zero_mem : fallthrough
|
case _OP_is_zero_mem : fallthrough
|
||||||
case _OP_is_zero_safe : fallthrough
|
case _OP_is_zero_safe : fallthrough
|
||||||
case _OP_map_check_key : fallthrough
|
case _OP_map_check_key : fallthrough
|
||||||
|
case _OP_map_write_key : fallthrough
|
||||||
case _OP_slice_next : fallthrough
|
case _OP_slice_next : fallthrough
|
||||||
case _OP_cond_testc : return true
|
case _OP_cond_testc : return true
|
||||||
default : return false
|
default : return false
|
||||||
|
|
@ -280,7 +285,8 @@ func (self _Instr) disassemble() string {
|
||||||
case _OP_is_zero_8 : fallthrough
|
case _OP_is_zero_8 : fallthrough
|
||||||
case _OP_is_zero_map : fallthrough
|
case _OP_is_zero_map : fallthrough
|
||||||
case _OP_cond_testc : fallthrough
|
case _OP_cond_testc : fallthrough
|
||||||
case _OP_map_check_key : return fmt.Sprintf("%-18sL_%d", self.op().String(), self.vi())
|
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_mem : fallthrough
|
||||||
case _OP_is_zero_safe : fallthrough
|
case _OP_is_zero_safe : fallthrough
|
||||||
case _OP_slice_next : return fmt.Sprintf("%-18sL_%d, %s", self.op().String(), self.vi(), self.vt())
|
case _OP_slice_next : return fmt.Sprintf("%-18sL_%d, %s", self.op().String(), self.vi(), self.vt())
|
||||||
|
|
@ -504,20 +510,27 @@ func (self *_Compiler) compileMapBody(p *_Program, sp int, vt reflect.Type) {
|
||||||
p.add(_OP_save)
|
p.add(_OP_save)
|
||||||
i := p.pc()
|
i := p.pc()
|
||||||
p.add(_OP_map_check_key)
|
p.add(_OP_map_check_key)
|
||||||
|
u := p.pc()
|
||||||
|
p.add(_OP_map_write_key)
|
||||||
self.compileMapBodyKey(p, vt.Key())
|
self.compileMapBodyKey(p, vt.Key())
|
||||||
|
p.pin(u)
|
||||||
p.int(_OP_byte, ':')
|
p.int(_OP_byte, ':')
|
||||||
p.add(_OP_map_value_next)
|
p.add(_OP_map_value_next)
|
||||||
self.compileOne(p, sp + 2, vt.Elem(), false)
|
self.compileOne(p, sp + 2, vt.Elem(), false)
|
||||||
j := p.pc()
|
j := p.pc()
|
||||||
p.add(_OP_map_check_key)
|
p.add(_OP_map_check_key)
|
||||||
p.int(_OP_byte, ',')
|
p.int(_OP_byte, ',')
|
||||||
|
v := p.pc()
|
||||||
|
p.add(_OP_map_write_key)
|
||||||
self.compileMapBodyKey(p, vt.Key())
|
self.compileMapBodyKey(p, vt.Key())
|
||||||
|
p.pin(v)
|
||||||
p.int(_OP_byte, ':')
|
p.int(_OP_byte, ':')
|
||||||
p.add(_OP_map_value_next)
|
p.add(_OP_map_value_next)
|
||||||
self.compileOne(p, sp + 1, vt.Elem(), false)
|
self.compileOne(p, sp + 1, vt.Elem(), false)
|
||||||
p.int(_OP_goto, j)
|
p.int(_OP_goto, j)
|
||||||
p.pin(i)
|
p.pin(i)
|
||||||
p.pin(j)
|
p.pin(j)
|
||||||
|
p.add(_OP_map_stop)
|
||||||
p.add(_OP_drop_2)
|
p.add(_OP_drop_2)
|
||||||
p.int(_OP_byte, '}')
|
p.int(_OP_byte, '}')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,37 @@ import (
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Options is a set of encoding options.
|
||||||
|
type Options uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
bitSortMapKeys = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SortMapKeys indicate that the keys of a map needs to be sorted before
|
||||||
|
// serializing into JSON.
|
||||||
|
// WARNING: This hurts performance A LOT, USE WITH CARE.
|
||||||
|
SortMapKeys Options = 1 << bitSortMapKeys
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encoder represents a specific set of encoder configurations.
|
||||||
|
type Encoder struct {
|
||||||
|
Opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode returns the JSON encoding of v.
|
||||||
|
func (self *Encoder) Encode(v interface{}) ([]byte, error) {
|
||||||
|
return Encode(v, self.Opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortKeys enables the SortMapKeys option.
|
||||||
|
func (self *Encoder) SortKeys() *Encoder {
|
||||||
|
self.Opts |= SortMapKeys
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quote returns the JSON-quoted version of s.
|
||||||
func Quote(s string) string {
|
func Quote(s string) string {
|
||||||
var n int
|
var n int
|
||||||
var p []byte
|
var p []byte
|
||||||
|
|
@ -42,9 +73,10 @@ func Quote(s string) string {
|
||||||
return rt.Mem2Str(p)
|
return rt.Mem2Str(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Encode(val interface{}) ([]byte, error) {
|
// Encode returns the JSON encoding of val, encoded with opts.
|
||||||
|
func Encode(val interface{}, opts Options) ([]byte, error) {
|
||||||
buf := newBytes()
|
buf := newBytes()
|
||||||
err := EncodeInto(&buf, val)
|
err := EncodeInto(&buf, val, opts)
|
||||||
|
|
||||||
/* check for errors */
|
/* check for errors */
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -61,24 +93,29 @@ func Encode(val interface{}) ([]byte, error) {
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func EncodeInto(buf *[]byte, val interface{}) error {
|
// EncodeInto is like Encode but uses a user-supplied buffer instead of allocating
|
||||||
|
// a new one.
|
||||||
|
func EncodeInto(buf *[]byte, val interface{}, opts Options) error {
|
||||||
stk := newStack()
|
stk := newStack()
|
||||||
efv := rt.UnpackEface(val)
|
efv := rt.UnpackEface(val)
|
||||||
err := encodeTypedPointer(buf, efv.Type, &efv.Value, stk)
|
err := encodeTypedPointer(buf, efv.Type, &efv.Value, stk, uint64(opts))
|
||||||
|
|
||||||
/* return the stack into pool */
|
/* return the stack into pool */
|
||||||
freeStack(stk)
|
freeStack(stk)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func EncodeIndented(val interface{}, prefix string, indent string) ([]byte, error) {
|
// EncodeIndented is like Encode but applies Indent to format the output.
|
||||||
|
// Each JSON element in the output will begin on a new line beginning with prefix
|
||||||
|
// followed by one or more copies of indent according to the indentation nesting.
|
||||||
|
func EncodeIndented(val interface{}, prefix string, indent string, opts Options) ([]byte, error) {
|
||||||
var err error
|
var err error
|
||||||
var out []byte
|
var out []byte
|
||||||
var buf *bytes.Buffer
|
var buf *bytes.Buffer
|
||||||
|
|
||||||
/* encode into the buffer */
|
/* encode into the buffer */
|
||||||
out = newBytes()
|
out = newBytes()
|
||||||
err = EncodeInto(&out, val)
|
err = EncodeInto(&out, val, opts)
|
||||||
|
|
||||||
/* check for errors */
|
/* check for errors */
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -107,6 +144,8 @@ func EncodeIndented(val interface{}, prefix string, indent string) ([]byte, erro
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
|
||||||
|
// order to reduce the first-hit latency.
|
||||||
func Pretouch(vt reflect.Type) (err error) {
|
func Pretouch(vt reflect.Type) (err error) {
|
||||||
_, err = findOrCompile(rt.UnpackType(vt))
|
_, err = findOrCompile(rt.UnpackType(vt))
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,11 @@ import (
|
||||||
|
|
||||||
gojson `github.com/goccy/go-json`
|
gojson `github.com/goccy/go-json`
|
||||||
`github.com/json-iterator/go`
|
`github.com/json-iterator/go`
|
||||||
`github.com/stretchr/testify/assert`
|
`github.com/stretchr/testify/require`
|
||||||
)
|
)
|
||||||
|
|
||||||
func runEncoderTest(t *testing.T, fn func(string)string, exp string, arg string) {
|
func runEncoderTest(t *testing.T, fn func(string)string, exp string, arg string) {
|
||||||
assert.Equal(t, exp, fn(arg))
|
require.Equal(t, exp, fn(arg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncoder_String(t *testing.T) {
|
func TestEncoder_String(t *testing.T) {
|
||||||
|
|
@ -51,8 +51,8 @@ type StringStruct struct {
|
||||||
func TestEncoder_FieldStringize(t *testing.T) {
|
func TestEncoder_FieldStringize(t *testing.T) {
|
||||||
x := 12345
|
x := 12345
|
||||||
v := StringStruct{X: &x, Y: []int{1, 2, 3}, Z: "4567456", W: "asdf"}
|
v := StringStruct{X: &x, Y: []int{1, 2, 3}, Z: "4567456", W: "asdf"}
|
||||||
r, e := Encode(v)
|
r, e := Encode(v, 0)
|
||||||
assert.Nil(t, e)
|
require.NoError(t, e)
|
||||||
println(string(r))
|
println(string(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,12 +70,12 @@ type MarshalerStruct struct {
|
||||||
|
|
||||||
func TestEncoder_Marshaler(t *testing.T) {
|
func TestEncoder_Marshaler(t *testing.T) {
|
||||||
v := MarshalerStruct{V: MarshalerImpl{X: 12345}}
|
v := MarshalerStruct{V: MarshalerImpl{X: 12345}}
|
||||||
ret, err := Encode(&v)
|
ret, err := Encode(&v, 0)
|
||||||
assert.Nil(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, `{"V":12345}`, string(ret))
|
require.Equal(t, `{"V":12345}`, string(ret))
|
||||||
ret, err = Encode(v)
|
ret, err = Encode(v, 0)
|
||||||
assert.Nil(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, `{"V":{"X":12345}}`, string(ret))
|
require.Equal(t, `{"V":{"X":12345}}`, string(ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
type RawMessageStruct struct {
|
type RawMessageStruct struct {
|
||||||
|
|
@ -86,9 +86,9 @@ func TestEncoder_RawMessage(t *testing.T) {
|
||||||
rms := RawMessageStruct{
|
rms := RawMessageStruct{
|
||||||
X: json.RawMessage("123456"),
|
X: json.RawMessage("123456"),
|
||||||
}
|
}
|
||||||
ret, err := Encode(&rms)
|
ret, err := Encode(&rms, 0)
|
||||||
assert.Nil(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, `{"X":123456}`, string(ret))
|
require.Equal(t, `{"X":123456}`, string(ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
var _GenericValue interface{}
|
var _GenericValue interface{}
|
||||||
|
|
@ -100,23 +100,46 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncoder_Generic(t *testing.T) {
|
func TestEncoder_Generic(t *testing.T) {
|
||||||
v, e := Encode(_GenericValue)
|
v, e := Encode(_GenericValue, 0)
|
||||||
assert.Nil(t, e)
|
require.NoError(t, e)
|
||||||
println(string(v))
|
println(string(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncoder_Binding(t *testing.T) {
|
func TestEncoder_Binding(t *testing.T) {
|
||||||
v, e := Encode(_BindingValue)
|
v, e := Encode(_BindingValue, 0)
|
||||||
assert.Nil(t, e)
|
require.NoError(t, e)
|
||||||
println(string(v))
|
println(string(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEncoder_MapSortKey(t *testing.T) {
|
||||||
|
m := map[string]string {
|
||||||
|
"C": "third",
|
||||||
|
"D": "forth",
|
||||||
|
"A": "first",
|
||||||
|
"F": "sixth",
|
||||||
|
"E": "fifth",
|
||||||
|
"B": "second",
|
||||||
|
}
|
||||||
|
v, e := Encode(m, SortMapKeys)
|
||||||
|
require.NoError(t, e)
|
||||||
|
require.Equal(t, `{"A":"first","B":"second","C":"third","D":"forth","E":"fifth","F":"sixth"}`, string(v))
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkEncoder_Generic_Sonic(b *testing.B) {
|
func BenchmarkEncoder_Generic_Sonic(b *testing.B) {
|
||||||
_, _ = Encode(_GenericValue)
|
_, _ = Encode(_GenericValue, 0)
|
||||||
b.SetBytes(int64(len(TwitterJson)))
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = Encode(_GenericValue)
|
_, _ = Encode(_GenericValue, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncoder_Generic_SonicSorted(b *testing.B) {
|
||||||
|
_, _ = Encode(_GenericValue, SortMapKeys)
|
||||||
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = Encode(_GenericValue, SortMapKeys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,11 +171,20 @@ func BenchmarkEncoder_Generic_StdLib(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkEncoder_Binding_Sonic(b *testing.B) {
|
func BenchmarkEncoder_Binding_Sonic(b *testing.B) {
|
||||||
_, _ = Encode(&_BindingValue)
|
_, _ = Encode(&_BindingValue, 0)
|
||||||
b.SetBytes(int64(len(TwitterJson)))
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = Encode(&_BindingValue)
|
_, _ = Encode(&_BindingValue, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncoder_Binding_SonicSorted(b *testing.B) {
|
||||||
|
_, _ = Encode(&_BindingValue, SortMapKeys)
|
||||||
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = Encode(&_BindingValue, SortMapKeys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,12 +216,23 @@ func BenchmarkEncoder_Binding_StdLib(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkEncoder_Parallel_Generic_Sonic(b *testing.B) {
|
func BenchmarkEncoder_Parallel_Generic_Sonic(b *testing.B) {
|
||||||
_, _ = Encode(_GenericValue)
|
_, _ = Encode(_GenericValue, 0)
|
||||||
b.SetBytes(int64(len(TwitterJson)))
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
_, _ = Encode(_GenericValue)
|
_, _ = Encode(_GenericValue, 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncoder_Parallel_Generic_SonicSorted(b *testing.B) {
|
||||||
|
_, _ = Encode(_GenericValue, SortMapKeys)
|
||||||
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, _ = Encode(_GenericValue, SortMapKeys)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -228,12 +271,23 @@ func BenchmarkEncoder_Parallel_Generic_StdLib(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkEncoder_Parallel_Binding_Sonic(b *testing.B) {
|
func BenchmarkEncoder_Parallel_Binding_Sonic(b *testing.B) {
|
||||||
_, _ = Encode(&_BindingValue)
|
_, _ = Encode(&_BindingValue, 0)
|
||||||
b.SetBytes(int64(len(TwitterJson)))
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
_, _ = Encode(&_BindingValue)
|
_, _ = Encode(&_BindingValue, 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncoder_Parallel_Binding_SonicSorted(b *testing.B) {
|
||||||
|
_, _ = Encode(&_BindingValue, SortMapKeys)
|
||||||
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, _ = Encode(&_BindingValue, SortMapKeys)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
189
encoder/mapiter.go
Normal file
189
encoder/mapiter.go
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding`
|
||||||
|
`reflect`
|
||||||
|
`sync`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
type _MapPair struct {
|
||||||
|
k string
|
||||||
|
v unsafe.Pointer
|
||||||
|
m [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type _MapIterator struct {
|
||||||
|
it rt.GoMapIterator // must be the first field
|
||||||
|
kv rt.GoSlice // slice of _MapPair
|
||||||
|
ki int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
iteratorPool = sync.Pool{}
|
||||||
|
iteratorPair = rt.UnpackType(reflect.TypeOf(_MapPair{}))
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if unsafe.Offsetof(_MapIterator{}.it) != 0 {
|
||||||
|
panic("_MapIterator.it is not the first field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func asText(v unsafe.Pointer) (string, error) {
|
||||||
|
text := assertI2I(_T_encoding_TextMarshaler, *(*rt.GoIface)(v))
|
||||||
|
r, e := (*(*encoding.TextMarshaler)(unsafe.Pointer(&text))).MarshalText()
|
||||||
|
return rt.Mem2Str(r), e
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIterator() *_MapIterator {
|
||||||
|
if v := iteratorPool.Get(); v == nil {
|
||||||
|
return new(_MapIterator)
|
||||||
|
} else {
|
||||||
|
return resetIterator(v.(*_MapIterator))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetIterator(p *_MapIterator) *_MapIterator {
|
||||||
|
p.ki = 0
|
||||||
|
p.it = rt.GoMapIterator{}
|
||||||
|
p.kv.Len = 0
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) at(i int) *_MapPair {
|
||||||
|
return (*_MapPair)(unsafe.Pointer(uintptr(self.kv.Ptr) + uintptr(i) * unsafe.Sizeof(_MapPair{})))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) add() (p *_MapPair) {
|
||||||
|
p = self.at(self.kv.Len)
|
||||||
|
self.kv.Len++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) data() (p []_MapPair) {
|
||||||
|
*(*rt.GoSlice)(unsafe.Pointer(&p)) = self.kv
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) append(t *rt.GoType, k unsafe.Pointer, v unsafe.Pointer) (err error) {
|
||||||
|
p := self.add()
|
||||||
|
p.v = v
|
||||||
|
|
||||||
|
/* check for strings */
|
||||||
|
if tk := t.Kind(); tk != reflect.String {
|
||||||
|
return self.appendGeneric(p, t, tk, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fast path for strings */
|
||||||
|
p.k = *(*string)(k)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) appendGeneric(p *_MapPair, t *rt.GoType, v reflect.Kind, k unsafe.Pointer) error {
|
||||||
|
switch v {
|
||||||
|
case reflect.Int : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int)(k)))]) ; return nil
|
||||||
|
case reflect.Int8 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int8)(k)))]) ; return nil
|
||||||
|
case reflect.Int16 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int16)(k)))]) ; return nil
|
||||||
|
case reflect.Int32 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int32)(k)))]) ; return nil
|
||||||
|
case reflect.Int64 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], *(*int64)(k))]) ; return nil
|
||||||
|
case reflect.Uint : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint)(k)))]) ; return nil
|
||||||
|
case reflect.Uint8 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint8)(k)))]) ; return nil
|
||||||
|
case reflect.Uint16 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint16)(k)))]) ; return nil
|
||||||
|
case reflect.Uint32 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint32)(k)))]) ; return nil
|
||||||
|
case reflect.Uint64 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], *(*uint64)(k))]) ; return nil
|
||||||
|
case reflect.Uintptr : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uintptr)(k)))]) ; return nil
|
||||||
|
case reflect.Interface : return self.appendInterface(p, t, k)
|
||||||
|
default : panic("unexpected map key type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) appendInterface(p *_MapPair, t *rt.GoType, k unsafe.Pointer) (err error) {
|
||||||
|
if len(rt.IfaceType(t).Methods) == 0 {
|
||||||
|
panic("unexpected map key type")
|
||||||
|
} else if p.k, err = asText(k); err == nil {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iteratorStop(p *_MapIterator) {
|
||||||
|
iteratorPool.Put(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func iteratorNext(p *_MapIterator) {
|
||||||
|
i := p.ki
|
||||||
|
t := &p.it
|
||||||
|
|
||||||
|
/* check for unordered iteration */
|
||||||
|
if i < 0 {
|
||||||
|
mapiternext(t)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for end of iteration */
|
||||||
|
if p.ki >= p.kv.Len {
|
||||||
|
t.K = nil
|
||||||
|
t.V = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the key-value pair, and increase the pointer */
|
||||||
|
t.K = unsafe.Pointer(&p.at(p.ki).k)
|
||||||
|
t.V = p.at(p.ki).v
|
||||||
|
p.ki++
|
||||||
|
}
|
||||||
|
|
||||||
|
func iteratorStart(t *rt.GoMapType, m *rt.GoMap, fv uint64) (*_MapIterator, error) {
|
||||||
|
it := newIterator()
|
||||||
|
mapiterinit(t, m, &it.it)
|
||||||
|
|
||||||
|
/* check for key-sorting
|
||||||
|
* empty map or map with only 1 item don't need sorting */
|
||||||
|
if m.Count <= 1 || (fv & uint64(SortMapKeys)) == 0 {
|
||||||
|
it.ki = -1
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pre-allocate space if needed */
|
||||||
|
if m.Count > it.kv.Cap {
|
||||||
|
it.kv = growslice(iteratorPair, it.kv, m.Count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dump all the key-value pairs */
|
||||||
|
for ; it.it.K != nil; mapiternext(&it.it) {
|
||||||
|
if err := it.append(t.Key, it.it.K, it.it.V); err != nil {
|
||||||
|
iteratorStop(it)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sort the keys */
|
||||||
|
it.ki = 1
|
||||||
|
radixQsort(it.data(), 0, maxDepth(it.kv.Len))
|
||||||
|
|
||||||
|
/* load the first pair into iterator */
|
||||||
|
it.it.V = it.at(0).v
|
||||||
|
it.it.K = unsafe.Pointer(&it.at(0).k)
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
@ -53,6 +53,7 @@ type _Encoder func(
|
||||||
rb *[]byte,
|
rb *[]byte,
|
||||||
vp unsafe.Pointer,
|
vp unsafe.Pointer,
|
||||||
sb *_Stack,
|
sb *_Stack,
|
||||||
|
fv uint64,
|
||||||
) error
|
) error
|
||||||
|
|
||||||
func newBytes() []byte {
|
func newBytes() []byte {
|
||||||
|
|
|
||||||
|
|
@ -66,15 +66,15 @@ func encodeString(buf *[]byte, val string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeTypedPointer(buf *[]byte, vt *rt.GoType, vp *unsafe.Pointer, sb *_Stack) error {
|
func encodeTypedPointer(buf *[]byte, vt *rt.GoType, vp *unsafe.Pointer, sb *_Stack, fv uint64) error {
|
||||||
if vt == nil {
|
if vt == nil {
|
||||||
return encodeNil(buf)
|
return encodeNil(buf)
|
||||||
} else if fn, err := findOrCompile(vt); err != nil {
|
} else if fn, err := findOrCompile(vt); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if (vt.KindFlags & rt.F_direct) == 0 {
|
} else if (vt.KindFlags & rt.F_direct) == 0 {
|
||||||
return fn(buf, *vp, sb)
|
return fn(buf, *vp, sb, fv)
|
||||||
} else {
|
} else {
|
||||||
return fn(buf, unsafe.Pointer(vp), sb)
|
return fn(buf, unsafe.Pointer(vp), sb, fv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
200
encoder/sort.go
Normal file
200
encoder/sort.go
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package encoder
|
||||||
|
|
||||||
|
// Algorithm 3-way Radix Quicksort, d means the radix.
|
||||||
|
// Reference: https://algs4.cs.princeton.edu/51radix/Quick3string.java.html
|
||||||
|
func radixQsort(kvs []_MapPair, d, maxDepth int) {
|
||||||
|
if maxDepth == 0 {
|
||||||
|
heapSort(kvs, 0, len(kvs))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
maxDepth--
|
||||||
|
for len(kvs) > 11 {
|
||||||
|
p := pivot(kvs, d)
|
||||||
|
lt, i, gt := 0, 0, len(kvs)
|
||||||
|
for i < gt {
|
||||||
|
c := byteAt(kvs[i].k, d)
|
||||||
|
if c < p {
|
||||||
|
swap(kvs, lt, i)
|
||||||
|
i++
|
||||||
|
lt++
|
||||||
|
} else if c > p {
|
||||||
|
gt--
|
||||||
|
swap(kvs, i, gt)
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// kvs[0:lt] < v = kvs[lt:gt] < kvs[gt:len(kvs)]
|
||||||
|
// Native:
|
||||||
|
// radixQsort(kvs[:lt], d, maxDepth)
|
||||||
|
// if p > -1 {
|
||||||
|
// radixQsort(kvs[lt:gt], d+1, maxDepth)
|
||||||
|
// }
|
||||||
|
// radixQsort(kvs[gt:], d, maxDepth)
|
||||||
|
// Optimize: make recursive calls only for the smaller parts.
|
||||||
|
// Reference: https://www.geeksforgeeks.org/quicksort-tail-call-optimization-reducing-worst-case-space-log-n/
|
||||||
|
if p == -1 {
|
||||||
|
if lt > len(kvs) - gt {
|
||||||
|
radixQsort(kvs[gt:], d, maxDepth)
|
||||||
|
kvs = kvs[:lt]
|
||||||
|
} else {
|
||||||
|
radixQsort(kvs[:lt], d, maxDepth)
|
||||||
|
kvs = kvs[gt:]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ml := maxThree(lt, gt-lt, len(kvs)-gt)
|
||||||
|
if ml == lt {
|
||||||
|
radixQsort(kvs[lt:gt], d+1, maxDepth)
|
||||||
|
radixQsort(kvs[gt:], d, maxDepth)
|
||||||
|
kvs = kvs[:lt]
|
||||||
|
} else if ml == gt-lt {
|
||||||
|
radixQsort(kvs[:lt], d, maxDepth)
|
||||||
|
radixQsort(kvs[gt:], d, maxDepth)
|
||||||
|
kvs = kvs[lt:gt]
|
||||||
|
d += 1
|
||||||
|
} else {
|
||||||
|
radixQsort(kvs[:lt], d, maxDepth)
|
||||||
|
radixQsort(kvs[lt:gt], d+1, maxDepth)
|
||||||
|
kvs = kvs[gt:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insertRadixSort(kvs, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertRadixSort(kvs []_MapPair, d int) {
|
||||||
|
for i := 1; i < len(kvs); i++ {
|
||||||
|
for j := i; j > 0 && lessFrom(kvs[j].k, kvs[j-1].k, d); j-- {
|
||||||
|
kvs[j], kvs[j-1] = kvs[j-1], kvs[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pivot(kvs []_MapPair, d int) int {
|
||||||
|
m := len(kvs) >> 1
|
||||||
|
if len(kvs) > 40 {
|
||||||
|
// Tukey's ``Ninther,'' median of three mediankvs of three.
|
||||||
|
t := len(kvs) / 8
|
||||||
|
return medianThree(
|
||||||
|
medianThree(byteAt(kvs[0].k, d), byteAt(kvs[t].k, d), byteAt(kvs[2*t].k, d)),
|
||||||
|
medianThree(byteAt(kvs[m].k, d), byteAt(kvs[m-t].k, d), byteAt(kvs[m+t].k, d)),
|
||||||
|
medianThree(byteAt(kvs[len(kvs)-1].k, d),
|
||||||
|
byteAt(kvs[len(kvs)-1-t].k, d),
|
||||||
|
byteAt(kvs[len(kvs)-1-2*t].k, d)))
|
||||||
|
}
|
||||||
|
return medianThree(byteAt(kvs[0].k, d), byteAt(kvs[m].k, d), byteAt(kvs[len(kvs)-1].k, d))
|
||||||
|
}
|
||||||
|
|
||||||
|
func medianThree(i, j, k int) int {
|
||||||
|
if i > j {
|
||||||
|
i, j = j, i
|
||||||
|
} // i < j
|
||||||
|
if k < i {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
if k > j {
|
||||||
|
return j
|
||||||
|
}
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxThree(i, j, k int) int {
|
||||||
|
max := i
|
||||||
|
if max < j {
|
||||||
|
max = j
|
||||||
|
}
|
||||||
|
if max < k {
|
||||||
|
max = k
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
// maxDepth returns a threshold at which quicksort should switch
|
||||||
|
// to heapsort. It returnkvs 2*ceil(lg(n+1)).
|
||||||
|
func maxDepth(n int) int {
|
||||||
|
var depth int
|
||||||
|
for i := n; i > 0; i >>= 1 {
|
||||||
|
depth++
|
||||||
|
}
|
||||||
|
return depth * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// siftDown implements the heap property on kvs[lo:hi].
|
||||||
|
// first is an offset into the array where the root of the heap lies.
|
||||||
|
func siftDown(kvs []_MapPair, lo, hi, first int) {
|
||||||
|
root := lo
|
||||||
|
for {
|
||||||
|
child := 2*root + 1
|
||||||
|
if child >= hi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if child+1 < hi && kvs[first+child].k < kvs[first+child+1].k {
|
||||||
|
child++
|
||||||
|
}
|
||||||
|
if kvs[first+root].k >= kvs[first+child].k {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
swap(kvs, first+root, first+child)
|
||||||
|
root = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func heapSort(kvs []_MapPair, a, b int) {
|
||||||
|
first := a
|
||||||
|
lo := 0
|
||||||
|
hi := b - a
|
||||||
|
|
||||||
|
// Build heap with the greatest element at top.
|
||||||
|
for i := (hi - 1) / 2; i >= 0; i-- {
|
||||||
|
siftDown(kvs, i, hi, first)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop elements, the largest first, into end of kvs.
|
||||||
|
for i := hi - 1; i >= 0; i-- {
|
||||||
|
swap(kvs, first, first+i)
|
||||||
|
siftDown(kvs, lo, i, first)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func swap(kvs []_MapPair, a, b int) {
|
||||||
|
kvs[a], kvs[b] = kvs[b], kvs[a]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare two strings from the pos d.
|
||||||
|
func lessFrom(a, b string, d int) bool {
|
||||||
|
l := len(a)
|
||||||
|
if l > len(b) {
|
||||||
|
l = len(b)
|
||||||
|
}
|
||||||
|
for i := d; i < l; i++ {
|
||||||
|
if a[i] == b[i] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return a[i] < b[i]
|
||||||
|
}
|
||||||
|
return len(a) < len(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func byteAt(b string, p int) int {
|
||||||
|
if p < len(b) {
|
||||||
|
return int(b[p])
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
168
encoder/sort_test.go
Normal file
168
encoder/sort_test.go
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var keyLen = 15
|
||||||
|
|
||||||
|
type encodedKeyValues []encodedKV
|
||||||
|
type encodedKV struct {
|
||||||
|
key string
|
||||||
|
_MapPair []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sv encodedKeyValues) Len() int { return len(sv) }
|
||||||
|
func (sv encodedKeyValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
|
||||||
|
func (sv encodedKeyValues) Less(i, j int) bool { return sv[i].key < sv[j].key }
|
||||||
|
|
||||||
|
func getKvs(std bool) interface{} {
|
||||||
|
var map_size = 1000
|
||||||
|
if std {
|
||||||
|
kvs := make(encodedKeyValues, map_size)
|
||||||
|
for i:=map_size-1; i>=0; i-- {
|
||||||
|
kvs[i] = encodedKV{
|
||||||
|
key: "\"test_" + strconv.Itoa(i) + "\"",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kvs
|
||||||
|
} else {
|
||||||
|
kvs := make([]_MapPair, map_size)
|
||||||
|
for i:=map_size-1; i>=0; i-- {
|
||||||
|
kvs[i] = _MapPair{
|
||||||
|
k: "\"test_" + strconv.Itoa(i) + "\"",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kvs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSort_Sonic(b *testing.B) {
|
||||||
|
ori := getKvs(false).([]_MapPair)
|
||||||
|
kvs := make([]_MapPair, len(ori))
|
||||||
|
b.ResetTimer()
|
||||||
|
for i:=0; i<b.N; i++ {
|
||||||
|
copy(kvs, ori)
|
||||||
|
radixQsort(kvs, 0, maxDepth(len(kvs)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSort_Std(b *testing.B) {
|
||||||
|
ori := getKvs(true).(encodedKeyValues)
|
||||||
|
kvs := make(encodedKeyValues, len(ori))
|
||||||
|
b.ResetTimer()
|
||||||
|
for i:=0; i<b.N; i++ {
|
||||||
|
copy(kvs, ori)
|
||||||
|
sort.Sort(kvs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSort_Parallel_Sonic(b *testing.B) {
|
||||||
|
ori := getKvs(false).([]_MapPair)
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(p *testing.PB) {
|
||||||
|
kvs := make([]_MapPair, len(ori))
|
||||||
|
for p.Next() {
|
||||||
|
copy(kvs, ori)
|
||||||
|
radixQsort(kvs, 0, maxDepth(len(kvs)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSort_Parallel_Std(b *testing.B) {
|
||||||
|
ori := getKvs(true).(encodedKeyValues)
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(p *testing.PB) {
|
||||||
|
kvs := make(encodedKeyValues, len(ori))
|
||||||
|
for p.Next() {
|
||||||
|
copy(kvs, ori)
|
||||||
|
sort.Sort(kvs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type kvSlice []_MapPair
|
||||||
|
|
||||||
|
// Make kvSlice meet sort.Interface.
|
||||||
|
func (self kvSlice) Less(i, j int) bool { return self[i].k < self[j].k }
|
||||||
|
func (self kvSlice) Swap(i, j int) { self[i], self[j] = self[j], self[i] }
|
||||||
|
func (self kvSlice) Len() int { return len(self) }
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func (self kvSlice) Sort() {
|
||||||
|
radixQsort(self, 0, maxDepth(len(self)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self kvSlice) String() string {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
for i, kv := range self {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
buf.WriteString(kv.k)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSort_SortRandomKeys(t *testing.T) {
|
||||||
|
kvs := getRandKvs(100, keyLen)
|
||||||
|
sorted := make([]_MapPair, len(kvs))
|
||||||
|
|
||||||
|
copy(sorted, kvs)
|
||||||
|
sort.Sort(kvSlice(sorted))
|
||||||
|
kvs.Sort()
|
||||||
|
|
||||||
|
got := kvs.String()
|
||||||
|
want := kvSlice(sorted).String()
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf(" got: %v\nwant: %v\n", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genKey(kl int) []byte {
|
||||||
|
l := int(rand.Uint32()%uint32(kl) + 2)
|
||||||
|
k := make([]byte, l)
|
||||||
|
k[0], k[l-1] = '"', '"'
|
||||||
|
for i := 1; i < l-1; i++ {
|
||||||
|
k[i] = byte('a' + int(rand.Uint32()%26))
|
||||||
|
}
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRandKvs(kn int, kl int) kvSlice {
|
||||||
|
keys := make(map[string]bool)
|
||||||
|
kvs := make(kvSlice, 0)
|
||||||
|
for len(keys) < kn {
|
||||||
|
k := genKey(kl)
|
||||||
|
keys[string(k)] = true
|
||||||
|
}
|
||||||
|
for k := range keys {
|
||||||
|
var kv _MapPair
|
||||||
|
kv.k = k
|
||||||
|
kv.v = unsafe.Pointer(&k)
|
||||||
|
kvs = append(kvs, kv)
|
||||||
|
}
|
||||||
|
return kvs
|
||||||
|
}
|
||||||
|
|
@ -32,10 +32,6 @@ var _subr__b64encode uintptr
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
//go:linkname newobject runtime.newobject
|
|
||||||
//goland:noinspection GoUnusedParameter
|
|
||||||
func newobject(typ *rt.GoType) unsafe.Pointer
|
|
||||||
|
|
||||||
//go:linkname growslice runtime.growslice
|
//go:linkname growslice runtime.growslice
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
@ -46,17 +42,12 @@ func assertI2I(inter *rt.GoType, i rt.GoIface) rt.GoIface
|
||||||
|
|
||||||
//go:linkname mapiternext runtime.mapiternext
|
//go:linkname mapiternext runtime.mapiternext
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func mapiternext(it unsafe.Pointer)
|
func mapiternext(it *rt.GoMapIterator)
|
||||||
|
|
||||||
//go:linkname mapiterinit runtime.mapiterinit
|
//go:linkname mapiterinit runtime.mapiterinit
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func mapiterinit(t *rt.GoType, m unsafe.Pointer, it *rt.GoMapIterator)
|
func mapiterinit(t *rt.GoMapType, m *rt.GoMap, it *rt.GoMapIterator)
|
||||||
|
|
||||||
//go:linkname isValidNumber encoding/json.isValidNumber
|
//go:linkname isValidNumber encoding/json.isValidNumber
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func isValidNumber(s string) bool
|
func isValidNumber(s string) bool
|
||||||
|
|
||||||
//go:noescape
|
|
||||||
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
|
|
||||||
//goland:noinspection GoUnusedParameter
|
|
||||||
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
|
||||||
|
|
|
||||||
|
|
@ -20,15 +20,11 @@ import (
|
||||||
`encoding`
|
`encoding`
|
||||||
`encoding/json`
|
`encoding/json`
|
||||||
`reflect`
|
`reflect`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
byteType = reflect.TypeOf(byte(0))
|
byteType = reflect.TypeOf(byte(0))
|
||||||
jsonNumberType = reflect.TypeOf(json.Number(""))
|
jsonNumberType = reflect.TypeOf(json.Number(""))
|
||||||
mapIteratorType = reflect.TypeOf(rt.GoMapIterator{})
|
|
||||||
mapPIteratorType = reflect.TypeOf(new(rt.GoMapIterator))
|
|
||||||
jsonUnsupportedValueType = reflect.TypeOf(new(json.UnsupportedValueError))
|
jsonUnsupportedValueType = reflect.TypeOf(new(json.UnsupportedValueError))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,16 @@ func Value(s unsafe.Pointer, n int, p int, v *types.JsonState, allow_control int
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func SkipOne(s *string, p *int, m *types.StateMachine) int
|
func SkipOne(s *string, p *int, m *types.StateMachine) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func I64toa(out *byte, val int64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func U64toa(out *byte, val uint64) (ret int)
|
||||||
|
|
||||||
func useAVX() {
|
func useAVX() {
|
||||||
S_f64toa = avx.S_f64toa
|
S_f64toa = avx.S_f64toa
|
||||||
S_i64toa = avx.S_i64toa
|
S_i64toa = avx.S_i64toa
|
||||||
|
|
|
||||||
|
|
@ -47,3 +47,15 @@ TEXT ·SkipOne(SB), NOSPLIT, $0 - 32
|
||||||
JE 2(PC)
|
JE 2(PC)
|
||||||
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__skip_one(SB)
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__skip_one(SB)
|
||||||
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__skip_one(SB)
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__skip_one(SB)
|
||||||
|
|
||||||
|
TEXT ·I64toa(SB), NOSPLIT, $0 - 32
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__i64toa(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__i64toa(SB)
|
||||||
|
|
||||||
|
TEXT ·U64toa(SB), NOSPLIT, $0 - 32
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__u64toa(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__u64toa(SB)
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,8 @@ type GoMap struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GoMapIterator struct {
|
type GoMapIterator struct {
|
||||||
Key unsafe.Pointer
|
K unsafe.Pointer
|
||||||
Elem unsafe.Pointer
|
V unsafe.Pointer
|
||||||
T *GoMapType
|
T *GoMapType
|
||||||
H *GoMap
|
H *GoMap
|
||||||
Buckets unsafe.Pointer
|
Buckets unsafe.Pointer
|
||||||
|
|
@ -144,6 +144,17 @@ type GoStructField struct {
|
||||||
OffEmbed uintptr
|
OffEmbed uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GoInterfaceType struct {
|
||||||
|
GoType
|
||||||
|
PkgPath *byte
|
||||||
|
Methods []GoInterfaceMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoInterfaceMethod struct {
|
||||||
|
Name int32
|
||||||
|
Type int32
|
||||||
|
}
|
||||||
|
|
||||||
type GoSlice struct {
|
type GoSlice struct {
|
||||||
Ptr unsafe.Pointer
|
Ptr unsafe.Pointer
|
||||||
Len int
|
Len int
|
||||||
|
|
@ -156,19 +167,15 @@ type GoString struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func PtrElem(t *GoType) *GoType {
|
func PtrElem(t *GoType) *GoType {
|
||||||
if t.Kind() != reflect.Ptr {
|
return (*GoPtrType)(unsafe.Pointer(t)).Elem
|
||||||
panic("not a pointer: " + t.String())
|
|
||||||
} else {
|
|
||||||
return (*GoPtrType)(unsafe.Pointer(t)).Elem
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapType(t *GoType) *GoMapType {
|
func MapType(t *GoType) *GoMapType {
|
||||||
if t.Kind() != reflect.Map {
|
return (*GoMapType)(unsafe.Pointer(t))
|
||||||
panic("not a map: " + t.String())
|
}
|
||||||
} else {
|
|
||||||
return (*GoMapType)(unsafe.Pointer(t))
|
func IfaceType(t *GoType) *GoInterfaceType {
|
||||||
}
|
return (*GoInterfaceType)(unsafe.Pointer(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnpackType(t reflect.Type) *GoType {
|
func UnpackType(t reflect.Type) *GoType {
|
||||||
|
|
|
||||||
2
sonic.go
2
sonic.go
|
|
@ -27,7 +27,7 @@ import (
|
||||||
|
|
||||||
// Marshal returns the JSON encoding of v.
|
// Marshal returns the JSON encoding of v.
|
||||||
func Marshal(val interface{}) ([]byte, error) {
|
func Marshal(val interface{}) ([]byte, error) {
|
||||||
return encoder.Encode(val)
|
return encoder.Encode(val, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal parses the JSON-encoded data and stores the result in the value
|
// Unmarshal parses the JSON-encoded data and stores the result in the value
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue