mirror of
https://github.com/ii64/sonic.git
synced 2026-06-23 18:06:44 +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_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
|
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_Sonic-16 71589 ns/op 182.08 MB/s 57531 B/op 723 allocs/op
|
||||||
BenchmarkDecoder_Generic_StdLib-16 146902 ns/op 88.73 MB/s 50872 B/op 772 allocs/op
|
BenchmarkDecoder_Generic_Sonic_Fast-16 57653 ns/op 226.10 MB/s 49743 B/op 313 allocs/op
|
||||||
BenchmarkDecoder_Generic_JsonIter-16 101592 ns/op 128.31 MB/s 55783 B/op 1068 allocs/op
|
BenchmarkDecoder_Generic_StdLib-16 143584 ns/op 90.78 MB/s 50870 B/op 772 allocs/op
|
||||||
BenchmarkDecoder_Generic_GoJson-16 97865 ns/op 133.19 MB/s 66367 B/op 973 allocs/op
|
BenchmarkDecoder_Generic_JsonIter-16 94775 ns/op 137.54 MB/s 55783 B/op 1068 allocs/op
|
||||||
BenchmarkDecoder_Binding_Sonic-16 31418 ns/op 414.90 MB/s 24779 B/op 34 allocs/op
|
BenchmarkDecoder_Generic_GoJson-16 88647 ns/op 147.04 MB/s 66371 B/op 973 allocs/op
|
||||||
BenchmarkDecoder_Binding_StdLib-16 131260 ns/op 99.31 MB/s 10576 B/op 208 allocs/op
|
BenchmarkDecoder_Binding_Sonic-16 32399 ns/op 402.33 MB/s 27814 B/op 137 allocs/op
|
||||||
BenchmarkDecoder_Binding_JsonIter-16 39298 ns/op 331.70 MB/s 14673 B/op 385 allocs/op
|
BenchmarkDecoder_Binding_Sonic_Fast-16 28655 ns/op 454.89 MB/s 25127 B/op 34 allocs/op
|
||||||
BenchmarkDecoder_Binding_GoJson-16 31852 ns/op 409.24 MB/s 22040 B/op 49 allocs/op
|
BenchmarkDecoder_Binding_StdLib-16 116617 ns/op 111.78 MB/s 7344 B/op 103 allocs/op
|
||||||
BenchmarkDecoder_Parallel_Generic_Sonic-16 10525 ns/op 1238.44 MB/s 49470 B/op 313 allocs/op
|
BenchmarkDecoder_Binding_JsonIter-16 36206 ns/op 360.02 MB/s 14673 B/op 385 allocs/op
|
||||||
BenchmarkDecoder_Parallel_Generic_StdLib-16 60223 ns/op 216.44 MB/s 50875 B/op 772 allocs/op
|
BenchmarkDecoder_Binding_GoJson-16 29396 ns/op 443.43 MB/s 22042 B/op 49 allocs/op
|
||||||
BenchmarkDecoder_Parallel_Generic_JsonIter-16 60918 ns/op 213.97 MB/s 55817 B/op 1068 allocs/op
|
BenchmarkDecoder_Parallel_Generic_Sonic-16 12243 ns/op 1064.68 MB/s 57135 B/op 723 allocs/op
|
||||||
BenchmarkDecoder_Parallel_Generic_GoJson-16 48386 ns/op 269.39 MB/s 66425 B/op 974 allocs/op
|
BenchmarkDecoder_Parallel_Generic_Sonic_Fast-16 10101 ns/op 1290.48 MB/s 49440 B/op 313 allocs/op
|
||||||
BenchmarkDecoder_Parallel_Binding_Sonic-16 7319 ns/op 1781.00 MB/s 24889 B/op 34 allocs/op
|
BenchmarkDecoder_Parallel_Generic_StdLib-16 57352 ns/op 227.28 MB/s 50877 B/op 772 allocs/op
|
||||||
BenchmarkDecoder_Parallel_Binding_StdLib-16 40494 ns/op 321.90 MB/s 10575 B/op 208 allocs/op
|
BenchmarkDecoder_Parallel_Generic_JsonIter-16 58693 ns/op 222.09 MB/s 55814 B/op 1068 allocs/op
|
||||||
BenchmarkDecoder_Parallel_Binding_JsonIter-16 18840 ns/op 691.89 MB/s 14679 B/op 385 allocs/op
|
BenchmarkDecoder_Parallel_Generic_GoJson-16 45245 ns/op 288.10 MB/s 66430 B/op 974 allocs/op
|
||||||
BenchmarkDecoder_Parallel_Binding_GoJson-16 17078 ns/op 763.28 MB/s 22211 B/op 49 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_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
|
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),
|
// If the type is too deep nesting (nesting depth > 5),
|
||||||
// you can set compile recursive depth in Pretouch for better stability in JIT.
|
// you can set compile recursive depth in Pretouch for better stability in JIT.
|
||||||
err := sonic.Pretouch(reflect.TypeOf(v), option.WithCompileRecursiveDepth(depth))
|
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`
|
### 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).
|
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)$"
|
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
|
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
|
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)$"
|
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_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_fargs = 80 // 80 bytes for passing arguments to other Go functions
|
||||||
_FP_saves = 40 // 40 bytes for saving the registers before CALL instructions
|
_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 (
|
const (
|
||||||
|
|
@ -189,6 +189,12 @@ var (
|
||||||
_VAR_ss_R9 = jit.Ptr(_SP, _FP_fargs + _FP_saves + 80)
|
_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 {
|
type _Assembler struct {
|
||||||
jit.BaseAssembler
|
jit.BaseAssembler
|
||||||
p _Program
|
p _Program
|
||||||
|
|
@ -214,6 +220,9 @@ func (self *_Assembler) compile() {
|
||||||
self.prologue()
|
self.prologue()
|
||||||
self.instrs()
|
self.instrs()
|
||||||
self.epilogue()
|
self.epilogue()
|
||||||
|
self.copy_string()
|
||||||
|
self.escape_string()
|
||||||
|
self.escape_string_twice()
|
||||||
self.type_error()
|
self.type_error()
|
||||||
self.field_error()
|
self.field_error()
|
||||||
self.range_error()
|
self.range_error()
|
||||||
|
|
@ -601,6 +610,79 @@ func (self *_Assembler) parse_unsigned() {
|
||||||
self.check_err()
|
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 **/
|
/** Range Checking Routines **/
|
||||||
|
|
||||||
var (
|
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
|
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.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}
|
self.Emit("MOVQ" , _SI, n) // MOVQ SI, ${n}
|
||||||
if stack {
|
if stack {
|
||||||
self.Emit("MOVQ", _DI, p) // MOVQ DI, ${p}
|
self.Emit("MOVQ", _DI, p)
|
||||||
} else {
|
} else {
|
||||||
self.WriteRecNotAX(10, _DI, p, false, false)
|
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) {
|
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.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.Sjmp("JNE" , _LB_char_m2_error) // JNE _char_m2_error
|
||||||
self.slice_from(_VAR_st_Iv, -3) // SLICE st.Iv, $-3
|
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
|
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("ADDQ" , _VAR_st_Iv, _AX) // ADDQ st.Iv, AX
|
||||||
self.Emit("CMPQ" , _VAR_st_Ep, _AX) // CMPQ st.Ep, AX
|
self.Emit("CMPQ" , _VAR_st_Ep, _AX) // CMPQ st.Ep, AX
|
||||||
self.Sjmp("JE" , "_noescape_{n}") // JE _noescape_{n}
|
self.Sjmp("JE" , "_noescape_{n}") // JE _noescape_{n}
|
||||||
self.malloc(_SI, _DX) // MALLOC SI, DX
|
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||||
self.Emit("MOVQ" , p, _DI) // MOVQ ${p}, DI
|
self.Sref("_unquote_twice_write_{n}", 4)
|
||||||
self.Emit("MOVQ" , n, _SI) // MOVQ ${n}, SI
|
self.Sjmp("JMP" , "_escape_string_twice")
|
||||||
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.Link("_noescape_{n}") // _noescape_{n}:
|
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 **/
|
/** 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) {
|
func (self *_Assembler) unmarshal_text(t reflect.Type, deref bool) {
|
||||||
self.parse_string() // PARSE STRING
|
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}
|
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 (
|
var (
|
||||||
_F_memequal = jit.Func(memequal)
|
_F_memequal = jit.Func(memequal)
|
||||||
|
_F_memmove = jit.Func(memmove)
|
||||||
_F_growslice = jit.Func(growslice)
|
_F_growslice = jit.Func(growslice)
|
||||||
_F_makeslice = jit.Func(makeslice)
|
_F_makeslice = jit.Func(makeslice)
|
||||||
_F_makemap_small = jit.Func(makemap_small)
|
_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) {
|
func (self *_Assembler) _asm_OP_str(_ *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
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) {
|
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) {
|
func (self *_Assembler) _asm_OP_num(_ *_Instr) {
|
||||||
self.parse_number() // PARSE NUMBER
|
self.parse_number() // PARSE NUMBER
|
||||||
self.slice_from(_VAR_st_Ep, 0) // SLICE st.Ep, $0
|
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.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) {
|
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) {
|
func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
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) {
|
if vt := p.vt(); !mapfast(vt) {
|
||||||
self.valloc(vt.Key(), _DI)
|
self.valloc(vt.Key(), _DI)
|
||||||
self.Emit("MOVOU", _VAR_sv, _X0)
|
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) {
|
func (self *_Assembler) _asm_OP_map_key_utext(p *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
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
|
self.mapassign_utext(p.vt(), false) // MAPASSIGN utext, ${p.vt()}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_key_utext_p(p *_Instr) {
|
func (self *_Assembler) _asm_OP_map_key_utext_p(p *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
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
|
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" , jit.Imm(-1), _AX) // MOVQ $-1, AX
|
||||||
self.Emit("MOVQ" , _AX, _VAR_sr) // MOVQ AX, sr
|
self.Emit("MOVQ" , _AX, _VAR_sr) // MOVQ AX, sr
|
||||||
self.parse_string() // PARSE STRING
|
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("LEAQ" , _VAR_sv, _AX) // LEAQ sv, AX
|
||||||
self.Emit("XORL" , _CX, _CX) // XORL CX, CX
|
self.Emit("XORL" , _CX, _CX) // XORL CX, CX
|
||||||
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
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_args = 72 // 72 bytes to pass and spill register arguements
|
||||||
_FP_fargs = 80 // 80 bytes for passing arguments to other Go functions
|
_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_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 (
|
const (
|
||||||
|
|
@ -184,6 +184,12 @@ var (
|
||||||
_VAR_ss_R9 = jit.Ptr(_SP, _FP_fargs + _FP_saves + 80)
|
_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 {
|
type _Assembler struct {
|
||||||
jit.BaseAssembler
|
jit.BaseAssembler
|
||||||
p _Program
|
p _Program
|
||||||
|
|
@ -209,6 +215,9 @@ func (self *_Assembler) compile() {
|
||||||
self.prologue()
|
self.prologue()
|
||||||
self.instrs()
|
self.instrs()
|
||||||
self.epilogue()
|
self.epilogue()
|
||||||
|
self.copy_string()
|
||||||
|
self.escape_string()
|
||||||
|
self.escape_string_twice()
|
||||||
self.type_error()
|
self.type_error()
|
||||||
self.field_error()
|
self.field_error()
|
||||||
self.range_error()
|
self.range_error()
|
||||||
|
|
@ -532,7 +541,7 @@ var (
|
||||||
_F_mallocgc = jit.Func(mallocgc)
|
_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", nb, _AX) // MOVQ ${nb}, AX
|
||||||
self.Emit("MOVQ", _T_byte, _BX) // MOVQ ${type(byte)}, BX
|
self.Emit("MOVQ", _T_byte, _BX) // MOVQ ${type(byte)}, BX
|
||||||
self.Emit("XORL", _CX, _CX) // XORL CX, CX
|
self.Emit("XORL", _CX, _CX) // XORL CX, CX
|
||||||
|
|
@ -611,6 +620,75 @@ func (self *_Assembler) parse_unsigned() {
|
||||||
self.check_err()
|
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 **/
|
/** Range Checking Routines **/
|
||||||
|
|
||||||
var (
|
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
|
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.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 {
|
if stack {
|
||||||
self.Emit("MOVQ", _DI, p) // MOVQ DI, ${p}
|
self.Emit("MOVQ", _DI, p)
|
||||||
} else {
|
} else {
|
||||||
self.WriteRecNotAX(10, _DI, p, false, false)
|
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) {
|
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.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.Sjmp("JNE" , _LB_char_m2_error) // JNE _char_m2_error
|
||||||
self.slice_from(_VAR_st_Iv, -3) // SLICE st.Iv, $-3
|
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
|
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("ADDQ" , _VAR_st_Iv, _AX) // ADDQ st.Iv, AX
|
||||||
self.Emit("CMPQ" , _VAR_st_Ep, _AX) // CMPQ st.Ep, AX
|
self.Emit("CMPQ" , _VAR_st_Ep, _AX) // CMPQ st.Ep, AX
|
||||||
self.Sjmp("JE" , "_noescape_{n}") // JE _noescape_{n}
|
self.Sjmp("JE" , "_noescape_{n}") // JE _noescape_{n}
|
||||||
self.malloc(_SI, _DX) // MALLOC SI, DX
|
self.Byte(0x4c, 0x8d, 0x0d) // LEAQ (PC), R9
|
||||||
self.Emit("MOVQ" , p, _DI) // MOVQ ${p}, DI
|
self.Sref("_unquote_twice_write_{n}", 4)
|
||||||
self.Emit("MOVQ" , n, _SI) // MOVQ ${n}, SI
|
self.Sjmp("JMP" , "_escape_string_twice")
|
||||||
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.Link("_noescape_{n}") // _noescape_{n}:
|
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 **/
|
/** 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) {
|
func (self *_Assembler) unmarshal_text(t reflect.Type, deref bool) {
|
||||||
self.parse_string() // PARSE STRING
|
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}
|
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 (
|
var (
|
||||||
_F_memequal = jit.Func(memequal)
|
_F_memequal = jit.Func(memequal)
|
||||||
|
_F_memmove = jit.Func(memmove)
|
||||||
_F_growslice = jit.Func(growslice)
|
_F_growslice = jit.Func(growslice)
|
||||||
_F_makeslice = jit.Func(makeslice)
|
_F_makeslice = jit.Func(makeslice)
|
||||||
_F_makemap_small = jit.Func(makemap_small)
|
_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) {
|
func (self *_Assembler) _asm_OP_str(_ *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
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) {
|
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("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("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.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 */
|
/* check for AVX2 support */
|
||||||
if !cpu.HasAVX2 {
|
if !cpu.HasAVX2 {
|
||||||
|
|
@ -1102,8 +1162,14 @@ func (self *_Assembler) _asm_OP_bool(_ *_Instr) {
|
||||||
func (self *_Assembler) _asm_OP_num(_ *_Instr) {
|
func (self *_Assembler) _asm_OP_num(_ *_Instr) {
|
||||||
self.parse_number() // PARSE NUMBER
|
self.parse_number() // PARSE NUMBER
|
||||||
self.slice_from(_VAR_st_Ep, 0) // SLICE st.Ep, $0
|
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.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) {
|
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) {
|
func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
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) {
|
if vt := p.vt(); !mapfast(vt) {
|
||||||
self.valloc(vt.Key(), _DI)
|
self.valloc(vt.Key(), _DI)
|
||||||
self.Emit("MOVOU", _VAR_sv, _X0)
|
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) {
|
func (self *_Assembler) _asm_OP_map_key_utext(p *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
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
|
self.mapassign_utext(p.vt(), false) // MAPASSIGN utext, ${p.vt()}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_key_utext_p(p *_Instr) {
|
func (self *_Assembler) _asm_OP_map_key_utext_p(p *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
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
|
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" , jit.Imm(-1), _AX) // MOVQ $-1, AX
|
||||||
self.Emit("MOVQ" , _AX, _VAR_sr) // MOVQ AX, sr
|
self.Emit("MOVQ" , _AX, _VAR_sr) // MOVQ AX, sr
|
||||||
self.parse_string() // PARSE STRING
|
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("LEAQ" , _VAR_sv, _AX) // LEAQ sv, AX
|
||||||
self.Emit("XORL" , _BX, _BX) // XORL BX, BX
|
self.Emit("XORL" , _BX, _BX) // XORL BX, BX
|
||||||
self.call_go(_F_strhash) // CALL_GO strhash
|
self.call_go(_F_strhash) // CALL_GO strhash
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ const (
|
||||||
_F_use_number
|
_F_use_number
|
||||||
_F_disable_urc
|
_F_disable_urc
|
||||||
_F_disable_unknown
|
_F_disable_unknown
|
||||||
|
_F_copy_string
|
||||||
)
|
)
|
||||||
|
|
||||||
// Decoder is the decoder context object
|
// Decoder is the decoder context object
|
||||||
|
|
@ -110,6 +111,11 @@ func (self *Decoder) DisallowUnknownFields() {
|
||||||
self.f |= 1 << _F_disable_unknown
|
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
|
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
|
||||||
// order to reduce the first-hit latency.
|
// order to reduce the first-hit latency.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,18 @@ package decoder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`encoding/json`
|
`encoding/json`
|
||||||
`testing`
|
|
||||||
`time`
|
|
||||||
`runtime`
|
`runtime`
|
||||||
`runtime/debug`
|
`runtime/debug`
|
||||||
`sync`
|
`sync`
|
||||||
|
`testing`
|
||||||
|
`time`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
`github.com/davecgh/go-spew/spew`
|
`github.com/davecgh/go-spew/spew`
|
||||||
gojson `github.com/goccy/go-json`
|
gojson `github.com/goccy/go-json`
|
||||||
`github.com/json-iterator/go`
|
`github.com/json-iterator/go`
|
||||||
`github.com/stretchr/testify/assert`
|
`github.com/stretchr/testify/assert`
|
||||||
`github.com/stretchr/testify/require`
|
`github.com/stretchr/testify/require`
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
|
@ -53,7 +53,7 @@ func TestGC(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var w interface{}
|
var w interface{}
|
||||||
out, err := decode(TwitterJson, &w)
|
out, err := decode(TwitterJson, &w, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +67,7 @@ func TestGC(t *testing.T) {
|
||||||
go func (wg *sync.WaitGroup) {
|
go func (wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
var w interface{}
|
var w interface{}
|
||||||
out, err := decode(TwitterJson, &w)
|
out, err := decode(TwitterJson, &w, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -86,8 +86,11 @@ func init() {
|
||||||
_ = json.Unmarshal([]byte(TwitterJson), &_BindingValue)
|
_ = 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)
|
d := NewDecoder(s)
|
||||||
|
if copy {
|
||||||
|
d.CopyString()
|
||||||
|
}
|
||||||
err := d.Decode(v)
|
err := d.Decode(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -95,9 +98,127 @@ func decode(s string, v interface{}) (int, error) {
|
||||||
return d.i, err
|
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) {
|
func TestDecoder_Basic(t *testing.T) {
|
||||||
var v int
|
var v int
|
||||||
pos, err := decode("12345", &v)
|
pos, err := decode("12345", &v, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 5, pos)
|
assert.Equal(t, 5, pos)
|
||||||
assert.Equal(t, 12345, v)
|
assert.Equal(t, 12345, v)
|
||||||
|
|
@ -105,7 +226,7 @@ func TestDecoder_Basic(t *testing.T) {
|
||||||
|
|
||||||
func TestDecoder_Generic(t *testing.T) {
|
func TestDecoder_Generic(t *testing.T) {
|
||||||
var v interface{}
|
var v interface{}
|
||||||
pos, err := decode(TwitterJson, &v)
|
pos, err := decode(TwitterJson, &v, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, len(TwitterJson), pos)
|
assert.Equal(t, len(TwitterJson), pos)
|
||||||
spew.Dump(v)
|
spew.Dump(v)
|
||||||
|
|
@ -113,7 +234,7 @@ func TestDecoder_Generic(t *testing.T) {
|
||||||
|
|
||||||
func TestDecoder_Binding(t *testing.T) {
|
func TestDecoder_Binding(t *testing.T) {
|
||||||
var v TwitterStruct
|
var v TwitterStruct
|
||||||
pos, err := decode(TwitterJson, &v)
|
pos, err := decode(TwitterJson, &v, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, len(TwitterJson), pos)
|
assert.Equal(t, len(TwitterJson), pos)
|
||||||
assert.Equal(t, _BindingValue, v, 0)
|
assert.Equal(t, _BindingValue, v, 0)
|
||||||
|
|
@ -122,7 +243,7 @@ func TestDecoder_Binding(t *testing.T) {
|
||||||
|
|
||||||
func TestDecoder_MapWithIndirectElement(t *testing.T) {
|
func TestDecoder_MapWithIndirectElement(t *testing.T) {
|
||||||
var v map[string]struct { A [129]byte }
|
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 {
|
if x, ok := err.(SyntaxError); ok {
|
||||||
println(x.Description())
|
println(x.Description())
|
||||||
}
|
}
|
||||||
|
|
@ -132,12 +253,23 @@ func TestDecoder_MapWithIndirectElement(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkDecoder_Generic_Sonic(b *testing.B) {
|
func BenchmarkDecoder_Generic_Sonic(b *testing.B) {
|
||||||
var w interface{}
|
var w interface{}
|
||||||
_, _ = decode(TwitterJson, &w)
|
_, _ = decode(TwitterJson, &w, true)
|
||||||
b.SetBytes(int64(len(TwitterJson)))
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
var v interface{}
|
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) {
|
func BenchmarkDecoder_Binding_Sonic(b *testing.B) {
|
||||||
var w TwitterStruct
|
var w TwitterStruct
|
||||||
_, _ = decode(TwitterJson, &w)
|
_, _ = decode(TwitterJson, &w, true)
|
||||||
b.SetBytes(int64(len(TwitterJson)))
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
var v TwitterStruct
|
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) {
|
func BenchmarkDecoder_Parallel_Generic_Sonic(b *testing.B) {
|
||||||
var w interface{}
|
var w interface{}
|
||||||
_, _ = decode(TwitterJson, &w)
|
_, _ = decode(TwitterJson, &w, true)
|
||||||
b.SetBytes(int64(len(TwitterJson)))
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
var v interface{}
|
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) {
|
func BenchmarkDecoder_Parallel_Binding_Sonic(b *testing.B) {
|
||||||
var w TwitterStruct
|
var w TwitterStruct
|
||||||
_, _ = decode(TwitterJson, &w)
|
_, _ = decode(TwitterJson, &w, true)
|
||||||
b.SetBytes(int64(len(TwitterJson)))
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
var v TwitterStruct
|
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_args = 8 // 8 bytes for passing arguments to this functions
|
||||||
_VD_fargs = 64 // 64 bytes for passing arguments to other Go 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_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 (
|
const (
|
||||||
|
|
@ -68,6 +68,13 @@ var (
|
||||||
_VAR_ss_Dc = jit.Ptr(_SP, _VD_fargs + _VD_saves + 48)
|
_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 {
|
type _ValueDecoder struct {
|
||||||
jit.BaseAssembler
|
jit.BaseAssembler
|
||||||
}
|
}
|
||||||
|
|
@ -365,7 +372,6 @@ func (self *_ValueDecoder) compile() {
|
||||||
|
|
||||||
/** V_STRING **/
|
/** V_STRING **/
|
||||||
self.Link("_decode_V_STRING") // _decode_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", _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX
|
||||||
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
|
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
|
||||||
self.Emit("SUBQ", _CX, _AX) // SUBQ CX, AX
|
self.Emit("SUBQ", _CX, _AX) // SUBQ CX, AX
|
||||||
|
|
@ -375,7 +381,12 @@ func (self *_ValueDecoder) compile() {
|
||||||
self.Sjmp("JNE" , "_unquote") // JNE _unquote
|
self.Sjmp("JNE" , "_unquote") // JNE _unquote
|
||||||
self.Emit("SUBQ", jit.Imm(1), _AX) // SUBQ $1, AX
|
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.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 */
|
/* strings with no escape sequences */
|
||||||
self.Link("_noescape") // _noescape:
|
self.Link("_noescape") // _noescape:
|
||||||
self.Emit("MOVL", jit.Imm(_S_omask), _DI) // MOVL _S_omask, DI
|
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.WriteRecNotAX(8 , _DI, jit.Ptr(_SI, 0), false) // MOVQ R10, (SI)
|
||||||
self.Sjmp("JMP" , "_array_append") // JMP _array_append
|
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 */
|
/* error handlers */
|
||||||
self.Link("_stack_overflow")
|
self.Link("_stack_overflow")
|
||||||
self.Emit("MOVL" , _E_recurse, _EP) // MOVQ _E_recurse, EP
|
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_args = 8 // 8 bytes for passing arguments to this functions
|
||||||
_VD_fargs = 64 // 64 bytes for passing arguments to other Go 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_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 (
|
const (
|
||||||
|
|
@ -75,6 +75,13 @@ type _ValueDecoder struct {
|
||||||
jit.BaseAssembler
|
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 {
|
func (self *_ValueDecoder) build() uintptr {
|
||||||
self.Init(self.compile)
|
self.Init(self.compile)
|
||||||
return *(*uintptr)(self.LoadWithFaker("decode_value", _VD_size, _VD_args, _Decoder_Generic_Shadow))
|
return *(*uintptr)(self.LoadWithFaker("decode_value", _VD_size, _VD_args, _Decoder_Generic_Shadow))
|
||||||
|
|
@ -376,7 +383,6 @@ func (self *_ValueDecoder) compile() {
|
||||||
|
|
||||||
/** V_STRING **/
|
/** V_STRING **/
|
||||||
self.Link("_decode_V_STRING") // _decode_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", _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX
|
||||||
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
|
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
|
||||||
self.Emit("SUBQ", _CX, _AX) // SUBQ CX, AX
|
self.Emit("SUBQ", _CX, _AX) // SUBQ CX, AX
|
||||||
|
|
@ -386,6 +392,12 @@ func (self *_ValueDecoder) compile() {
|
||||||
self.Sjmp("JNE" , "_unquote") // JNE _unquote
|
self.Sjmp("JNE" , "_unquote") // JNE _unquote
|
||||||
self.Emit("SUBQ", jit.Imm(1), _AX) // SUBQ $1, AX
|
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.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 */
|
/* strings with no escape sequences */
|
||||||
self.Link("_noescape") // _noescape:
|
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.WriteRecNotAX(8 , _DI, jit.Ptr(_SI, 0), false) // MOVQ R10, (SI)
|
||||||
self.Sjmp("JMP" , "_array_append") // JMP _array_append
|
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 */
|
/* error handlers */
|
||||||
self.Link("_stack_overflow")
|
self.Link("_stack_overflow")
|
||||||
self.Emit("MOVL" , _E_recurse, _EP) // MOVQ _E_recurse, EP
|
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
|
//goland:noinspection GoUnusedParameter
|
||||||
func memequal(a unsafe.Pointer, b unsafe.Pointer, size uintptr) bool
|
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
|
//go:linkname mallocgc runtime.mallocgc
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func mallocgc(size uintptr, typ *rt.GoType, needzero bool) unsafe.Pointer
|
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/encoder`
|
||||||
`github.com/bytedance/sonic/option`
|
`github.com/bytedance/sonic/option`
|
||||||
`github.com/bytedance/sonic/internal/native/types`
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Marshal returns the JSON encoding of v.
|
// 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
|
// Unmarshal parses the JSON-encoded data and stores the result in the value
|
||||||
// pointed to by v.
|
// 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 {
|
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.
|
// UnmarshalString is like Unmarshal, except buf is a string.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue