mirror of
https://github.com/ii64/sonic.git
synced 2026-06-21 00:46:43 +08:00
feat: optimaze ast.Node API as container of generic values (#150)
* opt: only skip one value when call Iterator.HasNext() * feat: V_NONE supports Set() and Add() * feat: add node type V_ANY * feat: ast.Node implements `json.Unmarshaler` * fmt: use space instead of tab for indent * feat: node V_ANY cast to Bool()\Int64()\Float64()\String()\Number()\Map()\Array()Interface() * feat: V_NULL supports Set() and Add() Co-authored-by: duanyi.aster <duanyi.aster@bytedance.com>
This commit is contained in:
parent
7d3b22100f
commit
8405d84e31
9 changed files with 942 additions and 543 deletions
210
ast/encode.go
210
ast/encode.go
|
|
@ -17,38 +17,45 @@
|
|||
package ast
|
||||
|
||||
import (
|
||||
`sync`
|
||||
`sync`
|
||||
|
||||
`github.com/bytedance/sonic/encoder`
|
||||
)
|
||||
|
||||
const (
|
||||
_MaxBuffer = 4 * 1024 // 4KB buffer size
|
||||
_MaxBuffer = 4 * 1024 // 4KB buffer size
|
||||
)
|
||||
|
||||
const (
|
||||
bytesNull = "null"
|
||||
bytesTrue = "true"
|
||||
bytesFalse = "false"
|
||||
bytesObject = "{}"
|
||||
bytesArray = "[]"
|
||||
bytesNull = "null"
|
||||
bytesTrue = "true"
|
||||
bytesFalse = "false"
|
||||
bytesObject = "{}"
|
||||
bytesArray = "[]"
|
||||
)
|
||||
|
||||
var bytesPool = sync.Pool{}
|
||||
|
||||
func (self *Node) MarshalJSON() ([]byte, error) {
|
||||
buf := newBuffer()
|
||||
err := self.encode(buf)
|
||||
ret := make([]byte, len(*buf))
|
||||
copy(ret, *buf)
|
||||
freeBuffer(buf)
|
||||
return ret, err
|
||||
buf := newBuffer()
|
||||
err := self.encode(buf)
|
||||
if err != nil {
|
||||
freeBuffer(buf)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]byte, len(*buf))
|
||||
copy(ret, *buf)
|
||||
freeBuffer(buf)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func newBuffer() *[]byte {
|
||||
if ret := bytesPool.Get(); ret != nil {
|
||||
return ret.(*[]byte)
|
||||
} else {
|
||||
buf := make([]byte, 0, _MaxBuffer)
|
||||
return &buf
|
||||
buf := make([]byte, 0, _MaxBuffer)
|
||||
return &buf
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,130 +65,135 @@ func freeBuffer(buf *[]byte) {
|
|||
}
|
||||
|
||||
func (self *Node) encode(buf *[]byte) error {
|
||||
if self.IsRaw() {
|
||||
return self.encodeRaw(buf)
|
||||
}
|
||||
switch self.Type() {
|
||||
case V_NONE : return ErrNotExist
|
||||
case V_ERROR : return self.Check()
|
||||
case V_NULL : return self.encodeNull(buf)
|
||||
case V_TRUE : return self.encodeTrue(buf)
|
||||
case V_FALSE : return self.encodeFalse(buf)
|
||||
case V_ARRAY : return self.encodeArray(buf)
|
||||
case V_OBJECT: return self.encodeObject(buf)
|
||||
case V_STRING: return self.encodeString(buf)
|
||||
case V_NUMBER: return self.encodeNumber(buf)
|
||||
default : return ErrUnsupportType
|
||||
}
|
||||
if self.IsRaw() {
|
||||
return self.encodeRaw(buf)
|
||||
}
|
||||
switch self.Type() {
|
||||
case V_NONE : return ErrNotExist
|
||||
case V_ERROR : return self.Check()
|
||||
case V_NULL : return self.encodeNull(buf)
|
||||
case V_TRUE : return self.encodeTrue(buf)
|
||||
case V_FALSE : return self.encodeFalse(buf)
|
||||
case V_ARRAY : return self.encodeArray(buf)
|
||||
case V_OBJECT: return self.encodeObject(buf)
|
||||
case V_STRING: return self.encodeString(buf)
|
||||
case V_NUMBER: return self.encodeNumber(buf)
|
||||
case V_ANY : return self.encodeInterface(buf)
|
||||
default : return ErrUnsupportType
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Node) encodeRaw(buf *[]byte) error {
|
||||
raw, err := self.Raw()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*buf = append(*buf, raw...)
|
||||
return nil
|
||||
raw, err := self.Raw()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*buf = append(*buf, raw...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeNull(buf *[]byte) error {
|
||||
*buf = append(*buf, bytesNull...)
|
||||
return nil
|
||||
*buf = append(*buf, bytesNull...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeTrue(buf *[]byte) error {
|
||||
*buf = append(*buf, bytesTrue...)
|
||||
return nil
|
||||
*buf = append(*buf, bytesTrue...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeFalse(buf *[]byte) error {
|
||||
*buf = append(*buf, bytesFalse...)
|
||||
return nil
|
||||
*buf = append(*buf, bytesFalse...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeNumber(buf *[]byte) error {
|
||||
str := addr2str(self.p, self.v)
|
||||
*buf = append(*buf, str...)
|
||||
return nil
|
||||
str := addr2str(self.p, self.v)
|
||||
*buf = append(*buf, str...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeString(buf *[]byte) error {
|
||||
str := addr2str(self.p, self.v)
|
||||
*buf = append(*buf, '"')
|
||||
*buf = append(*buf, str...)
|
||||
*buf = append(*buf, '"')
|
||||
return nil
|
||||
str := addr2str(self.p, self.v)
|
||||
*buf = append(*buf, '"')
|
||||
*buf = append(*buf, str...)
|
||||
*buf = append(*buf, '"')
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeArray(buf *[]byte) error {
|
||||
if self.isLazy() {
|
||||
if err := self.skipAllIndex(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if self.isLazy() {
|
||||
if err := self.skipAllIndex(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
nb := self.len()
|
||||
if nb == 0 {
|
||||
*buf = append(*buf, bytesArray...)
|
||||
return nil
|
||||
}
|
||||
|
||||
*buf = append(*buf, '[')
|
||||
nb := self.len()
|
||||
if nb == 0 {
|
||||
*buf = append(*buf, bytesArray...)
|
||||
return nil
|
||||
}
|
||||
|
||||
*buf = append(*buf, '[')
|
||||
|
||||
var p = (*Node)(self.p)
|
||||
err := p.encode(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err := p.encode(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 1; i < nb; i++ {
|
||||
*buf = append(*buf, ',')
|
||||
*buf = append(*buf, ',')
|
||||
p = p.unsafe_next()
|
||||
err := p.encode(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*buf = append(*buf, ']')
|
||||
*buf = append(*buf, ']')
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Pair) encode(buf *[]byte) error {
|
||||
*buf = append(*buf, '"')
|
||||
*buf = append(*buf, self.Key...)
|
||||
*buf = append(*buf, '"', ':')
|
||||
return self.Value.encode(buf)
|
||||
*buf = append(*buf, '"')
|
||||
*buf = append(*buf, self.Key...)
|
||||
*buf = append(*buf, '"', ':')
|
||||
return self.Value.encode(buf)
|
||||
}
|
||||
|
||||
func (self *Node) encodeObject(buf *[]byte) error {
|
||||
if self.isLazy() {
|
||||
if err := self.skipAllKey(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
nb := self.len()
|
||||
if nb == 0 {
|
||||
*buf = append(*buf, bytesObject...)
|
||||
return nil
|
||||
}
|
||||
|
||||
*buf = append(*buf, '{')
|
||||
if self.isLazy() {
|
||||
if err := self.skipAllKey(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
nb := self.len()
|
||||
if nb == 0 {
|
||||
*buf = append(*buf, bytesObject...)
|
||||
return nil
|
||||
}
|
||||
|
||||
*buf = append(*buf, '{')
|
||||
|
||||
var p = (*Pair)(self.p)
|
||||
err := p.encode(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err := p.encode(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 1; i < nb; i++ {
|
||||
*buf = append(*buf, ',')
|
||||
*buf = append(*buf, ',')
|
||||
p = p.unsafe_next()
|
||||
err := p.encode(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*buf = append(*buf, '}')
|
||||
*buf = append(*buf, '}')
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeInterface(buf *[]byte) error {
|
||||
return encoder.EncodeInto(buf, self.packAny(), 0)
|
||||
}
|
||||
|
|
@ -17,17 +17,19 @@
|
|||
package ast
|
||||
|
||||
import (
|
||||
`encoding/json`
|
||||
`testing`
|
||||
`runtime`
|
||||
`runtime/debug`
|
||||
`sync`
|
||||
`testing`
|
||||
|
||||
`github.com/bytedance/sonic/decoder`
|
||||
`github.com/bytedance/sonic/encoder`
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/stretchr/testify/assert`
|
||||
)
|
||||
|
||||
func TestGC_Encode(t *testing.T) {
|
||||
if debugSyncGC {
|
||||
if debugSyncGC {
|
||||
return
|
||||
}
|
||||
root, err := NewSearcher(_TwitterJson).GetByPath()
|
||||
|
|
@ -44,12 +46,12 @@ func TestGC_Encode(t *testing.T) {
|
|||
for i:=0; i<N; i++ {
|
||||
wg.Add(1)
|
||||
go func (wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
root, err := NewSearcher(_TwitterJson).GetByPath()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root.Load()
|
||||
defer wg.Done()
|
||||
root, err := NewSearcher(_TwitterJson).GetByPath()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root.Load()
|
||||
_, err = root.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -62,118 +64,126 @@ func TestGC_Encode(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEncodeValue(t *testing.T) {
|
||||
type Case struct {
|
||||
node Node
|
||||
exp string
|
||||
err bool
|
||||
}
|
||||
input := []Case{
|
||||
{NewNull(), "null", false},
|
||||
{NewBool(true), "true", false},
|
||||
{NewBool(false), "false", false},
|
||||
{NewNumber("0.0"), "0.0", false},
|
||||
{NewString(""), `""`, false},
|
||||
{NewArray([]Node{}), "[]", false},
|
||||
{NewArray([]Node{NewBool(true), NewString("true")}), `[true,"true"]`, false},
|
||||
{NewObject([]Pair{Pair{"a", NewNull()}, Pair{"b", NewNumber("0")}}), `{"a":null,"b":0}`, false},
|
||||
{NewObject([]Pair{}), `{}`, false},
|
||||
{newRawNode(`[{ }]`, types.V_ARRAY), "[{}]", false},
|
||||
{Node{}, "", true},
|
||||
{Node{t: types.V_EOF}, "", true},
|
||||
}
|
||||
for i, c := range input {
|
||||
buf, err := json.Marshal(&c.node)
|
||||
if c.err {
|
||||
if err == nil {
|
||||
t.Fatal(i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
if string(buf) != c.exp {
|
||||
t.Fatal(i, string(buf))
|
||||
}
|
||||
}
|
||||
obj := new(_TwitterStruct)
|
||||
if err := decoder.NewDecoder(_TwitterJson).Decode(obj); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buf, err := encoder.Encode(obj, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
type Case struct {
|
||||
node Node
|
||||
exp string
|
||||
err bool
|
||||
}
|
||||
input := []Case{
|
||||
{NewNull(), "null", false},
|
||||
{NewBool(true), "true", false},
|
||||
{NewBool(false), "false", false},
|
||||
{NewNumber("0.0"), "0.0", false},
|
||||
{NewString(""), `""`, false},
|
||||
{NewArray([]Node{}), "[]", false},
|
||||
{NewArray([]Node{NewBool(true), NewString("true")}), `[true,"true"]`, false},
|
||||
{NewObject([]Pair{Pair{"a", NewNull()}, Pair{"b", NewNumber("0")}}), `{"a":null,"b":0}`, false},
|
||||
{NewObject([]Pair{}), `{}`, false},
|
||||
{NewBytes([]byte("hello, world")), `"aGVsbG8sIHdvcmxk"`, false},
|
||||
{NewAny(obj), string(buf), false},
|
||||
{NewRaw(`[{ }]`), "[{}]", false},
|
||||
{Node{}, "", true},
|
||||
{Node{t: types.ValueType(1)}, "", true},
|
||||
}
|
||||
for i, c := range input {
|
||||
buf, err := encoder.Encode(&c.node, 0)
|
||||
if c.err {
|
||||
if err == nil {
|
||||
t.Fatal(i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(i, err)
|
||||
}
|
||||
assert.Equal(t, c.exp, string(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeNode(t *testing.T) {
|
||||
data := `{"a":[{},[],-0.1,true,false,null,""],"b":0,"c":true,"d":false,"e":null,"g":""}`
|
||||
root, e := NewSearcher(data).GetByPath()
|
||||
if e != nil {
|
||||
t.Fatal(root)
|
||||
}
|
||||
ret, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(ret) != data {
|
||||
t.Fatal(string(ret))
|
||||
}
|
||||
root.skipAllKey()
|
||||
ret, err = root.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(ret) != data {
|
||||
t.Fatal(string(ret))
|
||||
}
|
||||
root.loadAllKey()
|
||||
ret, err = root.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(ret) != data {
|
||||
t.Fatal(string(ret))
|
||||
}
|
||||
data := `{"a":[{},[],-0.1,true,false,null,""],"b":0,"c":true,"d":false,"e":null,"g":""}`
|
||||
root, e := NewSearcher(data).GetByPath()
|
||||
if e != nil {
|
||||
t.Fatal(root)
|
||||
}
|
||||
ret, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(ret) != data {
|
||||
t.Fatal(string(ret))
|
||||
}
|
||||
root.skipAllKey()
|
||||
ret, err = root.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(ret) != data {
|
||||
t.Fatal(string(ret))
|
||||
}
|
||||
root.loadAllKey()
|
||||
ret, err = root.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(ret) != data {
|
||||
t.Fatal(string(ret))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeRaw(b *testing.B) {
|
||||
data := _TwitterJson
|
||||
root, e := NewSearcher(data).GetByPath()
|
||||
if e != nil {
|
||||
b.Fatal(root)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
_, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
data := _TwitterJson
|
||||
root, e := NewSearcher(data).GetByPath()
|
||||
if e != nil {
|
||||
b.Fatal(root)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
_, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeSkip(b *testing.B) {
|
||||
data := _TwitterJson
|
||||
root, e := NewParser(data).Parse()
|
||||
if e != 0 {
|
||||
b.Fatal(root)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
_, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
data := _TwitterJson
|
||||
root, e := NewParser(data).Parse()
|
||||
if e != 0 {
|
||||
b.Fatal(root)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
_, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeLoad(b *testing.B) {
|
||||
data := _TwitterJson
|
||||
root, e := NewParser(data).Parse()
|
||||
if e != 0 {
|
||||
b.Fatal(root)
|
||||
}
|
||||
root.loadAllKey()
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
_, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
data := _TwitterJson
|
||||
root, e := NewParser(data).Parse()
|
||||
if e != 0 {
|
||||
b.Fatal(root)
|
||||
}
|
||||
root.loadAllKey()
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
_, err := root.MarshalJSON()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,11 +16,31 @@
|
|||
|
||||
package ast
|
||||
|
||||
import (
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
)
|
||||
|
||||
type Pair struct {
|
||||
Key string
|
||||
Value Node
|
||||
}
|
||||
|
||||
// Values returns iterator for array's children traversal
|
||||
func (self *Node) Values() (ListIterator, error) {
|
||||
if err := self.should(types.V_ARRAY, "an array"); err != nil {
|
||||
return ListIterator{}, err
|
||||
}
|
||||
return ListIterator{Iterator{p: self}}, nil
|
||||
}
|
||||
|
||||
// Properties returns iterator for object's children traversal
|
||||
func (self *Node) Properties() (ObjectIterator, error) {
|
||||
if err := self.should(types.V_OBJECT, "an object"); err != nil {
|
||||
return ObjectIterator{}, err
|
||||
}
|
||||
return ObjectIterator{Iterator{p: self}}, nil
|
||||
}
|
||||
|
||||
type Iterator struct {
|
||||
i int
|
||||
p *Node
|
||||
|
|
@ -34,18 +54,30 @@ func (self *Iterator) Len() int {
|
|||
return self.p.len()
|
||||
}
|
||||
|
||||
// HasNext reports if it is the end of iteration or has error.
|
||||
func (self *Iterator) HasNext() bool {
|
||||
return self.i < self.p.len() && self.p.Valid()
|
||||
if !self.p.isLazy() {
|
||||
return self.p.Valid() && self.i < self.p.len()
|
||||
} else if self.p.t == _V_ARRAY_LAZY {
|
||||
return self.p.skipNextNode().Valid()
|
||||
} else if self.p.t == _V_OBJECT_LAZY {
|
||||
return self.p.skipNextPair().Value.Valid()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ListIterator is specialized iterator for V_ARRAY
|
||||
type ListIterator struct {
|
||||
Iterator
|
||||
}
|
||||
|
||||
// ObjectIterator is specialized iterator for V_ARRAY
|
||||
type ObjectIterator struct {
|
||||
Iterator
|
||||
}
|
||||
|
||||
// Next scans through children of underlying V_ARRAY,
|
||||
// copies each child to v, and returns .HasNext().
|
||||
func (self *ListIterator) Next(v *Node) bool {
|
||||
if !self.HasNext() {
|
||||
return false
|
||||
|
|
@ -55,6 +87,8 @@ func (self *ListIterator) Next(v *Node) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// Next scans through children of underlying V_OBJECT,
|
||||
// copies each child to v, and returns .HasNext().
|
||||
func (self *ObjectIterator) Next(p *Pair) bool {
|
||||
if !self.HasNext() {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -23,20 +23,20 @@ import (
|
|||
)
|
||||
|
||||
func getTestIteratorSample() (string, int) {
|
||||
var data []int
|
||||
var v1 = ""
|
||||
var v2 = ""
|
||||
loop := _DEFAULT_NODE_CAP+1
|
||||
for i:=0;i<loop;i++{
|
||||
data = append(data, i*i)
|
||||
v1 += strconv.Itoa(i)
|
||||
v2 += `"k`+strconv.Itoa(i)+`":`+strconv.Itoa(i)
|
||||
if i!=loop-1{
|
||||
v1+=`,`
|
||||
v2+=`,`
|
||||
}
|
||||
}
|
||||
return `{"array":[`+v1+`], "object":{`+v2+`}}`, loop
|
||||
var data []int
|
||||
var v1 = ""
|
||||
var v2 = ""
|
||||
loop := _DEFAULT_NODE_CAP+1
|
||||
for i:=0;i<loop;i++{
|
||||
data = append(data, i*i)
|
||||
v1 += strconv.Itoa(i)
|
||||
v2 += `"k`+strconv.Itoa(i)+`":`+strconv.Itoa(i)
|
||||
if i!=loop-1{
|
||||
v1+=`,`
|
||||
v2+=`,`
|
||||
}
|
||||
}
|
||||
return `{"array":[`+v1+`], "object":{`+v2+`}}`, loop
|
||||
}
|
||||
|
||||
func TestRawIterator(t *testing.T) {
|
||||
|
|
@ -53,8 +53,8 @@ func TestRawIterator(t *testing.T) {
|
|||
v := &Node{}
|
||||
if !ai.Next(v) {
|
||||
t.Fatalf("no next")
|
||||
}
|
||||
x, _ := v.Int64()
|
||||
}
|
||||
x, _ := v.Int64()
|
||||
if i < int64(loop) && x != i {
|
||||
t.Fatalf("exp:%v, got:%v", i, v)
|
||||
}
|
||||
|
|
@ -63,6 +63,9 @@ func TestRawIterator(t *testing.T) {
|
|||
}
|
||||
i++
|
||||
}
|
||||
if i != int64(loop) {
|
||||
t.Fatal(i)
|
||||
}
|
||||
|
||||
root, err = NewSearcher(str).GetByPath("object")
|
||||
if err != nil {
|
||||
|
|
@ -74,10 +77,10 @@ func TestRawIterator(t *testing.T) {
|
|||
v := &Pair{}
|
||||
if !mi.Next(v) {
|
||||
t.Fatalf("no next")
|
||||
}
|
||||
x, _ := v.Value.Int64()
|
||||
}
|
||||
x, _ := v.Value.Int64()
|
||||
if i < int64(loop) &&( x != i ||v.Key != fmt.Sprintf("k%d", i)) {
|
||||
vv, _ := v.Value.Interface()
|
||||
vv, _ := v.Value.Interface()
|
||||
t.Fatalf("exp:%v, got:%v", i, vv)
|
||||
}
|
||||
if i != int64(mi.Pos())-1 || i >= int64(mi.Len()) {
|
||||
|
|
@ -85,111 +88,120 @@ func TestRawIterator(t *testing.T) {
|
|||
}
|
||||
i++
|
||||
}
|
||||
if i != int64(loop) {
|
||||
t.Fatal(i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIterator(t *testing.T) {
|
||||
str, loop := getTestIteratorSample()
|
||||
fmt.Println(str)
|
||||
str, loop := getTestIteratorSample()
|
||||
fmt.Println(str)
|
||||
|
||||
root, err := NewParser(str).Parse()
|
||||
if err != 0 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ai, _ := root.Get("array").Values()
|
||||
i := int64(0)
|
||||
for ai.HasNext() {
|
||||
v := &Node{}
|
||||
if !ai.Next(v) {
|
||||
t.Fatalf("no next")
|
||||
}
|
||||
x, _ := v.Int64()
|
||||
if i < int64(loop) && x != i {
|
||||
t.Fatalf("exp:%v, got:%v", i, v)
|
||||
}
|
||||
if i != int64(ai.Pos())-1 || i >= int64(ai.Len()) {
|
||||
t.Fatal(i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
root, err := NewParser(str).Parse()
|
||||
if err != 0 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ai, _ := root.Get("array").Values()
|
||||
i := int64(0)
|
||||
for ai.HasNext() {
|
||||
v := &Node{}
|
||||
if !ai.Next(v) {
|
||||
t.Fatalf("no next")
|
||||
}
|
||||
x, _ := v.Int64()
|
||||
if i < int64(loop) && x != i {
|
||||
t.Fatalf("exp:%v, got:%v", i, v)
|
||||
}
|
||||
if i != int64(ai.Pos())-1 || i >= int64(ai.Len()) {
|
||||
t.Fatal(i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i != int64(loop) {
|
||||
t.Fatal(i)
|
||||
}
|
||||
|
||||
root, err = NewParser(str).Parse()
|
||||
if err != 0 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mi, _ := root.Get("object").Properties()
|
||||
i = int64(0)
|
||||
for mi.HasNext() {
|
||||
v := &Pair{}
|
||||
if !mi.Next(v) {
|
||||
t.Fatalf("no next")
|
||||
}
|
||||
x, _ := v.Value.Int64()
|
||||
if i < int64(loop) &&( x != i ||v.Key != fmt.Sprintf("k%d", i)) {
|
||||
vv, _ := v.Value.Interface()
|
||||
t.Fatalf("exp:%v, got:%v", i, vv)
|
||||
}
|
||||
if i != int64(mi.Pos())-1 || i >= int64(mi.Len()) {
|
||||
t.Fatal(i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
root, err = NewParser(str).Parse()
|
||||
if err != 0 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mi, _ := root.Get("object").Properties()
|
||||
i = int64(0)
|
||||
for mi.HasNext() {
|
||||
v := &Pair{}
|
||||
if !mi.Next(v) {
|
||||
t.Fatalf("no next")
|
||||
}
|
||||
x, _ := v.Value.Int64()
|
||||
if i < int64(loop) &&( x != i ||v.Key != fmt.Sprintf("k%d", i)) {
|
||||
vv, _ := v.Value.Interface()
|
||||
t.Fatalf("exp:%v, got:%v", i, vv)
|
||||
}
|
||||
if i != int64(mi.Pos())-1 || i >= int64(mi.Len()) {
|
||||
t.Fatal(i)
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i != int64(loop) {
|
||||
t.Fatal(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkArrays(b *testing.B) {
|
||||
for i:=0;i<b.N;i++{
|
||||
root,err := NewSearcher(_TwitterJson).GetByPath("statuses",1,"entities","hashtags")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
a, _ := root.Array()
|
||||
for _,v := range a {
|
||||
_ = v
|
||||
}
|
||||
}
|
||||
for i:=0;i<b.N;i++{
|
||||
root,err := NewSearcher(_TwitterJson).GetByPath("statuses",1,"entities","hashtags")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
a, _ := root.Array()
|
||||
for _,v := range a {
|
||||
_ = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkListIterator(b *testing.B) {
|
||||
for i:=0;i<b.N;i++{
|
||||
root,err := NewSearcher(_TwitterJson).GetByPath("statuses",1,"entities","hashtags")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
it, _ := root.Values()
|
||||
for it.HasNext() {
|
||||
v := &Node{}
|
||||
if !it.Next(v) {
|
||||
b.Fatalf("no value")
|
||||
}
|
||||
}
|
||||
}
|
||||
for i:=0;i<b.N;i++{
|
||||
root,err := NewSearcher(_TwitterJson).GetByPath("statuses",1,"entities","hashtags")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
it, _ := root.Values()
|
||||
for it.HasNext() {
|
||||
v := &Node{}
|
||||
if !it.Next(v) {
|
||||
b.Fatalf("no value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMap(b *testing.B) {
|
||||
for i:=0;i<b.N;i++{
|
||||
root,err := NewSearcher(_TwitterJson).GetByPath("statuses",1, "user")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
m, _ := root.Map()
|
||||
for k,v := range m {
|
||||
_ = v
|
||||
_ = k
|
||||
}
|
||||
}
|
||||
for i:=0;i<b.N;i++{
|
||||
root,err := NewSearcher(_TwitterJson).GetByPath("statuses",1, "user")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
m, _ := root.Map()
|
||||
for k,v := range m {
|
||||
_ = v
|
||||
_ = k
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkObjectIterator(b *testing.B) {
|
||||
for i:=0;i<b.N;i++{
|
||||
root,err := NewSearcher(_TwitterJson).GetByPath("statuses",1, "user")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
it, _ := root.Properties()
|
||||
for it.HasNext() {
|
||||
v := &Pair{}
|
||||
if !it.Next(v) {
|
||||
b.Fatalf("no value")
|
||||
}
|
||||
}
|
||||
}
|
||||
for i:=0;i<b.N;i++{
|
||||
root,err := NewSearcher(_TwitterJson).GetByPath("statuses",1, "user")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
it, _ := root.Properties()
|
||||
for it.HasNext() {
|
||||
v := &Pair{}
|
||||
if !it.Next(v) {
|
||||
b.Fatalf("no value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
385
ast/node.go
385
ast/node.go
|
|
@ -17,13 +17,15 @@
|
|||
package ast
|
||||
|
||||
import (
|
||||
`encoding/json`
|
||||
`unsafe`
|
||||
`encoding/json`
|
||||
`fmt`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/decoder`
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
`github.com/bytedance/sonic/unquote`
|
||||
`github.com/bytedance/sonic/decoder`
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
`github.com/bytedance/sonic/unquote`
|
||||
`github.com/chenzhuoyu/base64x`
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -40,6 +42,7 @@ const (
|
|||
_V_LAZY types.ValueType = 1 << 7
|
||||
_V_RAW types.ValueType = 1 << 8
|
||||
_V_NUMBER = _V_NODE_BASE + 1
|
||||
_V_ANY = _V_NODE_BASE + 2
|
||||
_V_ARRAY_LAZY = _V_LAZY | types.V_ARRAY
|
||||
_V_OBJECT_LAZY = _V_LAZY | types.V_OBJECT
|
||||
_MASK_LAZY = _V_LAZY - 1
|
||||
|
|
@ -56,6 +59,7 @@ const (
|
|||
V_OBJECT = 6
|
||||
V_STRING = 7
|
||||
V_NUMBER = int(_V_NUMBER)
|
||||
V_ANY = int(_V_ANY)
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
|
|
@ -64,19 +68,27 @@ type Node struct {
|
|||
p unsafe.Pointer
|
||||
}
|
||||
|
||||
// UnmarshalJSON is just an adapter to json.Unmarshaler.
|
||||
// If you want better performance, use Searcher.GetByPath() directly
|
||||
func (self *Node) UnmarshalJSON(data []byte) (err error) {
|
||||
*self, err = NewSearcher(string(data)).GetByPath()
|
||||
return
|
||||
}
|
||||
|
||||
/** Node Type Accessor **/
|
||||
|
||||
// Type returns json type represented by the node
|
||||
// It will be one of belows:
|
||||
// V_NONE = 0
|
||||
// V_ERROR = 1
|
||||
// V_NULL = 2
|
||||
// V_TRUE = 3
|
||||
// V_FALSE = 4
|
||||
// V_ARRAY = 5
|
||||
// V_OBJECT = 6
|
||||
// V_STRING = 7
|
||||
// V_NUMBER = 33
|
||||
// V_NONE = 0 (empty node)
|
||||
// V_ERROR = 1 (error node)
|
||||
// V_NULL = 2 (json value `null`)
|
||||
// V_TRUE = 3 (json value `true`)
|
||||
// V_FALSE = 4 (json value `false`)
|
||||
// V_ARRAY = 5 (json value array)
|
||||
// V_OBJECT = 6 (json value object)
|
||||
// V_STRING = 7 (json value string)
|
||||
// V_NUMBER = 33 (json value number )
|
||||
// V_ANY = 34 (golang interface{})
|
||||
func (self Node) Type() int {
|
||||
return int(self.t & _MASK_LAZY & _MASK_RAW)
|
||||
}
|
||||
|
|
@ -85,25 +97,24 @@ func (self Node) itype() types.ValueType {
|
|||
return self.t & _MASK_LAZY & _MASK_RAW
|
||||
}
|
||||
|
||||
// Exists returns false only if the node is nil or got by invalid path
|
||||
// Exists returns false only if the self is nil or empty node V_NONE
|
||||
func (self *Node) Exists() bool {
|
||||
return self != nil && self.t != _V_NONE
|
||||
}
|
||||
|
||||
// Valid returns true when the node has any type of V_NULL ~ V_STRING, or V_NUMBER
|
||||
// Valid reports if self is NOT V_ERROR or nil
|
||||
func (self *Node) Valid() bool {
|
||||
if self == nil {
|
||||
return false
|
||||
}
|
||||
it := self.Type()
|
||||
return it >= V_NULL && it <= V_STRING || it == V_NUMBER
|
||||
return self.t != V_ERROR
|
||||
}
|
||||
|
||||
// Check check if the node itself is valid, and return:
|
||||
// - ErrNotFound If the node does not exist
|
||||
// Check checks if the node itself is valid, and return:
|
||||
// - ErrNotFound If the node is nil
|
||||
// - Its underlying error If the node is V_ERROR
|
||||
func (self *Node) Check() error {
|
||||
if self == nil || self.t == V_NONE {
|
||||
if self == nil {
|
||||
return ErrNotExist
|
||||
} else if self.t != V_ERROR {
|
||||
return nil
|
||||
|
|
@ -114,9 +125,7 @@ func (self *Node) Check() error {
|
|||
|
||||
// Error returns error message if the node is invalid
|
||||
func (self Node) Error() string {
|
||||
if self.t == V_NONE {
|
||||
return "unsupported type"
|
||||
} else if self.t != V_ERROR {
|
||||
if self.t != V_ERROR {
|
||||
return ""
|
||||
} else {
|
||||
return *(*string)(self.p)
|
||||
|
|
@ -128,17 +137,21 @@ func (self Node) IsRaw() bool {
|
|||
return self.t&_V_RAW != 0
|
||||
}
|
||||
|
||||
func (self Node) isLazy() bool {
|
||||
return self.t&_V_LAZY != 0
|
||||
func (self *Node) isLazy() bool {
|
||||
return self != nil && self.t&_V_LAZY != 0
|
||||
}
|
||||
|
||||
func (self *Node) isAny() bool {
|
||||
return self != nil && self.t == _V_ANY
|
||||
}
|
||||
|
||||
/** Simple Value Methods **/
|
||||
|
||||
// Raw returns underlying json string of an raw node,
|
||||
// which usually created by Search() api
|
||||
// Raw returns json representation of the node,
|
||||
func (self *Node) Raw() (string, error) {
|
||||
if !self.IsRaw() {
|
||||
return "", ErrUnsupportType
|
||||
buf, err := self.MarshalJSON()
|
||||
return rt.Mem2Str(buf), err
|
||||
}
|
||||
return addr2str(self.p, self.v), nil
|
||||
}
|
||||
|
|
@ -155,7 +168,8 @@ func (self *Node) checkRaw() error {
|
|||
|
||||
// Bool_E returns bool value represented by this node
|
||||
//
|
||||
// If node type is not types.V_TRUE or types.V_FALSE, or V_RAW (must be a bool json value),
|
||||
// If node type is not types.V_TRUE or types.V_FALSE,
|
||||
// V_RAW (must be a bool json value), or V_ANY (must be a bool type)
|
||||
// it will return error
|
||||
func (self *Node) Bool() (bool, error) {
|
||||
if err := self.checkRaw(); err != nil {
|
||||
|
|
@ -164,6 +178,12 @@ func (self *Node) Bool() (bool, error) {
|
|||
switch self.t {
|
||||
case types.V_TRUE : return true , nil
|
||||
case types.V_FALSE : return false, nil
|
||||
case _V_ANY :
|
||||
if v, ok := self.packAny().(bool); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return false, ErrUnsupportType
|
||||
}
|
||||
default : return false, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
|
|
@ -177,6 +197,21 @@ func (self *Node) Int64() (int64, error) {
|
|||
case _V_NUMBER : return numberToInt64(self)
|
||||
case types.V_TRUE : return 1, nil
|
||||
case types.V_FALSE : return 0, nil
|
||||
case _V_ANY :
|
||||
any := self.packAny()
|
||||
switch v := any.(type) {
|
||||
case int : return int64(v), nil
|
||||
case int8 : return int64(v), nil
|
||||
case int16 : return int64(v), nil
|
||||
case int32 : return int64(v), nil
|
||||
case int64 : return int64(v), nil
|
||||
case uint : return int64(v), nil
|
||||
case uint8 : return int64(v), nil
|
||||
case uint16: return int64(v), nil
|
||||
case uint32: return int64(v), nil
|
||||
case uint64: return int64(v), nil
|
||||
default: return 0, ErrUnsupportType
|
||||
}
|
||||
default : return 0, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
|
|
@ -190,6 +225,12 @@ func (self *Node) Number() (json.Number, error) {
|
|||
case _V_NUMBER : return toNumber(self) , nil
|
||||
case types.V_TRUE : return json.Number("1"), nil
|
||||
case types.V_FALSE : return json.Number("0"), nil
|
||||
case _V_ANY :
|
||||
if v, ok := self.packAny().(json.Number); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return json.Number(""), ErrUnsupportType
|
||||
}
|
||||
default : return json.Number(""), ErrUnsupportType
|
||||
}
|
||||
}
|
||||
|
|
@ -200,6 +241,7 @@ func (self *Node) Number() (json.Number, error) {
|
|||
// V_TRUE => "true",
|
||||
// V_FALSE => "false",
|
||||
// V_NUMBER => "[0-9\.]*"
|
||||
// V_ANY => interface{}.(string)
|
||||
func (self *Node) String() (string, error) {
|
||||
if err := self.checkRaw(); err != nil {
|
||||
return "", err
|
||||
|
|
@ -210,6 +252,12 @@ func (self *Node) String() (string, error) {
|
|||
case types.V_TRUE : return "true" , nil
|
||||
case types.V_FALSE : return "false", nil
|
||||
case types.V_STRING : return addr2str(self.p, self.v), nil
|
||||
case _V_ANY :
|
||||
if v, ok := self.packAny().(string); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return "", ErrUnsupportType
|
||||
}
|
||||
default : return "" , ErrUnsupportType
|
||||
}
|
||||
}
|
||||
|
|
@ -223,6 +271,13 @@ func (self *Node) Float64() (float64, error) {
|
|||
case _V_NUMBER : return numberToFloat64(self)
|
||||
case types.V_TRUE : return 1.0, nil
|
||||
case types.V_FALSE : return 0.0, nil
|
||||
case _V_ANY :
|
||||
any := self.packAny()
|
||||
switch v := any.(type) {
|
||||
case float32 : return float64(v), nil
|
||||
case float64 : return float64(v), nil
|
||||
default : return 0, ErrUnsupportType
|
||||
}
|
||||
default : return 0.0, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
|
|
@ -239,6 +294,8 @@ func (self *Node) Len() (int, error) {
|
|||
return int(self.v & _LEN_MASK), nil
|
||||
} else if self.t == types.V_STRING {
|
||||
return int(self.v), nil
|
||||
} else if self.t == _V_NONE || self.t == types.V_NULL {
|
||||
return 0, nil
|
||||
} else {
|
||||
return 0, ErrUnsupportType
|
||||
}
|
||||
|
|
@ -255,6 +312,8 @@ func (self *Node) Cap() (int, error) {
|
|||
}
|
||||
if self.t == types.V_ARRAY || self.t == types.V_OBJECT || self.t == _V_ARRAY_LAZY || self.t == _V_OBJECT_LAZY {
|
||||
return int(self.v >> _CAP_BITS), nil
|
||||
} else if self.t == _V_NONE || self.t == types.V_NULL {
|
||||
return 0, nil
|
||||
} else {
|
||||
return 0, ErrUnsupportType
|
||||
}
|
||||
|
|
@ -264,9 +323,15 @@ func (self Node) cap() int {
|
|||
return int(self.v >> _CAP_BITS)
|
||||
}
|
||||
|
||||
// Set sets the node of given key under object parent
|
||||
// If the key doesn't exist, it will be append to the last
|
||||
// Set sets the node of given key under self, and reports if the key has existed.
|
||||
//
|
||||
// If self is V_NONE or V_NULL, it becomes V_OBJECT and sets the node at the key.
|
||||
func (self *Node) Set(key string, node Node) (bool, error) {
|
||||
if self != nil && (self.t == _V_NONE || self.t == types.V_NULL) {
|
||||
*self = NewObject([]Pair{{key, node}})
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err := node.Check(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -296,7 +361,12 @@ func (self *Node) Set(key string, node Node) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// Unset remove the node of given key under object parent
|
||||
// SetAny wraps val with V_ANY node, and Set() the node.
|
||||
func (self *Node) SetAny(key string, val interface{}) (bool, error) {
|
||||
return self.Set(key, NewAny(val))
|
||||
}
|
||||
|
||||
// Unset remove the node of given key under object parent, and reports if the key has existed.
|
||||
func (self *Node) Unset(key string) (bool, error) {
|
||||
self.must(types.V_OBJECT, "an object")
|
||||
p, i := self.skipKey(key)
|
||||
|
|
@ -310,9 +380,9 @@ func (self *Node) Unset(key string) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// SetByIndex sets the node of given index
|
||||
// SetByIndex sets the node of given index, and reports if the key has existed.
|
||||
//
|
||||
// The index must within parent array's children
|
||||
// The index must be within self's children.
|
||||
func (self *Node) SetByIndex(index int, node Node) (bool, error) {
|
||||
if err := node.Check(); err != nil {
|
||||
return false, err
|
||||
|
|
@ -329,6 +399,11 @@ func (self *Node) SetByIndex(index int, node Node) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// SetAny wraps val with V_ANY node, and SetByIndex() the node.
|
||||
func (self *Node) SetAnyByIndex(index int, val interface{}) (bool, error) {
|
||||
return self.SetByIndex(index, NewAny(val))
|
||||
}
|
||||
|
||||
// UnsetByIndex remove the node of given index
|
||||
func (self *Node) UnsetByIndex(index int) (bool, error) {
|
||||
var p *Node
|
||||
|
|
@ -341,7 +416,7 @@ func (self *Node) UnsetByIndex(index int) (bool, error) {
|
|||
return false, ErrNotExist
|
||||
}
|
||||
p = &pr.Value
|
||||
}else{
|
||||
} else {
|
||||
return false, ErrUnsupportType
|
||||
}
|
||||
|
||||
|
|
@ -357,8 +432,15 @@ func (self *Node) UnsetByIndex(index int) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// Add appends the given node under array node
|
||||
// Add appends the given node under self.
|
||||
//
|
||||
// If self is V_NONE or V_NULL, it becomes V_ARRAY and sets the node at index 0.
|
||||
func (self *Node) Add(node Node) error {
|
||||
if self != nil && (self.t == _V_NONE || self.t == types.V_NULL) {
|
||||
*self = NewArray([]Node{node})
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := self.should(types.V_ARRAY, "an array"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -379,6 +461,11 @@ func (self *Node) Add(node Node) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetAny wraps val with V_ANY node, and Add() the node.
|
||||
func (self *Node) AddAny(val interface{}) error {
|
||||
return self.Add(NewAny(val))
|
||||
}
|
||||
|
||||
// GetByPath load given path on demands,
|
||||
// which only ensure nodes before this path got parsed
|
||||
func (self *Node) GetByPath(path ...interface{}) *Node {
|
||||
|
|
@ -428,12 +515,12 @@ func (self *Node) Index(idx int) *Node {
|
|||
}else if it == types.V_OBJECT {
|
||||
pr := self.skipIndexPair(idx)
|
||||
if pr == nil {
|
||||
return nodeNotExist
|
||||
return newError(_ERR_NOT_FOUND, "value not exists")
|
||||
}
|
||||
return &pr.Value
|
||||
|
||||
}else{
|
||||
return nodeUnsupportType
|
||||
} else {
|
||||
return newError(_ERR_UNSUPPORT_TYPE, fmt.Sprintf("unsupported type: %v", self.itype()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -461,32 +548,18 @@ func (self *Node) IndexOrGet(idx int, key string) *Node {
|
|||
return n
|
||||
}
|
||||
|
||||
// Values returns iterator for array's children traversal
|
||||
func (self *Node) Values() (ListIterator, error) {
|
||||
if err := self.should(types.V_ARRAY, "an array"); err != nil {
|
||||
return ListIterator{}, err
|
||||
}
|
||||
if err := self.skipAllIndex(); err != nil {
|
||||
return ListIterator{}, err
|
||||
}
|
||||
return ListIterator{Iterator{p: self}}, nil
|
||||
}
|
||||
|
||||
// Properties returns iterator for object's children traversal
|
||||
func (self *Node) Properties() (ObjectIterator, error) {
|
||||
if err := self.should(types.V_OBJECT, "an object"); err != nil {
|
||||
return ObjectIterator{}, err
|
||||
}
|
||||
if err := self.skipAllKey(); err != nil {
|
||||
return ObjectIterator{}, err
|
||||
}
|
||||
return ObjectIterator{Iterator{p: self}}, nil
|
||||
}
|
||||
|
||||
/** Generic Value Converters **/
|
||||
|
||||
// Map loads all keys of an object node
|
||||
func (self *Node) Map() (map[string]interface{}, error) {
|
||||
if self.isAny() {
|
||||
any := self.packAny()
|
||||
if v, ok := any.(map[string]interface{}); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return nil, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
if err := self.should(types.V_OBJECT, "an object"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -498,6 +571,14 @@ func (self *Node) Map() (map[string]interface{}, error) {
|
|||
|
||||
// MapUseNumber loads all keys of an object node, with numeric nodes casted to json.Number
|
||||
func (self *Node) MapUseNumber() (map[string]interface{}, error) {
|
||||
if self.isAny() {
|
||||
any := self.packAny()
|
||||
if v, ok := any.(map[string]interface{}); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return nil, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
if err := self.should(types.V_OBJECT, "an object"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -510,6 +591,14 @@ func (self *Node) MapUseNumber() (map[string]interface{}, error) {
|
|||
// MapUseNode scans both parsed and non-parsed chidren nodes,
|
||||
// and map them by their keys
|
||||
func (self *Node) MapUseNode() (map[string]Node, error) {
|
||||
if self.isAny() {
|
||||
any := self.packAny()
|
||||
if v, ok := any.(map[string]Node); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return nil, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
if err := self.should(types.V_OBJECT, "an object"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -534,6 +623,14 @@ func (self *Node) UnsafeMap() ([]Pair, error) {
|
|||
|
||||
// Array loads all indexes of an array node
|
||||
func (self *Node) Array() ([]interface{}, error) {
|
||||
if self.isAny() {
|
||||
any := self.packAny()
|
||||
if v, ok := any.([]interface{}); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return nil, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
if err := self.should(types.V_ARRAY, "an array"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -545,6 +642,14 @@ func (self *Node) Array() ([]interface{}, error) {
|
|||
|
||||
// ArrayUseNumber loads all indexes of an array node, with numeric nodes casted to json.Number
|
||||
func (self *Node) ArrayUseNumber() ([]interface{}, error) {
|
||||
if self.isAny() {
|
||||
any := self.packAny()
|
||||
if v, ok := any.([]interface{}); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return nil, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
if err := self.should(types.V_ARRAY, "an array"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -557,6 +662,14 @@ func (self *Node) ArrayUseNumber() ([]interface{}, error) {
|
|||
// ArrayUseNode copys both parsed and non-parsed chidren nodes,
|
||||
// and indexes them by original order
|
||||
func (self *Node) ArrayUseNode() ([]Node, error) {
|
||||
if self.isAny() {
|
||||
any := self.packAny()
|
||||
if v, ok := any.([]Node); ok {
|
||||
return v, nil
|
||||
} else {
|
||||
return nil, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
if err := self.should(types.V_ARRAY, "an array"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -610,10 +723,20 @@ func (self *Node) Interface() (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
return self.toGenericObject()
|
||||
case _V_ANY:
|
||||
switch v := self.packAny().(type) {
|
||||
case Node : return v.Interface()
|
||||
case *Node: return v.Interface()
|
||||
default : return v, nil
|
||||
}
|
||||
default : return nil, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Node) packAny() interface{} {
|
||||
return *(*interface{})(self.p)
|
||||
}
|
||||
|
||||
// InterfaceUseNumber works same with Interface()
|
||||
// except numberic nodes are casted to json.Number
|
||||
func (self *Node) InterfaceUseNumber() (interface{}, error) {
|
||||
|
|
@ -639,6 +762,7 @@ func (self *Node) InterfaceUseNumber() (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
return self.toGenericObjectUseNumber()
|
||||
case _V_ANY : return self.packAny(), nil
|
||||
default : return nil, ErrUnsupportType
|
||||
}
|
||||
}
|
||||
|
|
@ -662,66 +786,66 @@ func (self *Node) InterfaceUseNode() (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
return self.toGenericObjectUseNode()
|
||||
default : return *self, nil
|
||||
default : return *self, self.Check()
|
||||
}
|
||||
}
|
||||
|
||||
// LoadAll loads all the node's children and children's children as parsed.
|
||||
// After calling it, the node can be safely used on concurrency
|
||||
func (self *Node) LoadAll() error {
|
||||
if self.IsRaw() {
|
||||
self.parseRaw(true)
|
||||
return self.Check()
|
||||
}
|
||||
if self.IsRaw() {
|
||||
self.parseRaw(true)
|
||||
return self.Check()
|
||||
}
|
||||
|
||||
switch self.itype() {
|
||||
case types.V_ARRAY:
|
||||
e := self.len()
|
||||
if err := self.loadAllIndex(); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < e; i++ {
|
||||
n := self.nodeAt(i)
|
||||
n.parseRaw(true)
|
||||
if err := n.Check(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case types.V_OBJECT:
|
||||
e := self.len()
|
||||
if err := self.loadAllKey(); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < e; i++ {
|
||||
n := self.pairAt(i)
|
||||
n.Value.parseRaw(true)
|
||||
if err := n.Value.Check(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return self.Check()
|
||||
}
|
||||
switch self.itype() {
|
||||
case types.V_ARRAY:
|
||||
e := self.len()
|
||||
if err := self.loadAllIndex(); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < e; i++ {
|
||||
n := self.nodeAt(i)
|
||||
n.parseRaw(true)
|
||||
if err := n.Check(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case types.V_OBJECT:
|
||||
e := self.len()
|
||||
if err := self.loadAllKey(); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < e; i++ {
|
||||
n := self.pairAt(i)
|
||||
n.Value.parseRaw(true)
|
||||
if err := n.Value.Check(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return self.Check()
|
||||
}
|
||||
}
|
||||
|
||||
// Load loads the node's children as parsed.
|
||||
// After calling it, only the node itself can be used on concurrency (not include its children)
|
||||
func (self *Node) Load() error {
|
||||
if self.IsRaw() {
|
||||
self.parseRaw(false)
|
||||
return self.Load()
|
||||
}
|
||||
if self.IsRaw() {
|
||||
self.parseRaw(false)
|
||||
return self.Load()
|
||||
}
|
||||
|
||||
switch self.t {
|
||||
case _V_ARRAY_LAZY:
|
||||
return self.skipAllIndex()
|
||||
case _V_OBJECT_LAZY:
|
||||
return self.skipAllKey()
|
||||
default:
|
||||
return self.Check()
|
||||
}
|
||||
switch self.t {
|
||||
case _V_ARRAY_LAZY:
|
||||
return self.skipAllIndex()
|
||||
case _V_OBJECT_LAZY:
|
||||
return self.skipAllKey()
|
||||
default:
|
||||
return self.Check()
|
||||
}
|
||||
}
|
||||
|
||||
/**---------------------------------- Internal Helper Methods ----------------------------------**/
|
||||
|
|
@ -859,7 +983,7 @@ func (self *Node) skipNextNode() *Node {
|
|||
/* skip the value */
|
||||
if start, err := parser.skip(); err != 0 {
|
||||
return newSyntaxError(parser.syntaxError(err))
|
||||
}else{
|
||||
} else {
|
||||
t := switchRawType(parser.s[start])
|
||||
if t == _V_NONE {
|
||||
return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))
|
||||
|
|
@ -942,7 +1066,7 @@ func (self *Node) skipNextPair() (*Pair) {
|
|||
/* skip the value */
|
||||
if start, err := parser.skip(); err != 0 {
|
||||
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
|
||||
}else{
|
||||
} else {
|
||||
t := switchRawType(parser.s[start])
|
||||
if t == _V_NONE {
|
||||
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
|
||||
|
|
@ -1299,6 +1423,41 @@ var (
|
|||
emptyObjectNode = Node{t: types.V_OBJECT}
|
||||
)
|
||||
|
||||
// NewRaw creates a node of raw json, and decides its type by first char.
|
||||
func NewRaw(json string) Node {
|
||||
if json == "" {
|
||||
panic("empty json string")
|
||||
}
|
||||
it := switchRawType(json[0])
|
||||
return newRawNode(json, it)
|
||||
}
|
||||
|
||||
// NewAny creates a node of type V_ANY if any's type isn't Node or *Node,
|
||||
// which stores interface{} and can be only used for `.Interface()`\`.MarshalJSON()`.
|
||||
func NewAny(any interface{}) Node {
|
||||
switch n := any.(type) {
|
||||
case Node:
|
||||
return n
|
||||
case *Node:
|
||||
return *n
|
||||
default:
|
||||
return Node{
|
||||
t: _V_ANY,
|
||||
v: 0,
|
||||
p: unsafe.Pointer(&any),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewBytes encodes given src with Base64 (RFC 4648), and creates a node of type V_STRING.
|
||||
func NewBytes(src []byte) Node {
|
||||
if len(src) == 0 {
|
||||
panic("empty src bytes")
|
||||
}
|
||||
out := base64x.StdEncoding.EncodeToString(src)
|
||||
return NewString(out)
|
||||
}
|
||||
|
||||
// NewNull creates a node of type V_NULL
|
||||
func NewNull() Node {
|
||||
return Node{
|
||||
|
|
@ -1520,7 +1679,7 @@ func unwrapError(err error) *Node {
|
|||
return se
|
||||
}else if sse, ok := err.(Node); ok {
|
||||
return &sse
|
||||
}else{
|
||||
} else {
|
||||
msg := err.Error()
|
||||
return &Node{
|
||||
t: V_ERROR,
|
||||
|
|
|
|||
237
ast/node_test.go
237
ast/node_test.go
|
|
@ -20,21 +20,47 @@ import (
|
|||
`encoding/json`
|
||||
`fmt`
|
||||
`reflect`
|
||||
`runtime`
|
||||
`runtime/debug`
|
||||
`strconv`
|
||||
`testing`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
`github.com/stretchr/testify/assert`
|
||||
)
|
||||
|
||||
//go:noinline
|
||||
func stackObj() interface{} {
|
||||
var a int = 1
|
||||
return rt.UnpackEface(a).Pack()
|
||||
}
|
||||
|
||||
func TestStackAny(t *testing.T) {
|
||||
var obj = stackObj()
|
||||
any := NewAny(obj)
|
||||
fmt.Printf("any: %#v\n", any)
|
||||
runtime.GC()
|
||||
debug.FreeOSMemory()
|
||||
println("finish GC")
|
||||
buf, err := any.MarshalJSON()
|
||||
println("finish marshal")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(buf) != `1` {
|
||||
t.Fatal(string(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadAll(t *testing.T) {
|
||||
e := Node{}
|
||||
err := e.Load()
|
||||
if err != ErrNotExist {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = e.LoadAll()
|
||||
if err != ErrNotExist {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
@ -151,39 +177,96 @@ func TestTypeCast(t *testing.T) {
|
|||
exp interface{}
|
||||
err error
|
||||
}
|
||||
a1 := NewAny(1)
|
||||
lazyArray, _ := NewParser("[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]").Parse()
|
||||
lazyObject, _ := NewParser(`{"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16}`).Parse()
|
||||
var cases = []tcase{
|
||||
{"Raw", Node{}, "", ErrUnsupportType},
|
||||
{"Raw", newRawNode("[ ]", types.V_ARRAY), "[ ]", nil},
|
||||
{"Bool", Node{}, false, ErrNotExist},
|
||||
{"Bool", newRawNode("true", types.V_TRUE), true, nil},
|
||||
{"Bool", newRawNode("false", types.V_FALSE), false, nil},
|
||||
{"Int64", Node{}, int64(0), ErrNotExist},
|
||||
{"Int64", newRawNode("0", _V_NUMBER), int64(0), nil},
|
||||
{"Float64", Node{}, float64(0), ErrNotExist},
|
||||
{"Float64", newRawNode("0.0", _V_NUMBER), float64(0.0), nil},
|
||||
{"Number", Node{}, json.Number(""), ErrNotExist},
|
||||
{"Number", newRawNode("0.0", _V_NUMBER), json.Number("0.0"), nil},
|
||||
{"Number", newRawNode("true", types.V_TRUE), json.Number("1"), nil},
|
||||
{"Number", newRawNode("false", types.V_FALSE), json.Number("0"), nil},
|
||||
{"String", Node{}, "", ErrNotExist},
|
||||
{"String", newRawNode(`""`, types.V_STRING), ``, nil},
|
||||
{"String", newRawNode(`0.0`, _V_NUMBER), "0.0", nil},
|
||||
{"String", newRawNode(`null`, types.V_NULL), "null", nil},
|
||||
{"String", newRawNode(`true`, types.V_TRUE), "true", nil},
|
||||
{"String", newRawNode(`false`, types.V_FALSE), "false", nil},
|
||||
{"Len", NewNull(), 0, ErrUnsupportType},
|
||||
{"Len", newRawNode(`"1"`, types.V_STRING), 1, nil},
|
||||
{"Len", newRawNode(`[1]`, types.V_ARRAY), 0, nil},
|
||||
{"Interface", Node{}, interface{}(nil), ErrUnsupportType},
|
||||
{"Interface", NewAny(NewNumber("1")), float64(1), nil},
|
||||
{"Interface", NewAny(int64(1)), int64(1), nil},
|
||||
{"Interface", NewNumber("1"), float64(1), nil},
|
||||
{"InterfaceUseNode", Node{}, Node{}, nil},
|
||||
{"InterfaceUseNode", a1, a1, nil},
|
||||
{"InterfaceUseNode", NewNumber("1"), NewNumber("1"), nil},
|
||||
{"InterfaceUseNumber", Node{}, interface{}(nil), ErrUnsupportType},
|
||||
{"InterfaceUseNumber", NewAny(1), 1, nil},
|
||||
{"InterfaceUseNumber", NewNumber("1"), json.Number("1"), nil},
|
||||
{"Map", Node{}, map[string]interface{}(nil), ErrUnsupportType},
|
||||
{"Map", NewAny(map[string]Node{"a":NewNumber("1")}), map[string]interface{}(nil), ErrUnsupportType},
|
||||
{"Map", NewAny(map[string]interface{}{"a":1}), map[string]interface{}{"a":1}, nil},
|
||||
{"Map", NewObject([]Pair{{"a",NewNumber("1")}}), map[string]interface{}{"a":float64(1.0)}, nil},
|
||||
{"MapUseNode", Node{}, map[string]Node(nil), ErrUnsupportType},
|
||||
{"MapUseNode", NewAny(map[string]interface{}{"a":1}), map[string]Node(nil), ErrUnsupportType},
|
||||
{"MapUseNode", NewAny(map[string]Node{"a":NewNumber("1")}), map[string]Node{"a":NewNumber("1")}, nil},
|
||||
{"MapUseNode", NewObject([]Pair{{"a",NewNumber("1")}}), map[string]Node{"a":NewNumber("1")}, nil},
|
||||
{"MapUseNumber", Node{}, map[string]interface{}(nil), ErrUnsupportType},
|
||||
{"MapUseNumber", NewAny(map[string]interface{}{"a":1}), map[string]interface{}{"a":1}, nil},
|
||||
{"MapUseNumber", NewObject([]Pair{{"a",NewNumber("1")}}), map[string]interface{}{"a":json.Number("1")}, nil},
|
||||
{"Array", Node{}, []interface{}(nil), ErrUnsupportType},
|
||||
{"Array", NewAny([]interface{}{1}), []interface{}{1}, nil},
|
||||
{"Array", NewArray([]Node{NewNumber("1")}), []interface{}{float64(1.0)}, nil},
|
||||
{"ArrayUseNode", Node{}, []Node(nil), ErrUnsupportType},
|
||||
{"ArrayUseNode", NewAny([]interface{}{1}), []Node(nil), ErrUnsupportType},
|
||||
{"ArrayUseNode", NewAny([]Node{NewNumber("1")}), []Node{NewNumber("1")}, nil},
|
||||
{"ArrayUseNode", NewArray([]Node{NewNumber("1")}), []Node{NewNumber("1")}, nil},
|
||||
{"ArrayUseNumber", Node{}, []interface{}(nil), ErrUnsupportType},
|
||||
{"ArrayUseNumber", NewAny([]interface{}{1}), []interface{}{1}, nil},
|
||||
{"ArrayUseNumber", NewAny([]Node{NewNumber("1")}), []interface{}(nil), ErrUnsupportType},
|
||||
{"ArrayUseNumber", NewArray([]Node{NewNumber("1")}), []interface{}{json.Number("1")}, nil},
|
||||
{"Raw", Node{}, "", ErrNotExist},
|
||||
{"Raw", NewAny(""), `""`, nil},
|
||||
{"Raw", NewRaw("[ ]"), "[ ]", nil},
|
||||
{"Raw", NewBool(true), "true", nil},
|
||||
{"Raw", NewNumber("-0.0"), "-0.0", nil},
|
||||
{"Raw", NewString(""), `""`, nil},
|
||||
{"Raw", NewBytes([]byte("hello, world")), `"aGVsbG8sIHdvcmxk"`, nil},
|
||||
{"Bool", Node{}, false, ErrUnsupportType},
|
||||
{"Bool", NewAny(true), true, nil},
|
||||
{"Bool", NewAny(false), false, nil},
|
||||
{"Bool", NewRaw("true"), true, nil},
|
||||
{"Bool", NewRaw("false"), false, nil},
|
||||
{"Int64", NewAny(int(0)), int64(0), nil},
|
||||
{"Int64", NewAny(int8(0)), int64(0), nil},
|
||||
{"Int64", NewAny(int16(0)), int64(0), nil},
|
||||
{"Int64", NewAny(int32(0)), int64(0), nil},
|
||||
{"Int64", NewAny(int64(0)), int64(0), nil},
|
||||
{"Int64", NewAny(uint(0)), int64(0), nil},
|
||||
{"Int64", NewAny(uint8(0)), int64(0), nil},
|
||||
{"Int64", NewAny(uint32(0)), int64(0), nil},
|
||||
{"Int64", NewAny(uint64(0)), int64(0), nil},
|
||||
{"Int64", Node{}, int64(0), ErrUnsupportType},
|
||||
{"Int64", NewRaw("0"), int64(0), nil},
|
||||
{"Float64", Node{}, float64(0), ErrUnsupportType},
|
||||
{"Float64", NewAny(float32(0)), float64(0), nil},
|
||||
{"Float64", NewAny(float64(0)), float64(0), nil},
|
||||
{"Float64", NewRaw("0.0"), float64(0.0), nil},
|
||||
{"Number", Node{}, json.Number(""), ErrUnsupportType},
|
||||
{"Number", NewAny(json.Number("0")), json.Number("0"), nil},
|
||||
{"Number", NewRaw("0.0"), json.Number("0.0"), nil},
|
||||
{"Number", NewRaw("true"), json.Number("1"), nil},
|
||||
{"Number", NewRaw("false"), json.Number("0"), nil},
|
||||
{"String", Node{}, "", ErrUnsupportType},
|
||||
{"String", NewAny(""), "", nil},
|
||||
{"String", NewRaw(`""`), ``, nil},
|
||||
{"String", NewRaw(`0.0`), "0.0", nil},
|
||||
{"String", NewRaw(`null`), "null", nil},
|
||||
{"String", NewRaw(`true`), "true", nil},
|
||||
{"String", NewRaw(`false`), "false", nil},
|
||||
{"Len", Node{}, 0, nil},
|
||||
{"Len", NewAny(0), 0, ErrUnsupportType},
|
||||
{"Len", NewNull(), 0, nil},
|
||||
{"Len", NewRaw(`"1"`), 1, nil},
|
||||
{"Len", NewRaw(`[1]`), 0, nil},
|
||||
{"Len", NewArray([]Node{NewNull()}), 1, nil},
|
||||
{"Len", lazyArray, 0, nil},
|
||||
{"Len", newRawNode(`{"a":1}`, types.V_OBJECT), 0, nil},
|
||||
{"Len", NewRaw(`{"a":1}`), 0, nil},
|
||||
{"Len", lazyObject, 0, nil},
|
||||
{"Cap", NewNull(), 0, ErrUnsupportType},
|
||||
{"Cap", newRawNode(`[1]`, types.V_ARRAY), _DEFAULT_NODE_CAP, nil},
|
||||
{"Cap", Node{}, 0, nil},
|
||||
{"Cap", NewAny(0), 0, ErrUnsupportType},
|
||||
{"Cap", NewNull(), 0, nil},
|
||||
{"Cap", NewRaw(`[1]`), _DEFAULT_NODE_CAP, nil},
|
||||
{"Cap", NewObject([]Pair{{"",NewNull()}}), 1, nil},
|
||||
{"Cap", newRawNode(`{"a":1}`, types.V_OBJECT), _DEFAULT_NODE_CAP, nil},
|
||||
{"Cap", NewRaw(`{"a":1}`), _DEFAULT_NODE_CAP, nil},
|
||||
}
|
||||
lazyArray.skipAllIndex()
|
||||
lazyObject.skipAllKey()
|
||||
|
|
@ -195,15 +278,15 @@ func TestTypeCast(t *testing.T) {
|
|||
)
|
||||
|
||||
for i, c := range cases {
|
||||
fmt.Println(c)
|
||||
fmt.Println(i, c)
|
||||
rt := reflect.ValueOf(&c.node)
|
||||
m := rt.MethodByName(c.method)
|
||||
rets := m.Call([]reflect.Value{})
|
||||
if len(rets) != 2 {
|
||||
t.Fatal(i, rets)
|
||||
}
|
||||
if rets[0].Interface() != c.exp {
|
||||
t.Fatal(i, rets[0].Interface())
|
||||
if !reflect.DeepEqual(rets[0].Interface(), c.exp) {
|
||||
t.Fatal(i, rets[0].Interface(), c.exp)
|
||||
}
|
||||
if rets[1].Interface() != c.err {
|
||||
t.Fatal(i, rets[1].Interface())
|
||||
|
|
@ -212,6 +295,11 @@ func TestTypeCast(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCheckError(t *testing.T) {
|
||||
empty := Node{}
|
||||
if !empty.Valid() || empty.Check() != nil || empty.Error() != "" {
|
||||
t.Fatal()
|
||||
}
|
||||
|
||||
n := newRawNode("[hello]", types.V_ARRAY)
|
||||
n.parseRaw(false)
|
||||
if n.Check() != nil {
|
||||
|
|
@ -680,6 +768,56 @@ func TestNodeGetByPath(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNodeSet(t *testing.T) {
|
||||
empty := Node{}
|
||||
err := empty.Add(Node{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
empty2 := empty.Index(0)
|
||||
if empty2.Check() != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exist, err := empty2.SetByIndex(1, Node{})
|
||||
if exist || err == nil {
|
||||
t.Fatal(exist, err)
|
||||
}
|
||||
empty3 := empty.Index(0)
|
||||
if empty3.Check() != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exist, err = empty3.Set("a", NewNumber("-1"))
|
||||
if exist || err != nil {
|
||||
t.Fatal(exist, err)
|
||||
}
|
||||
if n, e := empty.Index(0).Get("a").Int64(); e != nil || n != -1 {
|
||||
t.Fatal(n, e)
|
||||
}
|
||||
|
||||
empty = NewNull()
|
||||
err = empty.Add(NewNull())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
empty2 = empty.Index(0)
|
||||
if empty2.Check() != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exist, err = empty2.SetByIndex(1, NewNull())
|
||||
if exist || err == nil {
|
||||
t.Fatal(exist, err)
|
||||
}
|
||||
empty3 = empty.Index(0)
|
||||
if empty3.Check() != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exist, err = empty3.Set("a", NewNumber("-1"))
|
||||
if exist || err != nil {
|
||||
t.Fatal(exist, err)
|
||||
}
|
||||
if n, e := empty.Index(0).Get("a").Int64(); e != nil || n != -1 {
|
||||
t.Fatal(n, e)
|
||||
}
|
||||
|
||||
root, derr := NewParser(_TwitterJson).Parse()
|
||||
if derr != 0 {
|
||||
t.Fatalf("decode failed: %v", derr.Error())
|
||||
|
|
@ -709,6 +847,43 @@ func TestNodeSet(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNodeAny(t *testing.T) {
|
||||
empty := Node{}
|
||||
_,err := empty.SetAny("any", map[string]interface{}{"a": []int{0}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if m, err := empty.Get("any").Interface(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if v, ok := m.(map[string]interface{}); !ok {
|
||||
t.Fatal(v)
|
||||
}
|
||||
if buf, err := empty.MarshalJSON(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if string(buf) != `{"any":{"a":[0]}}` {
|
||||
t.Fatal(string(buf))
|
||||
}
|
||||
if _, err := empty.Set("any2", Node{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := empty.Get("any2").AddAny(nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if buf, err := empty.MarshalJSON(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if string(buf) != `{"any":{"a":[0]},"any2":[null]}` {
|
||||
t.Fatal(string(buf))
|
||||
}
|
||||
if _, err := empty.Get("any2").SetAnyByIndex(0, NewNumber("-0.0")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if buf, err := empty.MarshalJSON(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if string(buf) != `{"any":{"a":[0]},"any2":[-0.0]}` {
|
||||
t.Fatal(string(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeSetByIndex(t *testing.T) {
|
||||
root, derr := NewParser(_TwitterJson).Parse()
|
||||
if derr != 0 {
|
||||
|
|
|
|||
|
|
@ -17,15 +17,15 @@
|
|||
package ast
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`sync`
|
||||
`fmt`
|
||||
`sync`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/decoder`
|
||||
`github.com/bytedance/sonic/internal/native`
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
`github.com/bytedance/sonic/unquote`
|
||||
`github.com/bytedance/sonic/decoder`
|
||||
`github.com/bytedance/sonic/internal/native`
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
`github.com/bytedance/sonic/unquote`
|
||||
)
|
||||
|
||||
const _DEFAULT_NODE_CAP int = 16
|
||||
|
|
@ -36,11 +36,8 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
nodeNotExist = newError(_ERR_NOT_FOUND, "value not exists")
|
||||
nodeUnsupportType = newError(_ERR_UNSUPPORT_TYPE, "unsupported type")
|
||||
|
||||
ErrNotExist error = nodeNotExist
|
||||
ErrUnsupportType error = nodeUnsupportType
|
||||
ErrNotExist error = newError(_ERR_NOT_FOUND, "value not exists")
|
||||
ErrUnsupportType error = newError(_ERR_UNSUPPORT_TYPE, "unsupported type")
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
`runtime/debug`
|
||||
`sync`
|
||||
`fmt`
|
||||
`math`
|
||||
`math`
|
||||
`strconv`
|
||||
|
||||
`github.com/tidwall/sjson`
|
||||
|
|
@ -277,83 +277,83 @@ func BenchmarkGetOne_Parallel_Sonic(b *testing.B) {
|
|||
}
|
||||
|
||||
func BenchmarkSetOne_Sonic(b *testing.B) {
|
||||
node, err := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
n := NewNumber(strconv.Itoa(math.MaxInt32))
|
||||
_, err = node.Set("id", n)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(_TwitterJson)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
node, _ := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
|
||||
_, _ = node.Set("id", n)
|
||||
}
|
||||
node, err := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
n := NewNumber(strconv.Itoa(math.MaxInt32))
|
||||
_, err = node.Set("id", n)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(_TwitterJson)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
node, _ := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
|
||||
_, _ = node.Set("id", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSetOne_Sjson(b *testing.B) {
|
||||
path := fmt.Sprintf("%s.%d.%s", "statuses", 3, "id")
|
||||
_, err := sjson.Set(_TwitterJson, path, math.MaxInt32)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(_TwitterJson)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sjson.Set(_TwitterJson, path, math.MaxInt32)
|
||||
}
|
||||
path := fmt.Sprintf("%s.%d.%s", "statuses", 3, "id")
|
||||
_, err := sjson.Set(_TwitterJson, path, math.MaxInt32)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(_TwitterJson)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sjson.Set(_TwitterJson, path, math.MaxInt32)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSetOne_Jsoniter(b *testing.B) {
|
||||
data := []byte(_TwitterJson)
|
||||
node, ok := jsoniter.Get(data, "statuses", 3).GetInterface().(map[string]interface{})
|
||||
if !ok {
|
||||
b.Fatal(node)
|
||||
}
|
||||
node, ok := jsoniter.Get(data, "statuses", 3).GetInterface().(map[string]interface{})
|
||||
if !ok {
|
||||
b.Fatal(node)
|
||||
}
|
||||
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
node, _ := jsoniter.Get(data, "statuses", 3).GetInterface().(map[string]interface{})
|
||||
node["id"] = math.MaxInt32
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
node, _ := jsoniter.Get(data, "statuses", 3).GetInterface().(map[string]interface{})
|
||||
node["id"] = math.MaxInt32
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSetOne_Parallel_Sonic(b *testing.B) {
|
||||
node, err := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
n := NewNumber(strconv.Itoa(math.MaxInt32))
|
||||
_, err = node.Set("id", n)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(_TwitterJson)))
|
||||
b.ReportAllocs()
|
||||
node, err := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
n := NewNumber(strconv.Itoa(math.MaxInt32))
|
||||
_, err = node.Set("id", n)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(_TwitterJson)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
node, _ := NewSearcher(_TwitterJson).GetByPath("statuses", 3)
|
||||
_, _ = node.Set("id", n)
|
||||
_, _ = node.Set("id", n)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSetOne_Parallel_Sjson(b *testing.B) {
|
||||
path := fmt.Sprintf("%s.%d.%s", "statuses", 3, "id")
|
||||
_, err := sjson.Set(_TwitterJson, path, math.MaxInt32)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(_TwitterJson)))
|
||||
b.ReportAllocs()
|
||||
path := fmt.Sprintf("%s.%d.%s", "statuses", 3, "id")
|
||||
_, err := sjson.Set(_TwitterJson, path, math.MaxInt32)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(_TwitterJson)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
|
|
@ -364,13 +364,13 @@ func BenchmarkSetOne_Parallel_Sjson(b *testing.B) {
|
|||
|
||||
func BenchmarkSetOne_Parallel_Jsoniter(b *testing.B) {
|
||||
data := []byte(_TwitterJson)
|
||||
node, ok := jsoniter.Get(data, "statuses", 3).GetInterface().(map[string]interface{})
|
||||
if !ok {
|
||||
b.Fatal(node)
|
||||
}
|
||||
node, ok := jsoniter.Get(data, "statuses", 3).GetInterface().(map[string]interface{})
|
||||
if !ok {
|
||||
b.Fatal(node)
|
||||
}
|
||||
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ func mem2ptr(s []byte) unsafe.Pointer {
|
|||
//go:nosplit
|
||||
func ptr2slice(s unsafe.Pointer, l int, c int) unsafe.Pointer {
|
||||
slice := &rt.GoSlice{
|
||||
Ptr: s,
|
||||
Len: l,
|
||||
Cap: c,
|
||||
Ptr: s,
|
||||
Len: l,
|
||||
Cap: c,
|
||||
}
|
||||
return unsafe.Pointer(slice)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue