用户登录和接口鉴权

This commit is contained in:
2025-09-07 21:13:15 +08:00
parent c4522b974b
commit 565cf3fa6a
380 changed files with 18330 additions and 16854 deletions

View File

@@ -1,4 +1,4 @@
// +build !amd64,!arm64 go1.26 !go1.17 arm64,!go1.20
// +build !amd64,!arm64 go1.23 !go1.16 arm64,!go1.20
/*
* Copyright 2022 ByteDance Inc.
@@ -23,17 +23,28 @@ import (
`unicode/utf8`
`github.com/bytedance/sonic/internal/native/types`
`github.com/bytedance/sonic/internal/compat`
`github.com/bytedance/sonic/internal/rt`
)
func init() {
compat.Warn("sonic/ast")
println("WARNING:(ast) sonic only supports Go1.16~1.22, but your environment is not suitable")
}
func quote(buf *[]byte, val string) {
quoteString(buf, val)
}
// unquote unescapes a internal JSON string (it doesn't count quotas at the begining and end)
func unquote(src string) (string, types.ParsingError) {
sp := rt.IndexChar(src, -1)
out, ok := unquoteBytes(rt.BytesFrom(sp, len(src)+2, len(src)+2))
if !ok {
return "", types.ERR_INVALID_ESCAPE
}
return rt.Mem2Str(out), 0
}
func (self *Parser) decodeValue() (val types.JsonState) {
e, v := decodeValue(self.s, self.p, self.dbuf == nil)
if e < 0 {

View File

@@ -17,35 +17,36 @@
package ast
import (
"encoding/base64"
"runtime"
"strconv"
"unsafe"
`encoding/base64`
`runtime`
`strconv`
`unsafe`
"github.com/bytedance/sonic/internal/native/types"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/internal/utils"
"github.com/bytedance/sonic/unquote"
`github.com/bytedance/sonic/internal/native/types`
`github.com/bytedance/sonic/internal/rt`
)
var bytesNull = []byte("null")
const _blankCharsMask = (1 << ' ') | (1 << '\t') | (1 << '\r') | (1 << '\n')
const (
strNull = "null"
bytesNull = "null"
bytesTrue = "true"
bytesFalse = "false"
bytesObject = "{}"
bytesArray = "[]"
)
func isSpace(c byte) bool {
return (int(1<<c) & _blankCharsMask) != 0
}
//go:nocheckptr
func skipBlank(src string, pos int) int {
se := uintptr(rt.IndexChar(src, len(src)))
sp := uintptr(rt.IndexChar(src, pos))
for sp < se {
if !utils.IsSpace(*(*byte)(unsafe.Pointer(sp))) {
if !isSpace(*(*byte)(unsafe.Pointer(sp))) {
break
}
sp += 1
@@ -62,7 +63,7 @@ func decodeNull(src string, pos int) (ret int) {
if ret > len(src) {
return -int(types.ERR_EOF)
}
if src[pos:ret] == strNull {
if src[pos:ret] == bytesNull {
return ret
} else {
return -int(types.ERR_INVALID_CHAR)
@@ -102,13 +103,13 @@ func decodeString(src string, pos int) (ret int, v string) {
return ret, v
}
result, err := unquote.String(src[pos:ret])
if err != 0 {
vv, ok := unquoteBytes(rt.Str2Mem(src[pos:ret]))
if !ok {
return -int(types.ERR_INVALID_CHAR), ""
}
runtime.KeepAlive(src)
return ret, result
return ret, rt.Mem2Str(vv)
}
func decodeBinary(src string, pos int) (ret int, v []byte) {
@@ -286,7 +287,67 @@ func decodeValue(src string, pos int, skipnum bool) (ret int, v types.JsonState)
//go:nocheckptr
func skipNumber(src string, pos int) (ret int) {
return utils.SkipNumber(src, pos)
sp := uintptr(rt.IndexChar(src, pos))
se := uintptr(rt.IndexChar(src, len(src)))
if uintptr(sp) >= se {
return -int(types.ERR_EOF)
}
if c := *(*byte)(unsafe.Pointer(sp)); c == '-' {
sp += 1
}
ss := sp
var pointer bool
var exponent bool
var lastIsDigit bool
var nextNeedDigit = true
for ; sp < se; sp += uintptr(1) {
c := *(*byte)(unsafe.Pointer(sp))
if isDigit(c) {
lastIsDigit = true
nextNeedDigit = false
continue
} else if nextNeedDigit {
return -int(types.ERR_INVALID_CHAR)
} else if c == '.' {
if !lastIsDigit || pointer || exponent || sp == ss {
return -int(types.ERR_INVALID_CHAR)
}
pointer = true
lastIsDigit = false
nextNeedDigit = true
continue
} else if c == 'e' || c == 'E' {
if !lastIsDigit || exponent {
return -int(types.ERR_INVALID_CHAR)
}
if sp == se-1 {
return -int(types.ERR_EOF)
}
exponent = true
lastIsDigit = false
nextNeedDigit = false
continue
} else if c == '-' || c == '+' {
if prev := *(*byte)(unsafe.Pointer(sp - 1)); prev != 'e' && prev != 'E' {
return -int(types.ERR_INVALID_CHAR)
}
lastIsDigit = false
nextNeedDigit = true
continue
} else {
break
}
}
if nextNeedDigit {
return -int(types.ERR_EOF)
}
runtime.KeepAlive(src)
return int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
}
//go:nocheckptr
@@ -544,7 +605,7 @@ func _DecodeString(src string, pos int, needEsc bool, validStr bool) (v string,
return str, p.p, true
}
/* unquote the string */
out, err := unquote.String(str)
out, err := unquote(str)
/* check for errors */
if err != 0 {
return "", -int(err), true

View File

@@ -17,12 +17,12 @@
package ast
import (
"sync"
"unicode/utf8"
`sync`
`unicode/utf8`
)
"github.com/bytedance/gopkg/lang/dirtmake"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/option"
const (
_MaxBuffer = 1024 // 1KB buffer size
)
func quoteString(e *[]byte, s string) {
@@ -30,7 +30,7 @@ func quoteString(e *[]byte, s string) {
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
if rt.SafeSet[b] {
if safeSet[b] {
i++
continue
}
@@ -54,8 +54,8 @@ func quoteString(e *[]byte, s string) {
// user-controlled strings are rendered into JSON
// and served to some browsers.
*e = append(*e, `u00`...)
*e = append(*e, rt.Hex[b>>4])
*e = append(*e, rt.Hex[b&0xF])
*e = append(*e, hex[b>>4])
*e = append(*e, hex[b&0xF])
}
i++
start = i
@@ -76,7 +76,7 @@ func quoteString(e *[]byte, s string) {
*e = append(*e, s[start:i]...)
}
*e = append(*e, `\u202`...)
*e = append(*e, rt.Hex[c&0xF])
*e = append(*e, hex[c&0xF])
i += size
start = i
continue
@@ -92,29 +92,16 @@ func quoteString(e *[]byte, s string) {
var bytesPool = sync.Pool{}
func (self *Node) MarshalJSON() ([]byte, error) {
if self == nil {
return bytesNull, nil
}
// fast path for raw node
if self.isRaw() {
return rt.Str2Mem(self.toString()), nil
}
buf := newBuffer()
err := self.encode(buf)
if err != nil {
freeBuffer(buf)
return nil, err
}
var ret []byte
if !rt.CanSizeResue(cap(*buf)) {
ret = *buf
} else {
ret = dirtmake.Bytes(len(*buf), len(*buf))
copy(ret, *buf)
freeBuffer(buf)
}
ret := make([]byte, len(*buf))
copy(ret, *buf)
freeBuffer(buf)
return ret, err
}
@@ -122,24 +109,21 @@ func newBuffer() *[]byte {
if ret := bytesPool.Get(); ret != nil {
return ret.(*[]byte)
} else {
buf := make([]byte, 0, option.DefaultAstBufferSize)
buf := make([]byte, 0, _MaxBuffer)
return &buf
}
}
func freeBuffer(buf *[]byte) {
if !rt.CanSizeResue(cap(*buf)) {
return
}
*buf = (*buf)[:0]
bytesPool.Put(buf)
}
func (self *Node) encode(buf *[]byte) error {
if self.isRaw() {
if self.IsRaw() {
return self.encodeRaw(buf)
}
switch int(self.itype()) {
switch self.Type() {
case V_NONE : return ErrNotExist
case V_ERROR : return self.Check()
case V_NULL : return self.encodeNull(buf)
@@ -155,21 +139,16 @@ func (self *Node) encode(buf *[]byte) error {
}
func (self *Node) encodeRaw(buf *[]byte) error {
lock := self.rlock()
if !self.isRaw() {
self.runlock()
return self.encode(buf)
}
raw := self.toString()
if lock {
self.runlock()
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, strNull...)
*buf = append(*buf, bytesNull...)
return nil
}

View File

@@ -17,10 +17,6 @@ func newError(err types.ParsingError, msg string) *Node {
}
}
func newErrorPair(err SyntaxError) *Pair {
return &Pair{0, "", *newSyntaxError(err)}
}
// Error returns error message if the node is invalid
func (self Node) Error() string {
if self.t != V_ERROR {
@@ -83,7 +79,7 @@ func (self SyntaxError) description() string {
/* check for empty source */
if self.Src == "" {
return fmt.Sprintf("no sources available, the input json is empty: %#v", self)
return fmt.Sprintf("no sources available: %#v", self)
}
/* prevent slicing before the beginning */

View File

@@ -17,29 +17,19 @@
package ast
import (
"fmt"
`fmt`
"github.com/bytedance/sonic/internal/caching"
"github.com/bytedance/sonic/internal/native/types"
`github.com/bytedance/sonic/internal/native/types`
)
type Pair struct {
hash uint64
Key string
Value Node
}
func NewPair(key string, val Node) Pair {
return Pair{
hash: caching.StrHash(key),
Key: key,
Value: val,
}
}
// Values returns iterator for array's children traversal
func (self *Node) Values() (ListIterator, error) {
if err := self.should(types.V_ARRAY); err != nil {
if err := self.should(types.V_ARRAY, "an array"); err != nil {
return ListIterator{}, err
}
return self.values(), nil
@@ -51,7 +41,7 @@ func (self *Node) values() ListIterator {
// Properties returns iterator for object's children traversal
func (self *Node) Properties() (ObjectIterator, error) {
if err := self.should(types.V_OBJECT); err != nil {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
return ObjectIterator{}, err
}
return self.properties(), nil
@@ -173,14 +163,11 @@ type Scanner func(path Sequence, node *Node) bool
// ForEach scans one V_OBJECT node's children from JSON head to tail,
// and pass the Sequence and Node of corresponding JSON value.
//
// Especially, if the node is not V_ARRAY or V_OBJECT,
// Especailly, if the node is not V_ARRAY or V_OBJECT,
// the node itself will be returned and Sequence.Index == -1.
//
// NOTICE: An unset node WON'T trigger sc, but its index still counts into Path.Index
// NOTICE: A unsetted node WON'T trigger sc, but its index still counts into Path.Index
func (self *Node) ForEach(sc Scanner) error {
if err := self.checkRaw(); err != nil {
return err
}
switch self.itype() {
case types.V_ARRAY:
iter, err := self.Values()

View File

@@ -17,15 +17,13 @@
package ast
import (
"encoding/json"
"fmt"
"strconv"
"sync"
"sync/atomic"
"unsafe"
"github.com/bytedance/sonic/internal/native/types"
"github.com/bytedance/sonic/internal/rt"
`encoding/json`
`fmt`
`strconv`
`unsafe`
`github.com/bytedance/sonic/internal/native/types`
`github.com/bytedance/sonic/internal/rt`
)
const (
@@ -38,7 +36,7 @@ const (
_V_ARRAY_LAZY = _V_LAZY | types.V_ARRAY
_V_OBJECT_LAZY = _V_LAZY | types.V_OBJECT
_MASK_LAZY = _V_LAZY - 1
_MASK_RAW = _V_RAW - 1
_MASK_RAW = _V_RAW - 1
)
const (
@@ -58,20 +56,19 @@ type Node struct {
t types.ValueType
l uint
p unsafe.Pointer
m *sync.RWMutex
}
// 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 = newRawNode(rt.Mem2Str(data), switchRawType(data[0]), false)
return nil
*self = NewRaw(string(data))
return self.Check()
}
/** Node Type Accessor **/
// Type returns json type represented by the node
// It will be one of bellows:
// It will be one of belows:
// V_NONE = 0 (empty node, key not exists)
// V_ERROR = 1 (error node)
// V_NULL = 2 (json value `null`, key exists)
@@ -82,39 +79,17 @@ func (self *Node) UnmarshalJSON(data []byte) (err error) {
// V_STRING = 7 (json value string)
// V_NUMBER = 33 (json value number )
// V_ANY = 34 (golang interface{})
//
// Deprecated: not concurrent safe. Use TypeSafe instead
func (self Node) Type() int {
return int(self.t & _MASK_LAZY & _MASK_RAW)
}
// Type concurrently-safe returns json type represented by the node
// It will be one of bellows:
// V_NONE = 0 (empty node, key not exists)
// V_ERROR = 1 (error node)
// V_NULL = 2 (json value `null`, key exists)
// 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) TypeSafe() int {
return int(self.loadt() & _MASK_LAZY & _MASK_RAW)
}
func (self *Node) itype() types.ValueType {
func (self Node) itype() types.ValueType {
return self.t & _MASK_LAZY & _MASK_RAW
}
// Exists returns false only if the self is nil or empty node V_NONE
func (self *Node) Exists() bool {
if self == nil {
return false
}
t := self.loadt()
return t != V_ERROR && t != _V_NONE
return self.Valid() && self.t != _V_NONE
}
// Valid reports if self is NOT V_ERROR or nil
@@ -122,7 +97,7 @@ func (self *Node) Valid() bool {
if self == nil {
return false
}
return self.loadt() != V_ERROR
return self.t != V_ERROR
}
// Check checks if the node itself is valid, and return:
@@ -131,31 +106,24 @@ func (self *Node) Valid() bool {
func (self *Node) Check() error {
if self == nil {
return ErrNotExist
} else if self.loadt() != V_ERROR {
} else if self.t != V_ERROR {
return nil
} else {
return self
}
}
// isRaw returns true if node's underlying value is raw json
//
// Deprecated: not concurrent safe
func (self Node) IsRaw() bool {
return self.t & _V_RAW != 0
}
// IsRaw returns true if node's underlying value is raw json
func (self *Node) isRaw() bool {
return self.loadt() & _V_RAW != 0
func (self Node) IsRaw() bool {
return self.t&_V_RAW != 0
}
func (self *Node) isLazy() bool {
return self != nil && self.t & _V_LAZY != 0
return self != nil && self.t&_V_LAZY != 0
}
func (self *Node) isAny() bool {
return self != nil && self.loadt() == _V_ANY
return self != nil && self.t == _V_ANY
}
/** Simple Value Methods **/
@@ -165,26 +133,18 @@ func (self *Node) Raw() (string, error) {
if self == nil {
return "", ErrNotExist
}
lock := self.rlock()
if !self.isRaw() {
if lock {
self.runlock()
}
if !self.IsRaw() {
buf, err := self.MarshalJSON()
return rt.Mem2Str(buf), err
}
ret := self.toString()
if lock {
self.runlock()
}
return ret, nil
return self.toString(), nil
}
func (self *Node) checkRaw() error {
if err := self.Check(); err != nil {
return err
}
if self.isRaw() {
if self.IsRaw() {
self.parseRaw(false)
}
return self.Check()
@@ -440,7 +400,7 @@ func (self *Node) String() (string, error) {
}
}
// StrictString returns string value (unescaped), including V_STRING, V_ANY of string.
// StrictString returns string value (unescaped), includeing V_STRING, V_ANY of string.
// In other cases, it will return empty string.
func (self *Node) StrictString() (string, error) {
if err := self.checkRaw(); err != nil {
@@ -509,24 +469,7 @@ func (self *Node) Float64() (float64, error) {
}
}
func (self *Node) StrictBool() (bool, error) {
if err := self.checkRaw(); err!= nil {
return false, err
}
switch self.t {
case types.V_TRUE : return true, nil
case types.V_FALSE : return false, nil
case _V_ANY :
any := self.packAny()
switch v := any.(type) {
case bool : return v, nil
default : return false, ErrUnsupportType
}
default : return false, ErrUnsupportType
}
}
// Float64 exports underlying float64 value, including V_NUMBER, V_ANY
// Float64 exports underlying float64 value, includeing V_NUMBER, V_ANY
func (self *Node) StrictFloat64() (float64, error) {
if err := self.checkRaw(); err != nil {
return 0.0, err
@@ -544,7 +487,7 @@ func (self *Node) StrictFloat64() (float64, error) {
}
}
/** Sequential Value Methods **/
/** Sequencial Value Methods **/
// Len returns children count of a array|object|string node
// WARN: For partially loaded node, it also works but only counts the parsed children
@@ -561,7 +504,7 @@ func (self *Node) Len() (int, error) {
}
}
func (self *Node) len() int {
func (self Node) len() int {
return int(self.l)
}
@@ -584,7 +527,7 @@ func (self *Node) Cap() (int, error) {
//
// 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 err := self.checkRaw(); err != nil {
if err := self.Check(); err != nil {
return false, err
}
if err := node.Check(); err != nil {
@@ -592,7 +535,7 @@ func (self *Node) Set(key string, node Node) (bool, error) {
}
if self.t == _V_NONE || self.t == types.V_NULL {
*self = NewObject([]Pair{NewPair(key, node)})
*self = NewObject([]Pair{{key, node}})
return false, nil
} else if self.itype() != types.V_OBJECT {
return false, ErrUnsupportType
@@ -606,7 +549,7 @@ func (self *Node) Set(key string, node Node) (bool, error) {
*self = newObject(new(linkedPairs))
}
s := (*linkedPairs)(self.p)
s.Push(NewPair(key, node))
s.Push(Pair{key, node})
self.l++
return false, nil
@@ -625,10 +568,10 @@ func (self *Node) SetAny(key string, val interface{}) (bool, error) {
// Unset REMOVE (soft) the node of given key under object parent, and reports if the key has existed.
func (self *Node) Unset(key string) (bool, error) {
if err := self.should(types.V_OBJECT); err != nil {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
return false, err
}
// NOTICE: must get accurate length before deduct
// NOTICE: must get acurate length before deduct
if err := self.skipAllKey(); err != nil {
return false, err
}
@@ -646,7 +589,7 @@ func (self *Node) Unset(key string) (bool, error) {
//
// The index must be within self's children.
func (self *Node) SetByIndex(index int, node Node) (bool, error) {
if err := self.checkRaw(); err != nil {
if err := self.Check(); err != nil {
return false, err
}
if err := node.Check(); err != nil {
@@ -674,7 +617,7 @@ func (self *Node) SetAnyByIndex(index int, val interface{}) (bool, error) {
return self.SetByIndex(index, NewAny(val))
}
// UnsetByIndex REMOVE (softly) the node of given index.
// UnsetByIndex REOMVE (softly) the node of given index.
//
// WARN: this will change address of elements, which is a dangerous action.
// Use Unset() for object or Pop() for array instead.
@@ -726,7 +669,7 @@ func (self *Node) UnsetByIndex(index int) (bool, error) {
//
// 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 err := self.checkRaw(); err != nil {
if err := self.Check(); err != nil {
return err
}
@@ -734,7 +677,7 @@ func (self *Node) Add(node Node) error {
*self = NewArray([]Node{node})
return nil
}
if err := self.should(types.V_ARRAY); err != nil {
if err := self.should(types.V_ARRAY, "an array"); err != nil {
return err
}
@@ -793,11 +736,11 @@ func (self *Node) Pop() error {
}
// Move moves the child at src index to dst index,
// meanwhile slides siblings from src+1 to dst.
// meanwhile slides sliblings from src+1 to dst.
//
// WARN: this will change address of elements, which is a dangerous action.
func (self *Node) Move(dst, src int) error {
if err := self.should(types.V_ARRAY); err != nil {
if err := self.should(types.V_ARRAY, "an array"); err != nil {
return err
}
@@ -833,7 +776,7 @@ func (self *Node) Move(dst, src int) error {
return nil
}
// AddAny wraps val with V_ANY node, and Add() the node.
// SetAny wraps val with V_ANY node, and Add() the node.
func (self *Node) AddAny(val interface{}) error {
return self.Add(NewAny(val))
}
@@ -869,7 +812,7 @@ func (self *Node) GetByPath(path ...interface{}) *Node {
// Get loads given key of an object node on demands
func (self *Node) Get(key string) *Node {
if err := self.should(types.V_OBJECT); err != nil {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
return unwrapError(err)
}
n, _ := self.skipKey(key)
@@ -902,14 +845,14 @@ func (self *Node) Index(idx int) *Node {
// IndexPair indexies pair at given idx,
// node type MUST be either V_OBJECT
func (self *Node) IndexPair(idx int) *Pair {
if err := self.should(types.V_OBJECT); err != nil {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
return nil
}
return self.skipIndexPair(idx)
}
func (self *Node) indexOrGet(idx int, key string) (*Node, int) {
if err := self.should(types.V_OBJECT); err != nil {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
return unwrapError(err), idx
}
@@ -946,16 +889,16 @@ func (self *Node) Map() (map[string]interface{}, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_OBJECT); err != nil {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
return nil, err
}
if err := self.loadAllKey(false); err != nil {
if err := self.loadAllKey(); err != nil {
return nil, err
}
return self.toGenericObject()
}
// MapUseNumber loads all keys of an object node, with numeric nodes cast to json.Number
// 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()
@@ -965,16 +908,16 @@ func (self *Node) MapUseNumber() (map[string]interface{}, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_OBJECT); err != nil {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
return nil, err
}
if err := self.loadAllKey(false); err != nil {
if err := self.loadAllKey(); err != nil {
return nil, err
}
return self.toGenericObjectUseNumber()
}
// MapUseNode scans both parsed and non-parsed children nodes,
// 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() {
@@ -985,7 +928,7 @@ func (self *Node) MapUseNode() (map[string]Node, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_OBJECT); err != nil {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
return nil, err
}
if err := self.skipAllKey(); err != nil {
@@ -1091,16 +1034,16 @@ func (self *Node) Array() ([]interface{}, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_ARRAY); err != nil {
if err := self.should(types.V_ARRAY, "an array"); err != nil {
return nil, err
}
if err := self.loadAllIndex(false); err != nil {
if err := self.loadAllIndex(); err != nil {
return nil, err
}
return self.toGenericArray()
}
// ArrayUseNumber loads all indexes of an array node, with numeric nodes cast to json.Number
// 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()
@@ -1110,16 +1053,16 @@ func (self *Node) ArrayUseNumber() ([]interface{}, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_ARRAY); err != nil {
if err := self.should(types.V_ARRAY, "an array"); err != nil {
return nil, err
}
if err := self.loadAllIndex(false); err != nil {
if err := self.loadAllIndex(); err != nil {
return nil, err
}
return self.toGenericArrayUseNumber()
}
// ArrayUseNode copies both parsed and non-parsed children nodes,
// ArrayUseNode copys both parsed and non-parsed chidren nodes,
// and indexes them by original order
func (self *Node) ArrayUseNode() ([]Node, error) {
if self.isAny() {
@@ -1130,7 +1073,7 @@ func (self *Node) ArrayUseNode() ([]Node, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_ARRAY); err != nil {
if err := self.should(types.V_ARRAY, "an array"); err != nil {
return nil, err
}
if err := self.skipAllIndex(); err != nil {
@@ -1164,9 +1107,9 @@ func (self *Node) unsafeArray() (*linkedNodes, error) {
return (*linkedNodes)(self.p), nil
}
// Interface loads all children under all paths from this node,
// Interface loads all children under all pathes from this node,
// and converts itself as generic type.
// WARN: all numeric nodes are cast to float64
// WARN: all numberic nodes are casted to float64
func (self *Node) Interface() (interface{}, error) {
if err := self.checkRaw(); err != nil {
return nil, err
@@ -1186,12 +1129,12 @@ func (self *Node) Interface() (interface{}, error) {
}
return v, nil
case _V_ARRAY_LAZY :
if err := self.loadAllIndex(false); err != nil {
if err := self.loadAllIndex(); err != nil {
return nil, err
}
return self.toGenericArray()
case _V_OBJECT_LAZY :
if err := self.loadAllKey(false); err != nil {
if err := self.loadAllKey(); err != nil {
return nil, err
}
return self.toGenericObject()
@@ -1210,7 +1153,7 @@ func (self *Node) packAny() interface{} {
}
// InterfaceUseNumber works same with Interface()
// except numeric nodes are cast to json.Number
// except numberic nodes are casted to json.Number
func (self *Node) InterfaceUseNumber() (interface{}, error) {
if err := self.checkRaw(); err != nil {
return nil, err
@@ -1225,12 +1168,12 @@ func (self *Node) InterfaceUseNumber() (interface{}, error) {
case types.V_STRING : return self.toString(), nil
case _V_NUMBER : return self.toNumber(), nil
case _V_ARRAY_LAZY :
if err := self.loadAllIndex(false); err != nil {
if err := self.loadAllIndex(); err != nil {
return nil, err
}
return self.toGenericArrayUseNumber()
case _V_OBJECT_LAZY :
if err := self.loadAllKey(false); err != nil {
if err := self.loadAllKey(); err != nil {
return nil, err
}
return self.toGenericObjectUseNumber()
@@ -1262,30 +1205,70 @@ func (self *Node) InterfaceUseNode() (interface{}, error) {
}
}
// LoadAll loads the node's children
// and ensure all its children can be READ concurrently (include its children's children)
// 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 {
return self.Load()
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)
if n.IsRaw() {
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)
if n.Value.IsRaw() {
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.
// and ensure all its children can be READ concurrently (include its children's children)
// After calling it, only the node itself can be used on concurrency (not include its children)
func (self *Node) Load() error {
if err := self.checkRaw(); err != nil {
return err
}
switch self.t {
case _V_ARRAY_LAZY: self.loadAllIndex(true)
case _V_OBJECT_LAZY: self.loadAllKey(true)
case V_ERROR: return self
case V_NONE: return nil
case _V_ARRAY_LAZY:
return self.skipAllIndex()
case _V_OBJECT_LAZY:
return self.skipAllKey()
default:
return self.Check()
}
if self.m == nil {
self.m = new(sync.RWMutex)
}
return self.checkRaw()
}
/**---------------------------------- Internal Helper Methods ----------------------------------**/
func (self *Node) should(t types.ValueType) error {
func (self *Node) should(t types.ValueType, s string) error {
if err := self.checkRaw(); err != nil {
return err
}
@@ -1456,17 +1439,13 @@ func (self *Node) skipIndexPair(index int) *Pair {
return nil
}
func (self *Node) loadAllIndex(loadOnce bool) error {
func (self *Node) loadAllIndex() error {
if !self.isLazy() {
return nil
}
var err types.ParsingError
parser, stack := self.getParserAndArrayStack()
if !loadOnce {
parser.noLazy = true
} else {
parser.loadOnce = true
}
parser.noLazy = true
*self, err = parser.decodeArray(&stack.v)
if err != 0 {
return parser.ExportError(err)
@@ -1474,19 +1453,14 @@ func (self *Node) loadAllIndex(loadOnce bool) error {
return nil
}
func (self *Node) loadAllKey(loadOnce bool) error {
func (self *Node) loadAllKey() error {
if !self.isLazy() {
return nil
}
var err types.ParsingError
parser, stack := self.getParserAndObjectStack()
if !loadOnce {
parser.noLazy = true
*self, err = parser.decodeObject(&stack.v)
} else {
parser.loadOnce = true
*self, err = parser.decodeObject(&stack.v)
}
parser.noLazy = true
*self, err = parser.decodeObject(&stack.v)
if err != 0 {
return parser.ExportError(err)
}
@@ -1655,23 +1629,7 @@ func NewRaw(json string) Node {
if it == _V_NONE {
return Node{}
}
return newRawNode(parser.s[start:parser.p], it, false)
}
// NewRawConcurrentRead creates a node of raw json, which can be READ
// (GetByPath/Get/Index/GetOrIndex/Int64/Bool/Float64/String/Number/Interface/Array/Map/Raw/MarshalJSON) concurrently.
// If the input json is invalid, NewRaw returns a error Node.
func NewRawConcurrentRead(json string) Node {
parser := NewParserObj(json)
start, err := parser.skip()
if err != 0 {
return *newError(err, err.Message())
}
it := switchRawType(parser.s[start])
if it == _V_NONE {
return Node{}
}
return newRawNode(parser.s[start:parser.p], it, true)
return newRawNode(parser.s[start:parser.p], it)
}
// NewAny creates a node of type V_ANY if any's type isn't Node or *Node,
@@ -1695,7 +1653,7 @@ func NewBytes(src []byte) Node {
if len(src) == 0 {
panic("empty src bytes")
}
out := rt.EncodeBase64ToString(src)
out := encodeBase64(src)
return NewString(out)
}
@@ -1731,15 +1689,15 @@ func NewNumber(v string) Node {
}
}
func (node *Node) toNumber() json.Number {
func (node Node) toNumber() json.Number {
return json.Number(rt.StrFrom(node.p, int64(node.l)))
}
func (self *Node) toString() string {
func (self Node) toString() string {
return rt.StrFrom(self.p, int64(self.l))
}
func (node *Node) toFloat64() (float64, error) {
func (node Node) toFloat64() (float64, error) {
ret, err := node.toNumber().Float64()
if err != nil {
return 0, err
@@ -1747,7 +1705,7 @@ func (node *Node) toFloat64() (float64, error) {
return ret, nil
}
func (node *Node) toInt64() (int64, error) {
func (node Node) toInt64() (int64, error) {
ret,err := node.toNumber().Int64()
if err != nil {
return 0, err
@@ -1783,8 +1741,6 @@ func NewArray(v []Node) Node {
return newArray(s)
}
const _Threshold_Index = 16
func newArray(v *linkedNodes) Node {
return Node{
t: types.V_ARRAY,
@@ -1808,9 +1764,6 @@ func NewObject(v []Pair) Node {
}
func newObject(v *linkedPairs) Node {
if v.size > _Threshold_Index {
v.BuildIndex()
}
return Node{
t: types.V_OBJECT,
l: uint(v.Len()),
@@ -1819,42 +1772,53 @@ func newObject(v *linkedPairs) Node {
}
func (self *Node) setObject(v *linkedPairs) {
if v.size > _Threshold_Index {
v.BuildIndex()
}
self.t = types.V_OBJECT
self.l = uint(v.Len())
self.p = unsafe.Pointer(v)
}
func (self *Node) parseRaw(full bool) {
lock := self.lock()
defer self.unlock()
if !self.isRaw() {
return
func newRawNode(str string, typ types.ValueType) Node {
return Node{
t: _V_RAW | typ,
p: rt.StrPtr(str),
l: uint(len(str)),
}
}
func (self *Node) parseRaw(full bool) {
raw := self.toString()
parser := NewParserObj(raw)
var e types.ParsingError
if full {
parser.noLazy = true
*self, e = parser.Parse()
} else if lock {
var n Node
parser.noLazy = true
parser.loadOnce = true
n, e = parser.Parse()
self.assign(n)
} else {
*self, e = parser.Parse()
parser.skipValue = false
}
var e types.ParsingError
*self, e = parser.Parse()
if e != 0 {
*self = *newSyntaxError(parser.syntaxError(e))
}
}
func (self *Node) assign(n Node) {
self.l = n.l
self.p = n.p
atomic.StoreInt64(&self.t, n.t)
var typeJumpTable = [256]types.ValueType{
'"' : types.V_STRING,
'-' : _V_NUMBER,
'0' : _V_NUMBER,
'1' : _V_NUMBER,
'2' : _V_NUMBER,
'3' : _V_NUMBER,
'4' : _V_NUMBER,
'5' : _V_NUMBER,
'6' : _V_NUMBER,
'7' : _V_NUMBER,
'8' : _V_NUMBER,
'9' : _V_NUMBER,
'[' : types.V_ARRAY,
'f' : types.V_FALSE,
'n' : types.V_NULL,
't' : types.V_TRUE,
'{' : types.V_OBJECT,
}
func switchRawType(c byte) types.ValueType {
return typeJumpTable[c]
}

View File

@@ -17,18 +17,14 @@
package ast
import (
"fmt"
"sync"
"sync/atomic"
`fmt`
"github.com/bytedance/sonic/internal/native/types"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/internal/utils"
"github.com/bytedance/sonic/unquote"
`github.com/bytedance/sonic/internal/native/types`
`github.com/bytedance/sonic/internal/rt`
)
const (
_DEFAULT_NODE_CAP int = 16
_DEFAULT_NODE_CAP int = 8
_APPEND_GROW_SHIFT = 1
)
@@ -49,7 +45,6 @@ type Parser struct {
p int
s string
noLazy bool
loadOnce bool
skipValue bool
dbuf *byte
}
@@ -65,7 +60,7 @@ func (self *Parser) delim() types.ParsingError {
return types.ERR_EOF
}
/* check for the delimiter */
/* check for the delimtier */
if self.s[p] != ':' {
return types.ERR_INVALID_CHAR
}
@@ -84,7 +79,7 @@ func (self *Parser) object() types.ParsingError {
return types.ERR_EOF
}
/* check for the delimiter */
/* check for the delimtier */
if self.s[p] != '{' {
return types.ERR_INVALID_CHAR
}
@@ -103,7 +98,7 @@ func (self *Parser) array() types.ParsingError {
return types.ERR_EOF
}
/* check for the delimiter */
/* check for the delimtier */
if self.s[p] != '[' {
return types.ERR_INVALID_CHAR
}
@@ -115,15 +110,11 @@ func (self *Parser) array() types.ParsingError {
func (self *Parser) lspace(sp int) int {
ns := len(self.s)
for ; sp<ns && utils.IsSpace(self.s[sp]); sp+=1 {}
for ; sp<ns && isSpace(self.s[sp]); sp+=1 {}
return sp
}
func (self *Parser) backward() {
for ; self.p >= 0 && utils.IsSpace(self.s[self.p]); self.p-=1 {}
}
func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) {
sp := self.p
ns := len(self.s)
@@ -157,7 +148,7 @@ func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) {
if t == _V_NONE {
return Node{}, types.ERR_INVALID_CHAR
}
val = newRawNode(self.s[start:self.p], t, false)
val = newRawNode(self.s[start:self.p], t)
}else{
/* decode the value */
if val, err = self.Parse(); err != 0 {
@@ -219,7 +210,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
/* check for escape sequence */
if njs.Ep != -1 {
if key, err = unquote.String(key); err != 0 {
if key, err = unquote(key); err != 0 {
return Node{}, err
}
}
@@ -243,7 +234,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
if t == _V_NONE {
return Node{}, types.ERR_INVALID_CHAR
}
val = newRawNode(self.s[start:self.p], t, false)
val = newRawNode(self.s[start:self.p], t)
} else {
/* decode the value */
if val, err = self.Parse(); err != 0 {
@@ -253,7 +244,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
/* add the value to result */
// FIXME: ret's address may change here, thus previous referred node in ret may be invalid !!
ret.Push(NewPair(key, val))
ret.Push(Pair{Key: key, Value: val})
self.p = self.lspace(self.p)
/* check for EOF */
@@ -284,7 +275,7 @@ func (self *Parser) decodeString(iv int64, ep int) (Node, types.ParsingError) {
}
/* unquote the string */
out, err := unquote.String(s)
out, err := unquote(s)
/* check for errors */
if err != 0 {
@@ -300,10 +291,6 @@ func (self *Parser) Pos() int {
return self.p
}
// Parse returns a ast.Node representing the parser's JSON.
// NOTICE: the specific parsing lazy dependens parser's option
// It only parse first layer and first child for Object or Array be default
func (self *Parser) Parse() (Node, types.ParsingError) {
switch val := self.decodeValue(); val.Vt {
case types.V_EOF : return Node{}, types.ERR_EOF
@@ -312,48 +299,22 @@ func (self *Parser) Parse() (Node, types.ParsingError) {
case types.V_FALSE : return falseNode, 0
case types.V_STRING : return self.decodeString(val.Iv, val.Ep)
case types.V_ARRAY:
s := self.p - 1;
if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == ']' {
self.p = p + 1
return Node{t: types.V_ARRAY}, 0
}
if self.noLazy {
if self.loadOnce {
self.noLazy = false
}
return self.decodeArray(new(linkedNodes))
}
// NOTICE: loadOnce always keep raw json for object or array
if self.loadOnce {
self.p = s
s, e := self.skipFast()
if e != 0 {
return Node{}, e
}
return newRawNode(self.s[s:self.p], types.V_ARRAY, true), 0
}
return newLazyArray(self), 0
case types.V_OBJECT:
s := self.p - 1;
if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == '}' {
self.p = p + 1
return Node{t: types.V_OBJECT}, 0
}
// NOTICE: loadOnce always keep raw json for object or array
if self.noLazy {
if self.loadOnce {
self.noLazy = false
}
return self.decodeObject(new(linkedPairs))
}
if self.loadOnce {
self.p = s
s, e := self.skipFast()
if e != 0 {
return Node{}, e
}
return newRawNode(self.s[s:self.p], types.V_OBJECT, true), 0
}
return newLazyObject(self), 0
case types.V_DOUBLE : return NewNumber(self.s[val.Ep:self.p]), 0
case types.V_INTEGER : return NewNumber(self.s[val.Ep:self.p]), 0
@@ -394,7 +355,7 @@ func (self *Parser) searchKey(match string) types.ParsingError {
/* check for escape sequence */
if njs.Ep != -1 {
if key, err = unquote.String(key); err != 0 {
if key, err = unquote(key); err != 0 {
return err
}
}
@@ -510,7 +471,7 @@ func (self *Node) skipNextNode() *Node {
if t == _V_NONE {
return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))
}
val = newRawNode(parser.s[start:parser.p], t, false)
val = newRawNode(parser.s[start:parser.p], t)
}
/* add the value to result */
@@ -549,7 +510,7 @@ func (self *Node) skipNextPair() (*Pair) {
/* check for EOF */
if parser.p = parser.lspace(sp); parser.p >= ns {
return newErrorPair(parser.syntaxError(types.ERR_EOF))
return &Pair{"", *newSyntaxError(parser.syntaxError(types.ERR_EOF))}
}
/* check for empty object */
@@ -566,7 +527,7 @@ func (self *Node) skipNextPair() (*Pair) {
/* decode the key */
if njs = parser.decodeValue(); njs.Vt != types.V_STRING {
return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
return &Pair{"", *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
}
/* extract the key */
@@ -575,35 +536,35 @@ func (self *Node) skipNextPair() (*Pair) {
/* check for escape sequence */
if njs.Ep != -1 {
if key, err = unquote.String(key); err != 0 {
return newErrorPair(parser.syntaxError(err))
if key, err = unquote(key); err != 0 {
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
}
}
/* expect a ':' delimiter */
if err = parser.delim(); err != 0 {
return newErrorPair(parser.syntaxError(err))
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
}
/* skip the value */
if start, err := parser.skipFast(); err != 0 {
return newErrorPair(parser.syntaxError(err))
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
} else {
t := switchRawType(parser.s[start])
if t == _V_NONE {
return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
}
val = newRawNode(parser.s[start:parser.p], t, false)
val = newRawNode(parser.s[start:parser.p], t)
}
/* add the value to result */
ret.Push(NewPair(key, val))
ret.Push(Pair{Key: key, Value: val})
self.l++
parser.p = parser.lspace(parser.p)
/* check for EOF */
if parser.p >= ns {
return newErrorPair(parser.syntaxError(types.ERR_EOF))
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_EOF))}
}
/* check for the next character */
@@ -616,7 +577,7 @@ func (self *Node) skipNextPair() (*Pair) {
self.setObject(ret)
return ret.At(ret.Len()-1)
default:
return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
}
}
@@ -640,7 +601,7 @@ func Loads(src string) (int, interface{}, error) {
}
}
// LoadsUseNumber parse all json into interface{}, with numeric nodes cast to json.Number
// LoadsUseNumber parse all json into interface{}, with numeric nodes casted to json.Number
func LoadsUseNumber(src string) (int, interface{}, error) {
ps := &Parser{s: src}
np, err := ps.Parse()
@@ -694,75 +655,6 @@ func (self *Parser) ExportError(err types.ParsingError) error {
}
func backward(src string, i int) int {
for ; i>=0 && utils.IsSpace(src[i]); i-- {}
for ; i>=0 && isSpace(src[i]); i-- {}
return i
}
func newRawNode(str string, typ types.ValueType, lock bool) Node {
ret := Node{
t: typ | _V_RAW,
p: rt.StrPtr(str),
l: uint(len(str)),
}
if lock {
ret.m = new(sync.RWMutex)
}
return ret
}
var typeJumpTable = [256]types.ValueType{
'"' : types.V_STRING,
'-' : _V_NUMBER,
'0' : _V_NUMBER,
'1' : _V_NUMBER,
'2' : _V_NUMBER,
'3' : _V_NUMBER,
'4' : _V_NUMBER,
'5' : _V_NUMBER,
'6' : _V_NUMBER,
'7' : _V_NUMBER,
'8' : _V_NUMBER,
'9' : _V_NUMBER,
'[' : types.V_ARRAY,
'f' : types.V_FALSE,
'n' : types.V_NULL,
't' : types.V_TRUE,
'{' : types.V_OBJECT,
}
func switchRawType(c byte) types.ValueType {
return typeJumpTable[c]
}
func (self *Node) loadt() types.ValueType {
return (types.ValueType)(atomic.LoadInt64(&self.t))
}
func (self *Node) lock() bool {
if m := self.m; m != nil {
m.Lock()
return true
}
return false
}
func (self *Node) unlock() {
if m := self.m; m != nil {
m.Unlock()
}
}
func (self *Node) rlock() bool {
if m := self.m; m != nil {
m.RLock()
return true
}
return false
}
func (self *Node) runlock() {
if m := self.m; m != nil {
m.RUnlock()
}
}

View File

@@ -21,23 +21,8 @@ import (
`github.com/bytedance/sonic/internal/native/types`
)
// SearchOptions controls Searcher's behavior
type SearchOptions struct {
// ValidateJSON indicates the searcher to validate the entire JSON
ValidateJSON bool
// CopyReturn indicates the searcher to copy the result JSON instead of refer from the input
// This can help to reduce memory usage if you cache the results
CopyReturn bool
// ConcurrentRead indicates the searcher to return a concurrently-READ-safe node,
// including: GetByPath/Get/Index/GetOrIndex/Int64/Bool/Float64/String/Number/Interface/Array/Map/Raw/MarshalJSON
ConcurrentRead bool
}
type Searcher struct {
parser Parser
SearchOptions
}
func NewSearcher(str string) *Searcher {
@@ -46,16 +31,12 @@ func NewSearcher(str string) *Searcher {
s: str,
noLazy: false,
},
SearchOptions: SearchOptions{
ValidateJSON: true,
},
}
}
// GetByPathCopy search in depth from top json and returns a **Copied** json node at the path location
func (self *Searcher) GetByPathCopy(path ...interface{}) (Node, error) {
self.CopyReturn = true
return self.getByPath(path...)
return self.getByPath(true, true, path...)
}
// GetByPathNoCopy search in depth from top json and returns a **Referenced** json node at the path location
@@ -63,15 +44,15 @@ func (self *Searcher) GetByPathCopy(path ...interface{}) (Node, error) {
// WARN: this search directly refer partial json from top json, which has faster speed,
// may consumes more memory.
func (self *Searcher) GetByPath(path ...interface{}) (Node, error) {
return self.getByPath(path...)
return self.getByPath(false, true, path...)
}
func (self *Searcher) getByPath(path ...interface{}) (Node, error) {
func (self *Searcher) getByPath(copystring bool, validate bool, path ...interface{}) (Node, error) {
var err types.ParsingError
var start int
self.parser.p = 0
start, err = self.parser.getByPath(self.ValidateJSON, path...)
start, err = self.parser.getByPath(validate, path...)
if err != 0 {
// for compatibility with old version
if err == types.ERR_NOT_FOUND {
@@ -90,12 +71,12 @@ func (self *Searcher) getByPath(path ...interface{}) (Node, error) {
// copy string to reducing memory usage
var raw string
if self.CopyReturn {
if copystring {
raw = rt.Mem2Str([]byte(self.parser.s[start:self.parser.p]))
} else {
raw = self.parser.s[start:self.parser.p]
}
return newRawNode(raw, t, self.ConcurrentRead), nil
return newRawNode(raw, t), nil
}
// GetByPath searches a path and returns relaction and types of target