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:
|
||||
runs-on: [arm]
|
||||
steps:
|
||||
- name: Clear repository
|
||||
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- 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:
|
||||
runs-on: [self-hosted, X64]
|
||||
steps:
|
||||
- name: Clear repository
|
||||
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- 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
|
||||
|
||||
steps:
|
||||
- name: Clear repository
|
||||
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
|
|
|
|||
3
.github/workflows/license-check.yml
vendored
3
.github/workflows/license-check.yml
vendored
|
|
@ -6,6 +6,9 @@ jobs:
|
|||
build:
|
||||
runs-on: [self-hosted, X64]
|
||||
steps:
|
||||
- name: Clear repository
|
||||
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- 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:
|
||||
runs-on: [self-hosted, X64]
|
||||
steps:
|
||||
- name: Clear repository
|
||||
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- 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]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Clear repository
|
||||
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- 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]
|
||||
runs-on: [self-hosted, X64]
|
||||
steps:
|
||||
- name: Clear repository
|
||||
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- 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]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Clear repository
|
||||
run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Go
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -48,4 +48,5 @@ ast/test.out
|
|||
ast/bench.sh
|
||||
|
||||
!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_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_avx2 := -msse -mno-sse4 -mavx -mavx2 -DUSE_AVX=1 -DUSE_AVX2=1
|
||||
CFLAGS_sse := -msse -mno-sse4 -mno-avx -mno-avx2
|
||||
CFLAGS_avx := -msse -mno-sse4 -mavx -mpclmul -mno-avx2 -DUSE_AVX=1 -DUSE_AVX2=0
|
||||
CFLAGS_avx2 := -msse -mno-sse4 -mavx -mpclmul -mavx2 -DUSE_AVX=1 -DUSE_AVX2=1
|
||||
CFLAGS_sse := -msse -mno-sse4 -mno-avx -mno-avx2 -mpclmul
|
||||
|
||||
CC_amd64 := clang
|
||||
ASM2ASM_amd64 := tools/asm2asm/asm2asm.py
|
||||
|
|
|
|||
|
|
@ -88,4 +88,39 @@ func (self *Parser) skip() (int, types.ParsingError) {
|
|||
func (self *Node) encodeInterface(buf *[]byte) error {
|
||||
//WARN: NOT compatible with json.Encoder
|
||||
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 (
|
||||
`encoding/base64`
|
||||
`encoding/json`
|
||||
`fmt`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
|
|
@ -52,6 +53,10 @@ func (self *Parser) skip() (int, types.ParsingError) {
|
|||
return s, 0
|
||||
}
|
||||
|
||||
func (self *Parser) skipFast() (int, types.ParsingError) {
|
||||
return self.skip()
|
||||
}
|
||||
|
||||
func (self *Node) encodeInterface(buf *[]byte) error {
|
||||
out, err := json.Marshal(self.packAny())
|
||||
if err != nil {
|
||||
|
|
@ -59,4 +64,40 @@ func (self *Node) encodeInterface(buf *[]byte) error {
|
|||
}
|
||||
*buf = append(*buf, out...)
|
||||
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 (
|
||||
`fmt`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
|
@ -131,7 +130,7 @@ func (self *Parser) decodeArray(ret []Node) (Node, types.ParsingError) {
|
|||
if self.skipValue {
|
||||
/* skip the value */
|
||||
var start int
|
||||
if start, err = self.skip(); err != 0 {
|
||||
if start, err = self.skipFast(); err != 0 {
|
||||
return Node{}, err
|
||||
}
|
||||
if self.p > ns {
|
||||
|
|
@ -217,7 +216,7 @@ func (self *Parser) decodeObject(ret []Pair) (Node, types.ParsingError) {
|
|||
if self.skipValue {
|
||||
/* skip the value */
|
||||
var start int
|
||||
if start, err = self.skip(); err != 0 {
|
||||
if start, err = self.skipFast(); err != 0 {
|
||||
return Node{}, err
|
||||
}
|
||||
if self.p > ns {
|
||||
|
|
@ -228,7 +227,7 @@ func (self *Parser) decodeObject(ret []Pair) (Node, types.ParsingError) {
|
|||
return Node{}, types.ERR_INVALID_CHAR
|
||||
}
|
||||
val = newRawNode(self.s[start:self.p], t)
|
||||
}else{
|
||||
} else {
|
||||
/* decode the value */
|
||||
if val, err = self.Parse(); err != 0 {
|
||||
return Node{}, err
|
||||
|
|
@ -448,7 +447,7 @@ func (self *Node) skipNextNode() *Node {
|
|||
|
||||
var val Node
|
||||
/* skip the value */
|
||||
if start, err := parser.skip(); err != 0 {
|
||||
if start, err := parser.skipFast(); err != 0 {
|
||||
return newSyntaxError(parser.syntaxError(err))
|
||||
} else {
|
||||
t := switchRawType(parser.s[start])
|
||||
|
|
@ -531,7 +530,7 @@ func (self *Node) skipNextPair() (*Pair) {
|
|||
}
|
||||
|
||||
/* skip the value */
|
||||
if start, err := parser.skip(); err != 0 {
|
||||
if start, err := parser.skipFast(); err != 0 {
|
||||
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
|
||||
} else {
|
||||
t := switchRawType(parser.s[start])
|
||||
|
|
|
|||
|
|
@ -16,12 +16,6 @@
|
|||
|
||||
package ast
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
)
|
||||
|
||||
type Searcher struct {
|
||||
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) {
|
||||
b.SetBytes(int64(len(_TwitterJson)))
|
||||
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 {
|
||||
Statuses []struct {
|
||||
Coordinates interface{} `json:"coordinates"`
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import (
|
|||
`math`
|
||||
`testing`
|
||||
|
||||
`github.com/bytedance/sonic/ast`
|
||||
`github.com/buger/jsonparser`
|
||||
jsoniter `github.com/json-iterator/go`
|
||||
`github.com/tidwall/gjson`
|
||||
`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) {
|
||||
b.SetBytes(int64(len(TwitterJson)))
|
||||
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
|
||||
|
||||
require (
|
||||
github.com/buger/jsonparser v1.1.1
|
||||
github.com/bytedance/sonic v1.4.0
|
||||
github.com/goccy/go-json v0.9.11
|
||||
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/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 h1:1sDoSuDPWzhkdzNVxCxtIaKiAe96ESVPv8coGwc1gZ4=
|
||||
|
|
|
|||
|
|
@ -20,57 +20,35 @@ package sonic_fuzz
|
|||
|
||||
import (
|
||||
`testing`
|
||||
`fmt`
|
||||
`github.com/bytedance/sonic`
|
||||
`github.com/bytedance/sonic/ast`
|
||||
`github.com/stretchr/testify/require`
|
||||
`github.com/davecgh/go-spew/spew`
|
||||
)
|
||||
|
||||
func fuzzASTGetFromObject(t *testing.T, data []byte, m map[string]interface{}) {
|
||||
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)
|
||||
require.NoErrorf(t, err, "error in ast get key -> %s", k)
|
||||
assertAstNode(t, node, expv)
|
||||
require.NoErrorf(t, err, "error in ast get key\n%s", msg)
|
||||
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{}) {
|
||||
var i = 0
|
||||
i := 0
|
||||
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)
|
||||
require.NoErrorf(t, err, "error in ast get index -> %s", i)
|
||||
assertAstNode(t, node, a[i])
|
||||
require.NoErrorf(t, err, "error in ast get index\n%s", msg)
|
||||
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)
|
||||
require.Errorf(t, err, "error in ast get index -> %s", i)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
require.Errorf(t, err, "no error in ast get out of range\nData:\n%s\n", spew.Sdump(data))
|
||||
}
|
||||
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) {
|
||||
f.Add([]byte(`{
|
||||
"object": {
|
||||
"slice": [
|
||||
1,
|
||||
2.0,
|
||||
"3",
|
||||
[4],
|
||||
{5: {}}
|
||||
]
|
||||
},
|
||||
"slice": [[]],
|
||||
"string": ":)",
|
||||
"int": 1e5,
|
||||
"float": 3e-9"
|
||||
}`))
|
||||
for _, corp := range(corpus()) {
|
||||
f.Add(corp)
|
||||
}
|
||||
f.Fuzz(fuzzMain)
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +55,6 @@ func fuzzMain(t *testing.T, data []byte) {
|
|||
func() interface{} { return new(uint64) },
|
||||
func() interface{} { return new(float64) },
|
||||
func() interface{} { return new(json.Number) },
|
||||
func() interface{} { return new(json.RawMessage) },
|
||||
func() interface{} { return new(S) },
|
||||
} {
|
||||
sv, jv := typ(), typ()
|
||||
|
|
@ -75,7 +62,7 @@ func fuzzMain(t *testing.T, data []byte) {
|
|||
jerr := json.Unmarshal([]byte(data), jv)
|
||||
require.Equalf(t, serr != nil, jerr != nil, "different error in sonic unmarshal %v", reflect.TypeOf(jv))
|
||||
if jerr != nil {
|
||||
return
|
||||
continue
|
||||
}
|
||||
require.Equal(t, sv, jv, "different result in sonic unmarshal %v", reflect.TypeOf(jv))
|
||||
sout, serr := sonic.Marshal(sv)
|
||||
|
|
@ -89,7 +76,7 @@ func fuzzMain(t *testing.T, data []byte) {
|
|||
jerr := json.Unmarshal(jout, jv)
|
||||
require.Equalf(t, serr != nil, jerr != nil, "different error in sonic unmarshal again %v", reflect.TypeOf(jv))
|
||||
if jerr != nil {
|
||||
return
|
||||
continue
|
||||
}
|
||||
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
|
||||
V uintptr
|
||||
W json.Number
|
||||
X json.RawMessage
|
||||
// X json.RawMessage
|
||||
Y Marshaller
|
||||
Z TextMarshaller
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,11 @@ func __vunsigned(s *string, p *int, v *types.JsonState)
|
|||
//goland:noinspection GoUnusedParameter
|
||||
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:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
|
|
@ -112,4 +117,9 @@ func __skip_number(s *string, p *int) (ret int)
|
|||
//go:nosplit
|
||||
//go:noescape
|
||||
//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, 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 (
|
||||
S_skip_one = _subr__skip_one
|
||||
S_skip_one_fast = _subr__skip_one_fast
|
||||
S_skip_array = _subr__skip_array
|
||||
S_skip_object = _subr__skip_object
|
||||
S_skip_number = _subr__skip_number
|
||||
S_get_by_path = _subr__get_by_path
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,29 +9,32 @@ package avx
|
|||
func __native_entry__() uintptr
|
||||
|
||||
var (
|
||||
_subr__f32toa = __native_entry__() + 24592
|
||||
_subr__f64toa = __native_entry__() + 496
|
||||
_subr__html_escape = __native_entry__() + 10480
|
||||
_subr__i64toa = __native_entry__() + 4176
|
||||
_subr__lspace = __native_entry__() + 80
|
||||
_subr__quote = __native_entry__() + 5552
|
||||
_subr__skip_array = __native_entry__() + 22864
|
||||
_subr__skip_number = __native_entry__() + 24336
|
||||
_subr__skip_object = __native_entry__() + 22912
|
||||
_subr__skip_one = __native_entry__() + 20992
|
||||
_subr__u64toa = __native_entry__() + 4288
|
||||
_subr__unquote = __native_entry__() + 7296
|
||||
_subr__validate_one = __native_entry__() + 24480
|
||||
_subr__value = __native_entry__() + 13728
|
||||
_subr__vnumber = __native_entry__() + 18736
|
||||
_subr__vsigned = __native_entry__() + 20288
|
||||
_subr__vstring = __native_entry__() + 15808
|
||||
_subr__vunsigned = __native_entry__() + 20640
|
||||
_subr__f32toa = __native_entry__() + 28656
|
||||
_subr__f64toa = __native_entry__() + 496
|
||||
_subr__get_by_path = __native_entry__() + 26848
|
||||
_subr__html_escape = __native_entry__() + 10480
|
||||
_subr__i64toa = __native_entry__() + 4176
|
||||
_subr__lspace = __native_entry__() + 80
|
||||
_subr__quote = __native_entry__() + 5552
|
||||
_subr__skip_array = __native_entry__() + 20160
|
||||
_subr__skip_number = __native_entry__() + 23472
|
||||
_subr__skip_object = __native_entry__() + 22048
|
||||
_subr__skip_one = __native_entry__() + 23616
|
||||
_subr__skip_one_fast = __native_entry__() + 23824
|
||||
_subr__u64toa = __native_entry__() + 4288
|
||||
_subr__unquote = __native_entry__() + 7296
|
||||
_subr__validate_one = __native_entry__() + 23648
|
||||
_subr__value = __native_entry__() + 13728
|
||||
_subr__vnumber = __native_entry__() + 17904
|
||||
_subr__vsigned = __native_entry__() + 19456
|
||||
_subr__vstring = __native_entry__() + 15808
|
||||
_subr__vunsigned = __native_entry__() + 19808
|
||||
)
|
||||
|
||||
const (
|
||||
_stack__f32toa = 64
|
||||
_stack__f64toa = 80
|
||||
_stack__get_by_path = 296
|
||||
_stack__html_escape = 64
|
||||
_stack__i64toa = 16
|
||||
_stack__lspace = 8
|
||||
|
|
@ -40,6 +43,7 @@ const (
|
|||
_stack__skip_number = 72
|
||||
_stack__skip_object = 128
|
||||
_stack__skip_one = 128
|
||||
_stack__skip_one_fast = 208
|
||||
_stack__u64toa = 8
|
||||
_stack__unquote = 72
|
||||
_stack__validate_one = 128
|
||||
|
|
@ -53,6 +57,7 @@ const (
|
|||
var (
|
||||
_ = _subr__f32toa
|
||||
_ = _subr__f64toa
|
||||
_ = _subr__get_by_path
|
||||
_ = _subr__html_escape
|
||||
_ = _subr__i64toa
|
||||
_ = _subr__lspace
|
||||
|
|
@ -61,6 +66,7 @@ var (
|
|||
_ = _subr__skip_number
|
||||
_ = _subr__skip_object
|
||||
_ = _subr__skip_one
|
||||
_ = _subr__skip_one_fast
|
||||
_ = _subr__u64toa
|
||||
_ = _subr__unquote
|
||||
_ = _subr__validate_one
|
||||
|
|
@ -74,6 +80,7 @@ var (
|
|||
const (
|
||||
_ = _stack__f32toa
|
||||
_ = _stack__f64toa
|
||||
_ = _stack__get_by_path
|
||||
_ = _stack__html_escape
|
||||
_ = _stack__i64toa
|
||||
_ = _stack__lspace
|
||||
|
|
@ -82,6 +89,7 @@ const (
|
|||
_ = _stack__skip_number
|
||||
_ = _stack__skip_object
|
||||
_ = _stack__skip_one
|
||||
_ = _stack__skip_one_fast
|
||||
_ = _stack__u64toa
|
||||
_ = _stack__unquote
|
||||
_ = _stack__validate_one
|
||||
|
|
|
|||
|
|
@ -94,6 +94,11 @@ func __vunsigned(s *string, p *int, v *types.JsonState)
|
|||
//goland:noinspection GoUnusedParameter
|
||||
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:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
|
|
@ -112,4 +117,9 @@ func __skip_number(s *string, p *int) (ret int)
|
|||
//go:nosplit
|
||||
//go:noescape
|
||||
//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, 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 (
|
||||
S_skip_one = _subr__skip_one
|
||||
S_skip_one_fast = _subr__skip_one_fast
|
||||
S_skip_array = _subr__skip_array
|
||||
S_skip_object = _subr__skip_object
|
||||
S_skip_number = _subr__skip_number
|
||||
S_get_by_path = _subr__get_by_path
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,29 +9,32 @@ package avx2
|
|||
func __native_entry__() uintptr
|
||||
|
||||
var (
|
||||
_subr__f32toa = __native_entry__() + 28464
|
||||
_subr__f64toa = __native_entry__() + 752
|
||||
_subr__html_escape = __native_entry__() + 12320
|
||||
_subr__i64toa = __native_entry__() + 4432
|
||||
_subr__lspace = __native_entry__() + 224
|
||||
_subr__quote = __native_entry__() + 5904
|
||||
_subr__skip_array = __native_entry__() + 26112
|
||||
_subr__skip_number = __native_entry__() + 28208
|
||||
_subr__skip_object = __native_entry__() + 26160
|
||||
_subr__skip_one = __native_entry__() + 24208
|
||||
_subr__u64toa = __native_entry__() + 4544
|
||||
_subr__unquote = __native_entry__() + 8848
|
||||
_subr__validate_one = __native_entry__() + 28352
|
||||
_subr__value = __native_entry__() + 16896
|
||||
_subr__vnumber = __native_entry__() + 21952
|
||||
_subr__vsigned = __native_entry__() + 23504
|
||||
_subr__vstring = __native_entry__() + 19280
|
||||
_subr__vunsigned = __native_entry__() + 23856
|
||||
_subr__f32toa = __native_entry__() + 32816
|
||||
_subr__f64toa = __native_entry__() + 752
|
||||
_subr__get_by_path = __native_entry__() + 30896
|
||||
_subr__html_escape = __native_entry__() + 12320
|
||||
_subr__i64toa = __native_entry__() + 4432
|
||||
_subr__lspace = __native_entry__() + 224
|
||||
_subr__quote = __native_entry__() + 5904
|
||||
_subr__skip_array = __native_entry__() + 23472
|
||||
_subr__skip_number = __native_entry__() + 27440
|
||||
_subr__skip_object = __native_entry__() + 25392
|
||||
_subr__skip_one = __native_entry__() + 27584
|
||||
_subr__skip_one_fast = __native_entry__() + 27984
|
||||
_subr__u64toa = __native_entry__() + 4544
|
||||
_subr__unquote = __native_entry__() + 8848
|
||||
_subr__validate_one = __native_entry__() + 27616
|
||||
_subr__value = __native_entry__() + 16896
|
||||
_subr__vnumber = __native_entry__() + 21216
|
||||
_subr__vsigned = __native_entry__() + 22768
|
||||
_subr__vstring = __native_entry__() + 19280
|
||||
_subr__vunsigned = __native_entry__() + 23120
|
||||
)
|
||||
|
||||
const (
|
||||
_stack__f32toa = 64
|
||||
_stack__f64toa = 80
|
||||
_stack__get_by_path = 304
|
||||
_stack__html_escape = 72
|
||||
_stack__i64toa = 16
|
||||
_stack__lspace = 8
|
||||
|
|
@ -40,19 +43,21 @@ const (
|
|||
_stack__skip_number = 80
|
||||
_stack__skip_object = 136
|
||||
_stack__skip_one = 136
|
||||
_stack__skip_one_fast = 216
|
||||
_stack__u64toa = 8
|
||||
_stack__unquote = 72
|
||||
_stack__validate_one = 136
|
||||
_stack__value = 336
|
||||
_stack__vnumber = 248
|
||||
_stack__vsigned = 16
|
||||
_stack__vstring = 128
|
||||
_stack__vstring = 136
|
||||
_stack__vunsigned = 24
|
||||
)
|
||||
|
||||
var (
|
||||
_ = _subr__f32toa
|
||||
_ = _subr__f64toa
|
||||
_ = _subr__get_by_path
|
||||
_ = _subr__html_escape
|
||||
_ = _subr__i64toa
|
||||
_ = _subr__lspace
|
||||
|
|
@ -61,6 +66,7 @@ var (
|
|||
_ = _subr__skip_number
|
||||
_ = _subr__skip_object
|
||||
_ = _subr__skip_one
|
||||
_ = _subr__skip_one_fast
|
||||
_ = _subr__u64toa
|
||||
_ = _subr__unquote
|
||||
_ = _subr__validate_one
|
||||
|
|
@ -74,6 +80,7 @@ var (
|
|||
const (
|
||||
_ = _stack__f32toa
|
||||
_ = _stack__f64toa
|
||||
_ = _stack__get_by_path
|
||||
_ = _stack__html_escape
|
||||
_ = _stack__i64toa
|
||||
_ = _stack__lspace
|
||||
|
|
@ -82,6 +89,7 @@ const (
|
|||
_ = _stack__skip_number
|
||||
_ = _stack__skip_object
|
||||
_ = _stack__skip_one
|
||||
_ = _stack__skip_one_fast
|
||||
_ = _stack__u64toa
|
||||
_ = _stack__unquote
|
||||
_ = _stack__validate_one
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ var (
|
|||
|
||||
var (
|
||||
S_skip_one uintptr
|
||||
S_skip_one_fast uintptr
|
||||
S_get_by_path uintptr
|
||||
S_skip_array uintptr
|
||||
S_skip_object 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
|
||||
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:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
|
|
@ -115,9 +127,11 @@ func useAVX() {
|
|||
S_vsigned = avx.S_vsigned
|
||||
S_vunsigned = avx.S_vunsigned
|
||||
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_object = avx.S_skip_object
|
||||
S_skip_number = avx.S_skip_number
|
||||
S_get_by_path = avx.S_get_by_path
|
||||
}
|
||||
|
||||
func useAVX2() {
|
||||
|
|
@ -134,9 +148,11 @@ func useAVX2() {
|
|||
S_vsigned = avx2.S_vsigned
|
||||
S_vunsigned = avx2.S_vunsigned
|
||||
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_object = avx2.S_skip_object
|
||||
S_skip_number = avx2.S_skip_number
|
||||
S_get_by_path = avx2.S_get_by_path
|
||||
}
|
||||
|
||||
func useSSE() {
|
||||
|
|
@ -153,9 +169,11 @@ func useSSE() {
|
|||
S_vsigned = sse.S_vsigned
|
||||
S_vunsigned = sse.S_vunsigned
|
||||
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_object = sse.S_skip_object
|
||||
S_skip_number = sse.S_skip_number
|
||||
S_get_by_path = sse.S_get_by_path
|
||||
}
|
||||
|
||||
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∕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
|
||||
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||
JE 2(PC)
|
||||
|
|
|
|||
|
|
@ -92,6 +92,11 @@ func __vunsigned(s *string, p *int, v *types.JsonState)
|
|||
//goland:noinspection GoUnusedParameter
|
||||
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:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
|
|
@ -110,4 +115,9 @@ func __skip_number(s *string, p *int) (ret int)
|
|||
//go:nosplit
|
||||
//go:noescape
|
||||
//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, 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 (
|
||||
S_skip_one = _subr__skip_one
|
||||
S_skip_one_fast = _subr__skip_one_fast
|
||||
S_skip_array = _subr__skip_array
|
||||
S_skip_object = _subr__skip_object
|
||||
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
|
||||
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:noescape
|
||||
//goland:noinspection GoUnusedParameter
|
||||
|
|
@ -112,4 +117,9 @@ func __skip_number(s *string, p *int) (ret int)
|
|||
//go:nosplit
|
||||
//go:noescape
|
||||
//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, 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 (
|
||||
S_skip_one = _subr__skip_one
|
||||
S_skip_one_fast = _subr__skip_one_fast
|
||||
S_skip_array = _subr__skip_array
|
||||
S_skip_object = _subr__skip_object
|
||||
S_skip_number = _subr__skip_number
|
||||
S_get_by_path = _subr__get_by_path
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,29 +9,32 @@ package sse
|
|||
func __native_entry__() uintptr
|
||||
|
||||
var (
|
||||
_subr__f32toa = __native_entry__() + 24640
|
||||
_subr__f64toa = __native_entry__() + 464
|
||||
_subr__html_escape = __native_entry__() + 10416
|
||||
_subr__i64toa = __native_entry__() + 4048
|
||||
_subr__lspace = __native_entry__() + 80
|
||||
_subr__quote = __native_entry__() + 5456
|
||||
_subr__skip_array = __native_entry__() + 22928
|
||||
_subr__skip_number = __native_entry__() + 24432
|
||||
_subr__skip_object = __native_entry__() + 22976
|
||||
_subr__skip_one = __native_entry__() + 21056
|
||||
_subr__u64toa = __native_entry__() + 4176
|
||||
_subr__unquote = __native_entry__() + 7232
|
||||
_subr__validate_one = __native_entry__() + 24576
|
||||
_subr__value = __native_entry__() + 13680
|
||||
_subr__vnumber = __native_entry__() + 18800
|
||||
_subr__vsigned = __native_entry__() + 20352
|
||||
_subr__vstring = __native_entry__() + 15760
|
||||
_subr__vunsigned = __native_entry__() + 20704
|
||||
_subr__f32toa = __native_entry__() + 29152
|
||||
_subr__f64toa = __native_entry__() + 464
|
||||
_subr__get_by_path = __native_entry__() + 27392
|
||||
_subr__html_escape = __native_entry__() + 10416
|
||||
_subr__i64toa = __native_entry__() + 4048
|
||||
_subr__lspace = __native_entry__() + 80
|
||||
_subr__quote = __native_entry__() + 5456
|
||||
_subr__skip_array = __native_entry__() + 20144
|
||||
_subr__skip_number = __native_entry__() + 23488
|
||||
_subr__skip_object = __native_entry__() + 22032
|
||||
_subr__skip_one = __native_entry__() + 23632
|
||||
_subr__skip_one_fast = __native_entry__() + 23840
|
||||
_subr__u64toa = __native_entry__() + 4176
|
||||
_subr__unquote = __native_entry__() + 7232
|
||||
_subr__validate_one = __native_entry__() + 23664
|
||||
_subr__value = __native_entry__() + 13680
|
||||
_subr__vnumber = __native_entry__() + 17888
|
||||
_subr__vsigned = __native_entry__() + 19440
|
||||
_subr__vstring = __native_entry__() + 15760
|
||||
_subr__vunsigned = __native_entry__() + 19792
|
||||
)
|
||||
|
||||
const (
|
||||
_stack__f32toa = 64
|
||||
_stack__f64toa = 80
|
||||
_stack__get_by_path = 232
|
||||
_stack__html_escape = 64
|
||||
_stack__i64toa = 16
|
||||
_stack__lspace = 8
|
||||
|
|
@ -40,6 +43,7 @@ const (
|
|||
_stack__skip_number = 72
|
||||
_stack__skip_object = 128
|
||||
_stack__skip_one = 128
|
||||
_stack__skip_one_fast = 144
|
||||
_stack__u64toa = 8
|
||||
_stack__unquote = 72
|
||||
_stack__validate_one = 128
|
||||
|
|
@ -53,6 +57,7 @@ const (
|
|||
var (
|
||||
_ = _subr__f32toa
|
||||
_ = _subr__f64toa
|
||||
_ = _subr__get_by_path
|
||||
_ = _subr__html_escape
|
||||
_ = _subr__i64toa
|
||||
_ = _subr__lspace
|
||||
|
|
@ -61,6 +66,7 @@ var (
|
|||
_ = _subr__skip_number
|
||||
_ = _subr__skip_object
|
||||
_ = _subr__skip_one
|
||||
_ = _subr__skip_one_fast
|
||||
_ = _subr__u64toa
|
||||
_ = _subr__unquote
|
||||
_ = _subr__validate_one
|
||||
|
|
@ -74,6 +80,7 @@ var (
|
|||
const (
|
||||
_ = _stack__f32toa
|
||||
_ = _stack__f64toa
|
||||
_ = _stack__get_by_path
|
||||
_ = _stack__html_escape
|
||||
_ = _stack__i64toa
|
||||
_ = _stack__lspace
|
||||
|
|
@ -82,6 +89,7 @@ const (
|
|||
_ = _stack__skip_number
|
||||
_ = _stack__skip_object
|
||||
_ = _stack__skip_one
|
||||
_ = _stack__skip_one_fast
|
||||
_ = _stack__u64toa
|
||||
_ = _stack__unquote
|
||||
_ = _stack__validate_one
|
||||
|
|
|
|||
|
|
@ -36,11 +36,61 @@
|
|||
#define is_infinity(v) ((as_uint64v(&v) << 1) == 0xFFE0000000000000)
|
||||
|
||||
typedef struct {
|
||||
char * buf;
|
||||
void * buf;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
} 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 {
|
||||
const char * buf;
|
||||
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_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
|
||||
|
|
|
|||
|
|
@ -980,4 +980,94 @@ ssize_t html_escape(const char *sp, ssize_t nb, char *dp, ssize_t *dn) {
|
|||
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_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) {
|
||||
fsm_init(m, FSM_ARR_0);
|
||||
return fsm_exec(m, src, p, flags);
|
||||
|
|
@ -1512,7 +1507,452 @@ long skip_number(const GoString *src, long *p) {
|
|||
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) {
|
||||
fsm_init(m, FSM_VAL);
|
||||
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>
|
||||
|
||||
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(
|
||||
"movq %rsi, %rdx"
|
||||
|
|
@ -36,12 +36,12 @@ static void __attribute__((naked)) write_syscall(const char *s, size_t n)
|
|||
"\n");
|
||||
}
|
||||
|
||||
static void printch(const char ch)
|
||||
static inline void printch(const char ch)
|
||||
{
|
||||
write_syscall(&ch, 1);
|
||||
}
|
||||
|
||||
static void printstr(const char *s)
|
||||
static inline void printstr(const char *s)
|
||||
{
|
||||
size_t n = 0;
|
||||
const char *p = s;
|
||||
|
|
@ -50,7 +50,7 @@ static void printstr(const char *s)
|
|||
write_syscall(s, n);
|
||||
}
|
||||
|
||||
static void printint(int64_t v)
|
||||
static inline void printint(int64_t v)
|
||||
{
|
||||
char neg = 0;
|
||||
char buf[32] = {};
|
||||
|
|
@ -62,18 +62,23 @@ static void printint(int64_t v)
|
|||
} else {
|
||||
u = v;
|
||||
}
|
||||
if (u == 0) {
|
||||
*--p = '0';
|
||||
goto sig;
|
||||
}
|
||||
while (u)
|
||||
{
|
||||
*--p = (u % 10) + '0';
|
||||
u /= 10;
|
||||
}
|
||||
sig:
|
||||
if (neg) {
|
||||
*--p = '-';
|
||||
}
|
||||
printstr(p);
|
||||
}
|
||||
|
||||
static void printuint(uint64_t v)
|
||||
static inline void printuint(uint64_t v)
|
||||
{
|
||||
char buf[32] = {};
|
||||
char *p = &buf[31];
|
||||
|
|
@ -92,7 +97,7 @@ static void printuint(uint64_t v)
|
|||
|
||||
static const char tab[] = "0123456789abcdef";
|
||||
|
||||
static void printhex(uintptr_t v)
|
||||
static inline void printhex(uintptr_t v)
|
||||
{
|
||||
if (v == 0)
|
||||
{
|
||||
|
|
@ -112,7 +117,7 @@ static void printhex(uintptr_t v)
|
|||
|
||||
#define MAX_BUF_LEN 100
|
||||
|
||||
static void printbytes(GoSlice *s)
|
||||
static inline void printbytes(GoSlice *s)
|
||||
{
|
||||
printch('[');
|
||||
int i = 0;
|
||||
|
|
@ -122,15 +127,16 @@ static void printbytes(GoSlice *s)
|
|||
}
|
||||
for (; i < s->len; i++)
|
||||
{
|
||||
printch(tab[((s->buf[i]) & 0xf0) >> 4]);
|
||||
printch(tab[(s->buf[i]) & 0x0f]);
|
||||
char* bytes = (char*)(s->buf);
|
||||
printch(tab[(bytes[i] & 0xf0) >> 4]);
|
||||
printch(tab[bytes[i] & 0x0f]);
|
||||
if (i != s->len - 1)
|
||||
printch(',');
|
||||
}
|
||||
printch(']');
|
||||
}
|
||||
|
||||
static void printgostr(GoString *s)
|
||||
static inline void printgostr(GoString *s)
|
||||
{
|
||||
printch('"');
|
||||
if (s->len < MAX_BUF_LEN)
|
||||
|
|
@ -139,12 +145,12 @@ static void printgostr(GoString *s)
|
|||
}
|
||||
else
|
||||
{
|
||||
write_syscall(&s->buf[s->len - MAX_BUF_LEN], MAX_BUF_LEN);
|
||||
write_syscall(s->buf, MAX_BUF_LEN);
|
||||
}
|
||||
printch('"');
|
||||
}
|
||||
|
||||
static void xprintf(const char *fmt, ...)
|
||||
static inline void xprintf(const char *fmt, ...)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
__builtin_va_list va;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ import (
|
|||
`testing`
|
||||
`time`
|
||||
|
||||
`github.com/davecgh/go-spew/spew`
|
||||
`github.com/stretchr/testify/assert`
|
||||
`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) {
|
||||
data := []byte(` { "xx" : [] ,"yy" :{ }, "test" : [ true , 0.1 , "abc", ["h"], {"a":"bc"} ] } `)
|
||||
node, e := Get(data, "zz")
|
||||
|
|
@ -107,6 +164,38 @@ func TestExampleSearchErr(t *testing.T) {
|
|||
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) {
|
||||
var lstr string
|
||||
defer func() {
|
||||
|
|
@ -153,15 +242,21 @@ func TestRandomValidStrings(t *testing.T) {
|
|||
}
|
||||
token, err := GetFromString(`{"str":`+string(sm)+`}`, "str")
|
||||
if err != nil {
|
||||
spew.Dump(string(sm))
|
||||
t.Fatal("search data failed:",err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestEmoji(t *testing.T) {
|
||||
var input = []byte(`{"utf8":"Example emoji, KO: \ud83d\udd13, \ud83c\udfc3 ` +
|
||||
`OK: \u2764\ufe0f "}`)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 62deb479a1080dd92ee9ca8dbd9c53796828aa3e
|
||||
Subproject commit f15d04bc1b29db464e832531c64ef195d54e4ff1
|
||||
Loading…
Reference in a new issue