blob: b729f5ad6e396ff181b4448d2983d1d534ec246b [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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 gremlingo
import (
"bytes"
"encoding/binary"
"fmt"
"math"
"math/big"
"reflect"
"time"
"github.com/google/uuid"
)
// Version 1.0
// dataType graphBinary types.
type dataType uint8
// dataType defined as constants.
const (
intType dataType = 0x01
longType dataType = 0x02
stringType dataType = 0x03
dateType dataType = 0x04
timestampType dataType = 0x05
classType dataType = 0x06
doubleType dataType = 0x07
floatType dataType = 0x08
listType dataType = 0x09
mapType dataType = 0x0a
setType dataType = 0x0b
uuidType dataType = 0x0c
edgeType dataType = 0x0d
pathType dataType = 0x0e
propertyType dataType = 0x0f
vertexType dataType = 0x11
vertexPropertyType dataType = 0x12
barrierType dataType = 0x13
bindingType dataType = 0x14
cardinalityType dataType = 0x16
bytecodeType dataType = 0x15
columnType dataType = 0x17
directionType dataType = 0x18
operatorType dataType = 0x19
orderType dataType = 0x1a
pickType dataType = 0x1b
popType dataType = 0x1c
lambdaType dataType = 0x1d
pType dataType = 0x1e
scopeType dataType = 0x1f
tType dataType = 0x20
traverserType dataType = 0x21
bigDecimalType dataType = 0x22
bigIntegerType dataType = 0x23
byteType dataType = 0x24
byteBuffer dataType = 0x25
shortType dataType = 0x26
booleanType dataType = 0x27
textPType dataType = 0x28
traversalStrategyType dataType = 0x29
bulkSetType dataType = 0x2a
mergeType dataType = 0x2e
metricsType dataType = 0x2c
traversalMetricsType dataType = 0x2d
durationType dataType = 0x81
nullType dataType = 0xFE
)
var nullBytes = []byte{nullType.getCodeByte(), 0x01}
func (dataType dataType) getCodeByte() byte {
return byte(dataType)
}
func (dataType dataType) getCodeBytes() []byte {
return []byte{dataType.getCodeByte()}
}
// graphBinaryTypeSerializer struct for the different types of serializers.
type graphBinaryTypeSerializer struct {
logHandler *logHandler
}
func (serializer *graphBinaryTypeSerializer) writeType(value interface{}, buffer *bytes.Buffer, writer writer) ([]byte, error) {
return serializer.writeTypeValue(value, buffer, writer, true)
}
func (serializer *graphBinaryTypeSerializer) writeTypeValue(value interface{}, buffer *bytes.Buffer, writer writer, nullable bool) ([]byte, error) {
if value == nil {
if !nullable {
serializer.logHandler.log(Error, unexpectedNull)
return nil, newError(err0401WriteTypeValueUnexpectedNullError)
}
serializer.writeValueFlagNull(buffer)
return buffer.Bytes(), nil
}
if nullable {
serializer.writeValueFlagNone(buffer)
}
return writer(value, buffer, serializer)
}
// Format: {length}{item_0}...{item_n}
func listWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
v := reflect.ValueOf(value)
valLen := v.Len()
err := binary.Write(buffer, binary.BigEndian, int32(valLen))
if err != nil {
return nil, err
}
for i := 0; i < valLen; i++ {
_, err := typeSerializer.write(v.Index(i).Interface(), buffer)
if err != nil {
return nil, err
}
}
return buffer.Bytes(), nil
}
// Format: {length}{value}
func byteBufferWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
var v ByteBuffer
if reflect.TypeOf(value).Kind() == reflect.Ptr {
v = *(value.(*ByteBuffer))
} else {
v = value.(ByteBuffer)
}
err := binary.Write(buffer, binary.BigEndian, int32(len(v.Data)))
if err != nil {
return nil, err
}
buffer.Write(v.Data)
return buffer.Bytes(), nil
}
// Format: {length}{item_0}...{item_n}
// Item format: {type_code}{type_info}{value_flag}{value}
func mapWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
if value == nil {
_, err := typeSerializer.writeValue(int32(0), buffer, false)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
v := reflect.ValueOf(value)
keys := v.MapKeys()
err := binary.Write(buffer, binary.BigEndian, int32(len(keys)))
if err != nil {
return nil, err
}
for _, k := range keys {
convKey := k.Convert(v.Type().Key())
// serialize k
_, err := typeSerializer.write(k.Interface(), buffer)
if err != nil {
return nil, err
}
// serialize v.MapIndex(c_key)
val := v.MapIndex(convKey)
_, err = typeSerializer.write(val.Interface(), buffer)
if err != nil {
return nil, err
}
}
return buffer.Bytes(), nil
}
func instructionWriter(instructions []instruction, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) error {
// Write {steps_length}, i.e number of steps.
err := binary.Write(buffer, binary.BigEndian, int32(len(instructions)))
if err != nil {
return err
}
// Write {step_0} to {step_n}.
for _, instruction := range instructions {
// Write {name} of {step_i}.
// Note: {name} follows string writing, therefore write string length followed by actual string.
_, err = typeSerializer.writeValue(instruction.operator, buffer, false)
if err != nil {
return err
}
// Write {values_length} of {step_i}.
err = binary.Write(buffer, binary.BigEndian, int32(len(instruction.arguments)))
if err != nil {
return err
}
// Write {values_0} to {values_n}.
for _, argument := range instruction.arguments {
_, err = typeSerializer.write(argument, buffer)
if err != nil {
return err
}
}
}
return nil
}
// Format: {steps_length}{step_0}…{step_n}{sources_length}{source_0}…{source_n}
// Where:
// {steps_length} is an Int value describing the amount of steps.
// {step_i} is composed of {name}{values_length}{value_0}…{value_n}, where:
// {name} is a String. This is also known as the operator.
// {values_length} is an Int describing the amount values.
// {value_i} is a fully qualified typed value composed of {type_code}{type_info}{value_flag}{value} describing the step argument.
func bytecodeWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
var bc Bytecode
switch typedVal := value.(type) {
case *GraphTraversal:
bc = *typedVal.Bytecode
case Bytecode:
bc = typedVal
case *Bytecode:
bc = *typedVal
default:
return nil, newError(err0402BytecodeWriterError)
}
// Write {steps_length} and {step_0} through {step_n}, then {sources_length} and {source_0} through {source_n}
err := instructionWriter(bc.stepInstructions, buffer, typeSerializer)
if err != nil {
return nil, err
}
err = instructionWriter(bc.sourceInstructions, buffer, typeSerializer)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
func stringWriter(value interface{}, buffer *bytes.Buffer, _ *graphBinaryTypeSerializer) ([]byte, error) {
err := binary.Write(buffer, binary.BigEndian, int32(len(value.(string))))
if err != nil {
return nil, err
}
_, err = buffer.WriteString(value.(string))
return buffer.Bytes(), err
}
func longWriter(value interface{}, buffer *bytes.Buffer, _ *graphBinaryTypeSerializer) ([]byte, error) {
switch v := value.(type) {
case int:
value = int64(v)
case uint32:
value = int64(v)
}
err := binary.Write(buffer, binary.BigEndian, value)
return buffer.Bytes(), err
}
func intWriter(value interface{}, buffer *bytes.Buffer, _ *graphBinaryTypeSerializer) ([]byte, error) {
switch v := value.(type) {
case uint16:
value = int32(v)
}
err := binary.Write(buffer, binary.BigEndian, value.(int32))
return buffer.Bytes(), err
}
func shortWriter(value interface{}, buffer *bytes.Buffer, _ *graphBinaryTypeSerializer) ([]byte, error) {
switch v := value.(type) {
case int8:
value = int16(v)
}
err := binary.Write(buffer, binary.BigEndian, value.(int16))
return buffer.Bytes(), err
}
// Golang stores BigIntegers with big.Int types
// it contains an unsigned representation of the number and uses a boolean to track +ve and -ve
// getSignedBytesFromBigInt gives us the signed(two's complement) byte array that represents the unsigned byte array in
// big.Int
func getSignedBytesFromBigInt(n *big.Int) []byte {
var one = big.NewInt(1)
if n.Sign() == 1 {
// add a buffer 0x00 byte to the start of byte array if number is positive and has a 1 in its MSB
b := n.Bytes()
if b[0]&0x80 > 0 {
b = append([]byte{0}, b...)
}
return b
} else if n.Sign() == -1 {
// Convert Unsigned byte array to signed byte array
length := uint(n.BitLen()/8+1) * 8
b := new(big.Int).Add(n, new(big.Int).Lsh(one, length)).Bytes()
// Strip any redundant 0xff bytes from the front of the byte array if the following byte starts with a 1
if len(b) >= 2 && b[0] == 0xff && b[1]&0x80 != 0 {
b = b[1:]
}
return b
}
return []byte{}
}
// Format: {length}{value_0}...{value_n}
func bigIntWriter(value interface{}, buffer *bytes.Buffer, _ *graphBinaryTypeSerializer) ([]byte, error) {
var v big.Int
switch val := value.(type) {
case uint:
v = *(new(big.Int).SetUint64(uint64(val)))
case uint64:
v = *(new(big.Int).SetUint64(val))
default:
if reflect.TypeOf(value).Kind() == reflect.Ptr {
v = *(value.(*big.Int))
} else {
v = value.(big.Int)
}
}
signedBytes := getSignedBytesFromBigInt(&v)
err := binary.Write(buffer, binary.BigEndian, int32(len(signedBytes)))
if err != nil {
return nil, err
}
for i := 0; i < len(signedBytes); i++ {
err := binary.Write(buffer, binary.BigEndian, signedBytes[i])
if err != nil {
return nil, err
}
}
return buffer.Bytes(), nil
}
// Format: {scale}{unscaled_value}
func bigDecimalWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
var v BigDecimal
if reflect.TypeOf(value).Kind() == reflect.Ptr {
v = *(value.(*BigDecimal))
} else {
v = value.(BigDecimal)
}
err := binary.Write(buffer, binary.BigEndian, v.Scale)
if err != nil {
return nil, err
}
return bigIntWriter(v.UnscaledValue, buffer, typeSerializer)
}
func classWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
var v GremlinType
if reflect.TypeOf(value).Kind() == reflect.Ptr {
v = *(value.(*GremlinType))
} else {
v = value.(GremlinType)
}
return stringWriter(v.Fqcn, buffer, typeSerializer)
}
// Format: {Id}{Label}{properties}
func vertexWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
v := value.(*Vertex)
_, err := typeSerializer.write(v.Id, buffer)
if err != nil {
return nil, err
}
// Not fully qualified.
_, err = typeSerializer.writeValue(v.Label, buffer, false)
if err != nil {
return nil, err
}
// Note that as TinkerPop currently send "references" only, properties will always be null
buffer.Write(nullBytes)
return buffer.Bytes(), nil
}
// Format: {Id}{Label}{inVId}{inVLabel}{outVId}{outVLabel}{parent}{properties}
func edgeWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
e := value.(*Edge)
_, err := typeSerializer.write(e.Id, buffer)
if err != nil {
return nil, err
}
// Not fully qualified
_, err = typeSerializer.writeValue(e.Label, buffer, false)
if err != nil {
return nil, err
}
// Write in-vertex
_, err = typeSerializer.write(e.InV.Id, buffer)
if err != nil {
return nil, err
}
// Not fully qualified.
_, err = typeSerializer.writeValue(e.InV.Label, buffer, false)
if err != nil {
return nil, err
}
// Write out-vertex
_, err = typeSerializer.write(e.OutV.Id, buffer)
if err != nil {
return nil, err
}
// Not fully qualified.
_, err = typeSerializer.writeValue(e.OutV.Label, buffer, false)
if err != nil {
return nil, err
}
// Note that as TinkerPop currently send "references" only, parent and properties will always be null
buffer.Write(nullBytes)
buffer.Write(nullBytes)
return buffer.Bytes(), nil
}
// Format: {Key}{Value}{parent}
func propertyWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
v := value.(*Property)
// Not fully qualified.
_, err := typeSerializer.writeValue(v.Key, buffer, false)
if err != nil {
return nil, err
}
_, err = typeSerializer.write(v.Value, buffer)
if err != nil {
return nil, err
}
// Note that as TinkerPop currently send "references" only, parent and properties will always be null
buffer.Write(nullBytes)
return buffer.Bytes(), nil
}
// Format: {Id}{Label}{Value}{parent}{properties}
func vertexPropertyWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
vp := value.(*VertexProperty)
_, err := typeSerializer.write(vp.Id, buffer)
if err != nil {
return nil, err
}
// Not fully qualified.
_, err = typeSerializer.writeValue(vp.Label, buffer, false)
if err != nil {
return nil, err
}
_, err = typeSerializer.write(vp.Value, buffer)
if err != nil {
return nil, err
}
// Note that as TinkerPop currently send "references" only, parent and properties will always be null
buffer.Write(nullBytes)
buffer.Write(nullBytes)
return buffer.Bytes(), nil
}
// Format: {Labels}{Objects}
func pathWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
p := value.(*Path)
_, err := typeSerializer.write(p.Labels, buffer)
if err != nil {
return nil, err
}
_, err = typeSerializer.write(p.Objects, buffer)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
// Format: Same as List.
// Mostly similar to listWriter with small changes
func setWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
slice := value.(Set).ToSlice()
return listWriter(slice, buffer, typeSerializer)
}
func timeWriter(value interface{}, buffer *bytes.Buffer, _ *graphBinaryTypeSerializer) ([]byte, error) {
t := value.(time.Time)
err := binary.Write(buffer, binary.BigEndian, t.UnixMilli())
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
func durationWriter(value interface{}, buffer *bytes.Buffer, _ *graphBinaryTypeSerializer) ([]byte, error) {
t := value.(time.Duration)
sec := int64(t / time.Second)
nanos := int32(t % time.Second)
err := binary.Write(buffer, binary.BigEndian, sec)
if err != nil {
return nil, err
}
err = binary.Write(buffer, binary.BigEndian, nanos)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
const (
valueFlagNull byte = 1
valueFlagNone byte = 0
)
func enumWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
_, err := typeSerializer.write(reflect.ValueOf(value).String(), buffer)
return buffer.Bytes(), err
}
// Format: {language}{script}{arguments_length}
func lambdaWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
lambda := value.(*Lambda)
if lambda.Language == "" {
lambda.Language = "gremlin-groovy"
}
_, err := typeSerializer.writeValue(lambda.Language, buffer, false)
if err != nil {
return nil, err
}
_, err = typeSerializer.writeValue(lambda.Script, buffer, false)
if err != nil {
return nil, err
}
// It's hard to know how many parameters there are without extensive string parsing.
// Instead, we can set -1 which means unknown.
err = binary.Write(buffer, binary.BigEndian, int32(-1))
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
// Format: {strategy_class}{configuration}
func traversalStrategyWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
ts := value.(*traversalStrategy)
_, err := typeSerializer.writeValue(ts.name, buffer, false)
if err != nil {
return nil, err
}
return mapWriter(ts.configuration, buffer, typeSerializer)
}
func pWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
var v p
if reflect.TypeOf(value).Kind() == reflect.Ptr {
v = *(value.(*p))
} else {
v = value.(p)
}
_, err := typeSerializer.writeValue(v.operator, buffer, false)
if err != nil {
return nil, err
}
err = binary.Write(buffer, binary.BigEndian, int32(len(v.values)))
if err != nil {
return nil, err
}
for _, pValue := range v.values {
_, err := typeSerializer.write(pValue, buffer)
if err != nil {
return nil, err
}
}
return buffer.Bytes(), err
}
func textPWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
var v textP
if reflect.TypeOf(value).Kind() == reflect.Ptr {
v = *(value.(*textP))
} else {
v = value.(textP)
}
_, err := typeSerializer.writeValue(v.operator, buffer, false)
if err != nil {
return nil, err
}
err = binary.Write(buffer, binary.BigEndian, int32(len(v.values)))
if err != nil {
return nil, err
}
for _, pValue := range v.values {
_, err := typeSerializer.write(pValue, buffer)
if err != nil {
return nil, err
}
}
return buffer.Bytes(), err
}
// Format: {key}{value}
func bindingWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) {
var v Binding
if reflect.TypeOf(value).Kind() == reflect.Ptr {
v = *(value.(*Binding))
} else {
v = value.(Binding)
}
// Not fully qualified.
_, err := typeSerializer.writeValue(v.Key, buffer, false)
if err != nil {
return nil, err
}
_, err = typeSerializer.write(v.Value, buffer)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
func (serializer *graphBinaryTypeSerializer) getType(val interface{}) (dataType, error) {
switch val.(type) {
case *Bytecode, Bytecode, *GraphTraversal:
return bytecodeType, nil
case string:
return stringType, nil
case uint, uint64, *big.Int:
return bigIntegerType, nil
case int64, int, uint32:
return longType, nil
case int32, uint16:
return intType, nil
case int8, int16: // GraphBinary doesn't have a type for signed 8-bit integer, serializing int8 as Short instead.
return shortType, nil
case uint8:
return byteType, nil
case bool:
return booleanType, nil
case uuid.UUID:
return uuidType, nil
case float32:
return floatType, nil
case float64:
return doubleType, nil
case *Vertex:
return vertexType, nil
case *Edge:
return edgeType, nil
case *Property:
return propertyType, nil
case *VertexProperty:
return vertexPropertyType, nil
case *Lambda:
return lambdaType, nil
case *traversalStrategy:
return traversalStrategyType, nil
case *Path:
return pathType, nil
case Set:
return setType, nil
case time.Time:
return dateType, nil
case time.Duration:
return durationType, nil
case cardinality:
return cardinalityType, nil
case column:
return columnType, nil
case direction:
return directionType, nil
case operator:
return operatorType, nil
case order:
return orderType, nil
case pick:
return pickType, nil
case pop:
return popType, nil
case t:
return tType, nil
case barrier:
return barrierType, nil
case scope:
return scopeType, nil
case merge:
return mergeType, nil
case p, Predicate:
return pType, nil
case textP, TextPredicate:
return textPType, nil
case *Binding, Binding:
return bindingType, nil
case *BigDecimal, BigDecimal:
return bigDecimalType, nil
case *GremlinType, GremlinType:
return classType, nil
case *Metrics, Metrics:
return metricsType, nil
case *TraversalMetrics, TraversalMetrics:
return traversalMetricsType, nil
case *ByteBuffer, ByteBuffer:
return byteBuffer, nil
default:
switch reflect.TypeOf(val).Kind() {
case reflect.Map:
return mapType, nil
case reflect.Array, reflect.Slice:
// We can write an array or slice into the list dataType.
return listType, nil
default:
serializer.logHandler.logf(Error, serializeDataTypeError, reflect.TypeOf(val).Name())
return intType, newError(err0407GetSerializerToWriteUnknownTypeError, reflect.TypeOf(val).Name())
}
}
}
func (serializer *graphBinaryTypeSerializer) getWriter(dataType dataType) (writer, error) {
if writer, ok := serializers[dataType]; ok {
return writer, nil
}
serializer.logHandler.logf(Error, deserializeDataTypeError, int32(dataType))
return nil, newError(err0407GetSerializerToWriteUnknownTypeError, dataType)
}
// gets the type of the serializer based on the value
func (serializer *graphBinaryTypeSerializer) getSerializerToWrite(val interface{}) (writer, dataType, error) {
dataType, err := serializer.getType(val)
if err != nil {
return nil, intType, err
}
writer, err := serializer.getWriter(dataType)
if err != nil {
return nil, intType, err
}
return writer, dataType, nil
}
// Writes an object in fully-qualified format, containing {type_code}{type_info}{value_flag}{value}.
func (serializer *graphBinaryTypeSerializer) write(valueObject interface{}, buffer *bytes.Buffer) (interface{}, error) {
if valueObject == nil {
// return Object of type "unspecified object null" with the value flag set to null.
buffer.Write(nullBytes)
return buffer.Bytes(), nil
}
writer, dataType, err := serializer.getSerializerToWrite(valueObject)
if err != nil {
return nil, err
}
buffer.Write(dataType.getCodeBytes())
return serializer.writeType(valueObject, buffer, writer)
}
// Writes a value without including type information.
func (serializer *graphBinaryTypeSerializer) writeValue(value interface{}, buffer *bytes.Buffer, nullable bool) (interface{}, error) {
if value == nil {
if !nullable {
serializer.logHandler.log(Error, unexpectedNull)
return nil, newError(err0403WriteValueUnexpectedNullError)
}
serializer.writeValueFlagNull(buffer)
return buffer.Bytes(), nil
}
writer, _, err := serializer.getSerializerToWrite(value)
if err != nil {
return nil, err
}
message, err := serializer.writeTypeValue(value, buffer, writer, nullable)
if err != nil {
return nil, err
}
return message, nil
}
func (serializer *graphBinaryTypeSerializer) writeValueFlagNull(buffer *bytes.Buffer) {
buffer.WriteByte(valueFlagNull)
}
func (serializer *graphBinaryTypeSerializer) writeValueFlagNone(buffer *bytes.Buffer) {
buffer.WriteByte(valueFlagNone)
}
// readers
func readTemp(data *[]byte, i *int, len int) *[]byte {
tmp := make([]byte, len)
for j := 0; j < len; j++ {
tmp[j] = (*data)[j+*i]
}
*i += len
return &tmp
}
// Primitive
func readBoolean(data *[]byte, i *int) (interface{}, error) {
b, _ := readByte(data, i)
return b != uint8(0), nil
}
func readByteSafe(data *[]byte, i *int) byte {
*i++
return (*data)[*i-1]
}
func readByte(data *[]byte, i *int) (interface{}, error) {
return readByteSafe(data, i), nil
}
func readShort(data *[]byte, i *int) (interface{}, error) {
return int16(binary.BigEndian.Uint16(*readTemp(data, i, 2))), nil
}
func readIntSafe(data *[]byte, i *int) int32 {
return int32(binary.BigEndian.Uint32(*readTemp(data, i, 4)))
}
func readInt(data *[]byte, i *int) (interface{}, error) {
return readIntSafe(data, i), nil
}
func readLongSafe(data *[]byte, i *int) int64 {
return int64(binary.BigEndian.Uint64(*readTemp(data, i, 8)))
}
func readLong(data *[]byte, i *int) (interface{}, error) {
return readLongSafe(data, i), nil
}
func readBigInt(data *[]byte, i *int) (interface{}, error) {
sz := readIntSafe(data, i)
b := readTemp(data, i, int(sz))
var newBigInt = big.NewInt(0).SetBytes(*b)
var one = big.NewInt(1)
if len(*b) == 0 {
return newBigInt, nil
}
// If the first bit in the first element of the byte array is a 1, we need to interpret the byte array as a two's complement representation
if (*b)[0]&0x80 == 0x00 {
newBigInt.SetBytes(*b)
return newBigInt, nil
}
// Undo two's complement to byte array and set negative boolean to true
length := uint((len(*b)*8)/8+1) * 8
b2 := new(big.Int).Sub(newBigInt, new(big.Int).Lsh(one, length)).Bytes()
// Strip the resulting 0xff byte at the start of array
b2 = b2[1:]
// Strip any redundant 0x00 byte at the start of array
if b2[0] == 0x00 {
b2 = b2[1:]
}
newBigInt = big.NewInt(0)
newBigInt.SetBytes(b2)
newBigInt.Neg(newBigInt)
return newBigInt, nil
}
func readBigDecimal(data *[]byte, i *int) (interface{}, error) {
bigDecimal := &BigDecimal{}
bigDecimal.Scale = readIntSafe(data, i)
unscaled, err := readBigInt(data, i)
if err != nil {
return nil, err
}
bigDecimal.UnscaledValue = *unscaled.(*big.Int)
return bigDecimal, nil
}
func readUint32Safe(data *[]byte, i *int) uint32 {
return binary.BigEndian.Uint32(*readTemp(data, i, 4))
}
func readFloat(data *[]byte, i *int) (interface{}, error) {
return math.Float32frombits(binary.BigEndian.Uint32(*readTemp(data, i, 4))), nil
}
func readDouble(data *[]byte, i *int) (interface{}, error) {
return math.Float64frombits(binary.BigEndian.Uint64(*readTemp(data, i, 8))), nil
}
func readString(data *[]byte, i *int) (interface{}, error) {
sz := int(readUint32Safe(data, i))
if sz == 0 {
return "", nil
}
*i += sz
return string((*data)[*i-sz : *i]), nil
}
func readDataType(data *[]byte, i *int) dataType {
return dataType(readByteSafe(data, i))
}
func getDefaultValue(dataType dataType) interface{} {
switch dataType {
case intType, bigIntegerType, longType, shortType, byteType, booleanType, floatType, doubleType:
return 0
case traverserType, stringType:
return ""
case uuidType:
return uuid.Nil
case vertexType:
return Vertex{}
case edgeType:
return Edge{}
case propertyType:
return Property{}
case vertexPropertyType:
return VertexProperty{}
case pathType:
return Path{}
case setType:
return SimpleSet{}
case dateType, timestampType:
return time.Time{}
case durationType:
return time.Duration(0)
default:
return nil
}
}
// Composite
func readList(data *[]byte, i *int) (interface{}, error) {
sz := readIntSafe(data, i)
var valList []interface{}
for j := int32(0); j < sz; j++ {
val, err := readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
valList = append(valList, val)
}
return valList, nil
}
func readByteBuffer(data *[]byte, i *int) (interface{}, error) {
r := &ByteBuffer{}
sz := readIntSafe(data, i)
r.Data = make([]byte, sz)
for j := int32(0); j < sz; j++ {
r.Data[j] = readByteSafe(data, i)
}
return r, nil
}
func readMap(data *[]byte, i *int) (interface{}, error) {
sz := readUint32Safe(data, i)
var mapData = make(map[interface{}]interface{})
for j := uint32(0); j < sz; j++ {
k, err := readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
v, err := readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
if k == nil {
mapData[nil] = v
} else {
switch reflect.TypeOf(k).Kind() {
case reflect.Map:
mapData[&k] = v
break
case reflect.Slice:
mapData[fmt.Sprint(k)] = v
break
default:
mapData[k] = v
break
}
}
}
return mapData, nil
}
func readMapUnqualified(data *[]byte, i *int) (interface{}, error) {
sz := readUint32Safe(data, i)
var mapData = make(map[string]interface{})
for j := uint32(0); j < sz; j++ {
keyDataType := readDataType(data, i)
if keyDataType != stringType {
return nil, newError(err0703ReadMapNonStringKeyError)
}
// Skip nullable, key must be present
*i++
k, err := readString(data, i)
if err != nil {
return nil, err
}
mapData[k.(string)], err = readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
}
return mapData, nil
}
func readSet(data *[]byte, i *int) (interface{}, error) {
list, err := readList(data, i)
if err != nil {
return nil, err
}
return NewSimpleSet(list.([]interface{})...), nil
}
func readUuid(data *[]byte, i *int) (interface{}, error) {
id, _ := uuid.FromBytes(*readTemp(data, i, 16))
return id, nil
}
func timeReader(data *[]byte, i *int) (interface{}, error) {
return time.UnixMilli(readLongSafe(data, i)), nil
}
func durationReader(data *[]byte, i *int) (interface{}, error) {
return time.Duration(readLongSafe(data, i)*int64(time.Second) + int64(readIntSafe(data, i))), nil
}
// Graph
// {fully qualified id}{unqualified label}
func vertexReader(data *[]byte, i *int) (interface{}, error) {
return vertexReaderReadingProperties(data, i, true)
}
// {fully qualified id}{unqualified label}{fully qualified properties}
func vertexReaderReadingProperties(data *[]byte, i *int, readProperties bool) (interface{}, error) {
var err error
v := new(Vertex)
v.Id, err = readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
label, err := readUnqualified(data, i, stringType, false)
if err != nil {
return nil, err
}
v.Label = label.(string)
if readProperties {
v.Properties, err = readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
}
return v, nil
}
// {fully qualified id}{unqualified label}{in vertex w/o null byte}{out vertex}{unused null byte}{fully qualified properties}
func edgeReader(data *[]byte, i *int) (interface{}, error) {
var err error
e := new(Edge)
e.Id, err = readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
label, err := readUnqualified(data, i, stringType, false)
if err != nil {
return nil, err
}
e.Label = label.(string)
v, err := vertexReaderReadingProperties(data, i, false)
if err != nil {
return nil, err
}
e.InV = *v.(*Vertex)
v, err = vertexReaderReadingProperties(data, i, false)
if err != nil {
return nil, err
}
e.OutV = *v.(*Vertex)
*i += 2
e.Properties, err = readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
return e, nil
}
// {unqualified key}{fully qualified value}{null byte}
func propertyReader(data *[]byte, i *int) (interface{}, error) {
p := new(Property)
key, err := readUnqualified(data, i, stringType, false)
if err != nil {
return nil, err
}
p.Key = key.(string)
p.Value, err = readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
*i += 2
return p, nil
}
// {fully qualified id}{unqualified label}{fully qualified value}{null byte}{null byte}
func vertexPropertyReader(data *[]byte, i *int) (interface{}, error) {
var err error
vp := new(VertexProperty)
vp.Id, err = readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
label, err := readUnqualified(data, i, stringType, false)
if err != nil {
return nil, err
}
vp.Label = label.(string)
vp.Value, err = readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
*i += 2
props, err := readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
vp.Properties = props
return vp, nil
}
// {list of set of strings}{list of fully qualified objects}
func pathReader(data *[]byte, i *int) (interface{}, error) {
path := new(Path)
newLabels, err := readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
for _, param := range newLabels.([]interface{}) {
path.Labels = append(path.Labels, param.(*SimpleSet))
}
objects, err := readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
path.Objects = objects.([]interface{})
return path, err
}
// {bulk int}{fully qualified value}
func traverserReader(data *[]byte, i *int) (interface{}, error) {
var err error
traverser := new(Traverser)
traverser.bulk = readLongSafe(data, i)
traverser.value, err = readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
return traverser, nil
}
// {int32 length}{fully qualified item_0}{int64 repetition_0}...{fully qualified item_n}{int64 repetition_n}
func bulkSetReader(data *[]byte, i *int) (interface{}, error) {
sz := int(readIntSafe(data, i))
var valList []interface{}
for j := 0; j < sz; j++ {
val, err := readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
rep := readLongSafe(data, i)
for k := 0; k < int(rep); k++ {
valList = append(valList, val)
}
}
return valList, nil
}
// {type code (always string so ignore)}{nil code (always false so ignore)}{int32 size}{string enum}
func enumReader(data *[]byte, i *int) (interface{}, error) {
typeCode := readDataType(data, i)
if typeCode != stringType {
return nil, newError(err0406EnumReaderInvalidTypeError)
}
*i++
return readString(data, i)
}
// {unqualified key}{fully qualified value}
func bindingReader(data *[]byte, i *int) (interface{}, error) {
b := new(Binding)
val, err := readUnqualified(data, i, stringType, false)
if err != nil {
return nil, err
}
b.Key = val.(string)
b.Value, err = readFullyQualifiedNullable(data, i, true)
if err != nil {
return nil, err
}
return b, nil
}
// {id}{name}{duration}{counts}{annotations}{nested_metrics}
func metricsReader(data *[]byte, i *int) (interface{}, error) {
metrics := new(Metrics)
val, err := readUnqualified(data, i, stringType, false)
if err != nil {
return nil, err
}
metrics.Id = val.(string)
val, err = readUnqualified(data, i, stringType, false)
if err != nil {
return nil, err
}
metrics.Name = val.(string)
dur, err := readLong(data, i)
if err != nil {
return nil, err
}
metrics.Duration = dur.(int64)
counts, err := readMap(data, i)
cmap := counts.(map[interface{}]interface{})
if err != nil {
return nil, err
}
metrics.Counts = make(map[string]int64, len(cmap))
for k := range cmap {
metrics.Counts[k.(string)] = cmap[k].(int64)
}
annotations, err := readMap(data, i)
if err != nil {
return nil, err
}
amap := annotations.(map[interface{}]interface{})
if err != nil {
return nil, err
}
metrics.Annotations = make(map[string]interface{}, len(amap))
for k := range amap {
metrics.Annotations[k.(string)] = amap[k]
}
nested, err := readList(data, i)
if err != nil {
return nil, err
}
list := nested.([]interface{})
metrics.NestedMetrics = make([]Metrics, len(list))
for i, metric := range list {
metrics.NestedMetrics[i] = metric.(Metrics)
}
return metrics, nil
}
// {id}{name}{duration}{counts}{annotations}{nested_metrics}
func traversalMetricsReader(data *[]byte, i *int) (interface{}, error) {
m := new(TraversalMetrics)
dur, err := readLong(data, i)
if err != nil {
return nil, err
}
m.Duration = dur.(int64)
nested, err := readList(data, i)
if err != nil {
return nil, err
}
list := nested.([]interface{})
m.Metrics = make([]Metrics, len(list))
for i, metric := range list {
m.Metrics[i] = *metric.(*Metrics)
}
return m, nil
}
// Format: A String containing the fqcn.
func readClass(data *[]byte, i *int) (interface{}, error) {
gremlinType := new(GremlinType)
str, err := readString(data, i)
if err != nil {
return nil, err
}
gremlinType.Fqcn = str.(string)
return gremlinType, nil
}
func readUnqualified(data *[]byte, i *int, dataTyp dataType, nullable bool) (interface{}, error) {
if nullable && readByteSafe(data, i) == valueFlagNull {
return getDefaultValue(dataTyp), nil
}
deserializer, ok := deserializers[dataTyp]
if !ok {
return nil, newError(err0408GetSerializerToReadUnknownTypeError, dataTyp)
}
return deserializer(data, i)
}
func readFullyQualifiedNullable(data *[]byte, i *int, nullable bool) (interface{}, error) {
dataTyp := readDataType(data, i)
if dataTyp == nullType {
if readByteSafe(data, i) != valueFlagNull {
return nil, newError(err0404ReadNullTypeError)
}
return nil, nil
} else if nullable {
if readByteSafe(data, i) == valueFlagNull {
return getDefaultValue(dataTyp), nil
}
}
deserializer, ok := deserializers[dataTyp]
if !ok {
return nil, newError(err0408GetSerializerToReadUnknownTypeError, dataTyp)
}
return deserializer(data, i)
}