mirror of
https://github.com/ii64/sonic.git
synced 2026-06-21 00:46:43 +08:00
feat:(decoder) add option CopyString() (#192)
* feat: add option `CopyString()` * test: diff copy and nocopy decoding bench * doc: update README.md * opt: use global link for `escape_string()` * feat: copy JSON on `Unmarshal()` * test: add norace test * fix: nocopy field key * test: add generic test * fix: remove useless instruction
This commit is contained in:
parent
8caa4eef05
commit
b66168fa77
11 changed files with 635 additions and 165 deletions
41
README.md
41
README.md
|
|
@ -41,22 +41,26 @@ BenchmarkEncoder_Parallel_Binding_JsonIter-16 6169 ns/op 2112.8
|
|||
BenchmarkEncoder_Parallel_Binding_GoJson-16 3492 ns/op 3733.14 MB/s 9492 B/op 1 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_StdLib-16 5170 ns/op 2521.50 MB/s 9482 B/op 1 allocs/op
|
||||
|
||||
BenchmarkDecoder_Generic_Sonic-16 69966 ns/op 186.30 MB/s 49543 B/op 313 allocs/op
|
||||
BenchmarkDecoder_Generic_StdLib-16 146902 ns/op 88.73 MB/s 50872 B/op 772 allocs/op
|
||||
BenchmarkDecoder_Generic_JsonIter-16 101592 ns/op 128.31 MB/s 55783 B/op 1068 allocs/op
|
||||
BenchmarkDecoder_Generic_GoJson-16 97865 ns/op 133.19 MB/s 66367 B/op 973 allocs/op
|
||||
BenchmarkDecoder_Binding_Sonic-16 31418 ns/op 414.90 MB/s 24779 B/op 34 allocs/op
|
||||
BenchmarkDecoder_Binding_StdLib-16 131260 ns/op 99.31 MB/s 10576 B/op 208 allocs/op
|
||||
BenchmarkDecoder_Binding_JsonIter-16 39298 ns/op 331.70 MB/s 14673 B/op 385 allocs/op
|
||||
BenchmarkDecoder_Binding_GoJson-16 31852 ns/op 409.24 MB/s 22040 B/op 49 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_Sonic-16 10525 ns/op 1238.44 MB/s 49470 B/op 313 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_StdLib-16 60223 ns/op 216.44 MB/s 50875 B/op 772 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_JsonIter-16 60918 ns/op 213.97 MB/s 55817 B/op 1068 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_GoJson-16 48386 ns/op 269.39 MB/s 66425 B/op 974 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_Sonic-16 7319 ns/op 1781.00 MB/s 24889 B/op 34 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_StdLib-16 40494 ns/op 321.90 MB/s 10575 B/op 208 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_JsonIter-16 18840 ns/op 691.89 MB/s 14679 B/op 385 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_GoJson-16 17078 ns/op 763.28 MB/s 22211 B/op 49 allocs/op
|
||||
BenchmarkDecoder_Generic_Sonic-16 71589 ns/op 182.08 MB/s 57531 B/op 723 allocs/op
|
||||
BenchmarkDecoder_Generic_Sonic_Fast-16 57653 ns/op 226.10 MB/s 49743 B/op 313 allocs/op
|
||||
BenchmarkDecoder_Generic_StdLib-16 143584 ns/op 90.78 MB/s 50870 B/op 772 allocs/op
|
||||
BenchmarkDecoder_Generic_JsonIter-16 94775 ns/op 137.54 MB/s 55783 B/op 1068 allocs/op
|
||||
BenchmarkDecoder_Generic_GoJson-16 88647 ns/op 147.04 MB/s 66371 B/op 973 allocs/op
|
||||
BenchmarkDecoder_Binding_Sonic-16 32399 ns/op 402.33 MB/s 27814 B/op 137 allocs/op
|
||||
BenchmarkDecoder_Binding_Sonic_Fast-16 28655 ns/op 454.89 MB/s 25127 B/op 34 allocs/op
|
||||
BenchmarkDecoder_Binding_StdLib-16 116617 ns/op 111.78 MB/s 7344 B/op 103 allocs/op
|
||||
BenchmarkDecoder_Binding_JsonIter-16 36206 ns/op 360.02 MB/s 14673 B/op 385 allocs/op
|
||||
BenchmarkDecoder_Binding_GoJson-16 29396 ns/op 443.43 MB/s 22042 B/op 49 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_Sonic-16 12243 ns/op 1064.68 MB/s 57135 B/op 723 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_Sonic_Fast-16 10101 ns/op 1290.48 MB/s 49440 B/op 313 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_StdLib-16 57352 ns/op 227.28 MB/s 50877 B/op 772 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_JsonIter-16 58693 ns/op 222.09 MB/s 55814 B/op 1068 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_GoJson-16 45245 ns/op 288.10 MB/s 66430 B/op 974 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_Sonic-16 7035 ns/op 1852.89 MB/s 27731 B/op 137 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_Sonic_Fast-16 6510 ns/op 2002.33 MB/s 24841 B/op 34 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_StdLib-16 33086 ns/op 393.97 MB/s 7344 B/op 103 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_JsonIter-16 17827 ns/op 731.18 MB/s 14680 B/op 385 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_GoJson-16 16813 ns/op 775.29 MB/s 22268 B/op 49 allocs/op
|
||||
|
||||
BenchmarkGetOne_Sonic-16 11328 ns/op 1149.64 MB/s 29 B/op 1 allocs/op
|
||||
BenchmarkGetOne_Gjson-16 12970 ns/op 1004.07 MB/s 0 B/op 0 allocs/op
|
||||
|
|
@ -247,7 +251,12 @@ import (
|
|||
// If the type is too deep nesting (nesting depth > 5),
|
||||
// you can set compile recursive depth in Pretouch for better stability in JIT.
|
||||
err := sonic.Pretouch(reflect.TypeOf(v), option.WithCompileRecursiveDepth(depth))
|
||||
}
|
||||
```
|
||||
|
||||
### Copy string
|
||||
When decoding **string values without any escaped characters**, sonic refers them from origin JSON buffer instead of mallocing a new buffer to copy. This helps a lot for CPU performance, but may leave the whole JSON buffer in memory as long as the decoded objects are being used. In practice, we found the extra memory introduced by referring JSON buffer is usually 20% ~ 80% of decoded objects. Once a application holds these objects for a long time (for example, cache the decoded objects for reusing), its inuse memory on server may go up. We provide option `decoder.CopyString()` for users to choose not to refer the JSON buffer, which may cause the decline of CPU performance in some degree.
|
||||
|
||||
### Accelerate `encoding.TextMarshaler`
|
||||
To ensure data security, sonic.Encoder quotes and escapes string values from `encoding.TextMarshaler` interfaces by default, which may degrade performance much if most of your data is in form of them. We provide `encoder.NoQuoteTextMarshaler` to skip these operations, which means you **MUST** ensure their output string escaped and quoted in accordance with [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259).
|
||||
|
||||
|
|
|
|||
2
bench.sh
2
bench.sh
|
|
@ -7,7 +7,7 @@ cd $pwd/encoder
|
|||
go test -benchmem -run=^$ -benchtime=100000x -bench "^(BenchmarkEncoder_Generic_Sonic|BenchmarkEncoder_Generic_Sonic_Fast|BenchmarkEncoder_Generic_JsonIter|BenchmarkEncoder_Generic_GoJson|BenchmarkEncoder_Generic_StdLib|BenchmarkEncoder_Binding_Sonic|BenchmarkEncoder_Binding_Sonic_Fast|BenchmarkEncoder_Binding_JsonIter|BenchmarkEncoder_Binding_GoJson|BenchmarkEncoder_Binding_StdLib|BenchmarkEncoder_Parallel_Generic_Sonic|BenchmarkEncoder_Parallel_Generic_Sonic_Fast|BenchmarkEncoder_Parallel_Generic_JsonIter|BenchmarkEncoder_Parallel_Generic_GoJson|BenchmarkEncoder_Parallel_Generic_StdLib|BenchmarkEncoder_Parallel_Binding_Sonic|BenchmarkEncoder_Parallel_Binding_Sonic_Fast|BenchmarkEncoder_Parallel_Binding_JsonIter|BenchmarkEncoder_Parallel_Binding_GoJson|BenchmarkEncoder_Parallel_Binding_StdLib)$"
|
||||
|
||||
cd $pwd/decoder
|
||||
go test -benchmem -run=^$ -benchtime=100000x -bench "^(BenchmarkDecoder_Generic_Sonic|BenchmarkDecoder_Generic_StdLib|BenchmarkDecoder_Generic_JsonIter|BenchmarkDecoder_Generic_GoJson|BenchmarkDecoder_Binding_Sonic|BenchmarkDecoder_Binding_StdLib|BenchmarkDecoder_Binding_JsonIter|BenchmarkDecoder_Binding_GoJson|BenchmarkDecoder_Parallel_Generic_Sonic|BenchmarkDecoder_Parallel_Generic_StdLib|BenchmarkDecoder_Parallel_Generic_JsonIter|BenchmarkDecoder_Parallel_Generic_GoJson|BenchmarkDecoder_Parallel_Binding_Sonic|BenchmarkDecoder_Parallel_Binding_StdLib|BenchmarkDecoder_Parallel_Binding_JsonIter|BenchmarkDecoder_Parallel_Binding_GoJson)$"
|
||||
go test -benchmem -run=^$ -benchtime=100000x -bench "^(BenchmarkDecoder_Generic_Sonic|BenchmarkDecoder_Generic_Sonic_Fast|BenchmarkDecoder_Generic_StdLib|BenchmarkDecoder_Generic_JsonIter|BenchmarkDecoder_Generic_GoJson|BenchmarkDecoder_Binding_Sonic|BenchmarkDecoder_Binding_Sonic_Fast|BenchmarkDecoder_Binding_StdLib|BenchmarkDecoder_Binding_JsonIter|BenchmarkDecoder_Binding_GoJson|BenchmarkDecoder_Parallel_Generic_Sonic|BenchmarkDecoder_Parallel_Generic_Sonic_Fast|BenchmarkDecoder_Parallel_Generic_StdLib|BenchmarkDecoder_Parallel_Generic_JsonIter|BenchmarkDecoder_Parallel_Generic_GoJson|BenchmarkDecoder_Parallel_Binding_Sonic|BenchmarkDecoder_Parallel_Binding_Sonic_Fast|BenchmarkDecoder_Parallel_Binding_StdLib|BenchmarkDecoder_Parallel_Binding_JsonIter|BenchmarkDecoder_Parallel_Binding_GoJson)$"
|
||||
|
||||
cd $pwd/ast
|
||||
go test -benchmem -run=^$ -benchtime=100000x -bench "^(BenchmarkGetOne_Sonic|BenchmarkGetOne_Gjson|BenchmarkGetOne_Jsoniter|BenchmarkGetOne_Parallel_Sonic|BenchmarkGetOne_Parallel_Gjson|BenchmarkGetOne_Parallel_Jsoniter|BenchmarkSetOne_Sonic|BenchmarkSetOne_Sjson|BenchmarkSetOne_Jsoniter|BenchmarkSetOne_Parallel_Sonic|BenchmarkSetOne_Parallel_Sjson|BenchmarkSetOne_Parallel_Jsoniter)$"
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ const (
|
|||
_FP_args = 96 // 96 bytes to pass arguments and return values for this function
|
||||
_FP_fargs = 80 // 80 bytes for passing arguments to other Go functions
|
||||
_FP_saves = 40 // 40 bytes for saving the registers before CALL instructions
|
||||
_FP_locals = 88 // 88 bytes for local variables
|
||||
_FP_locals = 112 // 112 bytes for local variables
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -189,6 +189,12 @@ var (
|
|||
_VAR_ss_R9 = jit.Ptr(_SP, _FP_fargs + _FP_saves + 80)
|
||||
)
|
||||
|
||||
var (
|
||||
_VAR_bs_p = jit.Ptr(_SP, _FP_fargs + _FP_saves + 88)
|
||||
_VAR_bs_n = jit.Ptr(_SP, _FP_fargs + _FP_saves + 96)
|
||||
_VAR_bs_LR = jit.Ptr(_SP, _FP_fargs + _FP_saves + 104)
|
||||
)
|
||||
|
||||
type _Assembler struct {
|
||||
jit.BaseAssembler
|
||||
p _Program
|
||||
|
|
@ -214,6 +220,9 @@ func (self *_Assembler) compile() {
|
|||
self.prologue()
|
||||
self.instrs()
|
||||
self.epilogue()
|
||||
self.copy_string()
|
||||
self.escape_string()
|
||||
self.escape_string_twice()
|
||||
self.type_error()
|
||||
self.field_error()
|
||||
self.range_error()
|
||||
|
|
@ -601,6 +610,79 @@ func (self *_Assembler) parse_unsigned() {
|
|||
self.check_err()
|
||||
}
|
||||
|
||||
// Pointer: DI, Size: SI, Return: R9
|
||||
func (self *_Assembler) copy_string() {
|
||||
self.Link("_copy_string")
|
||||
self.Emit("MOVQ", _DI, _VAR_bs_p)
|
||||
self.Emit("MOVQ", _SI, _VAR_bs_n)
|
||||
self.Emit("MOVQ", _R9, _VAR_bs_LR)
|
||||
self.malloc(_SI, _AX)
|
||||
self.Emit("MOVQ", _AX, _VAR_sv_p)
|
||||
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0))
|
||||
self.Emit("MOVQ", _VAR_bs_p, _DI)
|
||||
self.Emit("MOVQ", _DI, jit.Ptr(_SP, 8))
|
||||
self.Emit("MOVQ", _VAR_bs_n, _SI)
|
||||
self.Emit("MOVQ", _SI, jit.Ptr(_SP, 16))
|
||||
self.call_go(_F_memmove)
|
||||
self.Emit("MOVQ", _VAR_sv_p, _DI)
|
||||
self.Emit("MOVQ", _VAR_bs_n, _SI)
|
||||
self.Emit("MOVQ", _VAR_bs_LR, _R9)
|
||||
self.Rjmp("JMP", _R9)
|
||||
}
|
||||
|
||||
// Pointer: DI, Size: SI, Return: R9
|
||||
func (self *_Assembler) escape_string() {
|
||||
self.Link("_escape_string")
|
||||
self.Emit("MOVQ" , _DI, _VAR_bs_p)
|
||||
self.Emit("MOVQ" , _SI, _VAR_bs_n)
|
||||
self.Emit("MOVQ" , _R9, _VAR_bs_LR)
|
||||
self.malloc(_SI, _DX) // MALLOC SI, DX
|
||||
self.Emit("MOVQ" , _DX, _VAR_sv_p)
|
||||
self.Emit("MOVQ" , _VAR_bs_p, _DI)
|
||||
self.Emit("MOVQ" , _VAR_bs_n, _SI)
|
||||
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
||||
self.Emit("XORL" , _R8, _R8) // XORL R8, R8
|
||||
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _ARG_fv) // BTQ ${_F_disable_urc}, fv
|
||||
self.Emit("SETCC", _R8) // SETCC R8
|
||||
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _R8) // SHLQ ${types.B_UNICODE_REPLACE}, R8
|
||||
self.call(_F_unquote) // CALL unquote
|
||||
self.Emit("MOVQ" , _VAR_bs_n, _SI) // MOVQ ${n}, SI
|
||||
self.Emit("ADDQ" , jit.Imm(1), _SI) // ADDQ $1, SI
|
||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
||||
self.Emit("MOVQ" , _AX, _SI)
|
||||
self.Emit("MOVQ" , _VAR_sv_p, _DI)
|
||||
self.Emit("MOVQ" , _VAR_bs_LR, _R9)
|
||||
self.Rjmp("JMP", _R9)
|
||||
}
|
||||
|
||||
func (self *_Assembler) escape_string_twice() {
|
||||
self.Link("_escape_string_twice")
|
||||
self.Emit("MOVQ" , _DI, _VAR_bs_p)
|
||||
self.Emit("MOVQ" , _SI, _VAR_bs_n)
|
||||
self.Emit("MOVQ" , _R9, _VAR_bs_LR)
|
||||
self.malloc(_SI, _DX) // MALLOC SI, DX
|
||||
self.Emit("MOVQ" , _DX, _VAR_sv_p)
|
||||
self.Emit("MOVQ" , _VAR_bs_p, _DI)
|
||||
self.Emit("MOVQ" , _VAR_bs_n, _SI)
|
||||
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
||||
self.Emit("MOVL" , jit.Imm(types.F_DOUBLE_UNQUOTE), _R8) // MOVL ${types.F_DOUBLE_UNQUOTE}, R8
|
||||
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _ARG_fv) // BTQ ${_F_disable_urc}, AX
|
||||
self.Emit("XORL" , _AX, _AX) // XORL AX, AX
|
||||
self.Emit("SETCC", _AX) // SETCC AX
|
||||
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _AX) // SHLQ ${types.B_UNICODE_REPLACE}, AX
|
||||
self.Emit("ORQ" , _AX, _R8) // ORQ AX, R8
|
||||
self.call(_F_unquote) // CALL unquote
|
||||
self.Emit("MOVQ" , _VAR_bs_n, _SI) // MOVQ ${n}, SI
|
||||
self.Emit("ADDQ" , jit.Imm(3), _SI) // ADDQ $3, SI
|
||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
||||
self.Emit("MOVQ" , _AX, _SI)
|
||||
self.Emit("MOVQ" , _VAR_sv_p, _DI)
|
||||
self.Emit("MOVQ" , _VAR_bs_LR, _R9)
|
||||
self.Rjmp("JMP", _R9)
|
||||
}
|
||||
|
||||
/** Range Checking Routines **/
|
||||
|
||||
var (
|
||||
|
|
@ -669,37 +751,28 @@ func (self *_Assembler) slice_from_r(p obj.Addr, d int64) {
|
|||
self.Emit("LEAQ", jit.Sib(_IC, p, 1, d), _SI) // LEAQ d(IC)(${p}), SI
|
||||
}
|
||||
|
||||
func (self *_Assembler) unquote_once(p obj.Addr, n obj.Addr, stack bool) {
|
||||
func (self *_Assembler) unquote_once(p obj.Addr, n obj.Addr, stack bool, copy bool) {
|
||||
self.slice_from(_VAR_st_Iv, -1) // SLICE st.Iv, $-1
|
||||
self.Emit("CMPQ" , _VAR_st_Ep, jit.Imm(-1)) // CMPQ st.Ep, $-1
|
||||
self.Sjmp("JE" , "_noescape_{n}") // JE _noescape_{n}
|
||||
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||
self.Sref("_unquote_once_write_{n}", 4)
|
||||
self.Sjmp("JMP" , "_escape_string")
|
||||
self.Link("_noescape_{n}") // _noescape_{n}:
|
||||
if copy {
|
||||
self.Emit("BTQ" , jit.Imm(_F_copy_string), _ARG_fv)
|
||||
self.Sjmp("JNC", "_unquote_once_write_{n}")
|
||||
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||
self.Sref("_unquote_once_write_{n}", 4)
|
||||
self.Sjmp("JMP", "_copy_string")
|
||||
}
|
||||
self.Link("_unquote_once_write_{n}")
|
||||
self.Emit("MOVQ" , _SI, n) // MOVQ SI, ${n}
|
||||
if stack {
|
||||
self.Emit("MOVQ", _DI, p) // MOVQ DI, ${p}
|
||||
self.Emit("MOVQ", _DI, p)
|
||||
} else {
|
||||
self.WriteRecNotAX(10, _DI, p, false, false)
|
||||
}
|
||||
self.Emit("CMPQ" , _VAR_st_Ep, jit.Imm(-1)) // CMPQ st.Ep, $-1
|
||||
self.Sjmp("JE" , "_noescape_{n}") // JE _noescape_{n}
|
||||
self.malloc(_SI, _DX) // MALLOC SI, DX
|
||||
self.Emit("MOVQ" , p, _DI) // MOVQ ${p}, DI
|
||||
self.Emit("MOVQ" , n, _SI) // MOVQ ${n}, SI
|
||||
if stack {
|
||||
// no need for writeBarrier
|
||||
self.Emit("MOVQ", _DX, p) // MOVQ DX, ${p}
|
||||
} else {
|
||||
self.WriteRecNotAX(2, _DX, p, true, false) // MOVQ DX, ${p}
|
||||
}
|
||||
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
||||
self.Emit("XORL" , _R8, _R8) // XORL R8, R8
|
||||
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _ARG_fv) // BTQ ${_F_disable_urc}, fv
|
||||
self.Emit("SETCC", _R8) // SETCC R8
|
||||
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _R8) // SHLQ ${types.B_UNICODE_REPLACE}, R8
|
||||
self.call(_F_unquote) // CALL unquote
|
||||
self.Emit("MOVQ" , n, _SI) // MOVQ ${n}, SI
|
||||
self.Emit("ADDQ" , jit.Imm(1), _SI) // ADDQ $1, SI
|
||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
||||
self.Emit("MOVQ" , _AX, n) // MOVQ AX, ${n}
|
||||
self.Link("_noescape_{n}") // _noescape_{n}:
|
||||
}
|
||||
|
||||
func (self *_Assembler) unquote_twice(p obj.Addr, n obj.Addr, stack bool) {
|
||||
|
|
@ -710,39 +783,26 @@ func (self *_Assembler) unquote_twice(p obj.Addr, n obj.Addr, stack bool) {
|
|||
self.Emit("CMPB" , jit.Sib(_IP, _IC, 1, -2), jit.Imm('"')) // CMPB -2(IP)(IC), $'"'
|
||||
self.Sjmp("JNE" , _LB_char_m2_error) // JNE _char_m2_error
|
||||
self.slice_from(_VAR_st_Iv, -3) // SLICE st.Iv, $-3
|
||||
self.Emit("MOVQ" , _SI, n) // MOVQ SI, ${n}
|
||||
self.Emit("MOVQ" , _SI, _AX) // MOVQ SI, AX
|
||||
if stack {
|
||||
self.Emit("MOVQ" , _DI, p) // MOVQ DI, ${p}
|
||||
} else {
|
||||
self.WriteRecNotAX(9, _DI, p, false, false)
|
||||
}
|
||||
self.Emit("ADDQ" , _VAR_st_Iv, _AX) // ADDQ st.Iv, AX
|
||||
self.Emit("CMPQ" , _VAR_st_Ep, _AX) // CMPQ st.Ep, AX
|
||||
self.Sjmp("JE" , "_noescape_{n}") // JE _noescape_{n}
|
||||
self.malloc(_SI, _DX) // MALLOC SI, DX
|
||||
self.Emit("MOVQ" , p, _DI) // MOVQ ${p}, DI
|
||||
self.Emit("MOVQ" , n, _SI) // MOVQ ${n}, SI
|
||||
if stack {
|
||||
// no need for writeBarrier
|
||||
self.Emit("MOVQ", _DX, p) // MOVQ DX, ${p}
|
||||
} else {
|
||||
self.WriteRecNotAX(2, _DX, p, true, false) // MOVQ DX, ${p}
|
||||
}
|
||||
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
||||
self.Emit("MOVL" , jit.Imm(types.F_DOUBLE_UNQUOTE), _R8) // MOVL ${types.F_DOUBLE_UNQUOTE}, R8
|
||||
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _ARG_fv) // BTQ ${_F_disable_urc}, AX
|
||||
self.Emit("XORL" , _AX, _AX) // XORL AX, AX
|
||||
self.Emit("SETCC", _AX) // SETCC AX
|
||||
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _AX) // SHLQ ${types.B_UNICODE_REPLACE}, AX
|
||||
self.Emit("ORQ" , _AX, _R8) // ORQ AX, R8
|
||||
self.call(_F_unquote) // CALL unquote
|
||||
self.Emit("MOVQ" , n, _SI) // MOVQ ${n}, SI
|
||||
self.Emit("ADDQ" , jit.Imm(3), _SI) // ADDQ $3, SI
|
||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
||||
self.Emit("MOVQ" , _AX, n) // MOVQ AX, ${n}
|
||||
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||
self.Sref("_unquote_twice_write_{n}", 4)
|
||||
self.Sjmp("JMP" , "_escape_string_twice")
|
||||
self.Link("_noescape_{n}") // _noescape_{n}:
|
||||
self.Emit("BTQ" , jit.Imm(_F_copy_string), _ARG_fv)
|
||||
self.Sjmp("JNC", "_unquote_twice_write_{n}")
|
||||
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||
self.Sref("_unquote_twice_write_{n}", 4)
|
||||
self.Sjmp("JMP", "_copy_string")
|
||||
self.Link("_unquote_twice_write_{n}")
|
||||
self.Emit("MOVQ" , _SI, n) // MOVQ SI, ${n}
|
||||
if stack {
|
||||
self.Emit("MOVQ", _DI, p)
|
||||
} else {
|
||||
self.WriteRecNotAX(12, _DI, p, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
/** Memory Clearing Routines **/
|
||||
|
|
@ -885,7 +945,7 @@ func (self *_Assembler) unmarshal_json(t reflect.Type, deref bool) {
|
|||
|
||||
func (self *_Assembler) unmarshal_text(t reflect.Type, deref bool) {
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unmarshal_func(t, _F_decodeTextUnmarshaler, deref) // UNMARSHAL text, ${t}, ${deref}
|
||||
}
|
||||
|
||||
|
|
@ -956,6 +1016,7 @@ func (self *_Assembler) decode_dynamic(vt obj.Addr, vp obj.Addr) {
|
|||
|
||||
var (
|
||||
_F_memequal = jit.Func(memequal)
|
||||
_F_memmove = jit.Func(memmove)
|
||||
_F_growslice = jit.Func(growslice)
|
||||
_F_makeslice = jit.Func(makeslice)
|
||||
_F_makemap_small = jit.Func(makemap_small)
|
||||
|
|
@ -1040,7 +1101,7 @@ func (self *_Assembler) _asm_OP_dyn(p *_Instr) {
|
|||
|
||||
func (self *_Assembler) _asm_OP_str(_ *_Instr) {
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(jit.Ptr(_VP, 0), jit.Ptr(_VP, 8), false) // UNQUOTE once, (VP), 8(VP)
|
||||
self.unquote_once(jit.Ptr(_VP, 0), jit.Ptr(_VP, 8), false, true) // UNQUOTE once, (VP), 8(VP)
|
||||
}
|
||||
|
||||
func (self *_Assembler) _asm_OP_bin(_ *_Instr) {
|
||||
|
|
@ -1104,8 +1165,14 @@ func (self *_Assembler) _asm_OP_bool(_ *_Instr) {
|
|||
func (self *_Assembler) _asm_OP_num(_ *_Instr) {
|
||||
self.parse_number() // PARSE NUMBER
|
||||
self.slice_from(_VAR_st_Ep, 0) // SLICE st.Ep, $0
|
||||
self.WriteRecNotAX(5, _DI, jit.Ptr(_VP, 0), false, false) // MOVQ DI, (VP)
|
||||
self.Emit("BTQ", jit.Imm(_F_copy_string), _ARG_fv)
|
||||
self.Sjmp("JNC", "_num_write_{n}")
|
||||
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||
self.Sref("_num_write_{n}", 4)
|
||||
self.Sjmp("JMP", "_copy_string")
|
||||
self.Link("_num_write_{n}")
|
||||
self.Emit("MOVQ", _SI, jit.Ptr(_VP, 8)) // MOVQ SI, 8(VP)
|
||||
self.WriteRecNotAX(13, _DI, jit.Ptr(_VP, 0), false, false)
|
||||
}
|
||||
|
||||
func (self *_Assembler) _asm_OP_i8(_ *_Instr) {
|
||||
|
|
@ -1316,7 +1383,7 @@ func (self *_Assembler) _asm_OP_map_key_f64(p *_Instr) {
|
|||
|
||||
func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) {
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||
if vt := p.vt(); !mapfast(vt) {
|
||||
self.valloc(vt.Key(), _DI)
|
||||
self.Emit("MOVOU", _VAR_sv, _X0)
|
||||
|
|
@ -1331,13 +1398,13 @@ func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) {
|
|||
|
||||
func (self *_Assembler) _asm_OP_map_key_utext(p *_Instr) {
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.mapassign_utext(p.vt(), false) // MAPASSIGN utext, ${p.vt()}, false
|
||||
}
|
||||
|
||||
func (self *_Assembler) _asm_OP_map_key_utext_p(p *_Instr) {
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, false) // UNQUOTE once, sv.p, sv.n
|
||||
self.mapassign_utext(p.vt(), true) // MAPASSIGN utext, ${p.vt()}, true
|
||||
}
|
||||
|
||||
|
|
@ -1419,7 +1486,7 @@ func (self *_Assembler) _asm_OP_struct_field(p *_Instr) {
|
|||
self.Emit("MOVQ" , jit.Imm(-1), _AX) // MOVQ $-1, AX
|
||||
self.Emit("MOVQ" , _AX, _VAR_sr) // MOVQ AX, sr
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, false) // UNQUOTE once, sv.p, sv.n
|
||||
self.Emit("LEAQ" , _VAR_sv, _AX) // LEAQ sv, AX
|
||||
self.Emit("XORL" , _CX, _CX) // XORL CX, CX
|
||||
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ const (
|
|||
_FP_args = 72 // 72 bytes to pass and spill register arguements
|
||||
_FP_fargs = 80 // 80 bytes for passing arguments to other Go functions
|
||||
_FP_saves = 48 // 48 bytes for saving the registers before CALL instructions
|
||||
_FP_locals = 88 // 88 bytes for local variables
|
||||
_FP_locals = 112 // 112 bytes for local variables
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -184,6 +184,12 @@ var (
|
|||
_VAR_ss_R9 = jit.Ptr(_SP, _FP_fargs + _FP_saves + 80)
|
||||
)
|
||||
|
||||
var (
|
||||
_VAR_bs_p = jit.Ptr(_SP, _FP_fargs + _FP_saves + 88)
|
||||
_VAR_bs_n = jit.Ptr(_SP, _FP_fargs + _FP_saves + 96)
|
||||
_VAR_bs_LR = jit.Ptr(_SP, _FP_fargs + _FP_saves + 104)
|
||||
)
|
||||
|
||||
type _Assembler struct {
|
||||
jit.BaseAssembler
|
||||
p _Program
|
||||
|
|
@ -209,6 +215,9 @@ func (self *_Assembler) compile() {
|
|||
self.prologue()
|
||||
self.instrs()
|
||||
self.epilogue()
|
||||
self.copy_string()
|
||||
self.escape_string()
|
||||
self.escape_string_twice()
|
||||
self.type_error()
|
||||
self.field_error()
|
||||
self.range_error()
|
||||
|
|
@ -532,7 +541,7 @@ var (
|
|||
_F_mallocgc = jit.Func(mallocgc)
|
||||
)
|
||||
|
||||
func (self *_Assembler) malloc(nb obj.Addr, ret obj.Addr) {
|
||||
func (self *_Assembler) malloc_AX(nb obj.Addr, ret obj.Addr) {
|
||||
self.Emit("MOVQ", nb, _AX) // MOVQ ${nb}, AX
|
||||
self.Emit("MOVQ", _T_byte, _BX) // MOVQ ${type(byte)}, BX
|
||||
self.Emit("XORL", _CX, _CX) // XORL CX, CX
|
||||
|
|
@ -611,6 +620,75 @@ func (self *_Assembler) parse_unsigned() {
|
|||
self.check_err()
|
||||
}
|
||||
|
||||
// Pointer: DI, Size: SI, Return: R9
|
||||
func (self *_Assembler) copy_string() {
|
||||
self.Link("_copy_string")
|
||||
self.Emit("MOVQ", _DI, _VAR_bs_p)
|
||||
self.Emit("MOVQ", _SI, _VAR_bs_n)
|
||||
self.Emit("MOVQ", _R9, _VAR_bs_LR)
|
||||
self.malloc_AX(_SI, _VAR_sv_p)
|
||||
self.Emit("MOVQ", _VAR_bs_p, _BX)
|
||||
self.Emit("MOVQ", _VAR_bs_n, _CX)
|
||||
self.call_go(_F_memmove)
|
||||
self.Emit("MOVQ", _VAR_sv_p, _DI)
|
||||
self.Emit("MOVQ", _VAR_bs_n, _SI)
|
||||
self.Emit("MOVQ", _VAR_bs_LR, _R9)
|
||||
self.Rjmp("JMP", _R9)
|
||||
}
|
||||
|
||||
// Pointer: DI, Size: SI, Return: R9
|
||||
func (self *_Assembler) escape_string() {
|
||||
self.Link("_escape_string")
|
||||
self.Emit("MOVQ" , _DI, _VAR_bs_p)
|
||||
self.Emit("MOVQ" , _SI, _VAR_bs_n)
|
||||
self.Emit("MOVQ" , _R9, _VAR_bs_LR)
|
||||
self.malloc_AX(_SI, _DX) // MALLOC SI, DX
|
||||
self.Emit("MOVQ" , _DX, _VAR_sv_p)
|
||||
self.Emit("MOVQ" , _VAR_bs_p, _DI)
|
||||
self.Emit("MOVQ" , _VAR_bs_n, _SI)
|
||||
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
||||
self.Emit("XORL" , _R8, _R8) // XORL R8, R8
|
||||
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _ARG_fv) // BTQ ${_F_disable_urc}, fv
|
||||
self.Emit("SETCC", _R8) // SETCC R8
|
||||
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _R8) // SHLQ ${types.B_UNICODE_REPLACE}, R8
|
||||
self.call_c(_F_unquote) // CALL unquote
|
||||
self.Emit("MOVQ" , _VAR_bs_n, _SI) // MOVQ ${n}, SI
|
||||
self.Emit("ADDQ" , jit.Imm(1), _SI) // ADDQ $1, SI
|
||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
||||
self.Emit("MOVQ" , _AX, _SI)
|
||||
self.Emit("MOVQ" , _VAR_sv_p, _DI)
|
||||
self.Emit("MOVQ" , _VAR_bs_LR, _R9)
|
||||
self.Rjmp("JMP", _R9)
|
||||
}
|
||||
|
||||
func (self *_Assembler) escape_string_twice() {
|
||||
self.Link("_escape_string_twice")
|
||||
self.Emit("MOVQ" , _DI, _VAR_bs_p)
|
||||
self.Emit("MOVQ" , _SI, _VAR_bs_n)
|
||||
self.Emit("MOVQ" , _R9, _VAR_bs_LR)
|
||||
self.malloc_AX(_SI, _DX) // MALLOC SI, DX
|
||||
self.Emit("MOVQ" , _DX, _VAR_sv_p)
|
||||
self.Emit("MOVQ" , _VAR_bs_p, _DI)
|
||||
self.Emit("MOVQ" , _VAR_bs_n, _SI)
|
||||
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
||||
self.Emit("MOVL" , jit.Imm(types.F_DOUBLE_UNQUOTE), _R8) // MOVL ${types.F_DOUBLE_UNQUOTE}, R8
|
||||
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _ARG_fv) // BTQ ${_F_disable_urc}, AX
|
||||
self.Emit("XORL" , _AX, _AX) // XORL AX, AX
|
||||
self.Emit("SETCC", _AX) // SETCC AX
|
||||
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _AX) // SHLQ ${types.B_UNICODE_REPLACE}, AX
|
||||
self.Emit("ORQ" , _AX, _R8) // ORQ AX, R8
|
||||
self.call_c(_F_unquote) // CALL unquote
|
||||
self.Emit("MOVQ" , _VAR_bs_n, _SI) // MOVQ ${n}, SI
|
||||
self.Emit("ADDQ" , jit.Imm(3), _SI) // ADDQ $3, SI
|
||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
||||
self.Emit("MOVQ" , _AX, _SI)
|
||||
self.Emit("MOVQ" , _VAR_sv_p, _DI)
|
||||
self.Emit("MOVQ" , _VAR_bs_LR, _R9)
|
||||
self.Rjmp("JMP", _R9)
|
||||
}
|
||||
|
||||
/** Range Checking Routines **/
|
||||
|
||||
var (
|
||||
|
|
@ -679,36 +757,28 @@ func (self *_Assembler) slice_from_r(p obj.Addr, d int64) {
|
|||
self.Emit("LEAQ", jit.Sib(_IC, p, 1, d), _SI) // LEAQ d(IC)(${p}), SI
|
||||
}
|
||||
|
||||
func (self *_Assembler) unquote_once(p obj.Addr, n obj.Addr, stack bool) {
|
||||
func (self *_Assembler) unquote_once(p obj.Addr, n obj.Addr, stack bool, copy bool) {
|
||||
self.slice_from(_VAR_st_Iv, -1) // SLICE st.Iv, $-1
|
||||
self.Emit("MOVQ" , _SI, n) // MOVQ SI, ${n}
|
||||
self.Emit("CMPQ", _VAR_st_Ep, jit.Imm(-1)) // CMPQ st.Ep, $-1
|
||||
self.Sjmp("JE" , "_noescape_{n}") // JE _escape_{n}
|
||||
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||
self.Sref("_unquote_once_write_{n}", 4)
|
||||
self.Sjmp("JMP" , "_escape_string")
|
||||
self.Link("_noescape_{n}")
|
||||
if copy {
|
||||
self.Emit("BTQ" , jit.Imm(_F_copy_string), _ARG_fv)
|
||||
self.Sjmp("JNC", "_unquote_once_write_{n}")
|
||||
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||
self.Sref("_unquote_once_write_{n}", 4)
|
||||
self.Sjmp("JMP", "_copy_string")
|
||||
}
|
||||
self.Link("_unquote_once_write_{n}")
|
||||
self.Emit("MOVQ", _SI, n) // MOVQ SI, ${n}
|
||||
if stack {
|
||||
self.Emit("MOVQ", _DI, p) // MOVQ DI, ${p}
|
||||
self.Emit("MOVQ", _DI, p)
|
||||
} else {
|
||||
self.WriteRecNotAX(10, _DI, p, false, false)
|
||||
}
|
||||
self.Emit("CMPQ" , _VAR_st_Ep, jit.Imm(-1)) // CMPQ st.Ep, $-1
|
||||
self.Sjmp("JE" , "_noescape_{n}") // JE _noescape_{n}
|
||||
self.malloc(_SI, _DX) // MALLOC SI, DX
|
||||
self.Emit("MOVQ" , p, _DI) // MOVQ ${p}, DI
|
||||
self.Emit("MOVQ" , n, _SI) // MOVQ ${n}, SI
|
||||
if stack {
|
||||
self.Emit("MOVQ", _DX, p) // MOVQ DX, ${p}
|
||||
} else {
|
||||
self.WriteRecNotAX(2, _DX, p, true, false) // MOVQ DX, ${p}
|
||||
}
|
||||
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
||||
self.Emit("XORL" , _R8, _R8) // XORL R8, R8
|
||||
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _ARG_fv) // BTQ ${_F_disable_urc}, fv
|
||||
self.Emit("SETCC", _R8) // SETCC R8
|
||||
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _R8) // SHLQ ${types.B_UNICODE_REPLACE}, R8
|
||||
self.call_c(_F_unquote) // CALL unquote
|
||||
self.Emit("MOVQ" , n, _SI) // MOVQ ${n}, SI
|
||||
self.Emit("ADDQ" , jit.Imm(1), _SI) // ADDQ $1, SI
|
||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
||||
self.Emit("MOVQ" , _AX, n) // MOVQ AX, ${n}
|
||||
self.Link("_noescape_{n}") // _noescape_{n}:
|
||||
}
|
||||
|
||||
func (self *_Assembler) unquote_twice(p obj.Addr, n obj.Addr, stack bool) {
|
||||
|
|
@ -719,38 +789,27 @@ func (self *_Assembler) unquote_twice(p obj.Addr, n obj.Addr, stack bool) {
|
|||
self.Emit("CMPB" , jit.Sib(_IP, _IC, 1, -2), jit.Imm('"')) // CMPB -2(IP)(IC), $'"'
|
||||
self.Sjmp("JNE" , _LB_char_m2_error) // JNE _char_m2_error
|
||||
self.slice_from(_VAR_st_Iv, -3) // SLICE st.Iv, $-3
|
||||
self.Emit("MOVQ" , _SI, n) // MOVQ SI, ${n}
|
||||
self.Emit("MOVQ" , _SI, _AX) // MOVQ SI, AX
|
||||
if stack {
|
||||
self.Emit("MOVQ" , _DI, p) // MOVQ DI, ${p}
|
||||
} else {
|
||||
self.WriteRecNotAX(9, _DI, p, false, false)
|
||||
}
|
||||
self.Emit("ADDQ" , _VAR_st_Iv, _AX) // ADDQ st.Iv, AX
|
||||
self.Emit("CMPQ" , _VAR_st_Ep, _AX) // CMPQ st.Ep, AX
|
||||
self.Sjmp("JE" , "_noescape_{n}") // JE _noescape_{n}
|
||||
self.malloc(_SI, _DX) // MALLOC SI, DX
|
||||
self.Emit("MOVQ" , p, _DI) // MOVQ ${p}, DI
|
||||
self.Emit("MOVQ" , n, _SI) // MOVQ ${n}, SI
|
||||
if stack {
|
||||
self.Emit("MOVQ", _DX, p) // MOVQ DX, ${p}
|
||||
} else {
|
||||
self.WriteRecNotAX(2, _DX, p, true, false) // MOVQ DX, ${p}
|
||||
}
|
||||
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
||||
self.Emit("MOVL" , jit.Imm(types.F_DOUBLE_UNQUOTE), _R8) // MOVL ${types.F_DOUBLE_UNQUOTE}, R8
|
||||
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _ARG_fv) // BTQ ${_F_disable_urc}, AX
|
||||
self.Emit("XORL" , _AX, _AX) // XORL AX, AX
|
||||
self.Emit("SETCC", _AX) // SETCC AX
|
||||
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _AX) // SHLQ ${types.B_UNICODE_REPLACE}, AX
|
||||
self.Emit("ORQ" , _AX, _R8) // ORQ AX, R8
|
||||
self.call_c(_F_unquote) // CALL unquote
|
||||
self.Emit("MOVQ" , n, _SI) // MOVQ ${n}, SI
|
||||
self.Emit("ADDQ" , jit.Imm(3), _SI) // ADDQ $3, SI
|
||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
||||
self.Emit("MOVQ" , _AX, n) // MOVQ AX, ${n}
|
||||
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||
self.Sref("_unquote_twice_write_{n}", 4)
|
||||
self.Sjmp("JMP" , "_escape_string_twice")
|
||||
self.Link("_noescape_{n}") // _noescape_{n}:
|
||||
self.Emit("BTQ" , jit.Imm(_F_copy_string), _ARG_fv)
|
||||
self.Sjmp("JNC", "_unquote_twice_write_{n}")
|
||||
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||
self.Sref("_unquote_twice_write_{n}", 4)
|
||||
self.Sjmp("JMP", "_copy_string")
|
||||
self.Link("_unquote_twice_write_{n}")
|
||||
self.Emit("MOVQ" , _SI, n) // MOVQ SI, ${n}
|
||||
if stack {
|
||||
self.Emit("MOVQ", _DI, p)
|
||||
} else {
|
||||
self.WriteRecNotAX(12, _DI, p, false, false)
|
||||
}
|
||||
self.Link("_unquote_twice_end_{n}")
|
||||
}
|
||||
|
||||
/** Memory Clearing Routines **/
|
||||
|
|
@ -887,7 +946,7 @@ func (self *_Assembler) unmarshal_json(t reflect.Type, deref bool) {
|
|||
|
||||
func (self *_Assembler) unmarshal_text(t reflect.Type, deref bool) {
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unmarshal_func(t, _F_decodeTextUnmarshaler, deref) // UNMARSHAL text, ${t}, ${deref}
|
||||
}
|
||||
|
||||
|
|
@ -953,6 +1012,7 @@ func (self *_Assembler) decode_dynamic(vt obj.Addr, vp obj.Addr) {
|
|||
|
||||
var (
|
||||
_F_memequal = jit.Func(memequal)
|
||||
_F_memmove = jit.Func(memmove)
|
||||
_F_growslice = jit.Func(growslice)
|
||||
_F_makeslice = jit.Func(makeslice)
|
||||
_F_makemap_small = jit.Func(makemap_small)
|
||||
|
|
@ -1038,7 +1098,7 @@ func (self *_Assembler) _asm_OP_dyn(p *_Instr) {
|
|||
|
||||
func (self *_Assembler) _asm_OP_str(_ *_Instr) {
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(jit.Ptr(_VP, 0), jit.Ptr(_VP, 8), false) // UNQUOTE once, (VP), 8(VP)
|
||||
self.unquote_once(jit.Ptr(_VP, 0), jit.Ptr(_VP, 8), false, true) // UNQUOTE once, (VP), 8(VP)
|
||||
}
|
||||
|
||||
func (self *_Assembler) _asm_OP_bin(_ *_Instr) {
|
||||
|
|
@ -1049,7 +1109,7 @@ func (self *_Assembler) _asm_OP_bin(_ *_Instr) {
|
|||
self.Emit("SHRQ" , jit.Imm(2), _SI) // SHRQ $2, SI
|
||||
self.Emit("LEAQ" , jit.Sib(_SI, _SI, 2, 0), _SI) // LEAQ (SI)(SI*2), SI
|
||||
self.Emit("MOVQ" , _SI, jit.Ptr(_VP, 16)) // MOVQ SI, 16(VP)
|
||||
self.malloc(_SI, _SI) // MALLOC SI, SI
|
||||
self.malloc_AX(_SI, _SI) // MALLOC SI, SI
|
||||
|
||||
/* check for AVX2 support */
|
||||
if !cpu.HasAVX2 {
|
||||
|
|
@ -1102,8 +1162,14 @@ func (self *_Assembler) _asm_OP_bool(_ *_Instr) {
|
|||
func (self *_Assembler) _asm_OP_num(_ *_Instr) {
|
||||
self.parse_number() // PARSE NUMBER
|
||||
self.slice_from(_VAR_st_Ep, 0) // SLICE st.Ep, $0
|
||||
self.WriteRecNotAX(5, _DI, jit.Ptr(_VP, 0), false, false) // MOVQ DI, (VP)
|
||||
self.Emit("BTQ", jit.Imm(_F_copy_string), _ARG_fv)
|
||||
self.Sjmp("JNC", "_num_write_{n}")
|
||||
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||
self.Sref("_num_write_{n}", 4)
|
||||
self.Sjmp("JMP", "_copy_string")
|
||||
self.Link("_num_write_{n}")
|
||||
self.Emit("MOVQ", _SI, jit.Ptr(_VP, 8)) // MOVQ SI, 8(VP)
|
||||
self.WriteRecNotAX(13, _DI, jit.Ptr(_VP, 0), false, false)
|
||||
}
|
||||
|
||||
func (self *_Assembler) _asm_OP_i8(_ *_Instr) {
|
||||
|
|
@ -1315,7 +1381,7 @@ func (self *_Assembler) _asm_OP_map_key_f64(p *_Instr) {
|
|||
|
||||
func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) {
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||
if vt := p.vt(); !mapfast(vt) {
|
||||
self.valloc(vt.Key(), _DI)
|
||||
self.Emit("MOVOU", _VAR_sv, _X0)
|
||||
|
|
@ -1328,13 +1394,13 @@ func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) {
|
|||
|
||||
func (self *_Assembler) _asm_OP_map_key_utext(p *_Instr) {
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.mapassign_utext(p.vt(), false) // MAPASSIGN utext, ${p.vt()}, false
|
||||
}
|
||||
|
||||
func (self *_Assembler) _asm_OP_map_key_utext_p(p *_Instr) {
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.mapassign_utext(p.vt(), true) // MAPASSIGN utext, ${p.vt()}, true
|
||||
}
|
||||
|
||||
|
|
@ -1408,7 +1474,7 @@ func (self *_Assembler) _asm_OP_struct_field(p *_Instr) {
|
|||
self.Emit("MOVQ" , jit.Imm(-1), _AX) // MOVQ $-1, AX
|
||||
self.Emit("MOVQ" , _AX, _VAR_sr) // MOVQ AX, sr
|
||||
self.parse_string() // PARSE STRING
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true) // UNQUOTE once, sv.p, sv.n
|
||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, false) // UNQUOTE once, sv.p, sv.n
|
||||
self.Emit("LEAQ" , _VAR_sv, _AX) // LEAQ sv, AX
|
||||
self.Emit("XORL" , _BX, _BX) // XORL BX, BX
|
||||
self.call_go(_F_strhash) // CALL_GO strhash
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ const (
|
|||
_F_use_number
|
||||
_F_disable_urc
|
||||
_F_disable_unknown
|
||||
_F_copy_string
|
||||
)
|
||||
|
||||
// Decoder is the decoder context object
|
||||
|
|
@ -110,6 +111,11 @@ func (self *Decoder) DisallowUnknownFields() {
|
|||
self.f |= 1 << _F_disable_unknown
|
||||
}
|
||||
|
||||
// CopyString causes the Decoder to decode string values by copying instead of referring.
|
||||
func (self *Decoder) CopyString() {
|
||||
self.f |= 1 << _F_copy_string
|
||||
}
|
||||
|
||||
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
|
||||
// order to reduce the first-hit latency.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -18,18 +18,18 @@ package decoder
|
|||
|
||||
import (
|
||||
`encoding/json`
|
||||
`testing`
|
||||
`time`
|
||||
`runtime`
|
||||
`runtime/debug`
|
||||
`sync`
|
||||
`testing`
|
||||
`time`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
`github.com/davecgh/go-spew/spew`
|
||||
gojson `github.com/goccy/go-json`
|
||||
`github.com/json-iterator/go`
|
||||
`github.com/stretchr/testify/assert`
|
||||
`github.com/stretchr/testify/require`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
|
@ -53,7 +53,7 @@ func TestGC(t *testing.T) {
|
|||
return
|
||||
}
|
||||
var w interface{}
|
||||
out, err := decode(TwitterJson, &w)
|
||||
out, err := decode(TwitterJson, &w, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ func TestGC(t *testing.T) {
|
|||
go func (wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
var w interface{}
|
||||
out, err := decode(TwitterJson, &w)
|
||||
out, err := decode(TwitterJson, &w, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -86,8 +86,11 @@ func init() {
|
|||
_ = json.Unmarshal([]byte(TwitterJson), &_BindingValue)
|
||||
}
|
||||
|
||||
func decode(s string, v interface{}) (int, error) {
|
||||
func decode(s string, v interface{}, copy bool) (int, error) {
|
||||
d := NewDecoder(s)
|
||||
if copy {
|
||||
d.CopyString()
|
||||
}
|
||||
err := d.Decode(v)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
|
@ -95,9 +98,127 @@ func decode(s string, v interface{}) (int, error) {
|
|||
return d.i, err
|
||||
}
|
||||
|
||||
func TestCopyString(t *testing.T) {
|
||||
var data []byte
|
||||
var dc *Decoder
|
||||
var err error
|
||||
data = []byte(`{"A":"0","B":"1"}`)
|
||||
dc = NewDecoder(rt.Mem2Str(data))
|
||||
dc.UseNumber()
|
||||
dc.CopyString()
|
||||
var obj struct{
|
||||
A string
|
||||
B string
|
||||
}
|
||||
err = dc.Decode(&obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data[6] = '1'
|
||||
if obj.A != "0" {
|
||||
t.Fatal(obj)
|
||||
}
|
||||
data[14] = '0'
|
||||
if obj.B != "1" {
|
||||
t.Fatal(obj)
|
||||
}
|
||||
|
||||
data = []byte(`{"A":"0","B":"1"}`)
|
||||
dc = NewDecoder(rt.Mem2Str(data))
|
||||
dc.UseNumber()
|
||||
err = dc.Decode(&obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data[6] = '1'
|
||||
if obj.A != "1" {
|
||||
t.Fatal(obj)
|
||||
}
|
||||
data[14] = '0'
|
||||
if obj.B != "0" {
|
||||
t.Fatal(obj)
|
||||
}
|
||||
|
||||
data = []byte(`{"A":"0","B":"1"}`)
|
||||
dc = NewDecoder(rt.Mem2Str(data))
|
||||
dc.UseNumber()
|
||||
dc.CopyString()
|
||||
m := map[string]interface{}{}
|
||||
err = dc.Decode(&m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data[2] = 'C'
|
||||
data[6] = '1'
|
||||
if m["A"] != "0" {
|
||||
t.Fatal(m)
|
||||
}
|
||||
data[10] = 'D'
|
||||
data[14] = '0'
|
||||
if m["B"] != "1" {
|
||||
t.Fatal(m)
|
||||
}
|
||||
|
||||
data = []byte(`{"A":"0","B":"1"}`)
|
||||
dc = NewDecoder(rt.Mem2Str(data))
|
||||
dc.UseNumber()
|
||||
m = map[string]interface{}{}
|
||||
err = dc.Decode(&m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data[6] = '1'
|
||||
if m["A"] != "1" {
|
||||
t.Fatal(m)
|
||||
}
|
||||
data[14] = '0'
|
||||
if m["B"] != "0" {
|
||||
t.Fatal(m)
|
||||
}
|
||||
|
||||
data = []byte(`{"A":"0","B":"1"}`)
|
||||
dc = NewDecoder(rt.Mem2Str(data))
|
||||
dc.UseNumber()
|
||||
dc.CopyString()
|
||||
var x interface{}
|
||||
err = dc.Decode(&x)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data[2] = 'C'
|
||||
data[6] = '1'
|
||||
m = x.(map[string]interface{})
|
||||
if m["A"] != "0" {
|
||||
t.Fatal(m)
|
||||
}
|
||||
data[10] = 'D'
|
||||
data[14] = '0'
|
||||
if m["B"] != "1" {
|
||||
t.Fatal(m)
|
||||
}
|
||||
|
||||
data = []byte(`{"A":"0","B":"1"}`)
|
||||
dc = NewDecoder(rt.Mem2Str(data))
|
||||
dc.UseNumber()
|
||||
var y interface{}
|
||||
err = dc.Decode(&y)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m = y.(map[string]interface{})
|
||||
data[6] = '1'
|
||||
if m["A"] != "1" {
|
||||
t.Fatal(m)
|
||||
}
|
||||
data[14] = '0'
|
||||
if m["B"] != "0" {
|
||||
t.Fatal(m)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoder_Basic(t *testing.T) {
|
||||
var v int
|
||||
pos, err := decode("12345", &v)
|
||||
pos, err := decode("12345", &v, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, pos)
|
||||
assert.Equal(t, 12345, v)
|
||||
|
|
@ -105,7 +226,7 @@ func TestDecoder_Basic(t *testing.T) {
|
|||
|
||||
func TestDecoder_Generic(t *testing.T) {
|
||||
var v interface{}
|
||||
pos, err := decode(TwitterJson, &v)
|
||||
pos, err := decode(TwitterJson, &v, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(TwitterJson), pos)
|
||||
spew.Dump(v)
|
||||
|
|
@ -113,7 +234,7 @@ func TestDecoder_Generic(t *testing.T) {
|
|||
|
||||
func TestDecoder_Binding(t *testing.T) {
|
||||
var v TwitterStruct
|
||||
pos, err := decode(TwitterJson, &v)
|
||||
pos, err := decode(TwitterJson, &v, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(TwitterJson), pos)
|
||||
assert.Equal(t, _BindingValue, v, 0)
|
||||
|
|
@ -122,7 +243,7 @@ func TestDecoder_Binding(t *testing.T) {
|
|||
|
||||
func TestDecoder_MapWithIndirectElement(t *testing.T) {
|
||||
var v map[string]struct { A [129]byte }
|
||||
_, err := decode(`{"":{"A":[1,2,3,4,5]}}`, &v)
|
||||
_, err := decode(`{"":{"A":[1,2,3,4,5]}}`, &v, false)
|
||||
if x, ok := err.(SyntaxError); ok {
|
||||
println(x.Description())
|
||||
}
|
||||
|
|
@ -132,12 +253,23 @@ func TestDecoder_MapWithIndirectElement(t *testing.T) {
|
|||
|
||||
func BenchmarkDecoder_Generic_Sonic(b *testing.B) {
|
||||
var w interface{}
|
||||
_, _ = decode(TwitterJson, &w)
|
||||
_, _ = decode(TwitterJson, &w, true)
|
||||
b.SetBytes(int64(len(TwitterJson)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v interface{}
|
||||
_, _ = decode(TwitterJson, &v)
|
||||
_, _ = decode(TwitterJson, &v, true)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecoder_Generic_Sonic_Fast(b *testing.B) {
|
||||
var w interface{}
|
||||
_, _ = decode(TwitterJson, &w, false)
|
||||
b.SetBytes(int64(len(TwitterJson)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v interface{}
|
||||
_, _ = decode(TwitterJson, &v, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -179,12 +311,23 @@ func BenchmarkDecoder_Generic_GoJson(b *testing.B) {
|
|||
|
||||
func BenchmarkDecoder_Binding_Sonic(b *testing.B) {
|
||||
var w TwitterStruct
|
||||
_, _ = decode(TwitterJson, &w)
|
||||
_, _ = decode(TwitterJson, &w, true)
|
||||
b.SetBytes(int64(len(TwitterJson)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v TwitterStruct
|
||||
_, _ = decode(TwitterJson, &v)
|
||||
_, _ = decode(TwitterJson, &v, true)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecoder_Binding_Sonic_Fast(b *testing.B) {
|
||||
var w TwitterStruct
|
||||
_, _ = decode(TwitterJson, &w, false)
|
||||
b.SetBytes(int64(len(TwitterJson)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v TwitterStruct
|
||||
_, _ = decode(TwitterJson, &v, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,13 +369,26 @@ func BenchmarkDecoder_Binding_GoJson(b *testing.B) {
|
|||
|
||||
func BenchmarkDecoder_Parallel_Generic_Sonic(b *testing.B) {
|
||||
var w interface{}
|
||||
_, _ = decode(TwitterJson, &w)
|
||||
_, _ = decode(TwitterJson, &w, true)
|
||||
b.SetBytes(int64(len(TwitterJson)))
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
var v interface{}
|
||||
_, _ = decode(TwitterJson, &v)
|
||||
_, _ = decode(TwitterJson, &v, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDecoder_Parallel_Generic_Sonic_Fast(b *testing.B) {
|
||||
var w interface{}
|
||||
_, _ = decode(TwitterJson, &w, false)
|
||||
b.SetBytes(int64(len(TwitterJson)))
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
var v interface{}
|
||||
_, _ = decode(TwitterJson, &v, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -281,13 +437,26 @@ func BenchmarkDecoder_Parallel_Generic_GoJson(b *testing.B) {
|
|||
|
||||
func BenchmarkDecoder_Parallel_Binding_Sonic(b *testing.B) {
|
||||
var w TwitterStruct
|
||||
_, _ = decode(TwitterJson, &w)
|
||||
_, _ = decode(TwitterJson, &w, true)
|
||||
b.SetBytes(int64(len(TwitterJson)))
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
var v TwitterStruct
|
||||
_, _ = decode(TwitterJson, &v)
|
||||
_, _ = decode(TwitterJson, &v, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDecoder_Parallel_Binding_Sonic_Fast(b *testing.B) {
|
||||
var w TwitterStruct
|
||||
_, _ = decode(TwitterJson, &w, false)
|
||||
b.SetBytes(int64(len(TwitterJson)))
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
var v TwitterStruct
|
||||
_, _ = decode(TwitterJson, &v, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const (
|
|||
_VD_args = 8 // 8 bytes for passing arguments to this functions
|
||||
_VD_fargs = 64 // 64 bytes for passing arguments to other Go functions
|
||||
_VD_saves = 40 // 40 bytes for saving the registers before CALL instructions
|
||||
_VD_locals = 56 // 56 bytes for local variables
|
||||
_VD_locals = 88 // 88 bytes for local variables
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -68,6 +68,13 @@ var (
|
|||
_VAR_ss_Dc = jit.Ptr(_SP, _VD_fargs + _VD_saves + 48)
|
||||
)
|
||||
|
||||
var (
|
||||
_VAR_cs_LR = jit.Ptr(_SP, _VD_fargs + _VD_saves + 56)
|
||||
_VAR_cs_p = jit.Ptr(_SP, _VD_fargs + _VD_saves + 64)
|
||||
_VAR_cs_n = jit.Ptr(_SP, _VD_fargs + _VD_saves + 72)
|
||||
_VAR_cs_d = jit.Ptr(_SP, _VD_fargs + _VD_saves + 80)
|
||||
)
|
||||
|
||||
type _ValueDecoder struct {
|
||||
jit.BaseAssembler
|
||||
}
|
||||
|
|
@ -365,7 +372,6 @@ func (self *_ValueDecoder) compile() {
|
|||
|
||||
/** V_STRING **/
|
||||
self.Link("_decode_V_STRING") // _decode_V_STRING:
|
||||
self.Emit("XORL", _DX, _DX) // XORL DX, DX
|
||||
self.Emit("MOVQ", _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX
|
||||
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
|
||||
self.Emit("SUBQ", _CX, _AX) // SUBQ CX, AX
|
||||
|
|
@ -375,7 +381,12 @@ func (self *_ValueDecoder) compile() {
|
|||
self.Sjmp("JNE" , "_unquote") // JNE _unquote
|
||||
self.Emit("SUBQ", jit.Imm(1), _AX) // SUBQ $1, AX
|
||||
self.Emit("LEAQ", jit.Sib(_IP, _CX, 1, 0), _R8) // LEAQ (IP)(CX), R8
|
||||
|
||||
self.Byte(0x48, 0x8d, 0x3d) // LEAQ (PC), DI
|
||||
self.Sref("_copy_string_end", 4)
|
||||
self.Emit("BTQ", jit.Imm(_F_copy_string), _VAR_df)
|
||||
self.Sjmp("JC", "copy_string")
|
||||
self.Link("_copy_string_end")
|
||||
self.Emit("XORL", _DX, _DX) // XORL DX, DX
|
||||
/* strings with no escape sequences */
|
||||
self.Link("_noescape") // _noescape:
|
||||
self.Emit("MOVL", jit.Imm(_S_omask), _DI) // MOVL _S_omask, DI
|
||||
|
|
@ -622,6 +633,30 @@ func (self *_ValueDecoder) compile() {
|
|||
self.WriteRecNotAX(8 , _DI, jit.Ptr(_SI, 0), false) // MOVQ R10, (SI)
|
||||
self.Sjmp("JMP" , "_array_append") // JMP _array_append
|
||||
|
||||
/* copy string */
|
||||
self.Link("copy_string") // pointer: R8, length: AX, return addr: DI
|
||||
// self.Byte(0xcc)
|
||||
self.Emit("MOVQ", _R8, _VAR_cs_p)
|
||||
self.Emit("MOVQ", _AX, _VAR_cs_n)
|
||||
self.Emit("MOVQ", _DI, _VAR_cs_LR)
|
||||
self.Emit("MOVQ", _T_byte, jit.Ptr(_SP, 0))
|
||||
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 8))
|
||||
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))
|
||||
self.call_go(_F_makeslice)
|
||||
self.Emit("MOVQ", jit.Ptr(_SP, 24), _R8)
|
||||
self.Emit("MOVQ", _R8, _VAR_cs_d)
|
||||
self.Emit("MOVQ", _R8, jit.Ptr(_SP, 0))
|
||||
self.Emit("MOVQ", _VAR_cs_p, _R8)
|
||||
self.Emit("MOVQ", _R8, jit.Ptr(_SP, 8))
|
||||
self.Emit("MOVQ", _VAR_cs_n, _AX)
|
||||
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))
|
||||
self.call_go(_F_memmove)
|
||||
self.Emit("MOVQ", _VAR_cs_d, _R8)
|
||||
self.Emit("MOVQ", _VAR_cs_n, _AX)
|
||||
self.Emit("MOVQ", _VAR_cs_LR, _DI)
|
||||
// self.Byte(0xcc)
|
||||
self.Rjmp("JMP", _DI)
|
||||
|
||||
/* error handlers */
|
||||
self.Link("_stack_overflow")
|
||||
self.Emit("MOVL" , _E_recurse, _EP) // MOVQ _E_recurse, EP
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const (
|
|||
_VD_args = 8 // 8 bytes for passing arguments to this functions
|
||||
_VD_fargs = 64 // 64 bytes for passing arguments to other Go functions
|
||||
_VD_saves = 48 // 48 bytes for saving the registers before CALL instructions
|
||||
_VD_locals = 64 // 64 bytes for local variables
|
||||
_VD_locals = 96 // 96 bytes for local variables
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -75,6 +75,13 @@ type _ValueDecoder struct {
|
|||
jit.BaseAssembler
|
||||
}
|
||||
|
||||
var (
|
||||
_VAR_cs_LR = jit.Ptr(_SP, _VD_fargs + _VD_saves + 64)
|
||||
_VAR_cs_p = jit.Ptr(_SP, _VD_fargs + _VD_saves + 72)
|
||||
_VAR_cs_n = jit.Ptr(_SP, _VD_fargs + _VD_saves + 80)
|
||||
_VAR_cs_d = jit.Ptr(_SP, _VD_fargs + _VD_saves + 88)
|
||||
)
|
||||
|
||||
func (self *_ValueDecoder) build() uintptr {
|
||||
self.Init(self.compile)
|
||||
return *(*uintptr)(self.LoadWithFaker("decode_value", _VD_size, _VD_args, _Decoder_Generic_Shadow))
|
||||
|
|
@ -376,7 +383,6 @@ func (self *_ValueDecoder) compile() {
|
|||
|
||||
/** V_STRING **/
|
||||
self.Link("_decode_V_STRING") // _decode_V_STRING:
|
||||
self.Emit("XORL", _DX, _DX) // XORL DX, DX
|
||||
self.Emit("MOVQ", _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX
|
||||
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
|
||||
self.Emit("SUBQ", _CX, _AX) // SUBQ CX, AX
|
||||
|
|
@ -386,6 +392,12 @@ func (self *_ValueDecoder) compile() {
|
|||
self.Sjmp("JNE" , "_unquote") // JNE _unquote
|
||||
self.Emit("SUBQ", jit.Imm(1), _AX) // SUBQ $1, AX
|
||||
self.Emit("LEAQ", jit.Sib(_IP, _CX, 1, 0), _R8) // LEAQ (IP)(CX), R8
|
||||
self.Byte(0x48, 0x8d, 0x3d) // LEAQ (PC), DI
|
||||
self.Sref("_copy_string_end", 4)
|
||||
self.Emit("BTQ", jit.Imm(_F_copy_string), _VAR_df)
|
||||
self.Sjmp("JC", "copy_string")
|
||||
self.Link("_copy_string_end")
|
||||
self.Emit("XORL", _DX, _DX)
|
||||
|
||||
/* strings with no escape sequences */
|
||||
self.Link("_noescape") // _noescape:
|
||||
|
|
@ -625,6 +637,24 @@ func (self *_ValueDecoder) compile() {
|
|||
self.WriteRecNotAX(8 , _DI, jit.Ptr(_SI, 0), false) // MOVQ R10, (SI)
|
||||
self.Sjmp("JMP" , "_array_append") // JMP _array_append
|
||||
|
||||
/* copy string */
|
||||
self.Link("copy_string") // pointer: R8, length: AX, return addr: DI
|
||||
self.Emit("MOVQ", _R8, _VAR_cs_p)
|
||||
self.Emit("MOVQ", _AX, _VAR_cs_n)
|
||||
self.Emit("MOVQ", _DI, _VAR_cs_LR)
|
||||
self.Emit("MOVQ", _AX, _BX)
|
||||
self.Emit("MOVQ", _AX, _CX)
|
||||
self.Emit("MOVQ", _T_byte, _AX)
|
||||
self.call_go(_F_makeslice)
|
||||
self.Emit("MOVQ", _AX, _VAR_cs_d)
|
||||
self.Emit("MOVQ", _VAR_cs_p, _BX)
|
||||
self.Emit("MOVQ", _VAR_cs_n, _CX)
|
||||
self.call_go(_F_memmove)
|
||||
self.Emit("MOVQ", _VAR_cs_d, _R8)
|
||||
self.Emit("MOVQ", _VAR_cs_n, _AX)
|
||||
self.Emit("MOVQ", _VAR_cs_LR, _DI)
|
||||
self.Rjmp("JMP", _DI)
|
||||
|
||||
/* error handlers */
|
||||
self.Link("_stack_overflow")
|
||||
self.Emit("MOVL" , _E_recurse, _EP) // MOVQ _E_recurse, EP
|
||||
|
|
|
|||
82
decoder/norace_test.go
Normal file
82
decoder/norace_test.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
// +build !race
|
||||
|
||||
/*
|
||||
* 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 decoder
|
||||
|
||||
import (
|
||||
`testing`
|
||||
`unsafe`
|
||||
`runtime`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
var referred = false
|
||||
|
||||
func TestStringReferring(t *testing.T) {
|
||||
str := []byte(`{"A":"0","B":"1"}`)
|
||||
sp := *(**byte)(unsafe.Pointer(&str))
|
||||
println("malloc *byte ", sp)
|
||||
runtime.SetFinalizer(sp, func(sp *byte){
|
||||
referred = false
|
||||
println("*byte ", sp, " got free")
|
||||
})
|
||||
runtime.GC()
|
||||
println("first GC")
|
||||
var obj struct{
|
||||
A string
|
||||
B string
|
||||
}
|
||||
dc := NewDecoder(rt.Mem2Str(str))
|
||||
dc.CopyString()
|
||||
referred = true
|
||||
if err := dc.Decode(&obj); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runtime.GC()
|
||||
println("second GC")
|
||||
if referred {
|
||||
t.Fatal("*byte is being referred")
|
||||
}
|
||||
|
||||
str2 := []byte(`{"A":"0","B":"1"}`)
|
||||
sp2 := *(**byte)(unsafe.Pointer(&str2))
|
||||
println("malloc *byte ", sp2)
|
||||
runtime.SetFinalizer(sp2, func(sp *byte){
|
||||
referred = false
|
||||
println("*byte ", sp, " got free")
|
||||
})
|
||||
runtime.GC()
|
||||
println("first GC")
|
||||
var obj2 interface{}
|
||||
dc2 := NewDecoder(rt.Mem2Str(str2))
|
||||
dc2.UseNumber()
|
||||
dc2.CopyString()
|
||||
referred = true
|
||||
if err := dc2.Decode(&obj2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runtime.GC()
|
||||
println("second GC")
|
||||
if referred {
|
||||
t.Fatal("*byte is being referred")
|
||||
}
|
||||
|
||||
runtime.KeepAlive(&obj)
|
||||
runtime.KeepAlive(&obj2)
|
||||
}
|
||||
|
|
@ -58,6 +58,11 @@ func convTstring(v string) unsafe.Pointer
|
|||
//goland:noinspection GoUnusedParameter
|
||||
func memequal(a unsafe.Pointer, b unsafe.Pointer, size uintptr) bool
|
||||
|
||||
//go:noescape
|
||||
//go:linkname memmove runtime.memmove
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||
|
||||
//go:linkname mallocgc runtime.mallocgc
|
||||
//goland:noinspection GoUnusedParameter
|
||||
func mallocgc(size uintptr, typ *rt.GoType, needzero bool) unsafe.Pointer
|
||||
|
|
|
|||
5
sonic.go
5
sonic.go
|
|
@ -25,7 +25,6 @@ import (
|
|||
`github.com/bytedance/sonic/encoder`
|
||||
`github.com/bytedance/sonic/option`
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
// Marshal returns the JSON encoding of v.
|
||||
|
|
@ -35,8 +34,10 @@ func Marshal(val interface{}) ([]byte, error) {
|
|||
|
||||
// Unmarshal parses the JSON-encoded data and stores the result in the value
|
||||
// pointed to by v.
|
||||
// NOTICE: This API copies given buffer by default.
|
||||
// if you want to pass JSON more effecient, use UnmarshalString instead.
|
||||
func Unmarshal(buf []byte, val interface{}) error {
|
||||
return UnmarshalString(rt.Mem2Str(buf), val)
|
||||
return UnmarshalString(string(buf), val)
|
||||
}
|
||||
|
||||
// UnmarshalString is like Unmarshal, except buf is a string.
|
||||
|
|
|
|||
Loading…
Reference in a new issue