gotosocial/vendor/github.com/bytedance/sonic/internal/decoder/optdec/node.go

1279 lines
27 KiB
Go
Raw Normal View History

package optdec
import (
"encoding/json"
"math"
"unsafe"
"github.com/bytedance/sonic/internal/envs"
"github.com/bytedance/sonic/internal/rt"
)
type Context struct {
Parser *Parser
efacePool *efacePool
Stack bounedStack
Utf8Inv bool
}
func (ctx *Context) Options() uint64 {
return ctx.Parser.options
}
/************************* Stack and Pool Helper *******************/
type parentStat struct {
con unsafe.Pointer
remain uint64
}
type bounedStack struct {
stack []parentStat
index int
}
func newStack(size int) bounedStack {
return bounedStack{
stack: make([]parentStat, size + 2),
index: 0,
}
}
//go:nosplit
func (s *bounedStack) Pop() (unsafe.Pointer, int, bool){
s.index--
con := s.stack[s.index].con
remain := s.stack[s.index].remain &^ (uint64(1) << 63)
isObj := (s.stack[s.index].remain & (uint64(1) << 63)) != 0
s.stack[s.index].con = nil
s.stack[s.index].remain = 0
return con, int(remain), isObj
}
//go:nosplit
func (s *bounedStack) Push(p unsafe.Pointer, remain int, isObj bool) {
s.stack[s.index].con = p
s.stack[s.index].remain = uint64(remain)
if isObj {
s.stack[s.index].remain |= (uint64(1) << 63)
}
s.index++
}
type efacePool struct{
t64 rt.T64Pool
tslice rt.TslicePool
tstring rt.TstringPool
efaceSlice rt.SlicePool
}
func newEfacePool(stat *jsonStat, useNumber bool) *efacePool {
strs := int(stat.str)
nums := 0
if useNumber {
strs += int(stat.number)
} else {
nums = int(stat.number)
}
return &efacePool{
t64: rt.NewT64Pool(nums),
tslice: rt.NewTslicePool(int(stat.array)),
tstring: rt.NewTstringPool(strs),
efaceSlice: rt.NewPool(rt.AnyType, int(stat.array_elems)),
}
}
func (self *efacePool) GetMap(hint int) unsafe.Pointer {
m := make(map[string]interface{}, hint)
return *(*unsafe.Pointer)(unsafe.Pointer(&m))
}
func (self *efacePool) GetSlice(hint int) unsafe.Pointer {
return unsafe.Pointer(self.efaceSlice.GetSlice(hint))
}
func (self *efacePool) ConvTSlice(val rt.GoSlice, typ *rt.GoType, dst unsafe.Pointer) {
self.tslice.Conv(val, typ, (*interface{})(dst))
}
func (self *efacePool) ConvF64(val float64, dst unsafe.Pointer) {
self.t64.Conv(castU64(val), rt.Float64Type, (*interface{})(dst))
}
func (self *efacePool) ConvTstring(val string, dst unsafe.Pointer) {
self.tstring.Conv(val, (*interface{})(dst))
}
func (self *efacePool) ConvTnum(val json.Number, dst unsafe.Pointer) {
self.tstring.ConvNum(val, (*interface{})(dst))
}
/********************************************************/
func canUseFastMap( opts uint64, root *rt.GoType) bool {
return envs.UseFastMap && (opts & (1 << _F_copy_string)) == 0 && (opts & (1 << _F_use_int64)) == 0 && (root == rt.AnyType || root == rt.MapEfaceType || root == rt.SliceEfaceType)
}
func NewContext(json string, pos int, opts uint64, root *rt.GoType) (Context, error) {
ctx := Context{
Parser: newParser(json, pos, opts),
}
if root == rt.AnyType || root == rt.MapEfaceType || root == rt.SliceEfaceType {
ctx.Parser.isEface = true
}
ecode := ctx.Parser.parse()
if ecode != 0 {
return ctx, ctx.Parser.fixError(ecode)
}
useNumber := (opts & (1 << _F_use_number )) != 0
if canUseFastMap(opts, root) {
ctx.efacePool = newEfacePool(&ctx.Parser.nbuf.stat, useNumber)
ctx.Stack = newStack(int(ctx.Parser.nbuf.stat.max_depth))
}
return ctx, nil
}
func (ctx *Context) Delete() {
ctx.Parser.free()
ctx.Parser = nil
}
type Node struct {
cptr uintptr
}
func NewNode(cptr uintptr) Node {
return Node{cptr: cptr}
}
type Dom struct {
cdom uintptr
}
func (ctx *Context) Root() Node {
root := (uintptr)(((*rt.GoSlice)(unsafe.Pointer(&ctx.Parser.nodes))).Ptr)
return Node{cptr: root}
}
type Array struct {
cptr uintptr
}
type Object struct {
cptr uintptr
}
func (obj Object) Len() int {
cobj := ptrCast(obj.cptr)
return int(uint64(cobj.val) & ConLenMask)
}
func (arr Array) Len() int {
carr := ptrCast(arr.cptr)
return int(uint64(carr.val) & ConLenMask)
}
// / Helper functions to eliminate CGO calls
func (val Node) Type() uint8 {
ctype := ptrCast(val.cptr)
return uint8(ctype.typ & TypeMask)
}
func (val Node) Next() uintptr {
if val.Type() != KObject && val.Type() != KArray {
return PtrOffset(val.cptr, 1)
}
cobj := ptrCast(val.cptr)
offset := int64(uint64(cobj.val) >> ConLenBits)
return PtrOffset(val.cptr, offset)
}
func (val *Node) next() {
*val = NewNode(val.Next())
}
type NodeIter struct {
next uintptr
}
func NewNodeIter(node Node) NodeIter {
return NodeIter{next: node.cptr}
}
func (iter *NodeIter) Next() Node {
ret := NewNode(iter.next)
iter.next = PtrOffset(iter.next, 1)
return ret
}
func (iter *NodeIter) Peek() Node {
return NewNode(iter.next)
}
func (val Node) U64() uint64 {
cnum := ptrCast(val.cptr)
return *(*uint64)((unsafe.Pointer)(&(cnum.val)))
}
func (val Node) I64() int64 {
cnum := ptrCast(val.cptr)
return *(*int64)((unsafe.Pointer)(&(cnum.val)))
}
func (val Node) IsNull() bool {
return val.Type() == KNull
}
func (val Node) IsNumber() bool {
return val.Type() & KNumber != 0
}
func (val Node) F64() float64 {
cnum := ptrCast(val.cptr)
return *(*float64)((unsafe.Pointer)(&(cnum.val)))
}
func (val Node) Bool() bool {
return val.Type() == KTrue
}
func (self Node) AsU64(ctx *Context) (uint64, bool) {
if self.Type() == KUint {
return self.U64(), true
} else if self.Type() == KRawNumber {
num, err := ParseU64(self.Raw(ctx))
if err != nil {
return 0, false
}
return num, true
} else {
return 0, false
}
}
func (val *Node) AsObj() (Object, bool) {
var ret Object
if val.Type() != KObject {
return ret, false
}
return Object{
cptr: val.cptr,
}, true
}
func (val Node) Obj() Object {
return Object{cptr: val.cptr}
}
func (val Node) Arr() Array {
return Array{cptr: val.cptr}
}
func (val *Node) AsArr() (Array, bool) {
var ret Array
if val.Type() != KArray {
return ret, false
}
return Array{
cptr: val.cptr,
}, true
}
func (self Node) AsI64(ctx *Context) (int64, bool) {
typ := self.Type()
if typ == KUint && self.U64() <= math.MaxInt64 {
return int64(self.U64()), true
} else if typ == KSint {
return self.I64(), true
} else if typ == KRawNumber {
val, err := self.Number(ctx).Int64()
if err != nil {
return 0, false
}
return val, true
} else {
return 0, false
}
}
/********* Parse Node String into Value ***************/
func (val Node) ParseI64(ctx *Context) (int64, bool) {
s, ok := val.AsStrRef(ctx)
if !ok {
return 0, false
}
if s == "null" {
return 0, true
}
i, err := ParseI64(s)
if err != nil {
return 0, false
}
return i, true
}
func (val Node) ParseBool(ctx *Context) (bool, bool) {
s, ok := val.AsStrRef(ctx)
if !ok {
return false, false
}
if s == "null" {
return false, true
}
b, err := ParseBool(s)
if err != nil {
return false, false
}
return b, true
}
func (val Node) ParseU64(ctx *Context) (uint64, bool) {
s, ok := val.AsStrRef(ctx)
if !ok {
return 0, false
}
if s == "null" {
return 0, true
}
i, err := ParseU64(s)
if err != nil {
return 0, false
}
return i, true
}
func (val Node) ParseF64(ctx *Context) (float64, bool) {
s, ok := val.AsStrRef(ctx)
if !ok {
return 0, false
}
if s == "null" {
return 0, true
}
i, err := ParseF64(s)
if err != nil {
return 0, false
}
return i, true
}
func (val Node) ParseString(ctx *Context) (string, bool) {
// shoud not use AsStrRef
s, ok := val.AsStr(ctx)
if !ok {
return "", false
}
if s == "null" {
return "", true
}
s, err := Unquote(s)
if err != nil {
return "", false
}
return s, true
}
func (val Node) ParseNumber(ctx *Context) (json.Number, bool) {
// shoud not use AsStrRef
s, ok := val.AsStr(ctx)
if !ok {
return json.Number(""), false
}
if s == "null" {
return json.Number(""), true
}
end, ok := SkipNumberFast(s, 0)
// has error or trailing chars
if !ok || end != len(s) {
return json.Number(""), false
}
return json.Number(s), true
}
func (val Node) AsF64(ctx *Context) (float64, bool) {
switch val.Type() {
case KUint: return float64(val.U64()), true
case KSint: return float64(val.I64()), true
case KReal: return float64(val.F64()), true
case KRawNumber: f, err := val.Number(ctx).Float64(); return f, err == nil
default: return 0, false
}
}
func (val Node) AsBool() (bool, bool) {
switch val.Type() {
case KTrue: return true, true
case KFalse: return false, true
default: return false, false
}
}
func (val Node) AsStr(ctx *Context) (string, bool) {
switch val.Type() {
case KStringCommon:
s := val.StringRef(ctx)
if (ctx.Options() & (1 << _F_copy_string) == 0) {
return s, true
}
return string(rt.Str2Mem(s)), true
case KStringEscaped:
return val.StringCopyEsc(ctx), true
default: return "", false
}
}
func (val Node) AsStrRef(ctx *Context) (string, bool) {
switch val.Type() {
case KStringEscaped:
node := ptrCast(val.cptr)
offset := val.Position()
len := int(node.val)
return rt.Mem2Str(ctx.Parser.JsonBytes()[offset : offset + len]), true
case KStringCommon:
return val.StringRef(ctx), true
default:
return "", false
}
}
func (val Node) AsBytesRef(ctx *Context) ([]byte, bool) {
switch val.Type() {
case KStringEscaped:
node := ptrCast(val.cptr)
offset := val.Position()
len := int(node.val)
return ctx.Parser.JsonBytes()[offset : offset + len], true
case KStringCommon:
return rt.Str2Mem(val.StringRef(ctx)), true
default:
return nil, false
}
}
func (val Node) AsStringText(ctx *Context) ([]byte, bool) {
if !val.IsStr() {
return nil, false
}
// clone to new bytes
s, b := val.AsStrRef(ctx)
return []byte(s), b
}
func (val Node) IsStr() bool {
return (val.Type() == KStringCommon) || (val.Type() == KStringEscaped)
}
func (val Node) IsRawNumber() bool {
return val.Type() == KRawNumber
}
func (val Node) Number(ctx *Context) json.Number {
return json.Number(val.Raw(ctx))
}
func (val Node) Raw(ctx *Context) string {
node := ptrCast(val.cptr)
len := int(node.val)
offset := val.Position()
return ctx.Parser.Json[offset:int(offset+len)]
}
func (val Node) Position() int {
node := ptrCast(val.cptr)
return int(node.typ >> PosBits)
}
func (val Node) AsNumber(ctx *Context) (json.Number, bool) {
// parse JSON string as number
if val.IsStr() {
s, _ := val.AsStr(ctx)
if !ValidNumberFast(s) {
return "", false
} else {
return json.Number(s), true
}
}
return val.NonstrAsNumber(ctx)
}
func (val Node) NonstrAsNumber(ctx *Context) (json.Number, bool) {
// deal with raw number
if val.IsRawNumber() {
return val.Number(ctx), true
}
// deal with parse number
if !val.IsNumber() {
return json.Number(""), false
}
start := val.Position()
end, ok := SkipNumberFast(ctx.Parser.Json, start)
if !ok {
return "", false
}
return json.Number(ctx.Parser.Json[start:end]), true
}
func (val Node) AsRaw(ctx *Context) string {
// fast path for unescaped strings
switch val.Type() {
case KNull:
return "null"
case KTrue:
return "true"
case KFalse:
return "false"
case KStringCommon:
node := ptrCast(val.cptr)
len := int(node.val)
offset := val.Position()
// add start abd end quote
ref := rt.Str2Mem(ctx.Parser.Json)[offset-1 : offset+len+1]
return rt.Mem2Str(ref)
case KRawNumber: fallthrough
case KRaw: return val.Raw(ctx)
case KStringEscaped:
raw, _ := SkipOneFast(ctx.Parser.Json, val.Position() - 1)
return raw
default:
raw, err := SkipOneFast(ctx.Parser.Json, val.Position())
if err != nil {
break
}
return raw
}
panic("should always be valid json here")
}
// reference from the input JSON as possible
func (val Node) StringRef(ctx *Context) string {
return val.Raw(ctx)
}
//go:nocheckptr
func ptrCast(p uintptr) *node {
return (*node)(unsafe.Pointer(p))
}
func (val Node) StringCopyEsc(ctx *Context) string {
// check whether there are in padded
node := ptrCast(val.cptr)
len := int(node.val)
offset := val.Position()
return string(ctx.Parser.JsonBytes()[offset : offset + len])
}
func (val Node) Object() Object {
return Object{cptr: val.cptr}
}
func (val Node) Array() Array {
return Array{cptr: val.cptr}
}
func (val *Array) Children() uintptr {
return PtrOffset(val.cptr, 1)
}
func (val *Object) Children() uintptr {
return PtrOffset(val.cptr, 1)
}
func (val *Node) Equal(ctx *Context, lhs string) bool {
// check whether escaped
cstr := ptrCast(val.cptr)
offset := int(val.Position())
len := int(cstr.val)
return lhs == ctx.Parser.Json[offset:offset+len]
}
func (node *Node) AsMapEface(ctx *Context, vp unsafe.Pointer) error {
if node.IsNull() {
return nil
}
obj, ok := node.AsObj()
if !ok {
return newUnmatched(node.Position(), rt.MapEfaceType)
}
var err, gerr error
size := obj.Len()
var m map[string]interface{}
if *(*unsafe.Pointer)(vp) == nil {
if ctx.efacePool != nil {
p := ctx.efacePool.GetMap(size)
m = *(*map[string]interface{})(unsafe.Pointer(&p))
} else {
m = make(map[string]interface{}, size)
}
} else {
m = *(*map[string]interface{})(vp)
}
next := obj.Children()
for i := 0; i < size; i++ {
knode := NewNode(next)
key, _ := knode.AsStr(ctx)
val := NewNode(PtrOffset(next, 1))
m[key], err = val.AsEface(ctx)
next = val.cptr
if gerr == nil && err != nil {
gerr = err
}
}
*(*map[string]interface{})(vp) = m
return gerr
}
func (node *Node) AsMapString(ctx *Context, vp unsafe.Pointer) error {
obj, ok := node.AsObj()
if !ok {
return newUnmatched(node.Position(), rt.MapStringType)
}
size := obj.Len()
var m map[string]string
if *(*unsafe.Pointer)(vp) == nil {
m = make(map[string]string, size)
} else {
m = *(*map[string]string)(vp)
}
var gerr error
next := obj.Children()
for i := 0; i < size; i++ {
knode := NewNode(next)
key, _ := knode.AsStr(ctx)
val := NewNode(PtrOffset(next, 1))
m[key], ok = val.AsStr(ctx)
if !ok {
if gerr == nil {
gerr = newUnmatched(val.Position(), rt.StringType)
}
next = val.Next()
} else {
next = PtrOffset(val.cptr, 1)
}
}
*(*map[string]string)(vp) = m
return gerr
}
func (node *Node) AsSliceEface(ctx *Context, vp unsafe.Pointer) error {
arr, ok := node.AsArr()
if !ok {
return newUnmatched(node.Position(), rt.SliceEfaceType)
}
size := arr.Len()
var s []interface{}
if size != 0 && ctx.efacePool != nil {
slice := rt.GoSlice {
Ptr: ctx.efacePool.GetSlice(size),
Len: size,
Cap: size,
}
*(*rt.GoSlice)(unsafe.Pointer(&s)) = slice
} else {
s = *(*[]interface{})((unsafe.Pointer)(rt.MakeSlice(vp, rt.AnyType, size)))
}
*node = NewNode(arr.Children())
var err, gerr error
for i := 0; i < size; i++ {
s[i], err = node.AsEface(ctx)
if gerr == nil && err != nil {
gerr = err
}
}
*(*[]interface{})(vp) = s
return nil
}
func (node *Node) AsSliceI32(ctx *Context, vp unsafe.Pointer) error {
arr, ok := node.AsArr()
if !ok {
return newUnmatched(node.Position(), rt.SliceI32Type)
}
size := arr.Len()
s := *(*[]int32)((unsafe.Pointer)(rt.MakeSlice(vp, rt.Int32Type, size)))
next := arr.Children()
var gerr error
for i := 0; i < size; i++ {
val := NewNode(next)
ret, ok := val.AsI64(ctx)
if !ok || ret > math.MaxInt32 || ret < math.MinInt32 {
if gerr == nil {
gerr = newUnmatched(val.Position(), rt.Int32Type)
}
next = val.Next()
} else {
s[i] = int32(ret)
next = PtrOffset(val.cptr, 1)
}
}
*(*[]int32)(vp) = s
return gerr
}
func (node *Node) AsSliceI64(ctx *Context, vp unsafe.Pointer) error {
arr, ok := node.AsArr()
if !ok {
return newUnmatched(node.Position(), rt.SliceI64Type)
}
size := arr.Len()
s := *(*[]int64)((unsafe.Pointer)(rt.MakeSlice(vp, rt.Int64Type, size)))
next := arr.Children()
var gerr error
for i := 0; i < size; i++ {
val := NewNode(next)
ret, ok := val.AsI64(ctx)
if !ok {
if gerr == nil {
gerr = newUnmatched(val.Position(), rt.Int64Type)
}
next = val.Next()
} else {
s[i] = ret
next = PtrOffset(val.cptr, 1)
}
}
*(*[]int64)(vp) = s
return gerr
}
func (node *Node) AsSliceU32(ctx *Context, vp unsafe.Pointer) error {
arr, ok := node.AsArr()
if !ok {
return newUnmatched(node.Position(), rt.SliceU32Type)
}
size := arr.Len()
next := arr.Children()
s := *(*[]uint32)((unsafe.Pointer)(rt.MakeSlice(vp, rt.Uint32Type, size)))
var gerr error
for i := 0; i < size; i++ {
val := NewNode(next)
ret, ok := val.AsU64(ctx)
if !ok || ret > math.MaxUint32 {
if gerr == nil {
gerr = newUnmatched(val.Position(), rt.Uint32Type)
}
next = val.Next()
} else {
s[i] = uint32(ret)
next = PtrOffset(val.cptr, 1)
}
}
*(*[]uint32)(vp) = s
return gerr
}
func (node *Node) AsSliceU64(ctx *Context, vp unsafe.Pointer) error {
arr, ok := node.AsArr()
if !ok {
return newUnmatched(node.Position(), rt.SliceU64Type)
}
size := arr.Len()
next := arr.Children()
s := *(*[]uint64)((unsafe.Pointer)(rt.MakeSlice(vp, rt.Uint64Type, size)))
var gerr error
for i := 0; i < size; i++ {
val := NewNode(next)
ret, ok := val.AsU64(ctx)
if !ok {
if gerr == nil {
gerr = newUnmatched(val.Position(), rt.Uint64Type)
}
next = val.Next()
} else {
s[i] = ret
next = PtrOffset(val.cptr, 1)
}
}
*(*[]uint64)(vp) = s
return gerr
}
func (node *Node) AsSliceString(ctx *Context, vp unsafe.Pointer) error {
arr, ok := node.AsArr()
if !ok {
return newUnmatched(node.Position(), rt.SliceStringType)
}
size := arr.Len()
next := arr.Children()
s := *(*[]string)((unsafe.Pointer)(rt.MakeSlice(vp, rt.StringType, size)))
var gerr error
for i := 0; i < size; i++ {
val := NewNode(next)
ret, ok := val.AsStr(ctx)
if !ok {
if gerr == nil {
gerr = newUnmatched(val.Position(), rt.StringType)
}
next = val.Next()
} else {
s[i] = ret
next = PtrOffset(val.cptr, 1)
}
}
*(*[]string)(vp) = s
return gerr
}
func (node *Node) AsSliceBytes(ctx *Context) ([]byte, error) {
b, ok := node.AsBytesRef(ctx)
if !ok {
return nil, newUnmatched(node.Position(), rt.BytesType)
}
b64, err := rt.DecodeBase64(b)
if err != nil {
return nil, newUnmatched(node.Position(), rt.BytesType)
}
return b64, nil
}
// AsEface will always ok, because we have parse in native.
func (node *Node) AsEface(ctx *Context) (interface{}, error) {
if ctx.efacePool != nil {
iter := NewNodeIter(*node)
v := AsEfaceFast(&iter, ctx)
*node = iter.Peek()
return v, nil
} else {
return node.AsEfaceFallback(ctx)
}
}
func parseSingleNode(node Node, ctx *Context) interface{} {
var v interface{}
switch node.Type() {
case KObject: v = map[string]interface{}{}
case KArray: v = []interface{}{}
case KStringCommon: v = node.StringRef(ctx)
case KStringEscaped: v = node.StringCopyEsc(ctx)
case KTrue: v = true
case KFalse: v = false
case KNull: v = nil
case KUint: v = float64(node.U64())
case KSint: v = float64(node.I64())
case KReal: v = float64(node.F64())
case KRawNumber: v = node.Number(ctx)
default: panic("unreachable for as eface")
}
return v
}
func castU64(val float64) uint64 {
return *((*uint64)(unsafe.Pointer((&val))))
}
func AsEfaceFast(iter *NodeIter, ctx *Context) interface{} {
var mp, sp, parent unsafe.Pointer // current container pointer
var node Node
var size int
var isObj bool
var slice rt.GoSlice
var val unsafe.Pointer
var vt **rt.GoType
var vp *unsafe.Pointer
var rootM unsafe.Pointer
var rootS rt.GoSlice
var root interface{}
var key string
node = iter.Next()
switch node.Type() {
case KObject:
size = node.Object().Len()
if size != 0 {
ctx.Stack.Push(nil, 0, true)
mp = ctx.efacePool.GetMap(size)
rootM = mp
isObj = true
goto _object_key
} else {
return rt.GoEface {
Type: rt.MapEfaceType,
Value: ctx.efacePool.GetMap(0),
}.Pack()
}
case KArray:
size = node.Array().Len()
if size != 0 {
ctx.Stack.Push(nil, 0, false)
sp = ctx.efacePool.GetSlice(size)
slice = rt.GoSlice {
Ptr: sp,
Len: size,
Cap: size,
}
rootS = slice
isObj = false
val = sp
goto _arr_val;
} else {
ctx.efacePool.ConvTSlice(rt.EmptySlice, rt.SliceEfaceType, unsafe.Pointer(&root))
}
case KStringCommon: ctx.efacePool.ConvTstring(node.StringRef(ctx), unsafe.Pointer(&root))
case KStringEscaped: ctx.efacePool.ConvTstring(node.StringCopyEsc(ctx), unsafe.Pointer(&root))
case KTrue: root = true
case KFalse: root = false
case KNull: root = nil
case KUint: ctx.efacePool.ConvF64(float64(node.U64()), unsafe.Pointer(&root))
case KSint: ctx.efacePool.ConvF64(float64(node.I64()), unsafe.Pointer(&root))
case KReal: ctx.efacePool.ConvF64(node.F64(), unsafe.Pointer(&root))
case KRawNumber: ctx.efacePool.ConvTnum(node.Number(ctx), unsafe.Pointer(&root))
default: panic("unreachable for as eface")
}
return root
_object_key:
node = iter.Next()
if node.Type() == KStringCommon {
key = node.StringRef(ctx)
} else {
key = node.StringCopyEsc(ctx)
}
// interface{} slot in map bucket
val = rt.Mapassign_faststr(rt.MapEfaceMapType, mp, key)
vt = &(*rt.GoEface)(val).Type
vp = &(*rt.GoEface)(val).Value
// parse value node
node = iter.Next()
switch node.Type() {
case KObject:
newSize := node.Object().Len()
newMp := ctx.efacePool.GetMap(newSize)
*vt = rt.MapEfaceType
*vp = newMp
remain := size - 1
isObj = true
if newSize != 0 {
if remain > 0 {
ctx.Stack.Push(mp, remain, true)
}
mp = newMp
size = newSize
goto _object_key;
}
case KArray:
newSize := node.Array().Len()
if newSize == 0 {
ctx.efacePool.ConvTSlice(rt.EmptySlice, rt.SliceEfaceType, val)
break;
}
newSp := ctx.efacePool.GetSlice(newSize)
// pack to []interface{}
ctx.efacePool.ConvTSlice(rt.GoSlice{
Ptr: newSp,
Len: newSize,
Cap: newSize,
}, rt.SliceEfaceType, val)
remain := size - 1
if remain > 0 {
ctx.Stack.Push(mp, remain, true)
}
val = newSp
isObj = false
size = newSize
goto _arr_val;
case KStringCommon:
ctx.efacePool.ConvTstring(node.StringRef(ctx), val)
case KStringEscaped:
ctx.efacePool.ConvTstring(node.StringCopyEsc(ctx), val)
case KTrue:
rt.ConvTBool(true, (*interface{})(val))
case KFalse:
rt.ConvTBool(false, (*interface{})(val))
case KNull: /* skip */
case KUint:
ctx.efacePool.ConvF64(float64(node.U64()), val)
case KSint:
ctx.efacePool.ConvF64(float64(node.I64()), val)
case KReal:
ctx.efacePool.ConvF64(node.F64(), val)
case KRawNumber:
ctx.efacePool.ConvTnum(node.Number(ctx), val)
default:
panic("unreachable for as eface")
}
// check size
size -= 1
if size != 0 {
goto _object_key;
}
parent, size, isObj = ctx.Stack.Pop()
// parent is empty
if parent == nil {
if isObj {
return rt.GoEface {
Type: rt.MapEfaceType,
Value: rootM,
}.Pack()
} else {
ctx.efacePool.ConvTSlice(rootS, rt.SliceEfaceType, (unsafe.Pointer)(&root))
return root
}
}
// continue to parse parent
if isObj {
mp = parent
goto _object_key;
} else {
val = rt.PtrAdd(parent, rt.AnyType.Size)
goto _arr_val;
}
_arr_val:
// interface{} slot in slice
vt = &(*rt.GoEface)(val).Type
vp = &(*rt.GoEface)(val).Value
// parse value node
node = iter.Next()
switch node.Type() {
case KObject:
newSize := node.Object().Len()
newMp := ctx.efacePool.GetMap(newSize)
*vt = rt.MapEfaceType
*vp = newMp
remain := size - 1
if newSize != 0 {
// push next array elem into stack
if remain > 0 {
ctx.Stack.Push(val, remain, false)
}
mp = newMp
size = newSize
isObj = true
goto _object_key;
}
case KArray:
newSize := node.Array().Len()
if newSize == 0 {
ctx.efacePool.ConvTSlice(rt.EmptySlice, rt.SliceEfaceType, val)
break;
}
newSp := ctx.efacePool.GetSlice(newSize)
// pack to []interface{}
ctx.efacePool.ConvTSlice(rt.GoSlice {
Ptr: newSp,
Len: newSize,
Cap: newSize,
}, rt.SliceEfaceType, val)
remain := size - 1
if remain > 0 {
ctx.Stack.Push(val, remain, false)
}
val = newSp
isObj = false
size = newSize
goto _arr_val;
case KStringCommon:
ctx.efacePool.ConvTstring(node.StringRef(ctx), val)
case KStringEscaped:
ctx.efacePool.ConvTstring(node.StringCopyEsc(ctx), val)
case KTrue:
rt.ConvTBool(true, (*interface{})(val))
case KFalse:
rt.ConvTBool(false, (*interface{})(val))
case KNull: /* skip */
case KUint:
ctx.efacePool.ConvF64(float64(node.U64()), val)
case KSint:
ctx.efacePool.ConvF64(float64(node.I64()), val)
case KReal:
ctx.efacePool.ConvF64(node.F64(), val)
case KRawNumber:
ctx.efacePool.ConvTnum(node.Number(ctx), val)
default: panic("unreachable for as eface")
}
// check size
size -= 1
if size != 0 {
val = rt.PtrAdd(val, rt.AnyType.Size)
goto _arr_val;
}
parent, size, isObj = ctx.Stack.Pop()
// parent is empty
if parent == nil {
if isObj {
return rt.GoEface {
Type: rt.MapEfaceType,
Value: rootM,
}.Pack()
} else {
ctx.efacePool.ConvTSlice(rootS, rt.SliceEfaceType, unsafe.Pointer(&root))
return root
}
}
// continue to parse parent
if isObj {
mp = parent
goto _object_key;
} else {
val = rt.PtrAdd(parent, rt.AnyType.Size)
goto _arr_val;
}
}
func (node *Node) AsEfaceFallback(ctx *Context) (interface{}, error) {
switch node.Type() {
case KObject:
obj := node.Object()
size := obj.Len()
m := make(map[string]interface{}, size)
*node = NewNode(obj.Children())
var gerr, err error
for i := 0; i < size; i++ {
key, _ := node.AsStr(ctx)
*node = NewNode(PtrOffset(node.cptr, 1))
m[key], err = node.AsEfaceFallback(ctx)
if gerr == nil && err != nil {
gerr = err
}
}
return m, gerr
case KArray:
arr := node.Array()
size := arr.Len()
a := make([]interface{}, size)
*node = NewNode(arr.Children())
var gerr, err error
for i := 0; i < size; i++ {
a[i], err = node.AsEfaceFallback(ctx)
if gerr == nil && err != nil {
gerr = err
}
}
return a, gerr
case KStringCommon:
str, _ := node.AsStr(ctx)
*node = NewNode(PtrOffset(node.cptr, 1))
return str, nil
case KStringEscaped:
str := node.StringCopyEsc(ctx)
*node = NewNode(PtrOffset(node.cptr, 1))
return str, nil
case KTrue:
*node = NewNode(PtrOffset(node.cptr, 1))
return true, nil
case KFalse:
*node = NewNode(PtrOffset(node.cptr, 1))
return false, nil
case KNull:
*node = NewNode(PtrOffset(node.cptr, 1))
return nil, nil
default:
// use float64
if ctx.Parser.options & (1 << _F_use_number) != 0 {
num, ok := node.AsNumber(ctx)
if !ok {
// skip the unmacthed type
*node = NewNode(node.Next())
return nil, newUnmatched(node.Position(), rt.JsonNumberType)
} else {
*node = NewNode(PtrOffset(node.cptr, 1))
return num, nil
}
} else if ctx.Parser.options & (1 << _F_use_int64) != 0 {
// first try int64
i, ok := node.AsI64(ctx)
if ok {
*node = NewNode(PtrOffset(node.cptr, 1))
return i, nil
}
// is not integer, then use float64
f, ok := node.AsF64(ctx)
if ok {
*node = NewNode(PtrOffset(node.cptr, 1))
return f, nil
}
// skip the unmacthed type
*node = NewNode(node.Next())
return nil, newUnmatched(node.Position(), rt.Int64Type)
} else {
num, ok := node.AsF64(ctx)
if !ok {
// skip the unmacthed type
*node = NewNode(node.Next())
return nil, newUnmatched(node.Position(), rt.Float64Type)
} else {
*node = NewNode(PtrOffset(node.cptr, 1))
return num, nil
}
}
}
}
//go:nosplit
func PtrOffset(ptr uintptr, off int64) uintptr {
return uintptr(int64(ptr) + off * int64(unsafe.Sizeof(node{})))
}