2
0
Fork 0
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:
liu 2023-01-09 16:20:36 +08:00 committed by GitHub
parent 67cffb15bd
commit 2dc405d750
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 9116 additions and 3366 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

1
.gitignore vendored
View file

@ -49,3 +49,4 @@ ast/bench.sh
!testdata/*.json.gz
fuzz/testdata
*__debug_bin

View file

@ -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

View file

@ -89,3 +89,38 @@ 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
}

View file

@ -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 {
@ -60,3 +65,39 @@ 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
}

View file

@ -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])

View file

@ -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
}

View file

@ -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) {

View file

@ -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"`

View file

@ -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)
}
}
}

View file

@ -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

View file

@ -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=

View file

@ -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
View 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
}

View file

@ -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
}

View file

@ -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
@ -113,3 +118,8 @@ func __skip_number(s *string, p *int) (ret int)
//go:noescape
//goland:noinspection GoUnusedParameter
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

View file

@ -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)
}
}

View file

@ -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
)

View file

@ -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

View file

@ -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
@ -113,3 +118,8 @@ func __skip_number(s *string, p *int) (ret int)
//go:noescape
//goland:noinspection GoUnusedParameter
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

View file

@ -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)
}
}

View file

@ -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
)

View file

@ -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

View file

@ -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() {

View file

@ -64,6 +64,23 @@ TEXT ·SkipOne(SB), NOSPLIT, $0 - 40
JMP github·combytedancesonicinternalnativeavx·__skip_one(SB)
JMP github·combytedancesonicinternalnativesse·__skip_one(SB)
TEXT ·SkipOneFast(SB), NOSPLIT, $0 - 24
CMPB github·combytedancesonicinternalcpu·HasAVX2(SB), $0
JE 2(PC)
JMP github·combytedancesonicinternalnativeavx2·__skip_one_fast(SB)
CMPB github·combytedancesonicinternalcpu·HasAVX(SB), $0
JE 2(PC)
JMP github·combytedancesonicinternalnativeavx·__skip_one_fast(SB)
JMP github·combytedancesonicinternalnativesse·__skip_one_fast(SB)
TEXT ·GetByPath(SB), NOSPLIT, $0 - 32
CMPB github·combytedancesonicinternalcpu·HasAVX2(SB), $0
JE 2(PC)
JMP github·combytedancesonicinternalnativeavx2·__get_by_path(SB)
CMPB github·combytedancesonicinternalcpu·HasAVX(SB), $0
JE 2(PC)
JMP github·combytedancesonicinternalnativeavx·__get_by_path(SB)
JMP github·combytedancesonicinternalnativesse·__get_by_path(SB)
TEXT ·ValidateOne(SB), NOSPLIT, $0 - 32
CMPB github·combytedancesonicinternalcpu·HasAVX2(SB), $0
JE 2(PC)

View file

@ -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
@ -111,3 +116,8 @@ func __skip_number(s *string, p *int) (ret int)
//go:noescape
//goland:noinspection GoUnusedParameter
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)

View file

@ -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)
}
}

View file

@ -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
)

View file

@ -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
@ -113,3 +118,8 @@ func __skip_number(s *string, p *int) (ret int)
//go:noescape
//goland:noinspection GoUnusedParameter
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

View file

@ -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)
}
}

View file

@ -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
)

View file

@ -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

View file

@ -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

View file

@ -981,3 +981,93 @@ ssize_t html_escape(const char *sp, ssize_t nb, char *dp, ssize_t *dn) {
}
#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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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