mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 18:22:25 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			315 lines
		
	
	
	
		
			9.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
	
		
			9.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
 * Copyright 2021 ByteDance Inc.
 | 
						|
 *
 | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 * you may not use this file except in compliance with the License.
 | 
						|
 * You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 * Unless required by applicable law or agreed to in writing, software
 | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 * See the License for the specific language governing permissions and
 | 
						|
 * limitations under the License.
 | 
						|
 */
 | 
						|
 | 
						|
package ast
 | 
						|
 | 
						|
import (
 | 
						|
    `encoding/json`
 | 
						|
 | 
						|
    `github.com/bytedance/sonic/internal/native/types`
 | 
						|
)
 | 
						|
 | 
						|
// Visitor handles the callbacks during preorder traversal of a JSON AST.
 | 
						|
//
 | 
						|
// According to the JSON RFC8259, a JSON AST can be defined by
 | 
						|
// the following rules without separator / whitespace tokens.
 | 
						|
//
 | 
						|
//  JSON-AST  = value
 | 
						|
//  value     = false / null / true / object / array / number / string
 | 
						|
//  object    = begin-object [ member *( member ) ] end-object
 | 
						|
//  member    = string value
 | 
						|
//  array     = begin-array [ value *( value ) ] end-array
 | 
						|
//
 | 
						|
type Visitor interface {
 | 
						|
 | 
						|
    // OnNull handles a JSON null value.
 | 
						|
    OnNull() error
 | 
						|
 | 
						|
    // OnBool handles a JSON true / false value.
 | 
						|
    OnBool(v bool) error
 | 
						|
 | 
						|
    // OnString handles a JSON string value.
 | 
						|
    OnString(v string) error
 | 
						|
 | 
						|
    // OnInt64 handles a JSON number value with int64 type.
 | 
						|
    OnInt64(v int64, n json.Number) error
 | 
						|
 | 
						|
    // OnFloat64 handles a JSON number value with float64 type.
 | 
						|
    OnFloat64(v float64, n json.Number) error
 | 
						|
 | 
						|
    // OnObjectBegin handles the beginning of a JSON object value with a
 | 
						|
    // suggested capacity that can be used to make your custom object container.
 | 
						|
    //
 | 
						|
    // After this point the visitor will receive a sequence of callbacks like
 | 
						|
    // [string, value, string, value, ......, ObjectEnd].
 | 
						|
    //
 | 
						|
    // Note:
 | 
						|
    // 1. This is a recursive definition which means the value can
 | 
						|
    // also be a JSON object / array described by a sequence of callbacks.
 | 
						|
    // 2. The suggested capacity will be 0 if current object is empty.
 | 
						|
    // 3. Currently sonic use a fixed capacity for non-empty object (keep in
 | 
						|
    // sync with ast.Node) which might not be very suitable. This may be
 | 
						|
    // improved in future version.
 | 
						|
    OnObjectBegin(capacity int) error
 | 
						|
 | 
						|
    // OnObjectKey handles a JSON object key string in member.
 | 
						|
    OnObjectKey(key string) error
 | 
						|
 | 
						|
    // OnObjectEnd handles the ending of a JSON object value.
 | 
						|
    OnObjectEnd() error
 | 
						|
 | 
						|
    // OnArrayBegin handles the beginning of a JSON array value with a
 | 
						|
    // suggested capacity that can be used to make your custom array container.
 | 
						|
    //
 | 
						|
    // After this point the visitor will receive a sequence of callbacks like
 | 
						|
    // [value, value, value, ......, ArrayEnd].
 | 
						|
    //
 | 
						|
    // Note:
 | 
						|
    // 1. This is a recursive definition which means the value can
 | 
						|
    // also be a JSON object / array described by a sequence of callbacks.
 | 
						|
    // 2. The suggested capacity will be 0 if current array is empty.
 | 
						|
    // 3. Currently sonic use a fixed capacity for non-empty array (keep in
 | 
						|
    // sync with ast.Node) which might not be very suitable. This may be
 | 
						|
    // improved in future version.
 | 
						|
    OnArrayBegin(capacity int) error
 | 
						|
 | 
						|
    // OnArrayEnd handles the ending of a JSON array value.
 | 
						|
    OnArrayEnd() error
 | 
						|
}
 | 
						|
 | 
						|
// VisitorOptions contains all Visitor's options. The default value is an
 | 
						|
// empty VisitorOptions{}.
 | 
						|
