2
0
Fork 0
mirror of https://github.com/ii64/sonic.git synced 2026-06-21 08:56:45 +08:00

feat: supports map key-sorting via encoder options

This commit is contained in:
chenzhuoyu 2021-08-16 18:09:37 +08:00 committed by Oxygen
parent dd73e36cf4
commit 8383178c89
17 changed files with 811 additions and 139 deletions

View file

@ -77,7 +77,7 @@ func TestOmitEmpty(t *testing.T) {
o.Mr = map[string]interface{}{}
o.Mo = map[string]interface{}{}
got, err := encoder.EncodeIndented(&o, "", " ")
got, err := encoder.EncodeIndented(&o, "", " ", 0)
if err != nil {
t.Fatal(err)
}
@ -137,7 +137,7 @@ func TestRoundtripStringTag(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
// Indent with a tab prefix to make the multi-line string
// 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 {
t.Fatal(err)
}

View file

@ -20,7 +20,6 @@ import (
`fmt`
`reflect`
`strconv`
`sync`
`unsafe`
`github.com/bytedance/sonic/internal/jit`
@ -53,13 +52,14 @@ import (
/** 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)
* p : 8(FP)
* sb : 16(FP)
* err.vt : 24(FP)
* err.vp : 32(FP)
* fv : 24(FP)
* err.vt : 32(FP)
* err.vp : 40(FP)
*/
const (
@ -68,7 +68,7 @@ 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_saves = 64 // 64 bytes for saving the registers before CALL instructions
_FP_locals = 16 // 16 bytes for local variables
@ -145,11 +145,12 @@ var (
_ARG_rb = jit.Ptr(_SP, _FP_base)
_ARG_vp = jit.Ptr(_SP, _FP_base + 8)
_ARG_sb = jit.Ptr(_SP, _FP_base + 16)
_ARG_fv = jit.Ptr(_SP, _FP_base + 24)
)
var (
_RET_et = jit.Ptr(_SP, _FP_base + 24)
_RET_ep = jit.Ptr(_SP, _FP_base + 32)
_RET_et = jit.Ptr(_SP, _FP_base + 32)
_RET_ep = jit.Ptr(_SP, _FP_base + 40)
)
var (
@ -234,7 +235,9 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
_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,
_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_slice_len : (*_Assembler)._asm_OP_slice_len,
_OP_slice_next : (*_Assembler)._asm_OP_slice_next,
@ -770,18 +773,10 @@ func (self *_Assembler) check_zero(nb int, dest int) {
/** OpCode Assembler Functions **/
var (
_T_map_Iterator = rt.UnpackType(mapIteratorType)
_T_map_PIterator = rt.UnpackType(mapPIteratorType)
_T_json_Marshaler = rt.UnpackType(jsonMarshalerType)
_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 (
_F_f64toa = jit.Imm(int64(native.S_f64toa))
_F_i64toa = jit.Imm(int64(native.S_i64toa))
@ -790,19 +785,16 @@ var (
)
var (
_F_memmove = jit.Func(memmove)
_F_newobject = jit.Func(newobject)
_F_isZeroTyped = jit.Func(isZeroTyped)
_F_mapiternext = jit.Func(mapiternext)
_F_mapiterinit = jit.Func(mapiterinit)
_F_error_number = jit.Func(error_number)
_F_isValidNumber = jit.Func(isValidNumber)
_F_memclrNoHeapPointers = jit.Func(memclrNoHeapPointers)
_F_memmove = jit.Func(memmove)
_F_isZeroTyped = jit.Func(isZeroTyped)
_F_error_number = jit.Func(error_number)
_F_isValidNumber = jit.Func(isValidNumber)
)
var (
_F_sync_Pool_Get = jit.Func((*sync.Pool).Get)
_F_sync_Pool_Put = jit.Func((*sync.Pool).Put)
_F_iteratorStop = jit.Func(iteratorStop)
_F_iteratorNext = jit.Func(iteratorNext)
_F_iteratorStart = jit.Func(iteratorStart)
)
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("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" , _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.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET) // MOVQ 32(SP), ET
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP) // MOVQ 40(SP), EP
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _ET) // MOVQ 40(SP), ET
self.Emit("MOVQ" , jit.Ptr(_SP, 48), _EP) // MOVQ 48(SP), EP
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
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("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" , _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.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET) // MOVQ 32(SP), ET
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP) // MOVQ 40(SP), EP
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _ET) // MOVQ 40(SP), ET
self.Emit("MOVQ" , jit.Ptr(_SP, 48), _EP) // MOVQ 48(SP), EP
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
self.Sjmp("JNZ" , _LB_error) // JNZ _error
}
@ -1036,9 +1032,11 @@ func (self *_Assembler) _asm_OP_recurse(p *_Instr) {
/* call the encoder */
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" , _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.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET) // MOVQ 32(SP), ET
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP) // MOVQ 40(SP), EP
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _ET) // MOVQ 40(SP), ET
self.Emit("MOVQ" , jit.Ptr(_SP, 48), _EP) // MOVQ 48(SP), EP
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
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) {
self.Emit("MOVQ" , _V_iteratorPool, _AX) // MOVQ $&iteratorPool, AX
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
self.call_go(_F_sync_Pool_Get) // CALL_GO (*sync.Pool).Get
self.Emit("MOVQ" , jit.Ptr(_SP, 16), _SP_q) // MOVQ 16(SP), SP.q
self.Emit("TESTQ", _SP_q, _SP_q) // TESTQ SP.q, SP.q
self.Sjmp("JZ" , "_new_iter_{n}") // JZ _new_iter_{n}
self.Emit("MOVL" , _N_iteratorPool, _AX) // MOVL ${size(GoMapIterator)}, AX
self.Emit("MOVQ" , _SP_q, jit.Ptr(_SP, 0)) // MOVQ SP.q, (SP)
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 8)) // MOVQ AX, 8(SP)
self.call_go(_F_memclrNoHeapPointers) // CALL_GO memclrNoHeapPointers
self.Sjmp("JMP" , "_init_iter_{n}") // JMP _init_iter_{n}
self.Link("_new_iter_{n}") // _new_iter_{n}:
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
self.Emit("MOVQ" , jit.Ptr(_SP, 8), _SP_q) // MOVQ 8(SP), SP.q
self.Link("_init_iter_{n}") // _init_iter_{n}:
self.Emit("MOVQ" , jit.Type(p.vt()), _AX) // MOVQ $p.vt(), AX
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
self.Emit("MOVQ" , jit.Type(p.vt()), _AX) // MOVQ $p.vt(), AX
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" , _ARG_fv, _AX) // MOVQ fv, AX
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 16)) // MOVQ AX, 16(SP)
self.call_go(_F_iteratorStart) // CALL_GO iteratorStart
self.Emit("MOVQ" , jit.Ptr(_SP, 24), _SP_q) // MOVQ 24(SP), SP.q
self.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET) // MOVQ 32(SP), ET
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP) // MOVQ 40(SP), EP
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
self.Sjmp("JNZ" , _LB_error) // JNZ _error
}
func (self *_Assembler) _asm_OP_map_stop(_ *_Instr) {
self.Emit("MOVQ", _SP_q, jit.Ptr(_SP, 0)) // MOVQ SP.q, 0(SP)
self.call_go(_F_iteratorStop) // CALL_GO iteratorStop
self.Emit("XORL", _SP_q, _SP_q) // XORL SP.q, SP.q
}
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("TESTQ", _SP_p, _SP_p) // TESTQ SP.p, SP.p
self.Sjmp("JNZ" , "_map_next_{n}") // JNZ _map_next_{n}
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)
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_sync_Pool_Put) // CALL_GO (*sync.Pool).Put
self.Emit("XORL" , _SP_q, _SP_q) // XORL SP.q, SP.q
self.Xjmp("JMP" , p.vi()) // JMP p.vi()
self.Link("_map_next_{n}") // _map_next_{n}:
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.Xjmp("JZ" , p.vi()) // JNZ p.vi()
}
func (self *_Assembler) _asm_OP_map_write_key(p *_Instr) {
self.Emit("BTQ", jit.Imm(bitSortMapKeys), _ARG_fv) // BTQ ${SortMapKeys}, fv
self.Sjmp("JNC", "_unordered_key_{n}") // JNC _unordered_key_{n}
self.encode_string(false) // STR $false
self.Xjmp("JMP", p.vi()) // JMP ${p.vi()}
self.Link("_unordered_key_{n}") // _unordered_key_{n}:
}
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", _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) {

