Files
ent/dialect/gremlin/encoding/graphson/interface.go
Ariel Mashraki bd07c86b60 all: add license header to all go files
Summary:
Used addlicense to generate this:
 addlicense -c "Facebook Inc" -f license_header .

example was taken from: https://github.com/facebook/litho/blob/master/lib/soloader/BUCK

Reviewed By: alexsn

Differential Revision: D17070152

fbshipit-source-id: e7b91398d7f6181727be3400c1872ad5f28e38ed
2019-08-27 04:48:28 -07:00

154 lines
3.7 KiB
Go

// Copyright 2019-present Facebook Inc. All rights reserved.
// This source code is licensed under the Apache 2.0 license found
// in the LICENSE file in the root directory of this source tree.
package graphson
import (
"bytes"
"fmt"
"io"
"reflect"
"unsafe"
"github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"github.com/pkg/errors"
)
// DecoratorOfInterface decorates a value decoder of an interface type.
func (decodeExtension) DecoratorOfInterface(typ reflect2.Type, dec jsoniter.ValDecoder) jsoniter.ValDecoder {
if _, ok := typ.(*reflect2.UnsafeEFaceType); ok {
return efaceDecoder{typ, dec}
}
return dec
}
type efaceDecoder struct {
typ reflect2.Type
jsoniter.ValDecoder
}
func (dec efaceDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
switch next := iter.WhatIsNext(); next {
case jsoniter.StringValue, jsoniter.BoolValue, jsoniter.NilValue:
dec.ValDecoder.Decode(ptr, iter)
case jsoniter.ObjectValue:
dec.decode(ptr, iter)
default:
iter.ReportError("decode empty interface", fmt.Sprintf("unexpected value type: %d", next))
}
}
func (dec efaceDecoder) decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
data := iter.SkipAndReturnBytes()
if iter.Error != nil && iter.Error != io.EOF {
return
}
rtype, err := dec.reflectBytes(data)
if err != nil {
iter.ReportError("decode empty interface", err.Error())
return
}
it := config.BorrowIterator(data)
defer config.ReturnIterator(it)
var val interface{}
if rtype != nil {
val = rtype.New()
it.ReadVal(val)
val = rtype.Indirect(val)
} else {
if jsoniter.Get(data, TypeKey).LastError() == nil {
vk := jsoniter.Get(data, ValueKey)
if vk.LastError() == nil {
val = vk.GetInterface()
}
}
if val == nil {
val = it.Read()
}
}
if it.Error != nil && it.Error != io.EOF {
iter.ReportError("decode empty interface", it.Error.Error())
return
}
// nolint: gas
dec.typ.UnsafeSet(ptr, unsafe.Pointer(&val))
}
func (dec efaceDecoder) reflectBytes(data []byte) (reflect2.Type, error) {
typ := Type(jsoniter.Get(data, TypeKey).ToString())
rtype := dec.reflectType(typ)
if rtype != nil {
return rtype, nil
}
switch typ {
case listType:
return dec.reflectSlice(data)
case mapType:
return dec.reflectMap(data)
default:
return nil, nil
}
}
func (efaceDecoder) reflectType(typ Type) reflect2.Type {
switch typ {
case doubleType:
return reflect2.TypeOf(float64(0))
case floatType:
return reflect2.TypeOf(float32(0))
case byteType:
return reflect2.TypeOf(uint8(0))
case int16Type:
return reflect2.TypeOf(int16(0))
case int32Type:
return reflect2.TypeOf(int32(0))
case int64Type, bigIntegerType:
return reflect2.TypeOf(int64(0))
case byteBufferType:
return reflect2.TypeOf([]byte{})
default:
return nil
}
}
func (efaceDecoder) reflectSlice(data []byte) (reflect2.Type, error) {
var elem interface{}
if err := Unmarshal(data, &[...]*interface{}{&elem}); err != nil {
return nil, errors.Wrap(err, "cannot read first list element")
}
if elem == nil {
return reflect2.TypeOf([]interface{}{}), nil
}
sliceType := reflect.SliceOf(reflect.TypeOf(elem))
return reflect2.Type2(sliceType), nil
}
func (efaceDecoder) reflectMap(data []byte) (reflect2.Type, error) {
var key, elem interface{}
if err := Unmarshal(
bytes.Replace(data, []byte(mapType), []byte(listType), 1),
&[...]*interface{}{&key, &elem},
); err != nil {
return nil, errors.Wrap(err, "cannot unmarshal first map item")
}
if key == nil {
return reflect2.TypeOf(map[interface{}]interface{}{}), nil
} else if elem == nil {
return nil, errors.New("expect map element, but found only key")
}
mapType := reflect.MapOf(reflect.TypeOf(key), reflect.TypeOf(elem))
return reflect2.Type2(mapType), nil
}