type VisitorOptions struct {
 | 
						|
    // OnlyNumber indicates parser to directly return number value without
 | 
						|
    // conversion, then the first argument of OnInt64 / OnFloat64 will always
 | 
						|
    // be zero.
 | 
						|
    OnlyNumber bool
 | 
						|
}
 | 
						|
 | 
						|
var defaultVisitorOptions = &VisitorOptions{}
 | 
						|
 | 
						|
// Preorder decodes the whole JSON string and callbacks each AST node to visitor
 | 
						|
// during preorder traversal. Any visitor method with an error returned will
 | 
						|
// break the traversal and the given error will be directly returned. The opts
 | 
						|
// argument can be reused after every call.
 | 
						|
func Preorder(str string, visitor Visitor, opts *VisitorOptions) error {
 | 
						|
    if opts == nil {
 | 
						|
        opts = defaultVisitorOptions
 | 
						|
    }
 | 
						|
    // process VisitorOptions first to guarantee that all options will be
 | 
						|
    // constant during decoding and make options more readable.
 | 
						|
    var (
 | 
						|
        optDecodeNumber = !opts.OnlyNumber
 | 
						|
    )
 | 
						|
 | 
						|
    tv := &traverser{
 | 
						|
        parser: Parser{
 | 
						|
            s:         str,
 | 
						|
            noLazy:    true,
 | 
						|
            skipValue: false,
 | 
						|
        },
 | 
						|
        visitor: visitor,
 | 
						|
    }
 | 
						|
 | 
						|
    if optDecodeNumber {
 | 
						|
        tv.parser.decodeNumber(true)
 | 
						|
    }
 | 
						|
 | 
						|
    err := tv.decodeValue()
 | 
						|
 | 
						|
    if optDecodeNumber {
 | 
						|
        tv.parser.decodeNumber(false)
 | 
						|
    }
 | 
						|
    return err
 | 
						|
}
 | 
						|
 | 
						|
type traverser struct {
 | 
						|
    parser  Parser
 | 
						|
    visitor Visitor
 | 
						|
}
 | 
						|
 | 
						|
// NOTE: keep in sync with (*Parser).Parse method.
 | 
						|