View file

@ -41,7 +41,7 @@ func TestAssembler_CompileAndLoad(t *testing.T) {
/* true */
v := true
u := &v
e := f(&b, unsafe.Pointer(&u), s)
e := f(&b, unsafe.Pointer(&u), s, 0)
assert.Nil(t, e)
println(cap(b))
println(hex.Dump(b))
@ -50,7 +50,7 @@ func TestAssembler_CompileAndLoad(t *testing.T) {
v = false
u = &v
b = b[:0]
e = f(&b, unsafe.Pointer(&u), s)
e = f(&b, unsafe.Pointer(&u), s, 0)
assert.Nil(t, e)
println(cap(b))
println(hex.Dump(b))
@ -58,7 +58,7 @@ func TestAssembler_CompileAndLoad(t *testing.T) {
/* nil */
u = nil
b = b[:0]
e = f(&b, unsafe.Pointer(&u), s)
e = f(&b, unsafe.Pointer(&u), s, 0)
assert.Nil(t, e)
println(cap(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)
a := newAssembler(p)
f := a.Load()
e := f(&m, rt.UnpackEface(v).Value, s)
e := f(&m, rt.UnpackEface(v).Value, s, 0)
if err != nil {
assert.EqualError(t, e, err.Error())
} else {
@ -347,7 +347,7 @@ func TestAssembler_StringMoreSpace(t *testing.T) {
a := newAssembler(p)
f := a.Load()
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)
spew.Dump(m)
}
@ -359,7 +359,7 @@ func TestAssembler_TwitterJSON_Generic(t *testing.T) {
a := newAssembler(p)
f := a.Load()
v := &_GenericValue
e := f(&m, unsafe.Pointer(&v), s)
e := f(&m, unsafe.Pointer(&v), s, 0)
assert.Nil(t, e)
println(string(m))
}
@ -370,7 +370,7 @@ func TestAssembler_TwitterJSON_Structure(t *testing.T) {
s := new(_Stack)
a := newAssembler(p)
f := a.Load()
e := f(&m, unsafe.Pointer(&_BindingValue), s)
e := f(&m, unsafe.Pointer(&_BindingValue), s, 0)
assert.Nil(t, e)
println(string(m))
runtime.KeepAlive(s)

View file

@ -68,7 +68,9 @@ const (
_OP_is_zero_safe
_OP_goto
_OP_map_iter
_OP_map_stop
_OP_map_check_key
_OP_map_write_key
_OP_map_value_next
_OP_slice_len
_OP_slice_next
@ -129,7 +131,9 @@ var _OpNames = [256]string {
_OP_is_zero_safe : "is_zero_safe",
_OP_goto : "goto",
_OP_map_iter : "map_iter",
_OP_map_stop : "map_stop",
_OP_map_check_key : "map_check_key",
_OP_map_write_key : "map_write_key",
_OP_map_value_next : "map_value_next",
_OP_slice_len : "slice_len",
_OP_slice_next : "slice_next",
@ -254,6 +258,7 @@ func (self _Instr) isBranch() bool {
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
case _OP_cond_testc : return true
default : return false
@ -280,7 +285,8 @@ func (self _Instr) disassemble() string {
case _OP_is_zero_8 : fallthrough
case _OP_is_zero_map : 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_safe : fallthrough
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)
i := p.pc()
p.add(_OP_map_check_key)
u := p.pc()
p.add(_OP_map_write_key)
self.compileMapBodyKey(p, vt.Key())
p.pin(u)
p.int(_OP_byte, ':')
p.add(_OP_map_value_next)
self.compileOne(p, sp + 2, vt.Elem(), false)
j := p.pc()
p.add(_OP_map_check_key)
p.int(_OP_byte, ',')
v := p.pc()
p.add(_OP_map_write_key)
self.compileMapBodyKey(p, vt.Key())
p.pin(v)
p.int(_OP_byte, ':')
p.add(_OP_map_value_next)
self.compileOne(p, sp + 1, vt.Elem(), false)
p.int(_OP_goto, j)
p.pin(i)
p.pin(j)
p.add(_OP_map_stop)
p.add(_OP_drop_2)
p.int(_OP_byte, '}')
}

View file

@ -24,6 +24,37 @@ import (
`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 {
var n int
var p []byte
@ -42,9 +73,10 @@ func Quote(s string) string {
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()
err := EncodeInto(&buf, val)
err := EncodeInto(&buf, val, opts)
/* check for errors */
if err != nil {
@ -61,24 +93,29 @@ func Encode(val interface{}) ([]byte, error) {
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()
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 */
freeStack(stk)
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 out []byte
var buf *bytes.Buffer
/* encode into the buffer */
out = newBytes()
err = EncodeInto(&out, val)
err = EncodeInto(&out, val, opts)
/* check for errors */
if err != nil {
@ -107,6 +144,8 @@ func EncodeIndented(val interface{}, prefix string, indent string) ([]byte, erro
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) {
_, err = findOrCompile(rt.UnpackType(vt))
return

View file

@ -23,11 +23,11 @@ import (
gojson `github.com/goccy/go-json`
`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) {
assert.Equal(t, exp, fn(arg))
require.Equal(t, exp, fn(arg))
}
func TestEncoder_String(t *testing.T) {
@ -51,8 +51,8 @@ type StringStruct struct {
func TestEncoder_FieldStringize(t *testing.T) {
x := 12345
v := StringStruct{X: &x, Y: []int{1, 2, 3}, Z: "4567456", W: "asdf"}
r, e := Encode(v)
assert.Nil(t, e)
r, e := Encode(v, 0)
require.NoError(t, e)
println(string(r))
}
@ -70,12 +70,12 @@ type MarshalerStruct struct {
func TestEncoder_Marshaler(t *testing.T) {
v := MarshalerStruct{V: MarshalerImpl{X: 12345}}
ret, err := Encode(&v)
assert.Nil(t, err)
assert.Equal(t, `{"V":12345}`, string(ret))
ret, err = Encode(v)
assert.Nil(t, err)
assert.Equal(t, `{"V":{"X":12345}}`, string(ret))
ret, err := Encode(&v, 0)
require.NoError(t, err)
require.Equal(t, `{"V":12345}`, string(ret))
ret, err = Encode(v, 0)
require.NoError(t, err)
require.Equal(t, `{"V":{"X":12345}}`, string(ret))
}
type RawMessageStruct struct {
@ -86,9 +86,9 @@ func TestEncoder_RawMessage(t *testing.T) {
rms := RawMessageStruct{
X: json.RawMessage("123456"),
}
ret, err := Encode(&rms)
assert.Nil(t, err)
assert.Equal(t, `{"X":123456}`, string(ret))
ret, err := Encode(&rms, 0)
require.NoError(t, err)
require.Equal(t, `{"X":123456}`, string(ret))
}
var _GenericValue interface{}
@ -100,23 +100,46 @@ func init() {
}
func TestEncoder_Generic(t *testing.T) {
v, e := Encode(_GenericValue)
assert.Nil(t, e)
v, e := Encode(_GenericValue, 0)
require.NoError(t, e)
println(string(v))
}
func TestEncoder_Binding(t *testing.T) {
v, e := Encode(_BindingValue)
assert.Nil(t, e)
v, e := Encode(_BindingValue, 0)
require.NoError(t, e)
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) {
_, _ = Encode(_GenericValue)
_, _ = Encode(_GenericValue, 0)
b.SetBytes(int64(len(TwitterJson)))
b.ResetTimer()
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) {
_, _ = Encode(&_BindingValue)
_, _ = Encode(&_BindingValue, 0)
b.SetBytes(int64(len(TwitterJson)))
b.ResetTimer()
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) {
_, _ = Encode(_GenericValue)
_, _ = Encode(_GenericValue, 0)
b.SetBytes(int64(len(TwitterJson)))
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
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) {
_, _ = Encode(&_BindingValue)
_, _ = Encode(&_BindingValue, 0)
b.SetBytes(int64(len(TwitterJson)))
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
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
View 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
}

View file

@ -53,6 +53,7 @@ type _Encoder func(
rb *[]byte,
vp unsafe.Pointer,
sb *_Stack,
fv uint64,
) error
func newBytes() []byte {

View file

@ -66,15 +66,15 @@ func encodeString(buf *[]byte, val string) error {
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 {
return encodeNil(buf)
} else if fn, err := findOrCompile(vt); err != nil {
return err
} else if (vt.KindFlags & rt.F_direct) == 0 {
return fn(buf, *vp, sb)
return fn(buf, *vp, sb, fv)
} else {
return fn(buf, unsafe.Pointer(vp), sb)
return fn(buf, unsafe.Pointer(vp), sb, fv)
}
}

200
encoder/sort.go Normal file
View 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
View 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
}

View file

@ -32,10 +32,6 @@ var _subr__b64encode uintptr
//goland:noinspection GoUnusedParameter
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
//goland:noinspection GoUnusedParameter
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
//goland:noinspection GoUnusedParameter
func mapiternext(it unsafe.Pointer)
func mapiternext(it *rt.GoMapIterator)
//go:linkname mapiterinit runtime.mapiterinit
//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
//goland:noinspection GoUnusedParameter
func isValidNumber(s string) bool
//go:noescape
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
//goland:noinspection GoUnusedParameter
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)

View file

@ -20,15 +20,11 @@ import (
`encoding`
`encoding/json`
`reflect`
`github.com/bytedance/sonic/internal/rt`
)
var (
byteType = reflect.TypeOf(byte(0))
jsonNumberType = reflect.TypeOf(json.Number(""))
mapIteratorType = reflect.TypeOf(rt.GoMapIterator{})
mapPIteratorType = reflect.TypeOf(new(rt.GoMapIterator))
jsonUnsupportedValueType = reflect.TypeOf(new(json.UnsupportedValueError))
)

View file

@ -76,6 +76,16 @@ func Value(s unsafe.Pointer, n int, p int, v *types.JsonState, allow_control int
//goland:noinspection GoUnusedParameter
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() {
S_f64toa = avx.S_f64toa
S_i64toa = avx.S_i64toa

View file

@ -47,3 +47,15 @@ TEXT ·SkipOne(SB), NOSPLIT, $0 - 32
JE 2(PC)
JMP github·combytedancesonicinternalnativeavx2·__skip_one(SB)
JMP github·combytedancesonicinternalnativeavx·__skip_one(SB)
TEXT ·I64toa(SB), NOSPLIT, $0 - 32
CMPB github·combytedancesonicinternalcpu·HasAVX2(SB), $0
JE 2(PC)
JMP github·combytedancesonicinternalnativeavx2·__i64toa(SB)
JMP github·combytedancesonicinternalnativeavx·__i64toa(SB)
TEXT ·U64toa(SB), NOSPLIT, $0 - 32
CMPB github·combytedancesonicinternalcpu·HasAVX2(SB), $0
JE 2(PC)
JMP github·combytedancesonicinternalnativeavx2·__u64toa(SB)
JMP github·combytedancesonicinternalnativeavx·__u64toa(SB)

View file

@ -71,8 +71,8 @@ type GoMap struct {
}
type GoMapIterator struct {
Key unsafe.Pointer
Elem unsafe.Pointer
K unsafe.Pointer
V unsafe.Pointer
T *GoMapType
H *GoMap
Buckets unsafe.Pointer
@ -144,6 +144,17 @@ type GoStructField struct {
OffEmbed uintptr
}
type GoInterfaceType struct {
GoType
PkgPath *byte
Methods []GoInterfaceMethod
}
type GoInterfaceMethod struct {
Name int32
Type int32
}
type GoSlice struct {
Ptr unsafe.Pointer
Len int
@ -156,19 +167,15 @@ type GoString struct {
}
func PtrElem(t *GoType) *GoType {
if t.Kind() != reflect.Ptr {
panic("not a pointer: " + t.String())
} else {
return (*GoPtrType)(unsafe.Pointer(t)).Elem
}
return (*GoPtrType)(unsafe.Pointer(t)).Elem
}
func MapType(t *GoType) *GoMapType {
if t.Kind() != reflect.Map {
panic("not a map: " + t.String())
} else {
return (*GoMapType)(unsafe.Pointer(t))
}
return (*GoMapType)(unsafe.Pointer(t))
}
func IfaceType(t *GoType) *GoInterfaceType {
return (*GoInterfaceType)(unsafe.Pointer(t))
}
func UnpackType(t reflect.Type) *GoType {

View file

@ -27,7 +27,7 @@ import (
// Marshal returns the JSON encoding of v.
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