mirror of
https://github.com/ii64/sonic.git
synced 2026-06-20 16:45:22 +08:00
opt: faster skip in ast (#345)
* opt: faster skip in ast * ci: remove excited repo
This commit is contained in:
parent
67cffb15bd
commit
2dc405d750
48 changed files with 9116 additions and 3366 deletions
3
.github/workflows/benchmark-linux-arm64.yml
vendored
3
.github/workflows/benchmark-linux-arm64.yml
vendored
|
|
@ -6,6 +6,9 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: [arm]
|
runs-on: [arm]
|
||||||
steps:
|
steps:
|
||||||
|
- name: Clear repository
|
||||||
|
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Check Branch
|
- name: Check Branch
|
||||||
|
|
|
||||||
3
.github/workflows/benchmark-linux-x64.yml
vendored
3
.github/workflows/benchmark-linux-x64.yml
vendored
|
|
@ -6,6 +6,9 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: [self-hosted, X64]
|
runs-on: [self-hosted, X64]
|
||||||
steps:
|
steps:
|
||||||
|
- name: Clear repository
|
||||||
|
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Check Branch
|
- name: Check Branch
|
||||||
|
|
|
||||||
3
.github/workflows/codeql-analysis.yml
vendored
3
.github/workflows/codeql-analysis.yml
vendored
|
|
@ -38,6 +38,9 @@ jobs:
|
||||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Clear repository
|
||||||
|
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
|
|
||||||
3
.github/workflows/license-check.yml
vendored
3
.github/workflows/license-check.yml
vendored
|
|
@ -6,6 +6,9 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: [self-hosted, X64]
|
runs-on: [self-hosted, X64]
|
||||||
steps:
|
steps:
|
||||||
|
- name: Clear repository
|
||||||
|
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Check License Header
|
- name: Check License Header
|
||||||
|
|
|
||||||
3
.github/workflows/push-check-go118.yml
vendored
3
.github/workflows/push-check-go118.yml
vendored
|
|
@ -6,6 +6,9 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: [self-hosted, X64]
|
runs-on: [self-hosted, X64]
|
||||||
steps:
|
steps:
|
||||||
|
- name: Clear repository
|
||||||
|
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
|
|
|
||||||
3
.github/workflows/push-check-linux-arm64.yml
vendored
3
.github/workflows/push-check-linux-arm64.yml
vendored
|
|
@ -10,6 +10,9 @@ jobs:
|
||||||
os: [arm]
|
os: [arm]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Clear repository
|
||||||
|
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
|
|
|
||||||
3
.github/workflows/push-check-linux-x64.yml
vendored
3
.github/workflows/push-check-linux-x64.yml
vendored
|
|
@ -9,6 +9,9 @@ jobs:
|
||||||
go-version: [1.15.x, 1.16.x, 1.17.x, 1.19.x]
|
go-version: [1.15.x, 1.16.x, 1.17.x, 1.19.x]
|
||||||
runs-on: [self-hosted, X64]
|
runs-on: [self-hosted, X64]
|
||||||
steps:
|
steps:
|
||||||
|
- name: Clear repository
|
||||||
|
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
|
|
|
||||||
3
.github/workflows/push-check-qemu.yml
vendored
3
.github/workflows/push-check-qemu.yml
vendored
|
|
@ -10,6 +10,9 @@ jobs:
|
||||||
os: [arm]
|
os: [arm]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Clear repository
|
||||||
|
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
|
|
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -48,4 +48,5 @@ ast/test.out
|
||||||
ast/bench.sh
|
ast/bench.sh
|
||||||
|
|
||||||
!testdata/*.json.gz
|
!testdata/*.json.gz
|
||||||
fuzz/testdata
|
fuzz/testdata
|
||||||
|
*__debug_bin
|
||||||
6
Makefile
6
Makefile
|
|
@ -27,9 +27,9 @@ TMPL_avx := fastint_amd64_test fastfloat_amd64_test native_amd64_test native_ex
|
||||||
TMPL_avx2 := fastint_amd64_test fastfloat_amd64_test native_amd64_test native_export_amd64
|
TMPL_avx2 := fastint_amd64_test fastfloat_amd64_test native_amd64_test native_export_amd64
|
||||||
TMPL_sse := fastint_amd64_test fastfloat_amd64_test native_amd64_test native_export_amd64
|
TMPL_sse := fastint_amd64_test fastfloat_amd64_test native_amd64_test native_export_amd64
|
||||||
|
|
||||||
CFLAGS_avx := -msse -mno-sse4 -mavx -mno-avx2 -DUSE_AVX=1 -DUSE_AVX2=0
|
CFLAGS_avx := -msse -mno-sse4 -mavx -mpclmul -mno-avx2 -DUSE_AVX=1 -DUSE_AVX2=0
|
||||||
CFLAGS_avx2 := -msse -mno-sse4 -mavx -mavx2 -DUSE_AVX=1 -DUSE_AVX2=1
|
CFLAGS_avx2 := -msse -mno-sse4 -mavx -mpclmul -mavx2 -DUSE_AVX=1 -DUSE_AVX2=1
|
||||||
CFLAGS_sse := -msse -mno-sse4 -mno-avx -mno-avx2
|
CFLAGS_sse := -msse -mno-sse4 -mno-avx -mno-avx2 -mpclmul
|
||||||
|
|
||||||
CC_amd64 := clang
|
CC_amd64 := clang
|
||||||
ASM2ASM_amd64 := tools/asm2asm/asm2asm.py
|
ASM2ASM_amd64 := tools/asm2asm/asm2asm.py
|
||||||
|
|
|
||||||
|
|
@ -88,4 +88,39 @@ func (self *Parser) skip() (int, types.ParsingError) {
|
||||||
func (self *Node) encodeInterface(buf *[]byte) error {
|
func (self *Node) encodeInterface(buf *[]byte) error {
|
||||||
//WARN: NOT compatible with json.Encoder
|
//WARN: NOT compatible with json.Encoder
|
||||||
return encoder.EncodeInto(buf, self.packAny(), 0)
|
return encoder.EncodeInto(buf, self.packAny(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) skipFast() (int, types.ParsingError) {
|
||||||
|
start := native.SkipOneFast(&self.s, &self.p)
|
||||||
|
if start < 0 {
|
||||||
|
return self.p, types.ParsingError(-start)
|
||||||
|
}
|
||||||
|
return start, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) getByPath(path ...interface{}) (int, types.ParsingError) {
|
||||||
|
start := native.GetByPath(&self.s, &self.p, &path)
|
||||||
|
runtime.KeepAlive(path)
|
||||||
|
if start < 0 {
|
||||||
|
return self.p, types.ParsingError(-start)
|
||||||
|
}
|
||||||
|
return start, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (self *Searcher) GetByPath(path ...interface{}) (Node, error) {
|
||||||
|
var err types.ParsingError
|
||||||
|
var start int
|
||||||
|
|
||||||
|
self.parser.p = 0
|
||||||
|
start, err = self.parser.getByPath(path...)
|
||||||
|
if err != 0 {
|
||||||
|
return Node{}, self.parser.syntaxError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := switchRawType(self.parser.s[start])
|
||||||
|
if t == _V_NONE {
|
||||||
|
return Node{}, self.parser.ExportError(err)
|
||||||
|
}
|
||||||
|
return newRawNode(self.parser.s[start:self.parser.p], t), nil
|
||||||
}
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ package ast
|
||||||
import (
|
import (
|
||||||
`encoding/base64`
|
`encoding/base64`
|
||||||
`encoding/json`
|
`encoding/json`
|
||||||
|
`fmt`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/native/types`
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
|
@ -52,6 +53,10 @@ func (self *Parser) skip() (int, types.ParsingError) {
|
||||||
return s, 0
|
return s, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Parser) skipFast() (int, types.ParsingError) {
|
||||||
|
return self.skip()
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Node) encodeInterface(buf *[]byte) error {
|
func (self *Node) encodeInterface(buf *[]byte) error {
|
||||||
out, err := json.Marshal(self.packAny())
|
out, err := json.Marshal(self.packAny())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -59,4 +64,40 @@ func (self *Node) encodeInterface(buf *[]byte) error {
|
||||||
}
|
}
|
||||||
*buf = append(*buf, out...)
|
*buf = append(*buf, out...)
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Searcher) GetByPath(path ...interface{}) (Node, error) {
|
||||||
|
self.parser.p = 0
|
||||||
|
|
||||||
|
var err types.ParsingError
|
||||||
|
for _, p := range path {
|
||||||
|
switch p := p.(type) {
|
||||||
|
case int:
|
||||||
|
if err = self.parser.searchIndex(p); err != 0 {
|
||||||
|
return Node{}, self.parser.ExportError(err)
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
if err = self.parser.searchKey(p); err != 0 {
|
||||||
|
return Node{}, self.parser.ExportError(err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("path must be either int or string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var start = self.parser.p
|
||||||
|
if start, err = self.parser.skip(); err != 0 {
|
||||||
|
return Node{}, self.parser.ExportError(err)
|
||||||
|
}
|
||||||
|
ns := len(self.parser.s)
|
||||||
|
if self.parser.p > ns || start >= ns || start>=self.parser.p {
|
||||||
|
return Node{}, fmt.Errorf("skip %d char out of json boundary", start)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := switchRawType(self.parser.s[start])
|
||||||
|
if t == _V_NONE {
|
||||||
|
return Node{}, self.parser.ExportError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newRawNode(self.parser.s[start:self.parser.p], t), nil
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +18,6 @@ package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`fmt`
|
`fmt`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/native/types`
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
)
|
)
|
||||||
|
|
@ -131,7 +130,7 @@ func (self *Parser) decodeArray(ret []Node) (Node, types.ParsingError) {
|
||||||
if self.skipValue {
|
if self.skipValue {
|
||||||
/* skip the value */
|
/* skip the value */
|
||||||
var start int
|
var start int
|
||||||
if start, err = self.skip(); err != 0 {
|
if start, err = self.skipFast(); err != 0 {
|
||||||
return Node{}, err
|
return Node{}, err
|
||||||
}
|
}
|
||||||
if self.p > ns {
|
if self.p > ns {
|
||||||
|
|
@ -217,7 +216,7 @@ func (self *Parser) decodeObject(ret []Pair) (Node, types.ParsingError) {
|
||||||
if self.skipValue {
|
if self.skipValue {
|
||||||
/* skip the value */
|
/* skip the value */
|
||||||
var start int
|
var start int
|
||||||
if start, err = self.skip(); err != 0 {
|
if start, err = self.skipFast(); err != 0 {
|
||||||
return Node{}, err
|
return Node{}, err
|
||||||
}
|
}
|
||||||
if self.p > ns {
|
if self.p > ns {
|
||||||
|
|
@ -228,7 +227,7 @@ func (self *Parser) decodeObject(ret []Pair) (Node, types.ParsingError) {
|
||||||
return Node{}, types.ERR_INVALID_CHAR
|
return Node{}, types.ERR_INVALID_CHAR
|
||||||
}
|
}
|
||||||
val = newRawNode(self.s[start:self.p], t)
|
val = newRawNode(self.s[start:self.p], t)
|
||||||
}else{
|
} else {
|
||||||
/* decode the value */
|
/* decode the value */
|
||||||
if val, err = self.Parse(); err != 0 {
|
if val, err = self.Parse(); err != 0 {
|
||||||
return Node{}, err
|
return Node{}, err
|
||||||
|
|
@ -448,7 +447,7 @@ func (self *Node) skipNextNode() *Node {
|
||||||
|
|
||||||
var val Node
|
var val Node
|
||||||
/* skip the value */
|
/* skip the value */
|
||||||
if start, err := parser.skip(); err != 0 {
|
if start, err := parser.skipFast(); err != 0 {
|
||||||
return newSyntaxError(parser.syntaxError(err))
|
return newSyntaxError(parser.syntaxError(err))
|
||||||
} else {
|
} else {
|
||||||
t := switchRawType(parser.s[start])
|
t := switchRawType(parser.s[start])
|
||||||
|
|
@ -531,7 +530,7 @@ func (self *Node) skipNextPair() (*Pair) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* skip the value */
|
/* skip the value */
|
||||||
if start, err := parser.skip(); err != 0 {
|
if start, err := parser.skipFast(); err != 0 {
|
||||||
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
|
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
|
||||||
} else {
|
} else {
|
||||||
t := switchRawType(parser.s[start])
|
t := switchRawType(parser.s[start])
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,6 @@
|
||||||
|
|
||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
|
||||||
`fmt`
|
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/native/types`
|
|
||||||
)
|
|
||||||
|
|
||||||
type Searcher struct {
|
type Searcher struct {
|
||||||
parser Parser
|
parser Parser
|
||||||
}
|
}
|
||||||
|
|
@ -34,39 +28,3 @@ func NewSearcher(str string) *Searcher {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Searcher) GetByPath(path ...interface{}) (Node, error) {
|
|
||||||
self.parser.p = 0
|
|
||||||
|
|
||||||
var err types.ParsingError
|
|
||||||
for _, p := range path {
|
|
||||||
switch p := p.(type) {
|
|
||||||
case int:
|
|
||||||
if err = self.parser.searchIndex(p); err != 0 {
|
|
||||||
return Node{}, self.parser.ExportError(err)
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
if err = self.parser.searchKey(p); err != 0 {
|
|
||||||
return Node{}, self.parser.ExportError(err)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("path must be either int or string")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var start = self.parser.p
|
|
||||||
if start, err = self.parser.skip(); err != 0 {
|
|
||||||
return Node{}, self.parser.ExportError(err)
|
|
||||||
}
|
|
||||||
ns := len(self.parser.s)
|
|
||||||
if self.parser.p > ns || start >= ns || start>=self.parser.p {
|
|
||||||
return Node{}, fmt.Errorf("skip %d char out of json boundary", start)
|
|
||||||
}
|
|
||||||
|
|
||||||
t := switchRawType(self.parser.s[start])
|
|
||||||
if t == _V_NONE {
|
|
||||||
return Node{}, self.parser.ExportError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newRawNode(self.parser.s[start:self.parser.p], t), nil
|
|
||||||
}
|
|
||||||
|
|
@ -204,6 +204,21 @@ func BenchmarkGetOne_Sonic(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkGetWithManyCompare_Sonic(b *testing.B) {
|
||||||
|
b.SetBytes(int64(len(_LotsCompare)))
|
||||||
|
ast := NewSearcher(_LotsCompare)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
node, err := ast.GetByPath("is")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
x, _ := node.Int64()
|
||||||
|
if x != 1 {
|
||||||
|
b.Fatal(node.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkGetOne_Parallel_Sonic(b *testing.B) {
|
func BenchmarkGetOne_Parallel_Sonic(b *testing.B) {
|
||||||
b.SetBytes(int64(len(_TwitterJson)))
|
b.SetBytes(int64(len(_TwitterJson)))
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
|
|
||||||
|
|
@ -432,6 +432,8 @@ const _TwitterJson = `{
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
const _LotsCompare = `{"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"hi":0,"is":1}`
|
||||||
|
|
||||||
type _TwitterStruct struct {
|
type _TwitterStruct struct {
|
||||||
Statuses []struct {
|
Statuses []struct {
|
||||||
Coordinates interface{} `json:"coordinates"`
|
Coordinates interface{} `json:"coordinates"`
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ import (
|
||||||
`math`
|
`math`
|
||||||
`testing`
|
`testing`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/ast`
|
||||||
|
`github.com/buger/jsonparser`
|
||||||
jsoniter `github.com/json-iterator/go`
|
jsoniter `github.com/json-iterator/go`
|
||||||
`github.com/tidwall/gjson`
|
`github.com/tidwall/gjson`
|
||||||
`github.com/tidwall/sjson`
|
`github.com/tidwall/sjson`
|
||||||
|
|
@ -49,6 +51,39 @@ func BenchmarkGetOne_Jsoniter(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkGetOne_Sonic(b *testing.B) {
|
||||||
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
|
ast := ast.NewSearcher(TwitterJson)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
node, err := ast.GetByPath("statuses", 3, "id")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
x, _ := node.Int64()
|
||||||
|
if x != 249279667666817024 {
|
||||||
|
b.Fatal(node.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func BenchmarkGetOne_Parallel_Sonic(b *testing.B) {
|
||||||
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
ast := ast.NewSearcher(TwitterJson)
|
||||||
|
for pb.Next() {
|
||||||
|
node, err := ast.GetByPath("statuses", 3, "id")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
x, _ := node.Int64()
|
||||||
|
if x != 249279667666817024 {
|
||||||
|
b.Fatal(node.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkGetOne_Parallel_Gjson(b *testing.B) {
|
func BenchmarkGetOne_Parallel_Gjson(b *testing.B) {
|
||||||
b.SetBytes(int64(len(TwitterJson)))
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
|
@ -139,3 +174,36 @@ func BenchmarkSetOne_Parallel_Jsoniter(b *testing.B) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func BenchmarkGetByKeys_Sonic(b *testing.B) {
|
||||||
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
|
ast := ast.NewSearcher(TwitterJson)
|
||||||
|
const _count = 4
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
node, err := ast.GetByPath("search_metadata", "count")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
x, _ := node.Int64()
|
||||||
|
if x != _count {
|
||||||
|
b.Fatal(node.Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func BenchmarkGetByKeys_JsonParser(b *testing.B) {
|
||||||
|
b.SetBytes(int64(len(TwitterJson)))
|
||||||
|
data := []byte(TwitterJson)
|
||||||
|
const _count = 4
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
value, err := jsonparser.GetInt(data, "search_metadata", "count")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if value != _count {
|
||||||
|
b.Fatal(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ module github.com/bytedance/sonic/external_jsonlib_test
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/buger/jsonparser v1.1.1
|
||||||
github.com/bytedance/sonic v1.4.0
|
github.com/bytedance/sonic v1.4.0
|
||||||
github.com/goccy/go-json v0.9.11
|
github.com/goccy/go-json v0.9.11
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||||
|
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||||
github.com/bytedance/sonic v1.4.0 h1:d6vgPhwgHfpmEiz/9Fzea9fGzWY7RO1TQEySBiRwDLY=
|
github.com/bytedance/sonic v1.4.0 h1:d6vgPhwgHfpmEiz/9Fzea9fGzWY7RO1TQEySBiRwDLY=
|
||||||
github.com/bytedance/sonic v1.4.0/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ=
|
github.com/bytedance/sonic v1.4.0/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 h1:1sDoSuDPWzhkdzNVxCxtIaKiAe96ESVPv8coGwc1gZ4=
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 h1:1sDoSuDPWzhkdzNVxCxtIaKiAe96ESVPv8coGwc1gZ4=
|
||||||
|
|
|
||||||
|
|
@ -20,57 +20,35 @@ package sonic_fuzz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`testing`
|
`testing`
|
||||||
|
`fmt`
|
||||||
`github.com/bytedance/sonic`
|
`github.com/bytedance/sonic`
|
||||||
`github.com/bytedance/sonic/ast`
|
|
||||||
`github.com/stretchr/testify/require`
|
`github.com/stretchr/testify/require`
|
||||||
|
`github.com/davecgh/go-spew/spew`
|
||||||
)
|
)
|
||||||
|
|
||||||
func fuzzASTGetFromObject(t *testing.T, data []byte, m map[string]interface{}) {
|
func fuzzASTGetFromObject(t *testing.T, data []byte, m map[string]interface{}) {
|
||||||
for k, expv := range(m) {
|
for k, expv := range(m) {
|
||||||
|
msg := fmt.Sprintf("Data:\n%s\nKey:\n%s\n", spew.Sdump(&data), spew.Sdump(&k))
|
||||||
node, err := sonic.Get(data, k)
|
node, err := sonic.Get(data, k)
|
||||||
require.NoErrorf(t, err, "error in ast get key -> %s", k)
|
require.NoErrorf(t, err, "error in ast get key\n%s", msg)
|
||||||
assertAstNode(t, node, expv)
|
v, err := node.Interface()
|
||||||
|
require.NoErrorf(t, err, "error in node convert\n%s", msg)
|
||||||
|
require.Equalf(t, v, expv, "error in node equal\n%sGot:\n%s\nExp:\n%s\n",
|
||||||
|
msg, spew.Sdump(v), spew.Sdump(expv))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fuzzASTGetFromArray(t *testing.T, data []byte, a []interface{}) {
|
func fuzzASTGetFromArray(t *testing.T, data []byte, a []interface{}) {
|
||||||
var i = 0
|
i := 0
|
||||||
for ; i < len(a); i++ {
|
for ; i < len(a); i++ {
|
||||||
|
msg := fmt.Sprintf("Data:\n%s\nIndex:\n%d\n", spew.Sdump(data), i)
|
||||||
node, err := sonic.Get(data, i)
|
node, err := sonic.Get(data, i)
|
||||||
require.NoErrorf(t, err, "error in ast get index -> %s", i)
|
require.NoErrorf(t, err, "error in ast get index\n%s", msg)
|
||||||
assertAstNode(t, node, a[i])
|
v, err := node.Interface()
|
||||||
|
require.NoErrorf(t, err, "error in node convert\n%s", msg)
|
||||||
|
require.Equalf(t, v, a[i], "error in node equal\n%sGot:\n%s\nExp:\n%s\n",
|
||||||
|
msg, spew.Sdump(v), spew.Sdump(a[i]))
|
||||||
}
|
}
|
||||||
_, err := sonic.Get(data, i)
|
_, err := sonic.Get(data, i)
|
||||||
require.Errorf(t, err, "error in ast get index -> %s", i)
|
require.Errorf(t, err, "no error in ast get out of range\nData:\n%s\n", spew.Sdump(data))
|
||||||
}
|
|
||||||
|
|
||||||
func assertAstNode(t *testing.T, node ast.Node, expv interface{}) {
|
|
||||||
switch node.Type() {
|
|
||||||
case ast.V_NULL: require.Nilf(t, expv, "wrong in ast null")
|
|
||||||
case ast.V_TRUE: fallthrough
|
|
||||||
case ast.V_FALSE:
|
|
||||||
gotv, err := node.Bool()
|
|
||||||
require.NoErrorf(t, err, "error in ast get bool")
|
|
||||||
require.Equalf(t, gotv, expv, "wrong in get bool")
|
|
||||||
case ast.V_STRING:
|
|
||||||
gotv, err := node.String()
|
|
||||||
require.NoErrorf(t, err, "error in ast get string")
|
|
||||||
require.Equalf(t, gotv, expv, "wrong in get string")
|
|
||||||
case ast.V_ARRAY:
|
|
||||||
gotv, err := node.Array()
|
|
||||||
require.NoErrorf(t, err, "error in ast get array")
|
|
||||||
require.Equalf(t, gotv, expv, "wrong in get array")
|
|
||||||
case ast.V_OBJECT:
|
|
||||||
gotv, err := node.Map()
|
|
||||||
require.NoErrorf(t, err, "error in ast get object")
|
|
||||||
require.Equalf(t, gotv, expv, "wrong in get object")
|
|
||||||
case ast.V_NUMBER:
|
|
||||||
gotv, err := node.Float64()
|
|
||||||
require.NoErrorf(t, err, "error in ast get number")
|
|
||||||
require.Equalf(t, gotv, expv, "wrong in get number")
|
|
||||||
case ast.V_ANY:
|
|
||||||
gotv, err := node.Interface()
|
|
||||||
require.NoErrorf(t, err, "error in ast get any")
|
|
||||||
require.Equalf(t, gotv, expv, "wrong in get any")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
60
fuzz/corpus.go
Normal file
60
fuzz/corpus.go
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 sonic_fuzz
|
||||||
|
|
||||||
|
// corpus returns the simple and basic JSON corpus for fuzzing test.
|
||||||
|
func corpus() [][]byte {
|
||||||
|
data := []string {
|
||||||
|
`[]`, `{}`, `[{`, `}`, `{{}}`, `,`, `:`,// structural chars
|
||||||
|
`null`, `true`, `false`, `truf`, // primitive types
|
||||||
|
`1.234567890e-123`, `01`, `00`, "+1", // numbers
|
||||||
|
`e`, `-`, `+`, `.`, // signs
|
||||||
|
" ", "\n", "\t", "\r", // space
|
||||||
|
"\b", "\f", "\\", "/", "\"", "\u2028", "\x00", // unescaped chars
|
||||||
|
"\\b", "\\n", "\\f", "\\\\", "\\/", "\\\"", "\\r", "\\t", "\\u2028", // escaped chars
|
||||||
|
"<", ">", "&", "\u2028", "\u2029", // html escape
|
||||||
|
`😁`, "\xff", "\xf0", "\x80", // utf-8
|
||||||
|
"\xed\xa0\x80" /* \ud800 */, "\xef\xbf\xbf", /* \uffff */ "\xed\xbf\xbf", /* \udfff */
|
||||||
|
`"haha"`, `"你好"`, `"😁"`, `"\\uD800\\udc00"`, `""`, // json strings
|
||||||
|
"\"\u2028\u2029\"", `<>&`,
|
||||||
|
"\"\xff\"", "\"\x00\"", `"\\uDFFF"`,
|
||||||
|
`[2, 3, null, true, false, "hi"]`, // short json
|
||||||
|
`{
|
||||||
|
"object": {
|
||||||
|
"slice": [
|
||||||
|
1,
|
||||||
|
2.0,
|
||||||
|
"3",
|
||||||
|
[4],
|
||||||
|
{5: {}}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"slice": [[]],
|
||||||
|
"string": ":)",
|
||||||
|
"int": 1e5,
|
||||||
|
"float": 3e-9"
|
||||||
|
}`,
|
||||||
|
`{"a":{"a":1,"b":[1,1,1],"c":{"d":1,"e":1,"f":1},"d":"{\"你好\":\"hi\"}"}}`,
|
||||||
|
}
|
||||||
|
var corpus [][]byte
|
||||||
|
for _, t := range(data) {
|
||||||
|
corpus = append(corpus, []byte(t))
|
||||||
|
}
|
||||||
|
return corpus
|
||||||
|
}
|
||||||
|
|
@ -33,21 +33,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func FuzzMain(f *testing.F) {
|
func FuzzMain(f *testing.F) {
|
||||||
f.Add([]byte(`{
|
for _, corp := range(corpus()) {
|
||||||
"object": {
|
f.Add(corp)
|
||||||
"slice": [
|
}
|
||||||
1,
|
|
||||||
2.0,
|
|
||||||
"3",
|
|
||||||
[4],
|
|
||||||
{5: {}}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"slice": [[]],
|
|
||||||
"string": ":)",
|
|
||||||
"int": 1e5,
|
|
||||||
"float": 3e-9"
|
|
||||||
}`))
|
|
||||||
f.Fuzz(fuzzMain)
|
f.Fuzz(fuzzMain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,7 +55,6 @@ func fuzzMain(t *testing.T, data []byte) {
|
||||||
func() interface{} { return new(uint64) },
|
func() interface{} { return new(uint64) },
|
||||||
func() interface{} { return new(float64) },
|
func() interface{} { return new(float64) },
|
||||||
func() interface{} { return new(json.Number) },
|
func() interface{} { return new(json.Number) },
|
||||||
func() interface{} { return new(json.RawMessage) },
|
|
||||||
func() interface{} { return new(S) },
|
func() interface{} { return new(S) },
|
||||||
} {
|
} {
|
||||||
sv, jv := typ(), typ()
|
sv, jv := typ(), typ()
|
||||||
|
|
@ -75,7 +62,7 @@ func fuzzMain(t *testing.T, data []byte) {
|
||||||
jerr := json.Unmarshal([]byte(data), jv)
|
jerr := json.Unmarshal([]byte(data), jv)
|
||||||
require.Equalf(t, serr != nil, jerr != nil, "different error in sonic unmarshal %v", reflect.TypeOf(jv))
|
require.Equalf(t, serr != nil, jerr != nil, "different error in sonic unmarshal %v", reflect.TypeOf(jv))
|
||||||
if jerr != nil {
|
if jerr != nil {
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
require.Equal(t, sv, jv, "different result in sonic unmarshal %v", reflect.TypeOf(jv))
|
require.Equal(t, sv, jv, "different result in sonic unmarshal %v", reflect.TypeOf(jv))
|
||||||
sout, serr := sonic.Marshal(sv)
|
sout, serr := sonic.Marshal(sv)
|
||||||
|
|
@ -89,7 +76,7 @@ func fuzzMain(t *testing.T, data []byte) {
|
||||||
jerr := json.Unmarshal(jout, jv)
|
jerr := json.Unmarshal(jout, jv)
|
||||||
require.Equalf(t, serr != nil, jerr != nil, "different error in sonic unmarshal again %v", reflect.TypeOf(jv))
|
require.Equalf(t, serr != nil, jerr != nil, "different error in sonic unmarshal again %v", reflect.TypeOf(jv))
|
||||||
if jerr != nil {
|
if jerr != nil {
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
require.Equal(t, sv, jv, "different result in sonic unmarshal again %v", reflect.TypeOf(jv))
|
require.Equal(t, sv, jv, "different result in sonic unmarshal again %v", reflect.TypeOf(jv))
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +114,7 @@ type S struct {
|
||||||
U [2]int
|
U [2]int
|
||||||
V uintptr
|
V uintptr
|
||||||
W json.Number
|
W json.Number
|
||||||
X json.RawMessage
|
// X json.RawMessage
|
||||||
Y Marshaller
|
Y Marshaller
|
||||||
Z TextMarshaller
|
Z TextMarshaller
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,11 @@ func __vunsigned(s *string, p *int, v *types.JsonState)
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func __skip_one(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
func __skip_one(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_one_fast(s *string, p *int) (ret int)
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
|
|
@ -112,4 +117,9 @@ func __skip_number(s *string, p *int) (ret int)
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func __validate_one(s *string, p *int, m *types.StateMachine) (ret int)
|
func __validate_one(s *string, p *int, m *types.StateMachine) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __get_by_path(s *string, p *int, path *[]interface{}) (ret int)
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -591,3 +591,59 @@ func TestNative_SkipNumber(t *testing.T) {
|
||||||
assert.Equal(t, 9, p)
|
assert.Equal(t, 9, p)
|
||||||
assert.Equal(t, 0, q)
|
assert.Equal(t, 0, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNative_SkipOneFast(t *testing.T) {
|
||||||
|
p := 0
|
||||||
|
s := ` {"asdf": [null, true, false, 1, 2.0, -3]}, 1234.5`
|
||||||
|
q := __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 42, p)
|
||||||
|
assert.Equal(t, 1, q)
|
||||||
|
p = 0
|
||||||
|
s = `1, 2.5, -3, "asdf\nqwer", true, false, null, {}, [],`
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 1, p)
|
||||||
|
assert.Equal(t, 0, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 6, p)
|
||||||
|
assert.Equal(t, 3, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 10, p)
|
||||||
|
assert.Equal(t, 8, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 24, p)
|
||||||
|
assert.Equal(t, 12, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 30, p)
|
||||||
|
assert.Equal(t, 26, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 37, p)
|
||||||
|
assert.Equal(t, 32, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 43, p)
|
||||||
|
assert.Equal(t, 39, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 47, p)
|
||||||
|
assert.Equal(t, 45, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 51, p)
|
||||||
|
assert.Equal(t, 49, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNative_SkipOneFast_Error(t *testing.T) {
|
||||||
|
for _, s := range([]string{
|
||||||
|
"{{", "[{", "{{}",
|
||||||
|
`"asdf`, `"\\\"`,
|
||||||
|
}) {
|
||||||
|
p := 0
|
||||||
|
q := __skip_one_fast(&s, &p)
|
||||||
|
assert.True(t, q < 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -41,7 +41,9 @@ var (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
S_skip_one = _subr__skip_one
|
S_skip_one = _subr__skip_one
|
||||||
|
S_skip_one_fast = _subr__skip_one_fast
|
||||||
S_skip_array = _subr__skip_array
|
S_skip_array = _subr__skip_array
|
||||||
S_skip_object = _subr__skip_object
|
S_skip_object = _subr__skip_object
|
||||||
S_skip_number = _subr__skip_number
|
S_skip_number = _subr__skip_number
|
||||||
|
S_get_by_path = _subr__get_by_path
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,29 +9,32 @@ package avx
|
||||||
func __native_entry__() uintptr
|
func __native_entry__() uintptr
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_subr__f32toa = __native_entry__() + 24592
|
_subr__f32toa = __native_entry__() + 28656
|
||||||
_subr__f64toa = __native_entry__() + 496
|
_subr__f64toa = __native_entry__() + 496
|
||||||
_subr__html_escape = __native_entry__() + 10480
|
_subr__get_by_path = __native_entry__() + 26848
|
||||||
_subr__i64toa = __native_entry__() + 4176
|
_subr__html_escape = __native_entry__() + 10480
|
||||||
_subr__lspace = __native_entry__() + 80
|
_subr__i64toa = __native_entry__() + 4176
|
||||||
_subr__quote = __native_entry__() + 5552
|
_subr__lspace = __native_entry__() + 80
|
||||||
_subr__skip_array = __native_entry__() + 22864
|
_subr__quote = __native_entry__() + 5552
|
||||||
_subr__skip_number = __native_entry__() + 24336
|
_subr__skip_array = __native_entry__() + 20160
|
||||||
_subr__skip_object = __native_entry__() + 22912
|
_subr__skip_number = __native_entry__() + 23472
|
||||||
_subr__skip_one = __native_entry__() + 20992
|
_subr__skip_object = __native_entry__() + 22048
|
||||||
_subr__u64toa = __native_entry__() + 4288
|
_subr__skip_one = __native_entry__() + 23616
|
||||||
_subr__unquote = __native_entry__() + 7296
|
_subr__skip_one_fast = __native_entry__() + 23824
|
||||||
_subr__validate_one = __native_entry__() + 24480
|
_subr__u64toa = __native_entry__() + 4288
|
||||||
_subr__value = __native_entry__() + 13728
|
_subr__unquote = __native_entry__() + 7296
|
||||||
_subr__vnumber = __native_entry__() + 18736
|
_subr__validate_one = __native_entry__() + 23648
|
||||||
_subr__vsigned = __native_entry__() + 20288
|
_subr__value = __native_entry__() + 13728
|
||||||
_subr__vstring = __native_entry__() + 15808
|
_subr__vnumber = __native_entry__() + 17904
|
||||||
_subr__vunsigned = __native_entry__() + 20640
|
_subr__vsigned = __native_entry__() + 19456
|
||||||
|
_subr__vstring = __native_entry__() + 15808
|
||||||
|
_subr__vunsigned = __native_entry__() + 19808
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_stack__f32toa = 64
|
_stack__f32toa = 64
|
||||||
_stack__f64toa = 80
|
_stack__f64toa = 80
|
||||||
|
_stack__get_by_path = 296
|
||||||
_stack__html_escape = 64
|
_stack__html_escape = 64
|
||||||
_stack__i64toa = 16
|
_stack__i64toa = 16
|
||||||
_stack__lspace = 8
|
_stack__lspace = 8
|
||||||
|
|
@ -40,6 +43,7 @@ const (
|
||||||
_stack__skip_number = 72
|
_stack__skip_number = 72
|
||||||
_stack__skip_object = 128
|
_stack__skip_object = 128
|
||||||
_stack__skip_one = 128
|
_stack__skip_one = 128
|
||||||
|
_stack__skip_one_fast = 208
|
||||||
_stack__u64toa = 8
|
_stack__u64toa = 8
|
||||||
_stack__unquote = 72
|
_stack__unquote = 72
|
||||||
_stack__validate_one = 128
|
_stack__validate_one = 128
|
||||||
|
|
@ -53,6 +57,7 @@ const (
|
||||||
var (
|
var (
|
||||||
_ = _subr__f32toa
|
_ = _subr__f32toa
|
||||||
_ = _subr__f64toa
|
_ = _subr__f64toa
|
||||||
|
_ = _subr__get_by_path
|
||||||
_ = _subr__html_escape
|
_ = _subr__html_escape
|
||||||
_ = _subr__i64toa
|
_ = _subr__i64toa
|
||||||
_ = _subr__lspace
|
_ = _subr__lspace
|
||||||
|
|
@ -61,6 +66,7 @@ var (
|
||||||
_ = _subr__skip_number
|
_ = _subr__skip_number
|
||||||
_ = _subr__skip_object
|
_ = _subr__skip_object
|
||||||
_ = _subr__skip_one
|
_ = _subr__skip_one
|
||||||
|
_ = _subr__skip_one_fast
|
||||||
_ = _subr__u64toa
|
_ = _subr__u64toa
|
||||||
_ = _subr__unquote
|
_ = _subr__unquote
|
||||||
_ = _subr__validate_one
|
_ = _subr__validate_one
|
||||||
|
|
@ -74,6 +80,7 @@ var (
|
||||||
const (
|
const (
|
||||||
_ = _stack__f32toa
|
_ = _stack__f32toa
|
||||||
_ = _stack__f64toa
|
_ = _stack__f64toa
|
||||||
|
_ = _stack__get_by_path
|
||||||
_ = _stack__html_escape
|
_ = _stack__html_escape
|
||||||
_ = _stack__i64toa
|
_ = _stack__i64toa
|
||||||
_ = _stack__lspace
|
_ = _stack__lspace
|
||||||
|
|
@ -82,6 +89,7 @@ const (
|
||||||
_ = _stack__skip_number
|
_ = _stack__skip_number
|
||||||
_ = _stack__skip_object
|
_ = _stack__skip_object
|
||||||
_ = _stack__skip_one
|
_ = _stack__skip_one
|
||||||
|
_ = _stack__skip_one_fast
|
||||||
_ = _stack__u64toa
|
_ = _stack__u64toa
|
||||||
_ = _stack__unquote
|
_ = _stack__unquote
|
||||||
_ = _stack__validate_one
|
_ = _stack__validate_one
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,11 @@ func __vunsigned(s *string, p *int, v *types.JsonState)
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func __skip_one(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
func __skip_one(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_one_fast(s *string, p *int) (ret int)
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
|
|
@ -112,4 +117,9 @@ func __skip_number(s *string, p *int) (ret int)
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func __validate_one(s *string, p *int, m *types.StateMachine) (ret int)
|
func __validate_one(s *string, p *int, m *types.StateMachine) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __get_by_path(s *string, p *int, path *[]interface{}) (ret int)
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -591,3 +591,59 @@ func TestNative_SkipNumber(t *testing.T) {
|
||||||
assert.Equal(t, 9, p)
|
assert.Equal(t, 9, p)
|
||||||
assert.Equal(t, 0, q)
|
assert.Equal(t, 0, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNative_SkipOneFast(t *testing.T) {
|
||||||
|
p := 0
|
||||||
|
s := ` {"asdf": [null, true, false, 1, 2.0, -3]}, 1234.5`
|
||||||
|
q := __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 42, p)
|
||||||
|
assert.Equal(t, 1, q)
|
||||||
|
p = 0
|
||||||
|
s = `1, 2.5, -3, "asdf\nqwer", true, false, null, {}, [],`
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 1, p)
|
||||||
|
assert.Equal(t, 0, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 6, p)
|
||||||
|
assert.Equal(t, 3, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 10, p)
|
||||||
|
assert.Equal(t, 8, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 24, p)
|
||||||
|
assert.Equal(t, 12, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 30, p)
|
||||||
|
assert.Equal(t, 26, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 37, p)
|
||||||
|
assert.Equal(t, 32, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 43, p)
|
||||||
|
assert.Equal(t, 39, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 47, p)
|
||||||
|
assert.Equal(t, 45, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 51, p)
|
||||||
|
assert.Equal(t, 49, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNative_SkipOneFast_Error(t *testing.T) {
|
||||||
|
for _, s := range([]string{
|
||||||
|
"{{", "[{", "{{}",
|
||||||
|
`"asdf`, `"\\\"`,
|
||||||
|
}) {
|
||||||
|
p := 0
|
||||||
|
q := __skip_one_fast(&s, &p)
|
||||||
|
assert.True(t, q < 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -41,7 +41,9 @@ var (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
S_skip_one = _subr__skip_one
|
S_skip_one = _subr__skip_one
|
||||||
|
S_skip_one_fast = _subr__skip_one_fast
|
||||||
S_skip_array = _subr__skip_array
|
S_skip_array = _subr__skip_array
|
||||||
S_skip_object = _subr__skip_object
|
S_skip_object = _subr__skip_object
|
||||||
S_skip_number = _subr__skip_number
|
S_skip_number = _subr__skip_number
|
||||||
|
S_get_by_path = _subr__get_by_path
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,29 +9,32 @@ package avx2
|
||||||
func __native_entry__() uintptr
|
func __native_entry__() uintptr
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_subr__f32toa = __native_entry__() + 28464
|
_subr__f32toa = __native_entry__() + 32816
|
||||||
_subr__f64toa = __native_entry__() + 752
|
_subr__f64toa = __native_entry__() + 752
|
||||||
_subr__html_escape = __native_entry__() + 12320
|
_subr__get_by_path = __native_entry__() + 30896
|
||||||
_subr__i64toa = __native_entry__() + 4432
|
_subr__html_escape = __native_entry__() + 12320
|
||||||
_subr__lspace = __native_entry__() + 224
|
_subr__i64toa = __native_entry__() + 4432
|
||||||
_subr__quote = __native_entry__() + 5904
|
_subr__lspace = __native_entry__() + 224
|
||||||
_subr__skip_array = __native_entry__() + 26112
|
_subr__quote = __native_entry__() + 5904
|
||||||
_subr__skip_number = __native_entry__() + 28208
|
_subr__skip_array = __native_entry__() + 23472
|
||||||
_subr__skip_object = __native_entry__() + 26160
|
_subr__skip_number = __native_entry__() + 27440
|
||||||
_subr__skip_one = __native_entry__() + 24208
|
_subr__skip_object = __native_entry__() + 25392
|
||||||
_subr__u64toa = __native_entry__() + 4544
|
_subr__skip_one = __native_entry__() + 27584
|
||||||
_subr__unquote = __native_entry__() + 8848
|
_subr__skip_one_fast = __native_entry__() + 27984
|
||||||
_subr__validate_one = __native_entry__() + 28352
|
_subr__u64toa = __native_entry__() + 4544
|
||||||
_subr__value = __native_entry__() + 16896
|
_subr__unquote = __native_entry__() + 8848
|
||||||
_subr__vnumber = __native_entry__() + 21952
|
_subr__validate_one = __native_entry__() + 27616
|
||||||
_subr__vsigned = __native_entry__() + 23504
|
_subr__value = __native_entry__() + 16896
|
||||||
_subr__vstring = __native_entry__() + 19280
|
_subr__vnumber = __native_entry__() + 21216
|
||||||
_subr__vunsigned = __native_entry__() + 23856
|
_subr__vsigned = __native_entry__() + 22768
|
||||||
|
_subr__vstring = __native_entry__() + 19280
|
||||||
|
_subr__vunsigned = __native_entry__() + 23120
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_stack__f32toa = 64
|
_stack__f32toa = 64
|
||||||
_stack__f64toa = 80
|
_stack__f64toa = 80
|
||||||
|
_stack__get_by_path = 304
|
||||||
_stack__html_escape = 72
|
_stack__html_escape = 72
|
||||||
_stack__i64toa = 16
|
_stack__i64toa = 16
|
||||||
_stack__lspace = 8
|
_stack__lspace = 8
|
||||||
|
|
@ -40,19 +43,21 @@ const (
|
||||||
_stack__skip_number = 80
|
_stack__skip_number = 80
|
||||||
_stack__skip_object = 136
|
_stack__skip_object = 136
|
||||||
_stack__skip_one = 136
|
_stack__skip_one = 136
|
||||||
|
_stack__skip_one_fast = 216
|
||||||
_stack__u64toa = 8
|
_stack__u64toa = 8
|
||||||
_stack__unquote = 72
|
_stack__unquote = 72
|
||||||
_stack__validate_one = 136
|
_stack__validate_one = 136
|
||||||
_stack__value = 336
|
_stack__value = 336
|
||||||
_stack__vnumber = 248
|
_stack__vnumber = 248
|
||||||
_stack__vsigned = 16
|
_stack__vsigned = 16
|
||||||
_stack__vstring = 128
|
_stack__vstring = 136
|
||||||
_stack__vunsigned = 24
|
_stack__vunsigned = 24
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ = _subr__f32toa
|
_ = _subr__f32toa
|
||||||
_ = _subr__f64toa
|
_ = _subr__f64toa
|
||||||
|
_ = _subr__get_by_path
|
||||||
_ = _subr__html_escape
|
_ = _subr__html_escape
|
||||||
_ = _subr__i64toa
|
_ = _subr__i64toa
|
||||||
_ = _subr__lspace
|
_ = _subr__lspace
|
||||||
|
|
@ -61,6 +66,7 @@ var (
|
||||||
_ = _subr__skip_number
|
_ = _subr__skip_number
|
||||||
_ = _subr__skip_object
|
_ = _subr__skip_object
|
||||||
_ = _subr__skip_one
|
_ = _subr__skip_one
|
||||||
|
_ = _subr__skip_one_fast
|
||||||
_ = _subr__u64toa
|
_ = _subr__u64toa
|
||||||
_ = _subr__unquote
|
_ = _subr__unquote
|
||||||
_ = _subr__validate_one
|
_ = _subr__validate_one
|
||||||
|
|
@ -74,6 +80,7 @@ var (
|
||||||
const (
|
const (
|
||||||
_ = _stack__f32toa
|
_ = _stack__f32toa
|
||||||
_ = _stack__f64toa
|
_ = _stack__f64toa
|
||||||
|
_ = _stack__get_by_path
|
||||||
_ = _stack__html_escape
|
_ = _stack__html_escape
|
||||||
_ = _stack__i64toa
|
_ = _stack__i64toa
|
||||||
_ = _stack__lspace
|
_ = _stack__lspace
|
||||||
|
|
@ -82,6 +89,7 @@ const (
|
||||||
_ = _stack__skip_number
|
_ = _stack__skip_number
|
||||||
_ = _stack__skip_object
|
_ = _stack__skip_object
|
||||||
_ = _stack__skip_one
|
_ = _stack__skip_one
|
||||||
|
_ = _stack__skip_one_fast
|
||||||
_ = _stack__u64toa
|
_ = _stack__u64toa
|
||||||
_ = _stack__unquote
|
_ = _stack__unquote
|
||||||
_ = _stack__validate_one
|
_ = _stack__validate_one
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,8 @@ var (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
S_skip_one uintptr
|
S_skip_one uintptr
|
||||||
|
S_skip_one_fast uintptr
|
||||||
|
S_get_by_path uintptr
|
||||||
S_skip_array uintptr
|
S_skip_array uintptr
|
||||||
S_skip_object uintptr
|
S_skip_object uintptr
|
||||||
S_skip_number uintptr
|
S_skip_number uintptr
|
||||||
|
|
@ -81,6 +83,16 @@ func Value(s unsafe.Pointer, n int, p int, v *types.JsonState, flags uint64) int
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func SkipOne(s *string, p *int, m *types.StateMachine, flags uint64) int
|
func SkipOne(s *string, p *int, m *types.StateMachine, flags uint64) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func SkipOneFast(s *string, p *int) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func GetByPath(s *string, p *int, path *[]interface{}) int
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
|
|
@ -115,9 +127,11 @@ func useAVX() {
|
||||||
S_vsigned = avx.S_vsigned
|
S_vsigned = avx.S_vsigned
|
||||||
S_vunsigned = avx.S_vunsigned
|
S_vunsigned = avx.S_vunsigned
|
||||||
S_skip_one = avx.S_skip_one
|
S_skip_one = avx.S_skip_one
|
||||||
|
S_skip_one_fast = avx.S_skip_one_fast
|
||||||
S_skip_array = avx.S_skip_array
|
S_skip_array = avx.S_skip_array
|
||||||
S_skip_object = avx.S_skip_object
|
S_skip_object = avx.S_skip_object
|
||||||
S_skip_number = avx.S_skip_number
|
S_skip_number = avx.S_skip_number
|
||||||
|
S_get_by_path = avx.S_get_by_path
|
||||||
}
|
}
|
||||||
|
|
||||||
func useAVX2() {
|
func useAVX2() {
|
||||||
|
|
@ -134,9 +148,11 @@ func useAVX2() {
|
||||||
S_vsigned = avx2.S_vsigned
|
S_vsigned = avx2.S_vsigned
|
||||||
S_vunsigned = avx2.S_vunsigned
|
S_vunsigned = avx2.S_vunsigned
|
||||||
S_skip_one = avx2.S_skip_one
|
S_skip_one = avx2.S_skip_one
|
||||||
|
S_skip_one_fast = avx2.S_skip_one_fast
|
||||||
S_skip_array = avx2.S_skip_array
|
S_skip_array = avx2.S_skip_array
|
||||||
S_skip_object = avx2.S_skip_object
|
S_skip_object = avx2.S_skip_object
|
||||||
S_skip_number = avx2.S_skip_number
|
S_skip_number = avx2.S_skip_number
|
||||||
|
S_get_by_path = avx2.S_get_by_path
|
||||||
}
|
}
|
||||||
|
|
||||||
func useSSE() {
|
func useSSE() {
|
||||||
|
|
@ -153,9 +169,11 @@ func useSSE() {
|
||||||
S_vsigned = sse.S_vsigned
|
S_vsigned = sse.S_vsigned
|
||||||
S_vunsigned = sse.S_vunsigned
|
S_vunsigned = sse.S_vunsigned
|
||||||
S_skip_one = sse.S_skip_one
|
S_skip_one = sse.S_skip_one
|
||||||
|
S_skip_one_fast = sse.S_skip_one_fast
|
||||||
S_skip_array = sse.S_skip_array
|
S_skip_array = sse.S_skip_array
|
||||||
S_skip_object = sse.S_skip_object
|
S_skip_object = sse.S_skip_object
|
||||||
S_skip_number = sse.S_skip_number
|
S_skip_number = sse.S_skip_number
|
||||||
|
S_get_by_path = sse.S_get_by_path
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,23 @@ TEXT ·SkipOne(SB), NOSPLIT, $0 - 40
|
||||||
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__skip_one(SB)
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__skip_one(SB)
|
||||||
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__skip_one(SB)
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__skip_one(SB)
|
||||||
|
|
||||||
|
TEXT ·SkipOneFast(SB), NOSPLIT, $0 - 24
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__skip_one_fast(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__skip_one_fast(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__skip_one_fast(SB)
|
||||||
|
|
||||||
|
TEXT ·GetByPath(SB), NOSPLIT, $0 - 32
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__get_by_path(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__get_by_path(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__get_by_path(SB)
|
||||||
TEXT ·ValidateOne(SB), NOSPLIT, $0 - 32
|
TEXT ·ValidateOne(SB), NOSPLIT, $0 - 32
|
||||||
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
JE 2(PC)
|
JE 2(PC)
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,11 @@ func __vunsigned(s *string, p *int, v *types.JsonState)
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func __skip_one(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
func __skip_one(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_one_fast(s *string, p *int) (ret int)
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
|
|
@ -110,4 +115,9 @@ func __skip_number(s *string, p *int) (ret int)
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func __validate_one(s *string, p *int, m *types.StateMachine) (ret int)
|
func __validate_one(s *string, p *int, m *types.StateMachine) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __get_by_path(s *string, p *int, path *[]interface{}) (ret int)
|
||||||
|
|
@ -589,3 +589,59 @@ func TestNative_SkipNumber(t *testing.T) {
|
||||||
assert.Equal(t, 9, p)
|
assert.Equal(t, 9, p)
|
||||||
assert.Equal(t, 0, q)
|
assert.Equal(t, 0, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNative_SkipOneFast(t *testing.T) {
|
||||||
|
p := 0
|
||||||
|
s := ` {"asdf": [null, true, false, 1, 2.0, -3]}, 1234.5`
|
||||||
|
q := __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 42, p)
|
||||||
|
assert.Equal(t, 1, q)
|
||||||
|
p = 0
|
||||||
|
s = `1, 2.5, -3, "asdf\nqwer", true, false, null, {}, [],`
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 1, p)
|
||||||
|
assert.Equal(t, 0, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 6, p)
|
||||||
|
assert.Equal(t, 3, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 10, p)
|
||||||
|
assert.Equal(t, 8, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 24, p)
|
||||||
|
assert.Equal(t, 12, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 30, p)
|
||||||
|
assert.Equal(t, 26, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 37, p)
|
||||||
|
assert.Equal(t, 32, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 43, p)
|
||||||
|
assert.Equal(t, 39, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 47, p)
|
||||||
|
assert.Equal(t, 45, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 51, p)
|
||||||
|
assert.Equal(t, 49, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNative_SkipOneFast_Error(t *testing.T) {
|
||||||
|
for _, s := range([]string{
|
||||||
|
"{{", "[{", "{{}",
|
||||||
|
`"asdf`, `"\\\"`,
|
||||||
|
}) {
|
||||||
|
p := 0
|
||||||
|
q := __skip_one_fast(&s, &p)
|
||||||
|
assert.True(t, q < 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,9 @@ var (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
S_skip_one = _subr__skip_one
|
S_skip_one = _subr__skip_one
|
||||||
|
S_skip_one_fast = _subr__skip_one_fast
|
||||||
S_skip_array = _subr__skip_array
|
S_skip_array = _subr__skip_array
|
||||||
S_skip_object = _subr__skip_object
|
S_skip_object = _subr__skip_object
|
||||||
S_skip_number = _subr__skip_number
|
S_skip_number = _subr__skip_number
|
||||||
|
S_get_by_path = _subr__get_by_path
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,11 @@ func __vunsigned(s *string, p *int, v *types.JsonState)
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func __skip_one(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
func __skip_one(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_one_fast(s *string, p *int) (ret int)
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
|
|
@ -112,4 +117,9 @@ func __skip_number(s *string, p *int) (ret int)
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func __validate_one(s *string, p *int, m *types.StateMachine) (ret int)
|
func __validate_one(s *string, p *int, m *types.StateMachine) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __get_by_path(s *string, p *int, path *[]interface{}) (ret int)
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -591,3 +591,59 @@ func TestNative_SkipNumber(t *testing.T) {
|
||||||
assert.Equal(t, 9, p)
|
assert.Equal(t, 9, p)
|
||||||
assert.Equal(t, 0, q)
|
assert.Equal(t, 0, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNative_SkipOneFast(t *testing.T) {
|
||||||
|
p := 0
|
||||||
|
s := ` {"asdf": [null, true, false, 1, 2.0, -3]}, 1234.5`
|
||||||
|
q := __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 42, p)
|
||||||
|
assert.Equal(t, 1, q)
|
||||||
|
p = 0
|
||||||
|
s = `1, 2.5, -3, "asdf\nqwer", true, false, null, {}, [],`
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 1, p)
|
||||||
|
assert.Equal(t, 0, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 6, p)
|
||||||
|
assert.Equal(t, 3, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 10, p)
|
||||||
|
assert.Equal(t, 8, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 24, p)
|
||||||
|
assert.Equal(t, 12, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 30, p)
|
||||||
|
assert.Equal(t, 26, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 37, p)
|
||||||
|
assert.Equal(t, 32, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 43, p)
|
||||||
|
assert.Equal(t, 39, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 47, p)
|
||||||
|
assert.Equal(t, 45, q)
|
||||||
|
p += 1
|
||||||
|
q = __skip_one_fast(&s, &p)
|
||||||
|
assert.Equal(t, 51, p)
|
||||||
|
assert.Equal(t, 49, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNative_SkipOneFast_Error(t *testing.T) {
|
||||||
|
for _, s := range([]string{
|
||||||
|
"{{", "[{", "{{}",
|
||||||
|
`"asdf`, `"\\\"`,
|
||||||
|
}) {
|
||||||
|
p := 0
|
||||||
|
q := __skip_one_fast(&s, &p)
|
||||||
|
assert.True(t, q < 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -41,7 +41,9 @@ var (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
S_skip_one = _subr__skip_one
|
S_skip_one = _subr__skip_one
|
||||||
|
S_skip_one_fast = _subr__skip_one_fast
|
||||||
S_skip_array = _subr__skip_array
|
S_skip_array = _subr__skip_array
|
||||||
S_skip_object = _subr__skip_object
|
S_skip_object = _subr__skip_object
|
||||||
S_skip_number = _subr__skip_number
|
S_skip_number = _subr__skip_number
|
||||||
|
S_get_by_path = _subr__get_by_path
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,29 +9,32 @@ package sse
|
||||||
func __native_entry__() uintptr
|
func __native_entry__() uintptr
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_subr__f32toa = __native_entry__() + 24640
|
_subr__f32toa = __native_entry__() + 29152
|
||||||
_subr__f64toa = __native_entry__() + 464
|
_subr__f64toa = __native_entry__() + 464
|
||||||
_subr__html_escape = __native_entry__() + 10416
|
_subr__get_by_path = __native_entry__() + 27392
|
||||||
_subr__i64toa = __native_entry__() + 4048
|
_subr__html_escape = __native_entry__() + 10416
|
||||||
_subr__lspace = __native_entry__() + 80
|
_subr__i64toa = __native_entry__() + 4048
|
||||||
_subr__quote = __native_entry__() + 5456
|
_subr__lspace = __native_entry__() + 80
|
||||||
_subr__skip_array = __native_entry__() + 22928
|
_subr__quote = __native_entry__() + 5456
|
||||||
_subr__skip_number = __native_entry__() + 24432
|
_subr__skip_array = __native_entry__() + 20144
|
||||||
_subr__skip_object = __native_entry__() + 22976
|
_subr__skip_number = __native_entry__() + 23488
|
||||||
_subr__skip_one = __native_entry__() + 21056
|
_subr__skip_object = __native_entry__() + 22032
|
||||||
_subr__u64toa = __native_entry__() + 4176
|
_subr__skip_one = __native_entry__() + 23632
|
||||||
_subr__unquote = __native_entry__() + 7232
|
_subr__skip_one_fast = __native_entry__() + 23840
|
||||||
_subr__validate_one = __native_entry__() + 24576
|
_subr__u64toa = __native_entry__() + 4176
|
||||||
_subr__value = __native_entry__() + 13680
|
_subr__unquote = __native_entry__() + 7232
|
||||||
_subr__vnumber = __native_entry__() + 18800
|
_subr__validate_one = __native_entry__() + 23664
|
||||||
_subr__vsigned = __native_entry__() + 20352
|
_subr__value = __native_entry__() + 13680
|
||||||
_subr__vstring = __native_entry__() + 15760
|
_subr__vnumber = __native_entry__() + 17888
|
||||||
_subr__vunsigned = __native_entry__() + 20704
|
_subr__vsigned = __native_entry__() + 19440
|
||||||
|
_subr__vstring = __native_entry__() + 15760
|
||||||
|
_subr__vunsigned = __native_entry__() + 19792
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_stack__f32toa = 64
|
_stack__f32toa = 64
|
||||||
_stack__f64toa = 80
|
_stack__f64toa = 80
|
||||||
|
_stack__get_by_path = 232
|
||||||
_stack__html_escape = 64
|
_stack__html_escape = 64
|
||||||
_stack__i64toa = 16
|
_stack__i64toa = 16
|
||||||
_stack__lspace = 8
|
_stack__lspace = 8
|
||||||
|
|
@ -40,6 +43,7 @@ const (
|
||||||
_stack__skip_number = 72
|
_stack__skip_number = 72
|
||||||
_stack__skip_object = 128
|
_stack__skip_object = 128
|
||||||
_stack__skip_one = 128
|
_stack__skip_one = 128
|
||||||
|
_stack__skip_one_fast = 144
|
||||||
_stack__u64toa = 8
|
_stack__u64toa = 8
|
||||||
_stack__unquote = 72
|
_stack__unquote = 72
|
||||||
_stack__validate_one = 128
|
_stack__validate_one = 128
|
||||||
|
|
@ -53,6 +57,7 @@ const (
|
||||||
var (
|
var (
|
||||||
_ = _subr__f32toa
|
_ = _subr__f32toa
|
||||||
_ = _subr__f64toa
|
_ = _subr__f64toa
|
||||||
|
_ = _subr__get_by_path
|
||||||
_ = _subr__html_escape
|
_ = _subr__html_escape
|
||||||
_ = _subr__i64toa
|
_ = _subr__i64toa
|
||||||
_ = _subr__lspace
|
_ = _subr__lspace
|
||||||
|
|
@ -61,6 +66,7 @@ var (
|
||||||
_ = _subr__skip_number
|
_ = _subr__skip_number
|
||||||
_ = _subr__skip_object
|
_ = _subr__skip_object
|
||||||
_ = _subr__skip_one
|
_ = _subr__skip_one
|
||||||
|
_ = _subr__skip_one_fast
|
||||||
_ = _subr__u64toa
|
_ = _subr__u64toa
|
||||||
_ = _subr__unquote
|
_ = _subr__unquote
|
||||||
_ = _subr__validate_one
|
_ = _subr__validate_one
|
||||||
|
|
@ -74,6 +80,7 @@ var (
|
||||||
const (
|
const (
|
||||||
_ = _stack__f32toa
|
_ = _stack__f32toa
|
||||||
_ = _stack__f64toa
|
_ = _stack__f64toa
|
||||||
|
_ = _stack__get_by_path
|
||||||
_ = _stack__html_escape
|
_ = _stack__html_escape
|
||||||
_ = _stack__i64toa
|
_ = _stack__i64toa
|
||||||
_ = _stack__lspace
|
_ = _stack__lspace
|
||||||
|
|
@ -82,6 +89,7 @@ const (
|
||||||
_ = _stack__skip_number
|
_ = _stack__skip_number
|
||||||
_ = _stack__skip_object
|
_ = _stack__skip_object
|
||||||
_ = _stack__skip_one
|
_ = _stack__skip_one
|
||||||
|
_ = _stack__skip_one_fast
|
||||||
_ = _stack__u64toa
|
_ = _stack__u64toa
|
||||||
_ = _stack__unquote
|
_ = _stack__unquote
|
||||||
_ = _stack__validate_one
|
_ = _stack__validate_one
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,61 @@
|
||||||
#define is_infinity(v) ((as_uint64v(&v) << 1) == 0xFFE0000000000000)
|
#define is_infinity(v) ((as_uint64v(&v) << 1) == 0xFFE0000000000000)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char * buf;
|
void * buf;
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t cap;
|
size_t cap;
|
||||||
} GoSlice;
|
} GoSlice;
|
||||||
|
|
||||||
|
static const uint8_t GO_KIND_MASK = (1 << 5) - 1;
|
||||||
|
typedef enum {
|
||||||
|
Invalid = 0,
|
||||||
|
Bool,
|
||||||
|
Int,
|
||||||
|
Int8,
|
||||||
|
Int16,
|
||||||
|
Int32,
|
||||||
|
Int64,
|
||||||
|
Uint,
|
||||||
|
Uint8,
|
||||||
|
Uint16,
|
||||||
|
Uint32,
|
||||||
|
Uint64,
|
||||||
|
Uintptr,
|
||||||
|
Float32,
|
||||||
|
Float64,
|
||||||
|
Complex64,
|
||||||
|
Complex128,
|
||||||
|
Array,
|
||||||
|
Chan,
|
||||||
|
Func,
|
||||||
|
Interface,
|
||||||
|
Map,
|
||||||
|
Pointer,
|
||||||
|
Slice,
|
||||||
|
String,
|
||||||
|
Struct,
|
||||||
|
UnsafePointer,
|
||||||
|
} GoKind;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t size;
|
||||||
|
uint64_t ptr_data;
|
||||||
|
uint32_t hash;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t align;
|
||||||
|
uint8_t filed_align;
|
||||||
|
uint8_t kind_flags;
|
||||||
|
uint64_t traits;
|
||||||
|
void* gc_data;
|
||||||
|
int32_t str;
|
||||||
|
int32_t ptr_to_self;
|
||||||
|
} GoType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GoType * type;
|
||||||
|
void * value;
|
||||||
|
} GoIface;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char * buf;
|
const char * buf;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
@ -98,4 +148,6 @@ ssize_t utf8_validate(const char *sp, ssize_t nb);
|
||||||
long validate_string(const GoString *src, long *p);
|
long validate_string(const GoString *src, long *p);
|
||||||
long validate_one(const GoString *src, long *p, StateMachine *m);
|
long validate_one(const GoString *src, long *p, StateMachine *m);
|
||||||
|
|
||||||
|
long skip_one_fast(const GoString *src, long *p);
|
||||||
|
long get_by_path(const GoString *src, long *p, const GoSlice *path);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -980,4 +980,94 @@ ssize_t html_escape(const char *sp, ssize_t nb, char *dp, ssize_t *dn) {
|
||||||
return sp - ss;
|
return sp - ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef MAX_ESCAPED_BYTES
|
#undef MAX_ESCAPED_BYTES
|
||||||
|
|
||||||
|
static inline long unescape(const char** src, const char* end, char* dp) {
|
||||||
|
const char* sp = *src;
|
||||||
|
long nb = end - sp;
|
||||||
|
char cc = 0;
|
||||||
|
uint32_t r0, r1;
|
||||||
|
|
||||||
|
if (nb <= 0) return -ERR_EOF;
|
||||||
|
|
||||||
|
if ((cc = _UnquoteTab[(uint8_t)sp[1]]) == 0) {
|
||||||
|
*src += 1;
|
||||||
|
return -ERR_ESCAPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cc != -1) {
|
||||||
|
*dp = cc;
|
||||||
|
*src += 2;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nb < 4) {
|
||||||
|
*src += 1;
|
||||||
|
return -ERR_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for hexadecimal characters */
|
||||||
|
if (!unhex16_is(sp + 2)) {
|
||||||
|
*src += 2;
|
||||||
|
return -ERR_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* decode the code-point */
|
||||||
|
r0 = unhex16_fast(sp + 2);
|
||||||
|
sp += 6;
|
||||||
|
*src = sp;
|
||||||
|
|
||||||
|
/* ASCII characters, unlikely */
|
||||||
|
if (unlikely(r0 <= 0x7f)) {
|
||||||
|
*dp++ = (char)r0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-1 characters, unlikely */
|
||||||
|
if (unlikely(r0 <= 0x07ff)) {
|
||||||
|
*dp++ = (char)(0xc0 | (r0 >> 6));
|
||||||
|
*dp++ = (char)(0x80 | (r0 & 0x3f));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3-byte characters, likely */
|
||||||
|
if (likely(r0 < 0xd800 || r0 > 0xdfff)) {
|
||||||
|
*dp++ = (char)(0xe0 | ((r0 >> 12) ));
|
||||||
|
*dp++ = (char)(0x80 | ((r0 >> 6) & 0x3f));
|
||||||
|
*dp++ = (char)(0x80 | ((r0 ) & 0x3f));
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* surrogate half, must follows by the other half */
|
||||||
|
if (nb < 6 || r0 > 0xdbff || sp[0] != '\\' || sp[1] != 'u') {
|
||||||
|
return -ERR_UNICODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check the hexadecimal escape */
|
||||||
|
if (!unhex16_is(sp + 2)) {
|
||||||
|
*src += 2;
|
||||||
|
return -ERR_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* decode the second code-point */
|
||||||
|
r1 = unhex16_fast(sp + 2);
|
||||||
|
|
||||||
|
/* it must be the other half */
|
||||||
|
if (r1 < 0xdc00 || r1 > 0xdfff) {
|
||||||
|
*src += 2;
|
||||||
|
return -ERR_UNICODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* merge two surrogates */
|
||||||
|
r0 = (r0 - 0xd800) << 10;
|
||||||
|
r1 = (r1 - 0xdc00) + 0x010000;
|
||||||
|
r0 += r1;
|
||||||
|
|
||||||
|
/* encode the character */
|
||||||
|
*dp++ = (char)(0xf0 | ((r0 >> 18) ));
|
||||||
|
*dp++ = (char)(0x80 | ((r0 >> 12) & 0x3f));
|
||||||
|
*dp++ = (char)(0x80 | ((r0 >> 6) & 0x3f));
|
||||||
|
*dp++ = (char)(0x80 | ((r0 ) & 0x3f));
|
||||||
|
*src = sp + 6;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
@ -1423,11 +1423,6 @@ check_index:
|
||||||
#undef check_sidx
|
#undef check_sidx
|
||||||
#undef check_vidx
|
#undef check_vidx
|
||||||
|
|
||||||
long skip_one(const GoString *src, long *p, StateMachine *m, uint64_t flags) {
|
|
||||||
fsm_init(m, FSM_VAL);
|
|
||||||
return fsm_exec(m, src, p, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
long skip_array(const GoString *src, long *p, StateMachine *m, uint64_t flags) {
|
long skip_array(const GoString *src, long *p, StateMachine *m, uint64_t flags) {
|
||||||
fsm_init(m, FSM_ARR_0);
|
fsm_init(m, FSM_ARR_0);
|
||||||
return fsm_exec(m, src, p, flags);
|
return fsm_exec(m, src, p, flags);
|
||||||
|
|
@ -1512,7 +1507,452 @@ long skip_number(const GoString *src, long *p) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long skip_one(const GoString *src, long *p, StateMachine *m, uint64_t flags) {
|
||||||
|
fsm_init(m, FSM_VAL);
|
||||||
|
return fsm_exec(m, src, p, flags);
|
||||||
|
}
|
||||||
|
|
||||||
long validate_one(const GoString *src, long *p, StateMachine *m) {
|
long validate_one(const GoString *src, long *p, StateMachine *m) {
|
||||||
fsm_init(m, FSM_VAL);
|
fsm_init(m, FSM_VAL);
|
||||||
return fsm_exec(m, src, p, MASK_VALIDATE_STRING);
|
return fsm_exec(m, src, p, MASK_VALIDATE_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Faster skip api for sonic.ast */
|
||||||
|
|
||||||
|
static always_inline uint64_t get_maskx64(const char *s, char c) {
|
||||||
|
#if USE_AVX2
|
||||||
|
__m256i v0 = _mm256_loadu_si256((__m256i const *)s);
|
||||||
|
__m256i v1 = _mm256_loadu_si256((__m256i const *)(s + 32));
|
||||||
|
uint32_t m0 = _mm256_movemask_epi8(_mm256_cmpeq_epi8(v0, _mm256_set1_epi8(c)));
|
||||||
|
uint32_t m1 = _mm256_movemask_epi8(_mm256_cmpeq_epi8(v1, _mm256_set1_epi8(c)));
|
||||||
|
return ((uint64_t)(m1) << 32) | (uint64_t)(m0);
|
||||||
|
#else
|
||||||
|
__m128i v0 = _mm_loadu_si128((__m128i const*)s);
|
||||||
|
__m128i v1 = _mm_loadu_si128((__m128i const*)(s + 16));
|
||||||
|
__m128i v2 = _mm_loadu_si128((__m128i const*)(s + 32));
|
||||||
|
__m128i v3 = _mm_loadu_si128((__m128i const*)(s + 48));
|
||||||
|
uint32_t m0 = _mm_movemask_epi8(_mm_cmpeq_epi8(v0, _mm_set1_epi8(c)));
|
||||||
|
uint32_t m1 = _mm_movemask_epi8(_mm_cmpeq_epi8(v1, _mm_set1_epi8(c)));
|
||||||
|
uint32_t m2 = _mm_movemask_epi8(_mm_cmpeq_epi8(v2, _mm_set1_epi8(c)));
|
||||||
|
uint32_t m3 = _mm_movemask_epi8(_mm_cmpeq_epi8(v3, _mm_set1_epi8(c)));
|
||||||
|
return ((uint64_t)(m3) << 48) | ((uint64_t)(m2) << 32) | ((uint64_t)(m1) << 16) | (uint64_t)(m0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline uint64_t get_maskx32(const char *s, char c) {
|
||||||
|
#if USE_AVX2
|
||||||
|
__m256i v0 = _mm256_loadu_si256((__m256i const *)s);
|
||||||
|
uint64_t m0 = (unsigned)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v0, _mm256_set1_epi8(c)));
|
||||||
|
return m0;
|
||||||
|
#else
|
||||||
|
__m128i v0 = _mm_loadu_si128((__m128i const*)s);
|
||||||
|
__m128i v1 = _mm_loadu_si128((__m128i const*)(s + 16));
|
||||||
|
uint64_t m0 = (unsigned)_mm_movemask_epi8(_mm_cmpeq_epi8(v0, _mm_set1_epi8(c)));
|
||||||
|
uint64_t m1 = (unsigned)_mm_movemask_epi8(_mm_cmpeq_epi8(v1, _mm_set1_epi8(c)));
|
||||||
|
return m0 | (m1 << 16);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the string (besides in quote) mask
|
||||||
|
static always_inline uint64_t get_string_maskx64(const char *s, uint64_t *prev_inquote, uint64_t *prev_bs) {
|
||||||
|
uint64_t escaped = *prev_bs;
|
||||||
|
uint64_t quote_mask = 0, bs_mask = 0;
|
||||||
|
|
||||||
|
/* read and get the quote or backslash bitmask */
|
||||||
|
quote_mask = get_maskx64(s, '"');
|
||||||
|
bs_mask = get_maskx64(s, '\\');
|
||||||
|
|
||||||
|
/* get the escaped bitmask */
|
||||||
|
if (bs_mask || *prev_bs) {
|
||||||
|
bs_mask &= ~(*prev_bs);
|
||||||
|
uint64_t follow_bs = (bs_mask << 1) | *prev_bs;
|
||||||
|
uint64_t bs_start = bs_mask & ~follow_bs;
|
||||||
|
uint64_t odd_start = bs_start & ODD_MASK;
|
||||||
|
uint64_t even_or_oc = add64(odd_start, bs_mask, prev_bs);
|
||||||
|
uint64_t even_or_escaped = (even_or_oc << 1) ^ EVEN_MASK;
|
||||||
|
escaped = follow_bs & even_or_escaped;
|
||||||
|
} else {
|
||||||
|
*prev_bs = 0;
|
||||||
|
}
|
||||||
|
quote_mask &= ~escaped;
|
||||||
|
|
||||||
|
/* get the inquote bitmask */
|
||||||
|
uint64_t inquote = _mm_cvtsi128_si64(_mm_clmulepi64_si128(_mm_set_epi64x(0, quote_mask), _mm_set1_epi8('\xFF'), 0));
|
||||||
|
inquote ^= *prev_inquote;
|
||||||
|
*prev_inquote = (uint64_t)(((int64_t)(inquote)) >> 63);
|
||||||
|
return inquote;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the next json structural, '}', ']' or ','。
|
||||||
|
#if USE_AVX2
|
||||||
|
static always_inline int get_structural_maskx32(const char *s) {
|
||||||
|
__m256i v = _mm256_loadu_si256((const void *)s);
|
||||||
|
__m256i e1 = _mm256_cmpeq_epi8(v, _mm256_set1_epi8('}'));
|
||||||
|
__m256i e2 = _mm256_cmpeq_epi8(v, _mm256_set1_epi8(']'));
|
||||||
|
__m256i e3 = _mm256_cmpeq_epi8(v, _mm256_set1_epi8(','));
|
||||||
|
__m256i sv = _mm256_or_si256(_mm256_or_si256(e1, e2), e3);
|
||||||
|
return _mm256_movemask_epi8(sv);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static always_inline int get_structural_maskx16(const char *s) {
|
||||||
|
__m128i v = _mm_loadu_si128((const void *)s);
|
||||||
|
__m128i e1 = _mm_cmpeq_epi8(v, _mm_set1_epi8('}'));
|
||||||
|
__m128i e2 = _mm_cmpeq_epi8(v, _mm_set1_epi8(']'));
|
||||||
|
__m128i e3 = _mm_cmpeq_epi8(v, _mm_set1_epi8(','));
|
||||||
|
__m128i sv = _mm_or_si128(_mm_or_si128(e1, e2), e3);
|
||||||
|
return _mm_movemask_epi8(sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip the number at the next '}', ']' or ',' or the ending of json.
|
||||||
|
static always_inline long skip_number_fast(const GoString *src, long *p) {
|
||||||
|
size_t nb = src->len - *p;
|
||||||
|
const char *s = src->buf + *p;
|
||||||
|
long vi = *p - 1;
|
||||||
|
int m = 0;
|
||||||
|
|
||||||
|
#if USE_AVX2
|
||||||
|
while (likely(nb >= 32)) {
|
||||||
|
if ((m = get_structural_maskx32(s))) {
|
||||||
|
*p = s - src->buf + __builtin_ctzll(m);
|
||||||
|
return vi;
|
||||||
|
}
|
||||||
|
s += 32, nb -= 32;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (likely(nb >= 16)) {
|
||||||
|
if ((m = get_structural_maskx16(s))) {
|
||||||
|
*p = s - src->buf + __builtin_ctzll(m);
|
||||||
|
return vi;
|
||||||
|
}
|
||||||
|
s += 16, nb -= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (likely(nb > 0)) {
|
||||||
|
if (*s == '}' || *s == ']' || *s == ',') {
|
||||||
|
*p = s - src->buf;
|
||||||
|
return vi;
|
||||||
|
}
|
||||||
|
s++, nb--;
|
||||||
|
}
|
||||||
|
*p = s - src->buf;
|
||||||
|
return vi;
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline void memcpy_p64(char * restrict dp, const char * restrict sp, size_t n) {
|
||||||
|
long nb = n;
|
||||||
|
#if USE_AVX2
|
||||||
|
if (nb >= 32) { _mm256_storeu_si256((void *)dp, _mm256_loadu_si256((const void *)sp)); sp += 32, dp += 32, nb -= 32; }
|
||||||
|
#endif
|
||||||
|
while (nb >= 16) { _mm_storeu_si128((void *)dp, _mm_loadu_si128((const void *)sp)); sp += 16, dp += 16, nb -= 16; }
|
||||||
|
if (nb >= 8) { *(uint64_t *)dp = *(const uint64_t *)sp; sp += 8, dp += 8, nb -= 8; }
|
||||||
|
if (nb >= 4) { *(uint32_t *)dp = *(const uint32_t *)sp; sp += 4, dp += 4, nb -= 4; }
|
||||||
|
if (nb >= 2) { *(uint16_t *)dp = *(const uint16_t *)sp; sp += 2, dp += 2, nb -= 2; }
|
||||||
|
if (nb >= 1) { *dp = *sp; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline bool vec_cross_page(const void * p, size_t n) {
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
return (((size_t)(p)) & (PAGE_SIZE - 1)) > (PAGE_SIZE - n);
|
||||||
|
#undef PAGE_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline long skip_container_fast(const GoString *src, long *p, char lc, char rc) {
|
||||||
|
long nb = src->len - *p;
|
||||||
|
const char *s = src->buf + *p;
|
||||||
|
long vi = *p - 1;
|
||||||
|
|
||||||
|
uint64_t prev_inquote = 0, prev_bs = 0;
|
||||||
|
uint64_t lbrace = 0, rbrace = 0;
|
||||||
|
size_t lnum = 0, rnum = 0, last_lnum = 0;
|
||||||
|
uint64_t inquote = 0;
|
||||||
|
|
||||||
|
while (likely(nb >= 64)) {
|
||||||
|
skip:
|
||||||
|
inquote = get_string_maskx64(s, &prev_inquote, &prev_bs);
|
||||||
|
lbrace = get_maskx64(s, lc) & ~inquote;
|
||||||
|
rbrace = get_maskx64(s, rc) & ~inquote;
|
||||||
|
|
||||||
|
/* traverse each right brace */
|
||||||
|
last_lnum = lnum;
|
||||||
|
while (rbrace > 0) {
|
||||||
|
uint64_t lbrace_first = (rbrace - 1) & lbrace;
|
||||||
|
lnum = last_lnum + __builtin_popcountll((int64_t)lbrace_first);
|
||||||
|
bool is_closed = lnum <= rnum;
|
||||||
|
if (is_closed) {
|
||||||
|
*p = src->len - nb + __builtin_ctzll(rbrace) + 1;
|
||||||
|
// *p is out-of-bound access here
|
||||||
|
if (*p > src->len) {
|
||||||
|
*p = src->len;
|
||||||
|
return -ERR_EOF;
|
||||||
|
}
|
||||||
|
return vi;
|
||||||
|
}
|
||||||
|
rbrace &= (rbrace - 1); // clear the lowest right brace
|
||||||
|
rnum ++;
|
||||||
|
}
|
||||||
|
lnum = last_lnum + __builtin_popcountll((int64_t)lbrace);
|
||||||
|
s += 64, nb -= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nb <= 0) {
|
||||||
|
*p = src->len;
|
||||||
|
return -ERR_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
char tbuf[64] = {0};
|
||||||
|
bool cross_page = vec_cross_page(s, 64);
|
||||||
|
if (cross_page) {
|
||||||
|
memcpy_p64(tbuf, s, nb);
|
||||||
|
s = tbuf;
|
||||||
|
}
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline long skip_object_fast(const GoString *src, long *p) {
|
||||||
|
return skip_container_fast(src, p, '{', '}');
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline long skip_array_fast(const GoString *src, long *p) {
|
||||||
|
return skip_container_fast(src, p, '[', ']');
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline long skip_string_fast(const GoString *src, long *p) {
|
||||||
|
const char* s = src->buf + *p;
|
||||||
|
size_t nb = src->len - *p;
|
||||||
|
long vi = *p - 1;
|
||||||
|
uint64_t prev_bs = 0, escaped;
|
||||||
|
|
||||||
|
while (likely(nb >= 32)) {
|
||||||
|
uint32_t quote = get_maskx32(s, '"');
|
||||||
|
uint32_t bs_mask = get_maskx32(s, '\\');
|
||||||
|
if (bs_mask || prev_bs) {
|
||||||
|
bs_mask &= ~prev_bs;
|
||||||
|
uint64_t follow_bs = (bs_mask << 1) | prev_bs;
|
||||||
|
uint64_t bs_start = bs_mask & ~follow_bs;
|
||||||
|
uint64_t odd_start = bs_start & ODD_MASK;
|
||||||
|
uint64_t even_or_oc = add32(odd_start, bs_mask, &prev_bs);
|
||||||
|
uint64_t even_or_escaped = (even_or_oc << 1) ^ EVEN_MASK;
|
||||||
|
escaped = follow_bs & even_or_escaped;
|
||||||
|
quote &= ~escaped;
|
||||||
|
}
|
||||||
|
if (quote) {
|
||||||
|
*p = s + __builtin_ctzll(quote) + 1 - src->buf;
|
||||||
|
return vi;
|
||||||
|
}
|
||||||
|
s += 32;
|
||||||
|
nb -= 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(prev_bs != 0)) {
|
||||||
|
if (nb == 0) return -ERR_EOF;
|
||||||
|
s++, nb--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (likely(nb > 0)) {
|
||||||
|
if (*s == '\\') {
|
||||||
|
s += 2, nb -= 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*s == '"') {
|
||||||
|
*p = s - src->buf + 1;
|
||||||
|
return vi;
|
||||||
|
}
|
||||||
|
s++, nb--;
|
||||||
|
}
|
||||||
|
return -ERR_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
long skip_one_fast(const GoString *src, long *p) {
|
||||||
|
char c = advance_ns(src, p);
|
||||||
|
/* set the start address */
|
||||||
|
long vi = *p - 1;
|
||||||
|
switch (c) {
|
||||||
|
case '[': return skip_array_fast(src, p);
|
||||||
|
case '{': return skip_object_fast(src, p);
|
||||||
|
case '"': return skip_string_fast(src, p);
|
||||||
|
case '-': case '0' ... '9': return skip_number_fast(src, p);
|
||||||
|
case 't': case 'n': { if (*p + 3 < src->len) { *p += 3; } else { return -ERR_EOF; } }; break;
|
||||||
|
case 'f': { if (*p + 4 < src->len) { *p += 4; } else { return -ERR_EOF; } }; break;
|
||||||
|
case 0 : return -ERR_EOF;
|
||||||
|
default : *p -= 1; return -ERR_INVAL; // backward error position
|
||||||
|
}
|
||||||
|
return vi;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static always_inline GoKind kind(const GoIface* iface) {
|
||||||
|
return (iface->type->kind_flags) & GO_KIND_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline bool is_int(const GoIface* iface) {
|
||||||
|
return kind(iface) == Int;
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline bool is_str(const GoIface* iface) {
|
||||||
|
return kind(iface) == String;
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline GoString get_str(const GoIface* iface) {
|
||||||
|
return *(GoString*)(iface->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static always_inline int64_t get_int(const GoIface* iface) {
|
||||||
|
return *(int64_t*)(iface->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// xmemcmpeq return true if s1 and s2 is equal for the n bytes, otherwise, return false.
|
||||||
|
static always_inline bool xmemcmpeq(const char * s1, const char * s2, size_t n) {
|
||||||
|
bool c1, c2;
|
||||||
|
#if USE_AVX2
|
||||||
|
while (n >= 32) {
|
||||||
|
__m256i v1 = _mm256_loadu_si256((const void *)s1);
|
||||||
|
__m256i v2 = _mm256_loadu_si256((const void *)s2);
|
||||||
|
uint32_t mask = ~((uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v1, v2)));
|
||||||
|
if (mask) return false;
|
||||||
|
s1 += 32;
|
||||||
|
s2 += 32;
|
||||||
|
n -= 32;
|
||||||
|
};
|
||||||
|
c1 = vec_cross_page(s1, 32);
|
||||||
|
c2 = vec_cross_page(s2, 32);
|
||||||
|
// not cross page
|
||||||
|
if (!c1 && !c2) {
|
||||||
|
__m256i v1 = _mm256_loadu_si256((const void *)s1);
|
||||||
|
__m256i v2 = _mm256_loadu_si256((const void *)s2);
|
||||||
|
uint32_t mask = ~((uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v1, v2)));
|
||||||
|
bool eq = (mask == 0) || (__builtin_ctzll(mask) >= n);
|
||||||
|
return eq;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while (n >= 16) {
|
||||||
|
__m128i v1 = _mm_loadu_si128((const void *)s1);
|
||||||
|
__m128i v2 = _mm_loadu_si128((const void *)s2);
|
||||||
|
uint16_t mask = ~((uint16_t)_mm_movemask_epi8(_mm_cmpeq_epi8(v1, v2)));
|
||||||
|
if (mask != 0) return false;
|
||||||
|
s1 += 16;
|
||||||
|
s2 += 16;
|
||||||
|
n -= 16;
|
||||||
|
};
|
||||||
|
c1 = vec_cross_page(s1, 16);
|
||||||
|
c2 = vec_cross_page(s2, 16);
|
||||||
|
// not cross page
|
||||||
|
if (!c1 && !c2) {
|
||||||
|
__m128i v1 = _mm_loadu_si128((const void *)s1);
|
||||||
|
__m128i v2 = _mm_loadu_si128((const void *)s2);
|
||||||
|
uint16_t mask = ~((uint16_t)_mm_movemask_epi8(_mm_cmpeq_epi8(v1, v2)));
|
||||||
|
bool eq = (mask == 0) || (__builtin_ctzll(mask) >= n);
|
||||||
|
return eq;
|
||||||
|
}
|
||||||
|
// cross page
|
||||||
|
while (n > 0 && *s1++ == *s2++) n--;
|
||||||
|
return n == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// match_key return negative if errors, zero if not matched, one if matched.
|
||||||
|
static always_inline long match_key(const GoString *src, long *p, const GoString key) {
|
||||||
|
static const long not_match = 0;
|
||||||
|
int64_t v = -1;
|
||||||
|
long si = *p;
|
||||||
|
long se = advance_string_default(src, *p, &v);
|
||||||
|
if (unlikely(se < 0)) {
|
||||||
|
*p = src->len;
|
||||||
|
return -ERR_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update position */
|
||||||
|
*p = se;
|
||||||
|
|
||||||
|
/* compare non-escaped strings */
|
||||||
|
if (likely(v == -1 || v > se)) {
|
||||||
|
long sn = se - si - 1;
|
||||||
|
return sn == key.len && xmemcmpeq(src->buf + si, key.buf, key.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deal with escaped strings */
|
||||||
|
char buf[8] = {0}; // escaped buffer
|
||||||
|
const char* sp = src->buf + si;
|
||||||
|
const char* end = src->buf + se - 1;
|
||||||
|
const char* kp = key.buf;
|
||||||
|
const char* ke = key.buf + key.len;
|
||||||
|
while (sp < end && kp < ke) {
|
||||||
|
if (*sp == '\\') {
|
||||||
|
long en = unescape(&sp, end, buf);
|
||||||
|
if (en < 0) {
|
||||||
|
*p = sp - src->buf;
|
||||||
|
return en;
|
||||||
|
}
|
||||||
|
const char* ee = buf + en;
|
||||||
|
const char* ep = buf;
|
||||||
|
while (kp < ke && ep < ee && *kp == *ep) kp++, ep++;
|
||||||
|
if (ep != ee) {
|
||||||
|
return not_match;
|
||||||
|
}
|
||||||
|
} else if (*sp == *kp) {
|
||||||
|
sp++, kp++;
|
||||||
|
} else {
|
||||||
|
return not_match;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return sp == end && kp == ke;
|
||||||
|
}
|
||||||
|
|
||||||
|
long get_by_path(const GoString *src, long *p, const GoSlice *path) {
|
||||||
|
GoIface *ps = (GoIface*)(path->buf);
|
||||||
|
GoIface *pe = (GoIface*)(path->buf) + path->len;
|
||||||
|
char c = 0;
|
||||||
|
int64_t index;
|
||||||
|
long found;
|
||||||
|
|
||||||
|
query:
|
||||||
|
/* if empty path, skip the whole json */
|
||||||
|
if (ps == pe) {
|
||||||
|
return skip_one_fast(src, p);
|
||||||
|
}
|
||||||
|
/* match type: should query key in object, query index in array */
|
||||||
|
c = advance_ns(src, p);
|
||||||
|
if (is_str(ps)) {
|
||||||
|
if (c != '{') goto err_inval;
|
||||||
|
goto skip_in_obj;
|
||||||
|
} else if (is_int(ps)) {
|
||||||
|
if (c != '[') goto err_inval;
|
||||||
|
goto skip_in_arr;
|
||||||
|
} else {
|
||||||
|
goto err_inval;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_in_obj:
|
||||||
|
c = advance_ns(src, p);
|
||||||
|
if (c != '"') goto err_inval;
|
||||||
|
found = match_key(src, p, get_str(ps));
|
||||||
|
if (found < 0) return found; // parse string errors
|
||||||
|
|
||||||
|
/* value should after : */
|
||||||
|
c = advance_ns(src, p);
|
||||||
|
if (c != ':') goto err_inval;
|
||||||
|
if (found) {
|
||||||
|
ps++;
|
||||||
|
goto query;
|
||||||
|
} else {
|
||||||
|
skip_one_fast(src, p);
|
||||||
|
c = advance_ns(src, p);
|
||||||
|
if (c != ',') goto err_inval; // not found key
|
||||||
|
goto skip_in_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_in_arr:
|
||||||
|
index = get_int(ps);
|
||||||
|
/* skip array elem one by one */
|
||||||
|
while (index-- > 0) {
|
||||||
|
skip_one_fast(src, p);
|
||||||
|
c = advance_ns(src, p);
|
||||||
|
if (c != ',') goto err_inval; // out of range
|
||||||
|
}
|
||||||
|
ps++;
|
||||||
|
goto query;
|
||||||
|
|
||||||
|
err_inval:
|
||||||
|
*p -= 1; // backward error position
|
||||||
|
return -ERR_INVAL;
|
||||||
}
|
}
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
static void __attribute__((naked)) write_syscall(const char *s, size_t n)
|
static inline void __attribute__((naked)) write_syscall(const char *s, size_t n)
|
||||||
{
|
{
|
||||||
asm volatile(
|
asm volatile(
|
||||||
"movq %rsi, %rdx"
|
"movq %rsi, %rdx"
|
||||||
|
|
@ -36,12 +36,12 @@ static void __attribute__((naked)) write_syscall(const char *s, size_t n)
|
||||||
"\n");
|
"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printch(const char ch)
|
static inline void printch(const char ch)
|
||||||
{
|
{
|
||||||
write_syscall(&ch, 1);
|
write_syscall(&ch, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printstr(const char *s)
|
static inline void printstr(const char *s)
|
||||||
{
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
const char *p = s;
|
const char *p = s;
|
||||||
|
|
@ -50,7 +50,7 @@ static void printstr(const char *s)
|
||||||
write_syscall(s, n);
|
write_syscall(s, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printint(int64_t v)
|
static inline void printint(int64_t v)
|
||||||
{
|
{
|
||||||
char neg = 0;
|
char neg = 0;
|
||||||
char buf[32] = {};
|
char buf[32] = {};
|
||||||
|
|
@ -62,18 +62,23 @@ static void printint(int64_t v)
|
||||||
} else {
|
} else {
|
||||||
u = v;
|
u = v;
|
||||||
}
|
}
|
||||||
|
if (u == 0) {
|
||||||
|
*--p = '0';
|
||||||
|
goto sig;
|
||||||
|
}
|
||||||
while (u)
|
while (u)
|
||||||
{
|
{
|
||||||
*--p = (u % 10) + '0';
|
*--p = (u % 10) + '0';
|
||||||
u /= 10;
|
u /= 10;
|
||||||
}
|
}
|
||||||
|
sig:
|
||||||
if (neg) {
|
if (neg) {
|
||||||
*--p = '-';
|
*--p = '-';
|
||||||
}
|
}
|
||||||
printstr(p);
|
printstr(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printuint(uint64_t v)
|
static inline void printuint(uint64_t v)
|
||||||
{
|
{
|
||||||
char buf[32] = {};
|
char buf[32] = {};
|
||||||
char *p = &buf[31];
|
char *p = &buf[31];
|
||||||
|
|
@ -92,7 +97,7 @@ static void printuint(uint64_t v)
|
||||||
|
|
||||||
static const char tab[] = "0123456789abcdef";
|
static const char tab[] = "0123456789abcdef";
|
||||||
|
|
||||||
static void printhex(uintptr_t v)
|
static inline void printhex(uintptr_t v)
|
||||||
{
|
{
|
||||||
if (v == 0)
|
if (v == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -112,7 +117,7 @@ static void printhex(uintptr_t v)
|
||||||
|
|
||||||
#define MAX_BUF_LEN 100
|
#define MAX_BUF_LEN 100
|
||||||
|
|
||||||
static void printbytes(GoSlice *s)
|
static inline void printbytes(GoSlice *s)
|
||||||
{
|
{
|
||||||
printch('[');
|
printch('[');
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
@ -122,15 +127,16 @@ static void printbytes(GoSlice *s)
|
||||||
}
|
}
|
||||||
for (; i < s->len; i++)
|
for (; i < s->len; i++)
|
||||||
{
|
{
|
||||||
printch(tab[((s->buf[i]) & 0xf0) >> 4]);
|
char* bytes = (char*)(s->buf);
|
||||||
printch(tab[(s->buf[i]) & 0x0f]);
|
printch(tab[(bytes[i] & 0xf0) >> 4]);
|
||||||
|
printch(tab[bytes[i] & 0x0f]);
|
||||||
if (i != s->len - 1)
|
if (i != s->len - 1)
|
||||||
printch(',');
|
printch(',');
|
||||||
}
|
}
|
||||||
printch(']');
|
printch(']');
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printgostr(GoString *s)
|
static inline void printgostr(GoString *s)
|
||||||
{
|
{
|
||||||
printch('"');
|
printch('"');
|
||||||
if (s->len < MAX_BUF_LEN)
|
if (s->len < MAX_BUF_LEN)
|
||||||
|
|
@ -139,12 +145,12 @@ static void printgostr(GoString *s)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
write_syscall(&s->buf[s->len - MAX_BUF_LEN], MAX_BUF_LEN);
|
write_syscall(s->buf, MAX_BUF_LEN);
|
||||||
}
|
}
|
||||||
printch('"');
|
printch('"');
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xprintf(const char *fmt, ...)
|
static inline void xprintf(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
__builtin_va_list va;
|
__builtin_va_list va;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ import (
|
||||||
`testing`
|
`testing`
|
||||||
`time`
|
`time`
|
||||||
|
|
||||||
|
`github.com/davecgh/go-spew/spew`
|
||||||
|
`github.com/stretchr/testify/assert`
|
||||||
`github.com/bytedance/sonic/ast`
|
`github.com/bytedance/sonic/ast`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -80,6 +82,61 @@ func TestExampleSearch(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExampleSearchEscapedKey(t *testing.T) {
|
||||||
|
data := []byte(`
|
||||||
|
{
|
||||||
|
"xx" : [] ,
|
||||||
|
"yy" :{ },
|
||||||
|
"test\"" : [
|
||||||
|
true ,
|
||||||
|
0.1 ,
|
||||||
|
"abc",
|
||||||
|
[
|
||||||
|
"h"
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"a\u0008": {},
|
||||||
|
"b\\\\": null,
|
||||||
|
"\u2028\u2028": "\u2028\u2029",
|
||||||
|
"\u0026":2,
|
||||||
|
"0":1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
",,,,,,,,,,(,,15": ",",
|
||||||
|
",,,,,,,,,,(,,,16": "a,]}",
|
||||||
|
",,,,,,,,,,(,,,,17": 1,
|
||||||
|
",,,,,,,,,,(,,,,,,,,,,,,,,,,(,,,,34": "c"
|
||||||
|
} `)
|
||||||
|
|
||||||
|
type getTest struct{
|
||||||
|
path []interface{}
|
||||||
|
expect interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []getTest {
|
||||||
|
{[]interface{}{"test\"", 0}, true},
|
||||||
|
{[]interface{}{"test\"", 1}, 0.1},
|
||||||
|
{[]interface{}{"test\"", 2}, "abc"},
|
||||||
|
{[]interface{}{"test\"", 3}, []interface{}{"h"}},
|
||||||
|
{[]interface{}{"test\"", 4, "a\u0008"}, map[string]interface{}{}},
|
||||||
|
{[]interface{}{"test\"", 4, "b\\\\"}, nil},
|
||||||
|
{[]interface{}{"test\"", 4, "\u2028\u2028"}, "\u2028\u2029"},
|
||||||
|
{[]interface{}{"test\"", 4, "0"}, float64(1)},
|
||||||
|
{[]interface{}{",,,,,,,,,,(,,15"}, ","},
|
||||||
|
{[]interface{}{",,,,,,,,,,(,,,16"}, "a,]}"},
|
||||||
|
{[]interface{}{",,,,,,,,,,(,,,,17"}, float64(1)},
|
||||||
|
{[]interface{}{",,,,,,,,,,(,,,,,,,,,,,,,,,,(,,,,34"}, "c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range(tests) {
|
||||||
|
node, err := Get(data, test.path...)
|
||||||
|
assert.NoErrorf(t, err, "get return errors")
|
||||||
|
got, err := node.Interface()
|
||||||
|
assert.NoErrorf(t, err, "get convert errors")
|
||||||
|
assert.Equalf(t, test.expect, got, "get result is wrong from path %#v", test.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestExampleSearchErr(t *testing.T) {
|
func TestExampleSearchErr(t *testing.T) {
|
||||||
data := []byte(` { "xx" : [] ,"yy" :{ }, "test" : [ true , 0.1 , "abc", ["h"], {"a":"bc"} ] } `)
|
data := []byte(` { "xx" : [] ,"yy" :{ }, "test" : [ true , 0.1 , "abc", ["h"], {"a":"bc"} ] } `)
|
||||||
node, e := Get(data, "zz")
|
node, e := Get(data, "zz")
|
||||||
|
|
@ -107,6 +164,38 @@ func TestExampleSearchErr(t *testing.T) {
|
||||||
fmt.Println(e)
|
fmt.Println(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExampleSearchEscapedKeyError(t *testing.T) {
|
||||||
|
data := []byte(` { "xx" : [] ,"yy" :{ }, "x\u0008" : [] ,"y\\\"y" :{ }, "test" : [ true , 0.1 , "abc", ["h"], {"a":"bc"} ] } `)
|
||||||
|
node, e := Get(data, "zz")
|
||||||
|
if e == nil {
|
||||||
|
t.Fatalf("node: %v, err: %v", node, e)
|
||||||
|
}
|
||||||
|
fmt.Println(e)
|
||||||
|
|
||||||
|
node, e = Get(data, "x\u0008", 4)
|
||||||
|
if e == nil {
|
||||||
|
t.Fatalf("node: %v, err: %v", node, e)
|
||||||
|
}
|
||||||
|
fmt.Println(e)
|
||||||
|
|
||||||
|
node, e = Get(data, "yy", "a")
|
||||||
|
if e == nil {
|
||||||
|
t.Fatalf("node: %v, err: %v", node, e)
|
||||||
|
}
|
||||||
|
fmt.Println(e)
|
||||||
|
|
||||||
|
node, e = Get(data, "test", 4, "x")
|
||||||
|
if e == nil {
|
||||||
|
t.Fatalf("node: %v, err: %v", node, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
node, e = Get(data, "y\\\"y", 4, "x")
|
||||||
|
if e == nil {
|
||||||
|
t.Fatalf("node: %v, err: %v", node, e)
|
||||||
|
}
|
||||||
|
fmt.Println(e)
|
||||||
|
}
|
||||||
|
|
||||||
func TestRandomData(t *testing.T) {
|
func TestRandomData(t *testing.T) {
|
||||||
var lstr string
|
var lstr string
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -153,15 +242,21 @@ func TestRandomValidStrings(t *testing.T) {
|
||||||
}
|
}
|
||||||
token, err := GetFromString(`{"str":`+string(sm)+`}`, "str")
|
token, err := GetFromString(`{"str":`+string(sm)+`}`, "str")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
spew.Dump(string(sm))
|
||||||
t.Fatal("search data failed:",err)
|
t.Fatal("search data failed:",err)
|
||||||
}
|
}
|
||||||
x, _ := token.Interface()
|
x, _ := token.Interface()
|
||||||
if x.(string) != su {
|
st, ok := x.(string)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("type mismatch, exp: %v, got: %v", su, x)
|
||||||
|
}
|
||||||
|
if st != su {
|
||||||
t.Fatalf("string mismatch, exp: %v, got: %v", su, x)
|
t.Fatalf("string mismatch, exp: %v, got: %v", su, x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func TestEmoji(t *testing.T) {
|
func TestEmoji(t *testing.T) {
|
||||||
var input = []byte(`{"utf8":"Example emoji, KO: \ud83d\udd13, \ud83c\udfc3 ` +
|
var input = []byte(`{"utf8":"Example emoji, KO: \ud83d\udd13, \ud83c\udfc3 ` +
|
||||||
`OK: \u2764\ufe0f "}`)
|
`OK: \u2764\ufe0f "}`)
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 62deb479a1080dd92ee9ca8dbd9c53796828aa3e
|
Subproject commit f15d04bc1b29db464e832531c64ef195d54e4ff1
|
||||||
Loading…
Reference in a new issue