func (self *traverser) decodeValue() error {
 | 
						|
    switch val := self.parser.decodeValue(); val.Vt {
 | 
						|
    case types.V_EOF:
 | 
						|
        return types.ERR_EOF
 | 
						|
    case types.V_NULL:
 | 
						|
        return self.visitor.OnNull()
 | 
						|
    case types.V_TRUE:
 | 
						|
        return self.visitor.OnBool(true)
 | 
						|
    case types.V_FALSE:
 | 
						|
        return self.visitor.OnBool(false)
 | 
						|
    case types.V_STRING:
 | 
						|
        return self.decodeString(val.Iv, val.Ep)
 | 
						|
    case types.V_DOUBLE:
 | 
						|
        return self.visitor.OnFloat64(val.Dv,
 | 
						|
            json.Number(self.parser.s[val.Ep:self.parser.p]))
 | 
						|
    case types.V_INTEGER:
 | 
						|
        return self.visitor.OnInt64(val.Iv,
 | 
						|
            json.Number(self.parser.s[val.Ep:self.parser.p]))
 | 
						|
    case types.V_ARRAY:
 | 
						|
        return self.decodeArray()
 | 
						|
    case types.V_OBJECT:
 | 
						|
        return self.decodeObject()
 | 
						|
    default:
 | 
						|
        return types.ParsingError(-val.Vt)
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// NOTE: keep in sync with (*Parser).decodeArray method.
 | 
						|
func (self *traverser) decodeArray() error {
 | 
						|
    sp := self.parser.p
 | 
						|
    ns := len(self.parser.s)
 | 
						|
 | 
						|
    /* check for EOF */
 | 
						|
    self.parser.p = self.parser.lspace(sp)
 | 
						|
    if self.parser.p >= ns {
 | 
						|
        return types.ERR_EOF
 | 
						|
    }
 | 
						|
 | 
						|
    /* check for empty array */
 | 
						|
    if self.parser.s[self.parser.p] == ']' {
 | 
						|
        self.parser.p++
 | 
						|
        if err := self.visitor.OnArrayBegin(0); err != nil {
 | 
						|
            return err
 | 
						|
        }
 | 
						|
        return self.visitor.OnArrayEnd()
 | 
						|
    }
 | 
						|
 | 
						|
    /* allocate array space and parse every element */
 | 
						|
    if err := self.visitor.OnArrayBegin(_DEFAULT_NODE_CAP); err != nil {
 | 
						|
        return err
 | 
						|
    }
 | 
						|
    for {
 | 
						|
        /* decode the value */
 | 
						|
        if err := self.decodeValue(); err != nil {
 | 
						|
            return err
 | 
						|
        }
 | 
						|
        self.parser.p = self.parser.lspace(self.parser.p)
 | 
						|
 | 
						|
        /* check for EOF */
 | 
						|
        if self.parser.p >= ns {
 | 
						|
            return types.ERR_EOF
 | 
						|
        }
 | 
						|
 | 
						|
        /* check for the next character */
 | 
						|
        switch self.parser.s[self.parser.p] {
 | 
						|
        case ',':
 | 
						|
            self.parser.p++
 | 
						|
        case ']':
 | 
						|
            self.parser.p++
 | 
						|
            return self.visitor.OnArrayEnd()
 | 
						|
        default:
 | 
						|
            return types.ERR_INVALID_CHAR
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// NOTE: keep in sync with (*Parser).decodeObject method.
 | 
						|
func (self *traverser) decodeObject() error {
 | 
						|
    sp := self.parser.p
 | 
						|
    ns := len(self.parser.s)
 | 
						|
 | 
						|
    /* check for EOF */
 | 
						|
    self.parser.p = self.parser.lspace(sp)
 | 
						|
    if self.parser.p >= ns {
 | 
						|
        return types.ERR_EOF
 | 
						|
    }
 | 
						|
 | 
						|
    /* check for empty object */
 | 
						|
    if self.parser.s[self.parser.p] == '}' {
 | 
						|
        self.parser.p++
 | 
						|
        if err := self.visitor.OnObjectBegin(0); err != nil {
 | 
						|
            return err
 | 
						|
        }
 | 
						|
        return self.visitor.OnObjectEnd()
 | 
						|
    }
 | 
						|
 | 
						|
    /* allocate object space and decode each pair */
 | 
						|
    if err := self.visitor.OnObjectBegin(_DEFAULT_NODE_CAP); err != nil {
 | 
						|
        return err
 | 
						|
    }
 | 
						|
    for {
 | 
						|
        var njs types.JsonState
 | 
						|
        var err types.ParsingError
 | 
						|
 | 
						|
        /* decode the key */
 | 
						|
        if njs = self.parser.decodeValue(); njs.Vt != types.V_STRING {
 | 
						|
            return types.ERR_INVALID_CHAR
 | 
						|
        }
 | 
						|
 | 
						|
        /* extract the key */
 | 
						|
        idx := self.parser.p - 1
 | 
						|
        key := self.parser.s[njs.Iv:idx]
 | 
						|
 | 
						|
        /* check for escape sequence */
 | 
						|
        if njs.Ep != -1 {
 | 
						|
            if key, err = unquote(key); err != 0 {
 | 
						|
                return err
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if err := self.visitor.OnObjectKey(key); err != nil {
 | 
						|
            return err
 | 
						|
        }
 | 
						|
 | 
						|
        /* expect a ':' delimiter */
 | 
						|
        if err = self.parser.delim(); err != 0 {
 | 
						|
            return err
 | 
						|
        }
 | 
						|
 | 
						|
        /* decode the value */
 | 
						|
        if err := self.decodeValue(); err != nil {
 | 
						|
            return err
 | 
						|
        }
 | 
						|
 | 
						|
        self.parser.p = self.parser.lspace(self.parser.p)
 | 
						|
 | 
						|
        /* check for EOF */
 | 
						|
        if self.parser.p >= ns {
 | 
						|
            return types.ERR_EOF
 | 
						|
        }
 | 
						|
 | 
						|
        /* check for the next character */
 | 
						|
        switch self.parser.s[self.parser.p] {
 | 
						|
        case ',':
 | 
						|
            self.parser.p++
 | 
						|
        case '}':
 | 
						|
            self.parser.p++
 | 
						|
            return self.visitor.OnObjectEnd()
 | 
						|
        default:
 | 
						|
            return types.ERR_INVALID_CHAR
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// NOTE: keep in sync with (*Parser).decodeString method.
 | 
						|
func (self *traverser) decodeString(iv int64, ep int) error {
 | 
						|
    p := self.parser.p - 1
 | 
						|
    s := self.parser.s[iv:p]
 | 
						|
 | 
						|
    /* fast path: no escape sequence */
 | 
						|
    if ep == -1 {
 | 
						|
        return self.visitor.OnString(s)
 | 
						|
    }
 | 
						|
 | 
						|
    /* unquote the string */
 | 
						|
    out, err := unquote(s)
 | 
						|
    if err != 0 {
 | 
						|
        return err
 | 
						|
    }
 | 
						|
    return self.visitor.OnString(out)
 | 
						|
}
 |