mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
325 lines
7.0 KiB
325 lines
7.0 KiB
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 |
|
}
|
|
|