mirror of
https://github.com/ii64/sonic.git
synced 2026-06-21 00:46:43 +08:00
fix: 'string' option in struct tag as encoding/json (#148)
Co-authored-by: liuqiang <liuqiang.06@bytedance.com>
This commit is contained in:
parent
d2711a6af0
commit
d5104197c4
5 changed files with 132 additions and 18 deletions
|
|
@ -241,6 +241,7 @@
|
||||||
_OP_deref : (*_Assembler)._asm_OP_deref,
|
_OP_deref : (*_Assembler)._asm_OP_deref,
|
||||||
_OP_index : (*_Assembler)._asm_OP_index,
|
_OP_index : (*_Assembler)._asm_OP_index,
|
||||||
_OP_is_null : (*_Assembler)._asm_OP_is_null,
|
_OP_is_null : (*_Assembler)._asm_OP_is_null,
|
||||||
|
_OP_is_null_quote : (*_Assembler)._asm_OP_is_null_quote,
|
||||||
_OP_map_init : (*_Assembler)._asm_OP_map_init,
|
_OP_map_init : (*_Assembler)._asm_OP_map_init,
|
||||||
_OP_map_key_i8 : (*_Assembler)._asm_OP_map_key_i8,
|
_OP_map_key_i8 : (*_Assembler)._asm_OP_map_key_i8,
|
||||||
_OP_map_key_i16 : (*_Assembler)._asm_OP_map_key_i16,
|
_OP_map_key_i16 : (*_Assembler)._asm_OP_map_key_i16,
|
||||||
|
|
@ -1179,6 +1180,18 @@
|
||||||
self.Link("_not_null_{n}") // _not_null_{n}:
|
self.Link("_not_null_{n}") // _not_null_{n}:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) _asm_OP_is_null_quote(p *_Instr) {
|
||||||
|
self.Emit("LEAQ" , jit.Ptr(_IC, 5), _AX) // LEAQ 4(IC), AX
|
||||||
|
self.Emit("CMPQ" , _AX, _IL) // CMPQ AX, IL
|
||||||
|
self.Sjmp("JA" , "_not_null_quote_{n}") // JA _not_null_quote_{n}
|
||||||
|
self.Emit("CMPL" , jit.Sib(_IP, _IC, 1, 0), jit.Imm(_IM_null)) // CMPL (IP)(IC), $"null"
|
||||||
|
self.Sjmp("JNE" , "_not_null_quote_{n}") // JNE _not_null_quote_{n}
|
||||||
|
self.Emit("CMPB" , jit.Sib(_IP, _IC, 1, 4), jit.Imm('"')) // CMPB 4(IP)(IC), $'"'
|
||||||
|
self.Emit("CMOVQEQ", _AX, _IC) // CMOVQEQ AX, IC
|
||||||
|
self.Xjmp("JE" , p.vi()) // JE {p.vi()}
|
||||||
|
self.Link("_not_null_quote_{n}") // _not_null_quote_{n}:
|
||||||
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_init(_ *_Instr) {
|
func (self *_Assembler) _asm_OP_map_init(_ *_Instr) {
|
||||||
self.Emit("MOVQ" , jit.Ptr(_VP, 0), _AX) // MOVQ (VP), AX
|
self.Emit("MOVQ" , jit.Ptr(_VP, 0), _AX) // MOVQ (VP), AX
|
||||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,7 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
|
||||||
_OP_deref : (*_Assembler)._asm_OP_deref,
|
_OP_deref : (*_Assembler)._asm_OP_deref,
|
||||||
_OP_index : (*_Assembler)._asm_OP_index,
|
_OP_index : (*_Assembler)._asm_OP_index,
|
||||||
_OP_is_null : (*_Assembler)._asm_OP_is_null,
|
_OP_is_null : (*_Assembler)._asm_OP_is_null,
|
||||||
|
_OP_is_null_quote : (*_Assembler)._asm_OP_is_null_quote,
|
||||||
_OP_map_init : (*_Assembler)._asm_OP_map_init,
|
_OP_map_init : (*_Assembler)._asm_OP_map_init,
|
||||||
_OP_map_key_i8 : (*_Assembler)._asm_OP_map_key_i8,
|
_OP_map_key_i8 : (*_Assembler)._asm_OP_map_key_i8,
|
||||||
_OP_map_key_i16 : (*_Assembler)._asm_OP_map_key_i16,
|
_OP_map_key_i16 : (*_Assembler)._asm_OP_map_key_i16,
|
||||||
|
|
@ -1186,6 +1187,18 @@ func (self *_Assembler) _asm_OP_is_null(p *_Instr) {
|
||||||
self.Link("_not_null_{n}") // _not_null_{n}:
|
self.Link("_not_null_{n}") // _not_null_{n}:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) _asm_OP_is_null_quote(p *_Instr) {
|
||||||
|
self.Emit("LEAQ" , jit.Ptr(_IC, 5), _AX) // LEAQ 4(IC), AX
|
||||||
|
self.Emit("CMPQ" , _AX, _IL) // CMPQ AX, IL
|
||||||
|
self.Sjmp("JA" , "_not_null_quote_{n}") // JA _not_null_quote_{n}
|
||||||
|
self.Emit("CMPL" , jit.Sib(_IP, _IC, 1, 0), jit.Imm(_IM_null)) // CMPL (IP)(IC), $"null"
|
||||||
|
self.Sjmp("JNE" , "_not_null_quote_{n}") // JNE _not_null_quote_{n}
|
||||||
|
self.Emit("CMPB" , jit.Sib(_IP, _IC, 1, 4), jit.Imm('"')) // CMPB 4(IP)(IC), $'"'
|
||||||
|
self.Emit("CMOVQEQ", _AX, _IC) // CMOVQEQ AX, IC
|
||||||
|
self.Xjmp("JE" , p.vi()) // JE {p.vi()}
|
||||||
|
self.Link("_not_null_quote_{n}") // _not_null_quote_{n}:
|
||||||
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_init(_ *_Instr) {
|
func (self *_Assembler) _asm_OP_map_init(_ *_Instr) {
|
||||||
self.Emit("MOVQ" , jit.Ptr(_VP, 0), _AX) // MOVQ (VP), AX
|
self.Emit("MOVQ" , jit.Ptr(_VP, 0), _AX) // MOVQ (VP), AX
|
||||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,7 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
|
||||||
_OP_deref : (*_Assembler)._asm_OP_deref,
|
_OP_deref : (*_Assembler)._asm_OP_deref,
|
||||||
_OP_index : (*_Assembler)._asm_OP_index,
|
_OP_index : (*_Assembler)._asm_OP_index,
|
||||||
_OP_is_null : (*_Assembler)._asm_OP_is_null,
|
_OP_is_null : (*_Assembler)._asm_OP_is_null,
|
||||||
|
_OP_is_null_quote : (*_Assembler)._asm_OP_is_null_quote,
|
||||||
_OP_map_init : (*_Assembler)._asm_OP_map_init,
|
_OP_map_init : (*_Assembler)._asm_OP_map_init,
|
||||||
_OP_map_key_i8 : (*_Assembler)._asm_OP_map_key_i8,
|
_OP_map_key_i8 : (*_Assembler)._asm_OP_map_key_i8,
|
||||||
_OP_map_key_i16 : (*_Assembler)._asm_OP_map_key_i16,
|
_OP_map_key_i16 : (*_Assembler)._asm_OP_map_key_i16,
|
||||||
|
|
@ -1183,6 +1184,18 @@ func (self *_Assembler) _asm_OP_is_null(p *_Instr) {
|
||||||
self.Link("_not_null_{n}") // _not_null_{n}:
|
self.Link("_not_null_{n}") // _not_null_{n}:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) _asm_OP_is_null_quote(p *_Instr) {
|
||||||
|
self.Emit("LEAQ" , jit.Ptr(_IC, 5), _AX) // LEAQ 4(IC), AX
|
||||||
|
self.Emit("CMPQ" , _AX, _IL) // CMPQ AX, IL
|
||||||
|
self.Sjmp("JA" , "_not_null_quote_{n}") // JA _not_null_quote_{n}
|
||||||
|
self.Emit("CMPL" , jit.Sib(_IP, _IC, 1, 0), jit.Imm(_IM_null)) // CMPL (IP)(IC), $"null"
|
||||||
|
self.Sjmp("JNE" , "_not_null_quote_{n}") // JNE _not_null_quote_{n}
|
||||||
|
self.Emit("CMPB" , jit.Sib(_IP, _IC, 1, 4), jit.Imm('"')) // CMPB 4(IP)(IC), $'"'
|
||||||
|
self.Emit("CMOVQEQ", _AX, _IC) // CMOVQEQ AX, IC
|
||||||
|
self.Xjmp("JE" , p.vi()) // JE {p.vi()}
|
||||||
|
self.Link("_not_null_quote_{n}") // _not_null_quote_{n}:
|
||||||
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_init(_ *_Instr) {
|
func (self *_Assembler) _asm_OP_map_init(_ *_Instr) {
|
||||||
self.Emit("MOVQ" , jit.Ptr(_VP, 0), _AX) // MOVQ (VP), AX
|
self.Emit("MOVQ" , jit.Ptr(_VP, 0), _AX) // MOVQ (VP), AX
|
||||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ const (
|
||||||
_OP_deref
|
_OP_deref
|
||||||
_OP_index
|
_OP_index
|
||||||
_OP_is_null
|
_OP_is_null
|
||||||
|
_OP_is_null_quote
|
||||||
_OP_map_init
|
_OP_map_init
|
||||||
_OP_map_key_i8
|
_OP_map_key_i8
|
||||||
_OP_map_key_i16
|
_OP_map_key_i16
|
||||||
|
|
@ -129,6 +130,7 @@ var _OpNames = [256]string {
|
||||||
_OP_deref : "deref",
|
_OP_deref : "deref",
|
||||||
_OP_index : "index",
|
_OP_index : "index",
|
||||||
_OP_is_null : "is_null",
|
_OP_is_null : "is_null",
|
||||||
|
_OP_is_null_quote : "is_null_quote",
|
||||||
_OP_map_init : "map_init",
|
_OP_map_init : "map_init",
|
||||||
_OP_map_key_i8 : "map_key_i8",
|
_OP_map_key_i8 : "map_key_i8",
|
||||||
_OP_map_key_i16 : "map_key_i16",
|
_OP_map_key_i16 : "map_key_i16",
|
||||||
|
|
@ -304,11 +306,12 @@ func (self _Instr) vlen() int {
|
||||||
|
|
||||||
func (self _Instr) isBranch() bool {
|
func (self _Instr) isBranch() bool {
|
||||||
switch self.op() {
|
switch self.op() {
|
||||||
case _OP_goto : fallthrough
|
case _OP_goto : fallthrough
|
||||||
case _OP_switch : fallthrough
|
case _OP_switch : fallthrough
|
||||||
case _OP_is_null : fallthrough
|
case _OP_is_null : fallthrough
|
||||||
case _OP_check_char : return true
|
case _OP_is_null_quote : fallthrough
|
||||||
default : return false
|
case _OP_check_char : return true
|
||||||
|
default : return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,6 +340,7 @@ func (self _Instr) disassemble() string {
|
||||||
case _OP_unmarshal_text_p : fallthrough
|
case _OP_unmarshal_text_p : fallthrough
|
||||||
case _OP_recurse : return fmt.Sprintf("%-18s%s", self.op(), self.vt())
|
case _OP_recurse : return fmt.Sprintf("%-18s%s", self.op(), self.vt())
|
||||||
case _OP_goto : fallthrough
|
case _OP_goto : fallthrough
|
||||||
|
case _OP_is_null_quote : fallthrough
|
||||||
case _OP_is_null : return fmt.Sprintf("%-18sL_%d", self.op(), self.vi())
|
case _OP_is_null : return fmt.Sprintf("%-18sL_%d", self.op(), self.vi())
|
||||||
case _OP_index : fallthrough
|
case _OP_index : fallthrough
|
||||||
case _OP_array_clear : fallthrough
|
case _OP_array_clear : fallthrough
|
||||||
|
|
@ -932,18 +936,16 @@ func (self *_Compiler) compileStructFieldStr(p *_Program, sp int, vt reflect.Typ
|
||||||
p.add(_OP_is_null)
|
p.add(_OP_is_null)
|
||||||
p.chr(_OP_match_char, '"')
|
p.chr(_OP_match_char, '"')
|
||||||
|
|
||||||
/* dereference the pointer */
|
/* also check for inner "null" */
|
||||||
|
n1 = p.pc()
|
||||||
|
p.add(_OP_is_null_quote)
|
||||||
|
|
||||||
|
/* dereference the pointer only when it is not null */
|
||||||
if vk == reflect.Ptr {
|
if vk == reflect.Ptr {
|
||||||
vt = vt.Elem()
|
vt = vt.Elem()
|
||||||
p.rtt(_OP_deref, vt)
|
p.rtt(_OP_deref, vt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* also check for inner "null" for a type that is not `string` */
|
|
||||||
if vt.Kind() != reflect.String {
|
|
||||||
n1 = p.pc()
|
|
||||||
p.add(_OP_is_null)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* string opcode selector */
|
/* string opcode selector */
|
||||||
_OP_string := func() _Op {
|
_OP_string := func() _Op {
|
||||||
if ft == jsonNumberType {
|
if ft == jsonNumberType {
|
||||||
|
|
@ -973,16 +975,16 @@ func (self *_Compiler) compileStructFieldStr(p *_Program, sp int, vt reflect.Typ
|
||||||
default : panic("not reachable")
|
default : panic("not reachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pin the `is_null` jump location */
|
|
||||||
if n1 != -1 {
|
|
||||||
p.pin(n1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the closing quote is not needed when parsing a pure string */
|
/* the closing quote is not needed when parsing a pure string */
|
||||||
if vt == jsonNumberType || vt.Kind() != reflect.String {
|
if vt == jsonNumberType || vt.Kind() != reflect.String {
|
||||||
p.chr(_OP_match_char, '"')
|
p.chr(_OP_match_char, '"')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* pin the `is_null_quote` jump location */
|
||||||
|
if n1 != -1 && vk != reflect.Ptr {
|
||||||
|
p.pin(n1)
|
||||||
|
}
|
||||||
|
|
||||||
/* "null" but not a pointer, act as if the field is not present */
|
/* "null" but not a pointer, act as if the field is not present */
|
||||||
if vk != reflect.Ptr {
|
if vk != reflect.Ptr {
|
||||||
p.pin(n0)
|
p.pin(n0)
|
||||||
|
|
@ -992,7 +994,8 @@ func (self *_Compiler) compileStructFieldStr(p *_Program, sp int, vt reflect.Typ
|
||||||
/* the "null" case of the pointer */
|
/* the "null" case of the pointer */
|
||||||
pc := p.pc()
|
pc := p.pc()
|
||||||
p.add(_OP_goto)
|
p.add(_OP_goto)
|
||||||
p.pin(n0)
|
p.pin(n0) // `is_null` jump location
|
||||||
|
p.pin(n1) // `is_null_quote` jump location
|
||||||
p.add(_OP_nil_1)
|
p.add(_OP_nil_1)
|
||||||
p.pin(pc)
|
p.pin(pc)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
72
issue_test/issue144_test.go
Normal file
72
issue_test/issue144_test.go
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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 issue_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/json`
|
||||||
|
`testing`
|
||||||
|
|
||||||
|
`github.com/stretchr/testify/require`
|
||||||
|
`github.com/davecgh/go-spew/spew`
|
||||||
|
`github.com/bytedance/sonic`
|
||||||
|
)
|
||||||
|
|
||||||
|
type Issue144_StringOption struct {
|
||||||
|
S1 *string `json:"s1,string"`
|
||||||
|
S2 *string `json:"s2,string"`
|
||||||
|
S3 string `json:"s3,string"`
|
||||||
|
J1 json.Number `json:"j1,string"`
|
||||||
|
J2 *json.Number `json:"j2,string"`
|
||||||
|
J3 *json.Number `json:"j3,string"`
|
||||||
|
I1 int `json:"i1,string"`
|
||||||
|
I2 *int `json:"i2,string"`
|
||||||
|
I3 *int `json:"i3,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue144_StringOption(t *testing.T) {
|
||||||
|
data := []byte(`{
|
||||||
|
"s1":"\"null\"",
|
||||||
|
"s2":"null",
|
||||||
|
"s3":"null",
|
||||||
|
"j1":"null",
|
||||||
|
"j2":"null",
|
||||||
|
"j3":"123.456",
|
||||||
|
"i1":"null",
|
||||||
|
"i2":"null",
|
||||||
|
"i3":"-123"
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var v1, v2 Issue144_StringOption
|
||||||
|
e1 := json.Unmarshal(data, &v1)
|
||||||
|
e2 := sonic.Unmarshal(data, &v2)
|
||||||
|
require.NoError(t, e1)
|
||||||
|
require.NoError(t, e2)
|
||||||
|
require.Equal(t, v1, v2)
|
||||||
|
spew.Dump(v1)
|
||||||
|
|
||||||
|
i, j, s := int(1), json.Number("1"), "null"
|
||||||
|
v1.I2, v2.I2 = &i, &i
|
||||||
|
v1.J2, v2.J2 = &j, &j
|
||||||
|
v1.S2, v2.S2 = &s, &s
|
||||||
|
|
||||||
|
e1 = json.Unmarshal(data, &v1)
|
||||||
|
e2 = sonic.Unmarshal(data, &v2)
|
||||||
|
require.NoError(t, e1)
|
||||||
|
require.NoError(t, e2)
|
||||||
|
require.Equal(t, v1, v2)
|
||||||
|
spew.Dump(v1)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue