mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2024-12-29 22:23:10 +00:00
98263a7de6
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
325 lines
7 KiB
Go
325 lines
7 KiB
Go
package jsoniter
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/modern-go/reflect2"
|
|
"io"
|
|
"reflect"
|
|
"strconv"
|
|
"unsafe"
|
|
)
|
|
|
|
// Any generic object representation.
|
|
// The lazy json implementation holds []byte and parse lazily.
|
|
type Any interface {
|
|
LastError() error
|
|
ValueType() ValueType
|
|
MustBeValid() Any
|
|
ToBool() bool
|
|
ToInt() int
|
|
ToInt32() int32
|
|
ToInt64() int64
|
|
ToUint() uint
|
|
ToUint32() uint32
|
|
ToUint64() uint64
|
|
ToFloat32() float32
|
|
ToFloat64() float64
|
|
ToString() string
|
|
ToVal(val interface{})
|
|
Get(path ...interface{}) Any
|
|
Size() int
|
|
Keys() []string
|
|
GetInterface() interface{}
|
|
WriteTo(stream *Stream)
|
|
}
|
|
|
|
type baseAny struct{}
|
|
|
|
func (any *baseAny) Get(path ...interface{}) Any {
|
|
return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
|
|
}
|
|
|
|
func (any *baseAny) Size() int {
|
|
return 0
|
|
}
|
|
|
|
func (any *baseAny) Keys() []string {
|
|
return []string{}
|
|
}
|
|
|
|
func (any *baseAny) ToVal(obj interface{}) {
|
|
panic("not implemented")
|
|
}
|
|
|
|
// WrapInt32 turn int32 into Any interface
|
|
func WrapInt32(val int32) Any {
|
|
return &int32Any{baseAny{}, val}
|
|
}
|
|
|
|
// WrapInt64 turn int64 into Any interface
|
|
func WrapInt64(val int64) Any {
|
|
return &int64Any{baseAny{}, val}
|
|
}
|
|
|
|
// WrapUint32 turn uint32 into Any interface
|
|
func WrapUint32(val uint32) Any {
|
|
return &uint32Any{baseAny{}, val}
|
|
}
|
|
|
|
// WrapUint64 turn uint64 into Any interface
|
|
func WrapUint64(val uint64) Any {
|
|
return &uint64Any{baseAny{}, val}
|
|
}
|
|
|
|
// WrapFloat64 turn float64 into Any interface
|
|
func WrapFloat64(val float64) Any {
|
|
return &floatAny{baseAny{}, val}
|
|
}
|
|
|
|
// WrapString turn string into Any interface
|
|
func WrapString(val string) Any {
|
|
return &stringAny{baseAny{}, val}
|
|
}
|
|
|
|
// Wrap turn a go object into Any interface
|
|
func Wrap(val interface{}) Any {
|
|
if val == nil {
|
|
return &nilAny{}
|
|
}
|
|
asAny, isAny := val.(Any)
|
|
if isAny {
|
|
return asAny
|
|
}
|
|
typ := reflect2.TypeOf(val)
|
|
switch typ.Kind() {
|
|
case reflect.Slice:
|
|
return wrapArray(val)
|
|
case reflect.Struct:
|
|
return wrapStruct(val)
|
|
case reflect.Map:
|
|
return wrapMap(val)
|
|
case reflect.String:
|
|
return WrapString(val.(string))
|
|
case reflect.Int:
|
|
if strconv.IntSize == 32 {
|
|
return WrapInt32(int32(val.(int)))
|
|
}
|
|
return WrapInt64(int64(val.(int)))
|
|
case reflect.Int8:
|
|
return WrapInt32(int32(val.(int8)))
|
|
case reflect.Int16:
|
|
return WrapInt32(int32(val.(int16)))
|
|
case reflect.Int32:
|
|
return WrapInt32(val.(int32))
|
|
case reflect.Int64:
|
|
return WrapInt64(val.(int64))
|
|
case reflect.Uint:
|
|
if strconv.IntSize == 32 {
|
|
return WrapUint32(uint32(val.(uint)))
|
|
}
|
|
return WrapUint64(uint64(val.(uint)))
|
|
case reflect.Uintptr:
|
|
if ptrSize == 32 {
|
|
return WrapUint32(uint32(val.(uintptr)))
|
|
}
|
|
return WrapUint64(uint64(val.(uintptr)))
|
|
case reflect.Uint8:
|
|
return WrapUint32(uint32(val.(uint8)))
|
|
case reflect.Uint16:
|
|
return WrapUint32(uint32(val.(uint16)))
|
|
case reflect.Uint32:
|
|
return WrapUint32(uint32(val.(uint32)))
|
|
case reflect.Uint64:
|
|
return WrapUint64(val.(uint64))
|
|
case reflect.Float32:
|
|
return WrapFloat64(float64(val.(float32)))
|
|
case reflect.Float64:
|
|
return WrapFloat64(val.(float64))
|
|
case reflect.Bool:
|
|
if val.(bool) == true {
|
|
return &trueAny{}
|
|
}
|
|
return &falseAny{}
|
|
}
|
|
return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", typ)}
|
|
}
|
|
|
|
// ReadAny read next JSON element as an Any object. It is a better json.RawMessage.
|
|
func (iter *Iterator) ReadAny() Any {
|
|
return iter.readAny()
|
|
}
|
|
|
|
func (iter *Iterator) readAny() Any {
|
|
c := iter.nextToken()
|
|
switch c {
|
|
case '"':
|
|
iter.unreadByte()
|
|
return &stringAny{baseAny{}, iter.ReadString()}
|
|
case 'n':
|
|
iter.skipThreeBytes('u', 'l', 'l') // null
|
|
return &nilAny{}
|
|
case 't':
|
|
iter.skipThreeBytes('r', 'u', 'e') // true
|
|
return &trueAny{}
|
|
case 'f':
|
|
iter.skipFourBytes('a', 'l', 's', 'e') // false
|
|
return &falseAny{}
|
|
case '{':
|
|
return iter.readObjectAny()
|
|
case '[':
|
|
return iter.readArrayAny()
|
|
case '-':
|
|
return iter.readNumberAny(false)
|
|
case 0:
|
|
return &invalidAny{baseAny{}, errors.New("input is empty")}
|
|
default:
|
|
return iter.readNumberAny(true)
|
|
}
|
|
}
|
|
|
|
func (iter *Iterator) readNumberAny(positive bool) Any {
|
|
iter.startCapture(iter.head - 1)
|
|
iter.skipNumber()
|
|
lazyBuf := iter.stopCapture()
|
|
return &numberLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
|
}
|
|
|
|
func (iter *Iterator) readObjectAny() Any {
|
|
iter.startCapture(iter.head - 1)
|
|
iter.skipObject()
|
|
lazyBuf := iter.stopCapture()
|
|
return &objectLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
|
}
|
|
|
|
func (iter *Iterator) readArrayAny() Any {
|
|
iter.startCapture(iter.head - 1)
|
|
iter.skipArray()
|
|
lazyBuf := iter.stopCapture()
|
|
return &arrayLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
|
|
}
|
|
|
|
func locateObjectField(iter *Iterator, target string) []byte {
|
|
var found []byte
|
|
iter.ReadObjectCB(func(iter *Iterator, field string) bool {
|
|
if field == target {
|
|
found = iter.SkipAndReturnBytes()
|
|
return false
|
|
}
|
|
iter.Skip()
|
|
return true
|
|
})
|
|
return found
|
|
}
|
|
|
|
func locateArrayElement(iter *Iterator, target int) []byte {
|
|
var found []byte
|
|
n := 0
|
|
iter.ReadArrayCB(func(iter *Iterator) bool {
|
|
if n == target {
|
|
found = iter.SkipAndReturnBytes()
|
|
return false
|
|
}
|
|
iter.Skip()
|
|
n++
|
|
return true
|
|
})
|
|
return found
|
|
}
|
|
|
|
func locatePath(iter *Iterator, path []interface{}) Any {
|
|
for i, pathKeyObj := range path {
|
|
switch pathKey := pathKeyObj.(type) {
|
|
case string:
|
|
valueBytes := locateObjectField(iter, pathKey)
|
|
if valueBytes == nil {
|
|
return newInvalidAny(path[i:])
|
|
}
|
|
iter.ResetBytes(valueBytes)
|
|
case int:
|
|
valueBytes := locateArrayElement(iter, pathKey)
|
|
if valueBytes == nil {
|
|
return newInvalidAny(path[i:])
|
|
}
|
|
iter.ResetBytes(valueBytes)
|
|
case int32:
|
|
if '*' == pathKey {
|
|
return iter.readAny().Get(path[i:]...)
|
|
}
|
|
return newInvalidAny(path[i:])
|
|
default:
|
|
return newInvalidAny(path[i:])
|
|
}
|
|
}
|
|
if iter.Error != nil && iter.Error != io.EOF {
|
|
return &invalidAny{baseAny{}, iter.Error}
|
|
}
|
|
return iter.readAny()
|
|
}
|
|
|
|
var anyType = reflect2.TypeOfPtr((*Any)(nil)).Elem()
|
|
|
|
func createDecoderOfAny(ctx *ctx, typ reflect2.Type) ValDecoder {
|
|
if typ == anyType {
|
|
return &directAnyCodec{}
|
|
}
|
|
if typ.Implements(anyType) {
|
|
return &anyCodec{
|
|
valType: typ,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func createEncoderOfAny(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
if typ == anyType {
|
|
return &directAnyCodec{}
|
|
}
|
|
if typ.Implements(anyType) {
|
|
return &anyCodec{
|
|
valType: typ,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type anyCodec struct {
|
|
valType reflect2.Type
|
|
}
|
|
|
|
func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
obj := codec.valType.UnsafeIndirect(ptr)
|
|
any := obj.(Any)
|
|
any.WriteTo(stream)
|
|
}
|
|
|
|
func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
obj := codec.valType.UnsafeIndirect(ptr)
|
|
any := obj.(Any)
|
|
return any.Size() == 0
|
|
}
|
|
|
|
type directAnyCodec struct {
|
|
}
|
|
|
|
func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|
*(*Any)(ptr) = iter.readAny()
|
|
}
|
|
|
|
func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
any := *(*Any)(ptr)
|
|
if any == nil {
|
|
stream.WriteNil()
|
|
return
|
|
}
|
|
any.WriteTo(stream)
|
|
}
|
|
|
|
func (codec *directAnyCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
any := *(*Any)(ptr)
|
|
return any.Size() == 0
|
|
}
|