mirror of
https://github.com/ii64/sonic.git
synced 2026-06-21 00:46:43 +08:00
* fix:(StreamDecoder) Integer map key may be truncated and populated unexpectedly * feat: expose option `DefaultBufferSize` * test adjust * test adjust * feat: add options to make default buffer size adjustable
753 lines
No EOL
23 KiB
Go
753 lines
No EOL
23 KiB
Go
/*
|
|
* 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 (
|
|
`encoding/base64`
|
|
`encoding/json`
|
|
`reflect`
|
|
`testing`
|
|
`unsafe`
|
|
|
|
`github.com/bytedance/sonic/internal/caching`
|
|
`github.com/bytedance/sonic/internal/jit`
|
|
`github.com/bytedance/sonic/internal/native/types`
|
|
`github.com/bytedance/sonic/internal/rt`
|
|
`github.com/stretchr/testify/assert`
|
|
`github.com/stretchr/testify/require`
|
|
)
|
|
|
|
func TestAssembler_PrologueAndEpilogue(t *testing.T) {
|
|
a := newAssembler(nil)
|
|
_, e := a.Load()("", 0, nil, nil, 0, "", nil)
|
|
assert.Nil(t, e)
|
|
}
|
|
|
|
var utextVar []byte
|
|
type UtextValue int
|
|
|
|
func (UtextValue) UnmarshalText(text []byte) error {
|
|
utextVar = text
|
|
return nil
|
|
}
|
|
|
|
var ujsonVar []byte
|
|
type UjsonValue int
|
|
|
|
func (UjsonValue) UnmarshalJSON(json []byte) error {
|
|
ujsonVar = json
|
|
return nil
|
|
}
|
|
|
|
type UtextStruct struct {
|
|
V string
|
|
}
|
|
|
|
func (self *UtextStruct) UnmarshalText(text []byte) error {
|
|
self.V = string(text)
|
|
return nil
|
|
}
|
|
|
|
type UjsonStruct struct {
|
|
V string
|
|
}
|
|
|
|
func (self *UjsonStruct) UnmarshalJSON(v []byte) error {
|
|
self.V = string(v)
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
_OP_dbg_get_sr _Op = 253
|
|
_OP_dbg_set_sr _Op = 254
|
|
_OP_dbg_break _Op = 255
|
|
)
|
|
|
|
func (self *_Assembler) _asm_OP_dbg_get_sr(_ *_Instr) {
|
|
self.Emit("MOVQ", _VAR_sr, _AX)
|
|
self.Emit("MOVQ", _AX, jit.Ptr(_VP, 0))
|
|
}
|
|
|
|
func (self *_Assembler) _asm_OP_dbg_set_sr(p *_Instr) {
|
|
self.Emit("MOVQ", jit.Imm(p.i64()), _AX)
|
|
self.Emit("MOVQ", _AX, _VAR_sr)
|
|
}
|
|
|
|
func (self *_Assembler) _asm_OP_dbg_break(_ *_Instr) {
|
|
self.Byte(0xcc)
|
|
}
|
|
|
|
func init() {
|
|
_OpNames[_OP_dbg_get_sr] = "dbg_get_sr"
|
|
_OpNames[_OP_dbg_set_sr] = "dbg_set_sr"
|
|
_OpNames[_OP_dbg_break] = "dbg_break"
|
|
_OpFuncTab[_OP_dbg_get_sr] = (*_Assembler)._asm_OP_dbg_get_sr
|
|
_OpFuncTab[_OP_dbg_set_sr] = (*_Assembler)._asm_OP_dbg_set_sr
|
|
_OpFuncTab[_OP_dbg_break] = (*_Assembler)._asm_OP_dbg_break
|
|
}
|
|
|
|
type testOps struct {
|
|
key string
|
|
ins _Program
|
|
src string
|
|
pos int
|
|
opt uint64
|
|
vfn func(i int, v interface{})
|
|
exp interface{}
|
|
err error
|
|
val interface{}
|
|
}
|
|
|
|
func testOpCode(t *testing.T, ops *testOps) {
|
|
p := ops.ins
|
|
k := new(_Stack)
|
|
a := newAssembler(p)
|
|
f := a.Load()
|
|
i, e := f(ops.src, ops.pos, rt.UnpackEface(ops.val).Value, k, ops.opt, "", nil)
|
|
if ops.err != nil {
|
|
assert.EqualError(t, e, ops.err.Error())
|
|
} else {
|
|
assert.NoError(t, e)
|
|
if ops.vfn != nil {
|
|
if ops.val == nil {
|
|
ops.vfn(i, nil)
|
|
} else {
|
|
ops.vfn(i, reflect.Indirect(reflect.ValueOf(ops.val)).Interface())
|
|
}
|
|
} else {
|
|
if ops.val == nil {
|
|
assert.Nil(t, ops.exp)
|
|
} else {
|
|
assert.Equal(t, ops.exp, reflect.Indirect(reflect.ValueOf(ops.val)).Interface())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAssembler_OpCode(t *testing.T) {
|
|
tests := []testOps{
|
|
{
|
|
key: "_OP_any/stdlib",
|
|
ins: []_Instr{newInsOp(_OP_any)},
|
|
src: `{"a": [1, 2, 3]}`,
|
|
exp: map[string]interface{}{"a": []interface{}{1.0, 2.0, 3.0}},
|
|
val: new(interface{}),
|
|
},
|
|
{
|
|
key: "_OP_any/use_int64",
|
|
ins: []_Instr{newInsOp(_OP_any)},
|
|
src: `{"a": [1, 2, 3]}`,
|
|
opt: 1 << _F_use_int64,
|
|
exp: map[string]interface{}{"a": []interface{}{int64(1), int64(2), int64(3)}},
|
|
val: new(interface{}),
|
|
},
|
|
{
|
|
key: "_OP_any/use_number",
|
|
ins: []_Instr{newInsOp(_OP_any)},
|
|
src: `{"a": [1, 2, 3]}`,
|
|
opt: 1 << _F_use_number,
|
|
exp: map[string]interface{}{"a": []interface{}{json.Number("1"), json.Number("2"), json.Number("3")}},
|
|
val: new(interface{}),
|
|
},
|
|
{
|
|
key: "_OP_str/plain",
|
|
ins: []_Instr{newInsOp(_OP_str)},
|
|
src: `hello, world"`,
|
|
exp: "hello, world",
|
|
val: new(string),
|
|
}, {
|
|
key: "_OP_str/unquote",
|
|
ins: []_Instr{newInsOp(_OP_str)},
|
|
src: `hello, world \\ \" \/ \b \f \n \r \t \u666f 测试中文 \ud83d\ude00"`,
|
|
exp: "hello, world \\ \" / \b \f \n \r \t 景 测试中文 😀",
|
|
val: new(string),
|
|
}, {
|
|
key: "_OP_str/unquote_unirep",
|
|
ins: []_Instr{newInsOp(_OP_str)},
|
|
src: `hello\ud800world"`,
|
|
exp: "hello\ufffdworld",
|
|
val: new(string),
|
|
}, {
|
|
key: "_OP_str/error_eof",
|
|
ins: []_Instr{newInsOp(_OP_str)},
|
|
src: `12345`,
|
|
err: SyntaxError{Src: `12345`, Pos: 5, Code: types.ERR_EOF},
|
|
val: new(string),
|
|
}, {
|
|
key: "_OP_str/error_invalid_escape",
|
|
ins: []_Instr{newInsOp(_OP_str)},
|
|
src: `12\g345"`,
|
|
err: SyntaxError{Src: `12\g345"`, Pos: 3, Code: types.ERR_INVALID_ESCAPE},
|
|
val: new(string),
|
|
}, {
|
|
key: "_OP_str/error_invalid_unicode",
|
|
ins: []_Instr{newInsOp(_OP_str)},
|
|
src: `hello\ud800world"`,
|
|
opt: 1 << _F_disable_urc,
|
|
err: SyntaxError{Src: `hello\ud800world"`, Pos: 7, Code: types.ERR_INVALID_UNICODE},
|
|
val: new(string),
|
|
}, {
|
|
key: "_OP_str/error_invalid_char",
|
|
ins: []_Instr{newInsOp(_OP_str)},
|
|
src: `12\u1ggg345"`,
|
|
err: SyntaxError{Src: `12\u1ggg345"`, Pos: 5, Code: types.ERR_INVALID_CHAR},
|
|
val: new(string),
|
|
}, {
|
|
key: "_OP_bin",
|
|
ins: []_Instr{newInsOp(_OP_bin)},
|
|
src: `aGVsbG8sIHdvcmxk"`,
|
|
exp: []byte("hello, world"),
|
|
val: new([]byte),
|
|
}, {
|
|
key: "_OP_bin/error_eof",
|
|
ins: []_Instr{newInsOp(_OP_bin)},
|
|
src: `aGVsbG8sIHdvcmxk`,
|
|
err: SyntaxError{Src: `aGVsbG8sIHdvcmxk`, Pos: 16, Code: types.ERR_EOF},
|
|
val: new([]byte),
|
|
}, {
|
|
key: "_OP_bin/error_corrupt_input",
|
|
ins: []_Instr{newInsOp(_OP_bin)},
|
|
src: `aGVsbG8!sIHdvcmxk"`,
|
|
err: base64.CorruptInputError(7),
|
|
val: new([]byte),
|
|
}, {
|
|
key: "_OP_bool/true",
|
|
ins: []_Instr{newInsOp(_OP_bool)},
|
|
src: "true",
|
|
exp: true,
|
|
val: new(bool),
|
|
},
|
|
{
|
|
key: "_OP_bool/skip",
|
|
ins: []_Instr{newInsOp(_OP_bool)},
|
|
src: `"true"`,
|
|
exp: nil,
|
|
val: new(bool),
|
|
err: &MismatchTypeError{Src: `"true"`, Pos: 0, Type: reflect.TypeOf(true)},
|
|
},
|
|
{
|
|
key: "_OP_bool/false",
|
|
ins: []_Instr{newInsOp(_OP_bool)},
|
|
src: "false",
|
|
exp: false,
|
|
val: new(bool),
|
|
}, {
|
|
key: "_OP_bool/false_pos",
|
|
ins: []_Instr{newInsOp(_OP_bool)},
|
|
src: "false",
|
|
vfn: func(i int, v interface{}) { require.False(t, v.(bool)); assert.Equal(t, 5, i) },
|
|
val: new(bool),
|
|
}, {
|
|
key: "_OP_bool/error_eof_1",
|
|
ins: []_Instr{newInsOp(_OP_bool)},
|
|
src: "tru",
|
|
err: SyntaxError{Src: `tru`, Pos: 3, Code: types.ERR_EOF},
|
|
val: new(bool),
|
|
}, {
|
|
key: "_OP_bool/error_eof_2",
|
|
ins: []_Instr{newInsOp(_OP_bool)},
|
|
src: "fals",
|
|
err: SyntaxError{Src: `fals`, Pos: 4, Code: types.ERR_EOF},
|
|
val: new(bool),
|
|
}, {
|
|
key: "_OP_bool/error_invalid_char_1",
|
|
ins: []_Instr{newInsOp(_OP_bool)},
|
|
src: "falxe",
|
|
err: SyntaxError{Src: `falxe`, Pos: 3, Code: types.ERR_INVALID_CHAR},
|
|
val: new(bool),
|
|
}, {
|
|
key: "_OP_bool/error_invalid_char_2",
|
|
ins: []_Instr{newInsOp(_OP_bool)},
|
|
src: "falsx",
|
|
err: SyntaxError{Src: `falsx`, Pos: 4, Code: types.ERR_INVALID_CHAR},
|
|
val: new(bool),
|
|
},
|
|
{
|
|
key: "_OP_num/positive",
|
|
ins: []_Instr{newInsOp(_OP_num)},
|
|
src: "1.234e5",
|
|
exp: json.Number("1.234e5"),
|
|
val: new(json.Number),
|
|
}, {
|
|
key: "_OP_num/negative",
|
|
ins: []_Instr{newInsOp(_OP_num)},
|
|
src: "-1.234e5",
|
|
exp: json.Number("-1.234e5"),
|
|
val: new(json.Number),
|
|
}, {
|
|
key: "_OP_num/error_eof",
|
|
ins: []_Instr{newInsOp(_OP_num)},
|
|
src: "-",
|
|
err: SyntaxError{Src: `-`, Pos: 1, Code: types.ERR_INVALID_CHAR},
|
|
val: new(json.Number),
|
|
}, {
|
|
key: "_OP_num/error_invalid_char",
|
|
ins: []_Instr{newInsOp(_OP_num)},
|
|
src: "xxx",
|
|
err: SyntaxError{Src: `xxx`, Pos: 1, Code: types.ERR_INVALID_CHAR},
|
|
val: new(json.Number),
|
|
}, {
|
|
key: "_OP_i8",
|
|
ins: []_Instr{newInsOp(_OP_i8)},
|
|
src: "123",
|
|
exp: int8(123),
|
|
val: new(int8),
|
|
}, {
|
|
key: "_OP_i8/error_overflow",
|
|
ins: []_Instr{newInsOp(_OP_i8)},
|
|
src: "1234",
|
|
err: error_value("1234", reflect.TypeOf(int8(0))),
|
|
val: new(int8),
|
|
},
|
|
{
|
|
key: "_OP_i8/error_wrong_type",
|
|
ins: []_Instr{newInsOp(_OP_i8)},
|
|
src: "12.34",
|
|
err: &MismatchTypeError{Src: `12.34`, Pos: 0, Type: int8Type},
|
|
val: new(int8),
|
|
}, {
|
|
key: "_OP_u8",
|
|
ins: []_Instr{newInsOp(_OP_u8)},
|
|
src: "234",
|
|
exp: uint8(234),
|
|
val: new(uint8),
|
|
}, {
|
|
key: "_OP_u8/error_overflow",
|
|
ins: []_Instr{newInsOp(_OP_u8)},
|
|
src: "1234",
|
|
err: error_value("1234", reflect.TypeOf(uint8(0))),
|
|
val: new(uint8),
|
|
}, {
|
|
key: "_OP_u8/error_underflow",
|
|
ins: []_Instr{newInsOp(_OP_u8)},
|
|
src: "-123",
|
|
err: &MismatchTypeError{Src: `-123`, Pos: 0, Type: uint8Type},
|
|
val: new(uint8),
|
|
}, {
|
|
key: "_OP_u8/error_wrong_type",
|
|
ins: []_Instr{newInsOp(_OP_u8)},
|
|
src: "12.34",
|
|
err: &MismatchTypeError{Src: `12.34`, Pos: 0, Type: uint8Type},
|
|
val: new(uint8),
|
|
}, {
|
|
key: "_OP_f32",
|
|
ins: []_Instr{newInsOp(_OP_f32)},
|
|
src: "1.25e20",
|
|
exp: float32(1.25e20),
|
|
val: new(float32),
|
|
}, {
|
|
key: "_OP_f32/overflow",
|
|
ins: []_Instr{newInsOp(_OP_f32)},
|
|
src: "1.25e50",
|
|
err: error_value("1.25e50", reflect.TypeOf(float32(0))),
|
|
val: new(float32),
|
|
}, {
|
|
key: "_OP_f32/underflow",
|
|
ins: []_Instr{newInsOp(_OP_f32)},
|
|
src: "-1.25e50",
|
|
err: error_value("-1.25e50", reflect.TypeOf(float32(0))),
|
|
val: new(float32),
|
|
}, {
|
|
key: "_OP_f64",
|
|
ins: []_Instr{newInsOp(_OP_f64)},
|
|
src: "1.25e123",
|
|
exp: 1.25e123,
|
|
val: new(float64),
|
|
}, {
|
|
key: "_OP_unquote/plain",
|
|
ins: []_Instr{newInsOp(_OP_unquote)},
|
|
src: `\"hello, world\""`,
|
|
exp: "hello, world",
|
|
val: new(string),
|
|
}, {
|
|
key: "_OP_unquote/unquote",
|
|
ins: []_Instr{newInsOp(_OP_unquote)},
|
|
src: `\"hello, world \\\\ \\\" \\/ \\b \\f \\n \\r \\t \\u666f 测试中文 \\ud83d\\ude00\""`,
|
|
exp: "hello, world \\ \" / \b \f \n \r \t 景 测试中文 😀",
|
|
val: new(string),
|
|
}, {
|
|
key: "_OP_unquote/error_invalid_end",
|
|
ins: []_Instr{newInsOp(_OP_unquote)},
|
|
src: `\"te\\\"st"`,
|
|
err: SyntaxError{Src: `\"te\\\"st"`, Pos: 8, Code: types.ERR_INVALID_CHAR},
|
|
val: new(string),
|
|
}, {
|
|
key: "_OP_nil_1",
|
|
ins: []_Instr{newInsOp(_OP_nil_1)},
|
|
src: "",
|
|
exp: 0,
|
|
val: (func() *int { v := new(int); *v = 123; return v })(),
|
|
}, {
|
|
key: "_OP_nil_2",
|
|
ins: []_Instr{newInsOp(_OP_nil_2)},
|
|
src: "",
|
|
exp: error(nil),
|
|
val: (func() *error { v := new(error); *v = types.ERR_EOF; return v })(),
|
|
}, {
|
|
key: "_OP_nil_3",
|
|
ins: []_Instr{newInsOp(_OP_nil_3)},
|
|
src: "",
|
|
exp: []byte(nil),
|
|
val: &[]byte{1, 2, 3},
|
|
}, {
|
|
key: "_OP_deref",
|
|
ins: []_Instr{newInsVt(_OP_deref, reflect.TypeOf(0))},
|
|
src: "",
|
|
vfn: func(_ int, v interface{}) { require.NotNil(t, v); assert.NotNil(t, v.(*int)) },
|
|
val: new(*int),
|
|
}, {
|
|
key: "_OP_map_init",
|
|
ins: []_Instr{newInsOp(_OP_map_init)},
|
|
src: "",
|
|
vfn: func(_ int, v interface{}) { require.NotNil(t, v); assert.NotNil(t, v.(map[string]int)) },
|
|
val: new(map[string]int),
|
|
}, {
|
|
key: "_OP_map_key_i8",
|
|
ins: []_Instr{newInsVt(_OP_map_key_i8, reflect.TypeOf(map[int8]int{}))},
|
|
src: `123"`,
|
|
exp: map[int8]int{123: 0},
|
|
val: map[int8]int{},
|
|
}, {
|
|
key: "_OP_map_key_i32",
|
|
ins: []_Instr{newInsVt(_OP_map_key_i32, reflect.TypeOf(map[int32]int{}))},
|
|
src: `123456789"`,
|
|
exp: map[int32]int{123456789: 0},
|
|
val: map[int32]int{},
|
|
}, {
|
|
key: "_OP_map_key_i64",
|
|
ins: []_Instr{newInsVt(_OP_map_key_i64, reflect.TypeOf(map[int64]int{}))},
|
|
src: `123456789123456789"`,
|
|
exp: map[int64]int{123456789123456789: 0},
|
|
val: map[int64]int{},
|
|
}, {
|
|
key: "_OP_map_key_u8",
|
|
ins: []_Instr{newInsVt(_OP_map_key_u8, reflect.TypeOf(map[uint8]int{}))},
|
|
src: `123"`,
|
|
exp: map[uint8]int{123: 0},
|
|
val: map[uint8]int{},
|
|
}, {
|
|
key: "_OP_map_key_u32",
|
|
ins: []_Instr{newInsVt(_OP_map_key_u32, reflect.TypeOf(map[uint32]int{}))},
|
|
src: `123456789"`,
|
|
exp: map[uint32]int{123456789: 0},
|
|
val: map[uint32]int{},
|
|
}, {
|
|
key: "_OP_map_key_u64",
|
|
ins: []_Instr{newInsVt(_OP_map_key_u64, reflect.TypeOf(map[uint64]int{}))},
|
|
src: `123456789123456789"`,
|
|
exp: map[uint64]int{123456789123456789: 0},
|
|
val: map[uint64]int{},
|
|
}, {
|
|
key: "_OP_map_key_f32",
|
|
ins: []_Instr{newInsVt(_OP_map_key_f32, reflect.TypeOf(map[float32]int{}))},
|
|
src: `1.25"`,
|
|
exp: map[float32]int{1.25: 0},
|
|
val: map[float32]int{},
|
|
}, {
|
|
key: "_OP_map_key_f64",
|
|
ins: []_Instr{newInsVt(_OP_map_key_f64, reflect.TypeOf(map[float64]int{}))},
|
|
src: `1.25"`,
|
|
exp: map[float64]int{1.25: 0},
|
|
val: map[float64]int{},
|
|
}, {
|
|
key: "_OP_map_key_str/plain",
|
|
ins: []_Instr{newInsVt(_OP_map_key_str, reflect.TypeOf(map[string]int{}))},
|
|
src: `foo"`,
|
|
exp: map[string]int{"foo": 0},
|
|
val: map[string]int{},
|
|
}, {
|
|
key: "_OP_map_key_str/unquote",
|
|
ins: []_Instr{newInsVt(_OP_map_key_str, reflect.TypeOf(map[string]int{}))},
|
|
src: `foo\nbar"`,
|
|
exp: map[string]int{"foo\nbar": 0},
|
|
val: map[string]int{},
|
|
},
|
|
{
|
|
key: "_OP_map_key_utext/value",
|
|
ins: []_Instr{newInsVt(_OP_map_key_utext, reflect.TypeOf(map[UtextValue]int{}))},
|
|
src: `foo"`,
|
|
vfn: func(_ int, v interface{}) {
|
|
m := v.(map[UtextValue]int)
|
|
assert.Equal(t, 1, len(m))
|
|
for k := range m {
|
|
assert.Equal(t, UtextValue(0), k)
|
|
}
|
|
assert.Equal(t, []byte("foo"), utextVar)
|
|
},
|
|
val: map[UtextValue]int{},
|
|
},
|
|
{
|
|
key: "_OP_map_key_utext/pointer",
|
|
ins: []_Instr{newInsVt(_OP_map_key_utext, reflect.TypeOf(map[*UtextStruct]int{}))},
|
|
src: `foo"`,
|
|
vfn: func(_ int, v interface{}) {
|
|
m := v.(map[*UtextStruct]int)
|
|
assert.Equal(t, 1, len(m))
|
|
for k := range m {
|
|
assert.Equal(t, "foo", k.V)
|
|
}
|
|
},
|
|
val: map[*UtextStruct]int{},
|
|
},
|
|
{
|
|
key: "_OP_map_key_utext_p",
|
|
ins: []_Instr{newInsVt(_OP_map_key_utext_p, reflect.TypeOf(map[UtextStruct]int{}))},
|
|
src: `foo"`,
|
|
exp: map[UtextStruct]int{UtextStruct{V: "foo"}: 0},
|
|
val: map[UtextStruct]int{},
|
|
},
|
|
{
|
|
key: "_OP_array_skip",
|
|
ins: []_Instr{newInsOp(_OP_array_skip)},
|
|
src: `[1,2.0,true,false,null,"asdf",{"qwer":[1,2,3,4]}]`,
|
|
pos: 1,
|
|
vfn: func(i int, _ interface{}) { assert.Equal(t, 49, i) },
|
|
val: nil,
|
|
},{
|
|
key: "_OP_slice_init",
|
|
ins: []_Instr{newInsVt(_OP_slice_init, reflect.TypeOf(0))},
|
|
src: "",
|
|
vfn: func(_ int, v interface{}) {
|
|
require.NotNil(t, v)
|
|
assert.Equal(t, 0, len(v.([]int)))
|
|
assert.Equal(t, _MinSlice, cap(v.([]int)))
|
|
},
|
|
val: new([]int),
|
|
}, {
|
|
key: "_OP_slice_append",
|
|
ins: []_Instr{newInsVt(_OP_slice_append, reflect.TypeOf(0)), newInsOp(_OP_nil_1)},
|
|
src: "",
|
|
exp: []int{123, 0},
|
|
val: &[]int{123},
|
|
}, {
|
|
key: "_OP_object_skip",
|
|
ins: []_Instr{newInsOp(_OP_object_skip)},
|
|
src: `{"zxcv":[1,2.0],"asdf":[true,false,null,"asdf",{"qwer":345}]}`,
|
|
pos: 1,
|
|
vfn: func(i int, _ interface{}) { assert.Equal(t, 61, i) },
|
|
val: nil,
|
|
}, {
|
|
key: "_OP_object_next",
|
|
ins: []_Instr{newInsOp(_OP_object_next)},
|
|
src: `{"asdf":[1,2.0,true,false,null,"asdf",{"qwer":345}]}`,
|
|
vfn: func(i int, _ interface{}) { assert.Equal(t, 52, i) },
|
|
val: nil,
|
|
}, {
|
|
key: "_OP_struct_field",
|
|
ins: []_Instr{
|
|
newInsVf(_OP_struct_field, (func() *caching.FieldMap {
|
|
ret := caching.CreateFieldMap(2)
|
|
ret.Set("bab", 1)
|
|
ret.Set("bac", 2)
|
|
ret.Set("bad", 3)
|
|
return ret
|
|
})()),
|
|
newInsOp(_OP_dbg_get_sr),
|
|
},
|
|
src: `bac"`,
|
|
exp: 2,
|
|
val: new(int),
|
|
}, {
|
|
key: "_OP_struct_field/case_insensitive",
|
|
ins: []_Instr{
|
|
newInsVf(_OP_struct_field, (func() *caching.FieldMap {
|
|
ret := caching.CreateFieldMap(2)
|
|
ret.Set("Bac", 2)
|
|
ret.Set("BAC", 1)
|
|
ret.Set("baC", 3)
|
|
return ret
|
|
})()),
|
|
newInsOp(_OP_dbg_get_sr),
|
|
},
|
|
src: `bac"`,
|
|
exp: 1,
|
|
val: new(int),
|
|
}, {
|
|
key: "_OP_struct_field/not_found",
|
|
ins: []_Instr{
|
|
newInsVf(_OP_struct_field, (func() *caching.FieldMap {
|
|
ret := caching.CreateFieldMap(2)
|
|
ret.Set("bab", 1)
|
|
ret.Set("bac", 2)
|
|
ret.Set("bad", 3)
|
|
return ret
|
|
})()),
|
|
newInsOp(_OP_dbg_get_sr),
|
|
},
|
|
src: `bae"`,
|
|
exp: -1,
|
|
val: new(int),
|
|
}, {
|
|
key: "_OP_unmarshal/value",
|
|
ins: []_Instr{newInsVt(_OP_unmarshal, reflect.TypeOf(UjsonValue(0)))},
|
|
src: `{"asdf":[1,2.0,true,false,null,"asdf",{"qwer":345}]}`,
|
|
vfn: func(_ int, v interface{}) {
|
|
assert.Equal(t, []byte(`{"asdf":[1,2.0,true,false,null,"asdf",{"qwer":345}]}`), ujsonVar)
|
|
},
|
|
val: new(UjsonValue),
|
|
}, {
|
|
key: "_OP_unmarshal/pointer",
|
|
ins: []_Instr{newInsVt(_OP_unmarshal, reflect.TypeOf(new(UjsonStruct)))},
|
|
src: `{"asdf":[1,2.0,true,false,null,"asdf",{"qwer":345}]}`,
|
|
exp: &UjsonStruct{V: `{"asdf":[1,2.0,true,false,null,"asdf",{"qwer":345}]}`},
|
|
val: new(*UjsonStruct),
|
|
}, {
|
|
key: "_OP_unmarshal_p",
|
|
ins: []_Instr{newInsVt(_OP_unmarshal_p, reflect.TypeOf(new(UjsonStruct)))},
|
|
src: `{"asdf":[1,2.0,true,false,null,"asdf",{"qwer":345}]}`,
|
|
exp: UjsonStruct{V: `{"asdf":[1,2.0,true,false,null,"asdf",{"qwer":345}]}`},
|
|
val: new(UjsonStruct),
|
|
}, {
|
|
key: "_OP_unmarshal_text/value",
|
|
ins: []_Instr{newInsVt(_OP_unmarshal_text, reflect.TypeOf(UtextValue(0)))},
|
|
src: `hello\n\r\tworld"`,
|
|
vfn: func(_ int, v interface{}) {
|
|
assert.Equal(t, []byte("hello\n\r\tworld"), utextVar)
|
|
},
|
|
val: new(UtextValue),
|
|
}, {
|
|
key: "_OP_unmarshal_text/pointer",
|
|
ins: []_Instr{newInsVt(_OP_unmarshal_text, reflect.TypeOf(new(UtextStruct)))},
|
|
src: `hello\n\r\tworld"`,
|
|
exp: &UtextStruct{V: "hello\n\r\tworld"},
|
|
val: new(*UtextStruct),
|
|
}, {
|
|
key: "_OP_unmarshal_text_p",
|
|
ins: []_Instr{newInsVt(_OP_unmarshal_text_p, reflect.TypeOf(new(UtextStruct)))},
|
|
src: `hello\n\r\tworld"`,
|
|
exp: UtextStruct{V: "hello\n\r\tworld"},
|
|
val: new(UtextStruct),
|
|
}, {
|
|
key: "_OP_lspace",
|
|
ins: []_Instr{newInsOp(_OP_lspace)},
|
|
src: " \t\r\na",
|
|
vfn: func(i int, _ interface{}) { assert.Equal(t, 4, i) },
|
|
val: nil,
|
|
}, {
|
|
key: "_OP_lspace/error",
|
|
ins: []_Instr{newInsOp(_OP_lspace)},
|
|
src: "",
|
|
err: SyntaxError{Src: ``, Pos: 0, Code: types.ERR_EOF},
|
|
val: nil,
|
|
}, {
|
|
key: "_OP_match_char/correct",
|
|
ins: []_Instr{newInsVb(_OP_match_char, 'a')},
|
|
src: "a",
|
|
exp: nil,
|
|
val: nil,
|
|
}, {
|
|
key: "_OP_match_char/error",
|
|
ins: []_Instr{newInsVb(_OP_match_char, 'b')},
|
|
src: "a",
|
|
err: SyntaxError{Src: `a`, Pos: 0, Code: types.ERR_INVALID_CHAR},
|
|
val: nil,
|
|
}, {
|
|
key: "_OP_switch",
|
|
ins: []_Instr{
|
|
newInsVi(_OP_dbg_set_sr, 1),
|
|
newInsVs(_OP_switch, []int{4, 6, 8}),
|
|
newInsOp(_OP_i8),
|
|
newInsVi(_OP_goto, 9),
|
|
newInsOp(_OP_i16),
|
|
newInsVi(_OP_goto, 9),
|
|
newInsOp(_OP_i32),
|
|
newInsVi(_OP_goto, 9),
|
|
newInsOp(_OP_u8),
|
|
},
|
|
src: "-1234567",
|
|
exp: int32(-1234567),
|
|
val: new(int32),
|
|
},
|
|
}
|
|
for _, tv := range tests {
|
|
t.Run(tv.key, func(t *testing.T) {
|
|
testOpCode(t, &tv)
|
|
})
|
|
}
|
|
}
|
|
|
|
type JsonStruct struct {
|
|
A int
|
|
B string
|
|
C map[string]int
|
|
D []int
|
|
}
|
|
|
|
func TestAssembler_DecodeStruct(t *testing.T) {
|
|
var v JsonStruct
|
|
s := `{"A": 123, "B": "asdf", "C": {"qwer": 4567}, "D": [1, 2, 3, 4, 5]}`
|
|
p, err := newCompiler().compile(reflect.TypeOf(v))
|
|
require.NoError(t, err)
|
|
k := new(_Stack)
|
|
a := newAssembler(p)
|
|
f := a.Load()
|
|
pos, err := f(s, 0, unsafe.Pointer(&v), k, 0, "", nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, len(s), pos)
|
|
assert.Equal(t, JsonStruct{
|
|
A: 123,
|
|
B: "asdf",
|
|
C: map[string]int{"qwer": 4567},
|
|
D: []int{1, 2, 3, 4, 5},
|
|
}, v)
|
|
}
|
|
|
|
type Tx struct {
|
|
x int
|
|
}
|
|
|
|
func TestAssembler_DecodeStruct_SinglePrivateField(t *testing.T) {
|
|
var v Tx
|
|
s := `{"x": 1}`
|
|
p, err := newCompiler().compile(reflect.TypeOf(v))
|
|
require.NoError(t, err)
|
|
k := new(_Stack)
|
|
a := newAssembler(p)
|
|
f := a.Load()
|
|
pos, err := f(s, 0, unsafe.Pointer(&v), k, 0, "", nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, len(s), pos)
|
|
assert.Equal(t, Tx{}, v)
|
|
}
|
|
|
|
func TestAssembler_DecodeByteSlice_Bin(t *testing.T) {
|
|
var v []byte
|
|
s := `"aGVsbG8sIHdvcmxk"`
|
|
p, err := newCompiler().compile(reflect.TypeOf(v))
|
|
require.NoError(t, err)
|
|
k := new(_Stack)
|
|
a := newAssembler(p)
|
|
f := a.Load()
|
|
pos, err := f(s, 0, unsafe.Pointer(&v), k, 0, "", nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, len(s), pos)
|
|
assert.Equal(t, []byte("hello, world"), v)
|
|
}
|
|
|
|
func TestAssembler_DecodeByteSlice_List(t *testing.T) {
|
|
var v []byte
|
|
s := `[104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100]`
|
|
p, err := newCompiler().compile(reflect.TypeOf(v))
|
|
require.NoError(t, err)
|
|
k := new(_Stack)
|
|
a := newAssembler(p)
|
|
f := a.Load()
|
|
pos, err := f(s, 0, unsafe.Pointer(&v), k, 0, "", nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, len(s), pos)
|
|
assert.Equal(t, []byte("hello, world"), v)
|
|
} |