385 lines
13 KiB
Go
385 lines
13 KiB
Go
package tape
|
|
|
|
// dont smoke reflection, kids!!!!!!!!!
|
|
// totally reflectric, reflectrified, etc. this is probably souper slow but
|
|
// certainly no slower than the built in json encoder i'd imagine.
|
|
// TODO: add support for struct tags: `tape:"0000"`, tape:"0001"` so they can get
|
|
// transformed into tables with a defined schema
|
|
|
|
import "fmt"
|
|
import "reflect"
|
|
|
|
var dummyMap map[uint16] any
|
|
|
|
// EncodeAny encodes an "any" value. Returns an error if the underlying type is
|
|
// unsupported. Supported types are:
|
|
//
|
|
// - int
|
|
// - int<N>
|
|
// - uint
|
|
// - uint<N>
|
|
// - string
|
|
// - []<supported type>
|
|
// - map[uint16]<supported type>
|
|
func EncodeAny(encoder *Encoder, value any, tag Tag) (n int, err error) {
|
|
// TODO use reflection for all of this to ignore type names
|
|
// primitives
|
|
switch value := value.(type) {
|
|
case int: return encoder.WriteInt32(int32(value))
|
|
case uint: return encoder.WriteUint32(uint32(value))
|
|
case int8: return encoder.WriteInt8(value)
|
|
case uint8: return encoder.WriteUint8(value)
|
|
case int16: return encoder.WriteInt16(value)
|
|
case uint16: return encoder.WriteUint16(value)
|
|
case int32: return encoder.WriteInt32(value)
|
|
case uint32: return encoder.WriteUint32(value)
|
|
case int64: return encoder.WriteInt64(value)
|
|
case uint64: return encoder.WriteUint64(value)
|
|
case string: return EncodeAny(encoder, []byte(value), tag)
|
|
case []byte:
|
|
if tag.Is(LBA) {
|
|
nn, err := encoder.WriteUintN(uint64(len(value)), tag.CN() + 1)
|
|
n += nn; if err != nil { return n, err }
|
|
}
|
|
nn, err := encoder.Write(value)
|
|
n += nn; if err != nil { return n, err }
|
|
return n, nil
|
|
}
|
|
|
|
// aggregates
|
|
reflectType := reflect.TypeOf(value)
|
|
switch reflectType.Kind() {
|
|
case reflect.Slice:
|
|
return encodeAnySlice(encoder, value, tag)
|
|
// case reflect.Array:
|
|
// return encodeAnySlice(encoder, reflect.ValueOf(value).Slice(0, reflectType.Len()).Interface(), tag)
|
|
case reflect.Map:
|
|
if reflectType.Key() == reflect.TypeOf(uint16(0)) {
|
|
return encodeAnyMap(encoder, value, tag)
|
|
}
|
|
return n, fmt.Errorf("cannot encode map key %T, key must be uint16", value)
|
|
}
|
|
return n, fmt.Errorf("cannot encode type %T", value)
|
|
}
|
|
|
|
// DecodeAny decodes data and places it into destination, which must be a
|
|
// pointer to a supported type. See [EncodeAny] for a list of supported types.
|
|
func DecodeAny(decoder *Decoder, destination any, tag Tag) (n int, err error) {
|
|
reflectDestination := reflect.ValueOf(destination)
|
|
if reflectDestination.Kind() != reflect.Pointer {
|
|
return n, fmt.Errorf("expected pointer destination, not %v", destination)
|
|
}
|
|
return decodeAny(decoder, reflectDestination.Elem(), tag)
|
|
}
|
|
|
|
// unknownSlicePlaceholder is inserted by skeletonValue and informs the program
|
|
// that the destination for the slice needs to be generated based on the item
|
|
// tag in the OTA.
|
|
type unknownSlicePlaceholder struct { }
|
|
var unknownSlicePlaceholderType = reflect.TypeOf(unknownSlicePlaceholder { })
|
|
|
|
// decodeAny is internal to [DecodeAny]. It takes in an addressable
|
|
// [reflect.Value] as the destination.
|
|
func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err error) {
|
|
errWrongDestinationType := func(expected string) error {
|
|
panic(fmt.Errorf(
|
|
// return fmt.Errorf(
|
|
"expected %s destination, not %v",
|
|
expected, destination))
|
|
}
|
|
|
|
switch tag.WithoutCN() {
|
|
case SI:
|
|
// SI: (none)
|
|
err = setInt(destination, uint64(tag.CN()))
|
|
if err != nil { return n, err }
|
|
case LI:
|
|
// LI: <value: IntN>
|
|
nn, err := decodeAndSetInt(decoder, destination, tag.CN() + 1)
|
|
n += nn; if err != nil { return n, err }
|
|
case FP:
|
|
// FP: <value: FloatN>
|
|
nn, err := decodeAndSetFloat(decoder, destination, tag.CN() + 1)
|
|
n += nn; if err != nil { return n, err }
|
|
case SBA:
|
|
// SBA: <data: U8>*
|
|
buffer := make([]byte, tag.CN())
|
|
nn, err := decoder.Read(buffer)
|
|
n += nn; if err != nil { return n, err }
|
|
err = setByteArray(destination, buffer)
|
|
if err != nil { return n, err }
|
|
case LBA:
|
|
// LBA: <length: UN> <data: U8>*
|
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
|
n += nn; if err != nil { return n, err }
|
|
buffer := make([]byte, length)
|
|
nn, err = decoder.Read(buffer)
|
|
n += nn; if err != nil { return n, err }
|
|
err = setByteArray(destination, buffer)
|
|
if err != nil { return n, err }
|
|
case OTA:
|
|
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
|
n += nn; if err != nil { return n, err }
|
|
oneTag, nn, err := decoder.ReadTag()
|
|
n += nn; if err != nil { return n, err }
|
|
if destination.Kind() != reflect.Slice {
|
|
return n, errWrongDestinationType("slice")
|
|
}
|
|
if destination.Cap() < int(length) {
|
|
destination.Grow(destination.Cap() - int(length))
|
|
}
|
|
destination.SetLen(int(length))
|
|
for index := range length {
|
|
nn, err := decodeAny(decoder, destination.Index(int(index)), oneTag)
|
|
n += nn; if err != nil { return n, err }
|
|
}
|
|
case KTV:
|
|
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
|
table := destination
|
|
if table.Type() != reflect.TypeOf(dummyMap) {
|
|
return n, errWrongDestinationType("map[uint16] any")
|
|
}
|
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
|
n += nn; if err != nil { return n, err }
|
|
table.Clear()
|
|
for _ = range length {
|
|
key, nn, err := decoder.ReadUint16()
|
|
n += nn; if err != nil { return n, err }
|
|
itemTag, nn, err := decoder.ReadTag()
|
|
n += nn; if err != nil { return n, err }
|
|
value, err := skeletonValue(decoder, itemTag)
|
|
if err != nil { return n, err }
|
|
nn, err = decodeAny(decoder, value.Elem(), itemTag)
|
|
n += nn; if err != nil { return n, err }
|
|
table.SetMapIndex(reflect.ValueOf(key), value)
|
|
}
|
|
default:
|
|
return n, fmt.Errorf("unknown TN %d", tag.TN())
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// TagAny returns the correct tag for an "any" value. Returns an error if the
|
|
// underlying type is unsupported. See [EncodeAny] for a list of supported
|
|
// types.
|
|
func TagAny(value any) (Tag, error) {
|
|
// TODO use reflection for all of this to ignore type names
|
|
// primitives
|
|
switch value := value.(type) {
|
|
case int, uint: return LI.WithCN(3), nil
|
|
case int8, uint8: return LI.WithCN(0), nil
|
|
case int16, uint16: return LI.WithCN(1), nil
|
|
case int32, uint32: return LI.WithCN(3), nil
|
|
case int64, uint64: return LI.WithCN(7), nil
|
|
case string: return bufferLenTag(len(value)), nil
|
|
case []byte: return bufferLenTag(len(value)), nil
|
|
}
|
|
|
|
// aggregates
|
|
reflectType := reflect.TypeOf(value)
|
|
switch reflectType.Kind() {
|
|
case reflect.Slice: return OTA.WithCN(IntBytes(uint64(reflect.ValueOf(value).Len())) - 1), nil
|
|
case reflect.Array: return OTA.WithCN(reflectType.Len()), nil
|
|
case reflect.Map:
|
|
if reflectType.Key() == reflect.TypeOf(uint16(0)) {
|
|
return KTV.WithCN(IntBytes(uint64(reflect.ValueOf(value).Len())) - 1), nil
|
|
}
|
|
return 0, fmt.Errorf("cannot encode map key %T, key must be uint16", value)
|
|
}
|
|
return 0, fmt.Errorf("cannot get tag of type %T", value)
|
|
}
|
|
|
|
func encodeAnySlice(encoder *Encoder, value any, tag Tag) (n int, err error) {
|
|
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
|
reflectValue := reflect.ValueOf(value)
|
|
nn, err := encoder.WriteUintN(uint64(reflectValue.Len()), tag.CN() + 1)
|
|
n += nn; if err != nil { return n, err }
|
|
reflectType := reflect.TypeOf(value)
|
|
oneTag, err := TagAny(reflect.Zero(reflectType.Elem()).Interface())
|
|
if err != nil { return n, err }
|
|
for index := 0; index < reflectValue.Len(); index += 1 {
|
|
item := reflectValue.Index(index).Interface()
|
|
itemTag, err := TagAny(item)
|
|
if err != nil { return n, err }
|
|
if itemTag.CN() > oneTag.CN() { oneTag = itemTag }
|
|
}
|
|
if oneTag.Is(SBA) { oneTag += 1 << 5 }
|
|
nn, err = encoder.WriteUint8(uint8(oneTag))
|
|
n += nn; if err != nil { return n, err }
|
|
for index := 0; index < reflectValue.Len(); index += 1 {
|
|
item := reflectValue.Index(index).Interface()
|
|
nn, err = EncodeAny(encoder, item, oneTag)
|
|
n += nn; if err != nil { return n, err }
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
func encodeAnyMap(encoder *Encoder, value any, tag Tag) (n int, err error) {
|
|
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
|
reflectValue := reflect.ValueOf(value)
|
|
nn, err := encoder.WriteUintN(uint64(reflectValue.Len()), tag.CN() + 1)
|
|
n += nn; if err != nil { return n, err }
|
|
iter := reflectValue.MapRange()
|
|
for iter.Next() {
|
|
key := iter.Key().Interface().(uint16)
|
|
value := iter.Value().Interface()
|
|
nn, err = encoder.WriteUint16(key)
|
|
n += nn; if err != nil { return n, err }
|
|
itemTag, err := TagAny(value)
|
|
if err != nil { return n, err }
|
|
nn, err = encoder.WriteUint8(uint8(itemTag))
|
|
n += nn; if err != nil { return n, err }
|
|
nn, err = EncodeAny(encoder, value, itemTag)
|
|
n += nn; if err != nil { return n, err }
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// setInt expects a settable destination.
|
|
func setInt(destination reflect.Value, value uint64) error {
|
|
switch {
|
|
case destination.CanInt():
|
|
destination.Set(reflect.ValueOf(int64(value)).Convert(destination.Type()))
|
|
case destination.CanUint():
|
|
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
|
|
default:
|
|
return fmt.Errorf("cannot assign integer to %T", destination.Interface())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// setInt expects a settable destination.
|
|
func setFloat(destination reflect.Value, value float64) error {
|
|
if !destination.CanFloat() {
|
|
return fmt.Errorf("cannot assign float to %T", destination.Interface())
|
|
}
|
|
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
|
|
return nil
|
|
}
|
|
|
|
// setByteArrayexpects a settable destination.
|
|
func setByteArray(destination reflect.Value, value []byte) error {
|
|
typ := destination.Type()
|
|
if typ.Kind() != reflect.Slice {
|
|
return fmt.Errorf("cannot assign %T to ", value)
|
|
}
|
|
if typ.Elem() != reflect.TypeOf(byte(0)) {
|
|
return fmt.Errorf("cannot convert %T to *[]byte", value)
|
|
}
|
|
destination.Set(reflect.ValueOf(value))
|
|
return nil
|
|
}
|
|
|
|
// decodeAndSetInt expects a settable destination.
|
|
func decodeAndSetInt(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) {
|
|
value, nn, err := decoder.ReadUintN(bytes)
|
|
n += nn; if err != nil { return n, err }
|
|
return n, setInt(destination, value)
|
|
}
|
|
|
|
// decodeAndSetInt expects a settable destination.
|
|
func decodeAndSetFloat(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) {
|
|
switch bytes {
|
|
case 8:
|
|
value, nn, err := decoder.ReadFloat64()
|
|
n += nn; if err != nil { return n, err }
|
|
return n, setFloat(destination, float64(value))
|
|
case 4:
|
|
value, nn, err := decoder.ReadFloat32()
|
|
n += nn; if err != nil { return n, err }
|
|
return n, setFloat(destination, float64(value))
|
|
}
|
|
return n, fmt.Errorf("cannot decode float%d", bytes * 8)
|
|
}
|
|
|
|
// skeletonValue returns a pointer value. In order for it to be set, it must be
|
|
// dereferenced using Elem().
|
|
func skeletonValue(decoder *Decoder, tag Tag) (reflect.Value, error) {
|
|
typ, err := typeOf(decoder, tag)
|
|
if err != nil { return reflect.Value { }, err }
|
|
return reflect.New(typ), nil
|
|
}
|
|
|
|
// typeOf returns the type of the current tag being decoded. It does not use up
|
|
// the decoder, it only peeks.
|
|
func typeOf(decoder *Decoder, tag Tag) (reflect.Type, error) {
|
|
switch tag.WithoutCN() {
|
|
case SI:
|
|
return reflect.TypeOf(uint8(0)), nil
|
|
case LI:
|
|
switch tag.CN() {
|
|
case 0: return reflect.TypeOf(uint8(0)), nil
|
|
case 1: return reflect.TypeOf(uint16(0)), nil
|
|
case 3: return reflect.TypeOf(uint32(0)), nil
|
|
case 7: return reflect.TypeOf(uint64(0)), nil
|
|
}
|
|
return nil, fmt.Errorf("unknown CN %d for LI", tag.CN())
|
|
case FP:
|
|
switch tag.CN() {
|
|
case 3: return reflect.TypeOf(float32(0)), nil
|
|
case 7: return reflect.TypeOf(float64(0)), nil
|
|
}
|
|
return nil, fmt.Errorf("unknown CN %d for FP", tag.CN())
|
|
case SBA: return reflect.SliceOf(reflect.TypeOf(byte(0))), nil
|
|
case LBA: return reflect.SliceOf(reflect.TypeOf(byte(0))), nil
|
|
case OTA:
|
|
elemTag, dimension, err := peekSlice(decoder, tag)
|
|
if err != nil { return nil, err }
|
|
if elemTag.Is(OTA) { panic("peekSlice cannot return OTA") }
|
|
typ, err := typeOf(decoder, elemTag)
|
|
if err != nil { return nil, err }
|
|
for _ = range dimension {
|
|
typ = reflect.SliceOf(typ)
|
|
}
|
|
return typ, nil
|
|
case KTV: return reflect.TypeOf(dummyMap), nil
|
|
}
|
|
return nil, fmt.Errorf("unknown TN %d", tag.TN())
|
|
}
|
|
|
|
// peekSlice returns the element tag and dimension count of the OTA currently
|
|
// being decoded. It does not use up the decoder, it only peeks.
|
|
func peekSlice(decoder *Decoder, tag Tag) (Tag, int, error) {
|
|
offset := 0
|
|
dimension := 0
|
|
for {
|
|
elem, populated, n, err := peekSliceOnce(decoder, tag, offset)
|
|
if err != nil { return 0, 0, err }
|
|
offset += n
|
|
dimension += 1
|
|
if elem.Is(OTA) {
|
|
if !populated {
|
|
return LBA, dimension + 1, nil
|
|
}
|
|
} else {
|
|
return elem, dimension, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// peekSliceOnce returns the element tag of the OTA located offset bytes ahead
|
|
// of the current position. It does not use up the decoder, it only peeks. The n
|
|
// return value denotes how far away from 0 it peeked. If the OTA has more than
|
|
// zero items, populated will be set to true.
|
|
func peekSliceOnce(decoder *Decoder, tag Tag, offset int) (elem Tag, populated bool, n int, err error) {
|
|
lengthStart := offset
|
|
lengthEnd := lengthStart + tag.CN() + 1
|
|
elemTagStart := lengthEnd
|
|
elemTagEnd := elemTagStart + 1
|
|
|
|
headerBytes, err := decoder.Peek(elemTagEnd)
|
|
if err != nil { return 0, false, 0, err }
|
|
|
|
elem = Tag(headerBytes[len(headerBytes)])
|
|
for index := lengthStart; index < lengthEnd; index += 1 {
|
|
if headerBytes[index] > 0 {
|
|
populated = true
|
|
break
|
|
}
|
|
}
|
|
n = elemTagEnd
|
|
|
|
return
|
|
}
|