567 lines
20 KiB
Go
567 lines
20 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
|
||
|
||
// TODO: test all of these smaller functions individually
|
||
|
||
// For an explanation as to why this package always treats LBA/SBA as strings,
|
||
// refer to https://go.dev/blog/strings:
|
||
//
|
||
// It’s important to state right up front that a string holds arbitrary
|
||
// bytes. It is not required to hold Unicode text, UTF-8 text, or any other
|
||
// predefined format. As far as the content of a string is concerned, it is
|
||
// exactly equivalent to a slice of bytes.
|
||
//
|
||
// Arbitrary byte slices and blobs won't be as common of a use case as text
|
||
// data, and if you need that anyway you can just cast it to a byte slice.
|
||
|
||
import "fmt"
|
||
import "reflect"
|
||
|
||
var dummyMap map[uint16] any
|
||
var dummyBuffer []byte
|
||
|
||
type errCantAssign string
|
||
func (err errCantAssign) Error() string {
|
||
return string(err)
|
||
}
|
||
func errCantAssignf(format string, v ...any) errCantAssign {
|
||
return errCantAssign(fmt.Sprintf(format, v...))
|
||
}
|
||
|
||
// 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) {
|
||
// primitives
|
||
reflectValue := reflect.ValueOf(value)
|
||
switch reflectValue.Kind() {
|
||
case reflect.Int: return encoder.WriteInt32(int32(reflectValue.Int()))
|
||
case reflect.Uint: return encoder.WriteUint32(uint32(reflectValue.Uint()))
|
||
case reflect.Int8: return encoder.WriteInt8(int8(reflectValue.Int()))
|
||
case reflect.Uint8: return encoder.WriteUint8(uint8(reflectValue.Uint()))
|
||
case reflect.Int16: return encoder.WriteInt16(int16(reflectValue.Int()))
|
||
case reflect.Uint16: return encoder.WriteUint16(uint16(reflectValue.Uint()))
|
||
case reflect.Int32: return encoder.WriteInt32(int32(reflectValue.Int()))
|
||
case reflect.Uint32: return encoder.WriteUint32(uint32(reflectValue.Uint()))
|
||
case reflect.Int64: return encoder.WriteInt64(int64(reflectValue.Int()))
|
||
case reflect.Uint64: return encoder.WriteUint64(uint64(reflectValue.Uint()))
|
||
case reflect.Float32: return encoder.WriteFloat32(float32(reflectValue.Float()))
|
||
case reflect.Float64: return encoder.WriteFloat64(float64(reflectValue.Float()))
|
||
case reflect.String:
|
||
if reflectValue.Len() > MaxStructureLength {
|
||
return 0, ErrTooLong
|
||
}
|
||
return EncodeAny(encoder, []byte(reflectValue.String()), tag)
|
||
}
|
||
if reflectValue.CanConvert(reflect.TypeOf(dummyBuffer)) {
|
||
if reflectValue.Len() > MaxStructureLength {
|
||
return 0, ErrTooLong
|
||
}
|
||
if tag.Is(LBA) {
|
||
nn, err := encoder.WriteUintN(uint64(reflectValue.Len()), tag.CN() + 1)
|
||
n += nn; if err != nil { return n, err }
|
||
}
|
||
nn, err := encoder.Write(reflectValue.Bytes())
|
||
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:
|
||
// TODO: we can encode arrays. but can we decode into them?
|
||
// that's the fucken question. maybe we just do the first
|
||
// return encodeAnySlice(encoder, reflect.ValueOf(value).Slice(0, reflectType.Len()).Interface(), tag)
|
||
case reflect.Map:
|
||
if reflectValue.Len() > MaxStructureLength {
|
||
return 0, ErrTooLong
|
||
}
|
||
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)
|
||
}
|
||
|
||
// DecodeAnyInto 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 DecodeAnyInto(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)
|
||
}
|
||
|
||
// DecodeAny is like [DecodeAnyInto], but it automatically creates the
|
||
// destination from the tag and data.
|
||
func DecodeAny(decoder *Decoder, tag Tag) (value any, n int, err error) {
|
||
destination, err := skeletonValue(decoder, tag)
|
||
if err != nil { return nil, n, err }
|
||
nn, err := DecodeAnyInto(decoder, destination, tag)
|
||
n += nn; if err != nil { return nil, n, err }
|
||
return destination, n, err
|
||
}
|
||
|
||
// 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. If the decoded value cannot fit in the
|
||
// destination, it skims over the payload, leaves the destination empty, and
|
||
// returns without an error.
|
||
func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err error) {
|
||
n, err = decodeAnyOrError(decoder, destination, tag)
|
||
if _, ok := err.(errCantAssign); ok {
|
||
if n > 0 { panic(fmt.Sprintf("decodeAnyOrError decoded more than it should: %d", n)) }
|
||
nn, err := Skim(decoder, tag)
|
||
n += nn; if err != nil { return n, err }
|
||
return n, nil
|
||
}
|
||
return n, err
|
||
}
|
||
|
||
// decodeAnyOrError is internal to [decodeAny]. It takes in an addressable
|
||
// [reflect.Value] as the destination. If the decoded value cannot fit in the
|
||
// destination, it decodes nothing and returns an error of type errCantAssign,
|
||
// except for the case of a mismatched OTA element tag, wherein it will skim
|
||
// over the rest of the payload, leave the destination empty, and return without
|
||
// an error.
|
||
func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err error) {
|
||
err = canSet(destination.Type(), tag)
|
||
if err != nil { return n, err }
|
||
|
||
switch tag.WithoutCN() {
|
||
case SI:
|
||
// SI: (none)
|
||
setUint(destination, uint64(tag.CN()), 1)
|
||
case LI:
|
||
// LI: <value: IntN>
|
||
nn, err := decodeAndSetUint(decoder, destination, tag.CN() + 1)
|
||
n += nn; if err != nil { return n, err }
|
||
case LSI:
|
||
// LSI: <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>*
|
||
length := tag.CN()
|
||
if length > MaxStructureLength {
|
||
return 0, ErrTooLong
|
||
}
|
||
buffer := make([]byte, length)
|
||
nn, err := decoder.Read(buffer)
|
||
n += nn; if err != nil { return n, err }
|
||
setString(destination, string(buffer))
|
||
case LBA:
|
||
// LBA: <length: UN> <data: U8>*
|
||
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||
n += nn; if err != nil { return n, err }
|
||
if length > uint64(MaxStructureLength) {
|
||
return 0, ErrTooLong
|
||
}
|
||
buffer := make([]byte, length)
|
||
nn, err = decoder.Read(buffer)
|
||
n += nn; if err != nil { return n, err }
|
||
setString(destination, string(buffer))
|
||
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 }
|
||
if length > uint64(MaxStructureLength) {
|
||
return 0, ErrTooLong
|
||
}
|
||
lengthCast, err := Uint64ToIntSafe(length)
|
||
if err != nil { return n, err }
|
||
oneTag, nn, err := decoder.ReadTag()
|
||
n += nn; if err != nil { return n, err }
|
||
if destination.Cap() < lengthCast {
|
||
destination.Grow(lengthCast - destination.Cap())
|
||
}
|
||
// skip the rest of the array if the one tag doesn't
|
||
// match up with the destination
|
||
err = canSet(destination.Type().Elem(), oneTag)
|
||
if _, ok := err.(errCantAssign); ok {
|
||
for _ = range length {
|
||
nn, err := Skim(decoder, oneTag)
|
||
n += nn; if err != nil { return n, err }
|
||
}
|
||
break
|
||
}
|
||
if err != nil { return n, err }
|
||
destination.SetLen(lengthCast)
|
||
for index := range length {
|
||
nn, err := decodeAny(decoder, destination.Index(int(index)), oneTag)
|
||
n += nn
|
||
if _, ok := err.(errCantAssign); ok {
|
||
continue
|
||
} else if err != nil {
|
||
return n, err
|
||
}
|
||
}
|
||
case KTV:
|
||
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
||
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||
n += nn; if err != nil { return n, err }
|
||
if length > uint64(MaxStructureLength) {
|
||
return 0, ErrTooLong
|
||
}
|
||
destination.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 }
|
||
destination.SetMapIndex(reflect.ValueOf(key), value.Elem())
|
||
}
|
||
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) {
|
||
return tagAny(reflect.ValueOf(value))
|
||
}
|
||
|
||
func tagAny(reflectValue reflect.Value) (Tag, error) {
|
||
// primitives
|
||
switch reflectValue.Kind() {
|
||
case reflect.Int: return LSI.WithCN(3), nil
|
||
case reflect.Int8: return LSI.WithCN(0), nil
|
||
case reflect.Int16: return LSI.WithCN(1), nil
|
||
case reflect.Int32: return LSI.WithCN(3), nil
|
||
case reflect.Int64: return LSI.WithCN(7), nil
|
||
case reflect.Uint: return LI.WithCN(3), nil
|
||
case reflect.Uint8: return LI.WithCN(0), nil
|
||
case reflect.Uint16: return LI.WithCN(1), nil
|
||
case reflect.Uint32: return LI.WithCN(3), nil
|
||
case reflect.Uint64: return LI.WithCN(7), nil
|
||
case reflect.Float32: return FP.WithCN(3), nil
|
||
case reflect.Float64: return FP.WithCN(7), nil
|
||
case reflect.String: return bufferLenTag(reflectValue.Len()), nil
|
||
}
|
||
if reflectValue.CanConvert(reflect.TypeOf(dummyBuffer)) {
|
||
return bufferLenTag(reflectValue.Len()), nil
|
||
}
|
||
|
||
// aggregates
|
||
reflectType := reflectValue.Type()
|
||
switch reflectType.Kind() {
|
||
case reflect.Slice: return OTA.WithCN(IntBytes(uint64(reflectValue.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(reflectValue.Len())) - 1), nil
|
||
}
|
||
return 0, fmt.Errorf("cannot encode map key %v, key must be uint16", reflectType.Key())
|
||
}
|
||
return 0, fmt.Errorf("cannot get tag of type %v", reflectType)
|
||
}
|
||
|
||
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()))
|
||
if err != nil { return n, err }
|
||
for index := 0; index < reflectValue.Len(); index += 1 {
|
||
itemTag, err := tagAny(reflectValue.Index(index))
|
||
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() {
|
||
reflectValue := iter.Value().Elem()
|
||
key := iter.Key().Interface().(uint16)
|
||
value := reflectValue.Interface()
|
||
nn, err = encoder.WriteUint16(key)
|
||
n += nn; if err != nil { return n, err }
|
||
itemTag, err := tagAny(reflectValue)
|
||
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
|
||
}
|
||
|
||
func canSet(destination reflect.Type, tag Tag) error {
|
||
// anything can be assigned to `any`
|
||
if isTypeAny(destination) {
|
||
return nil
|
||
}
|
||
switch tag.WithoutCN() {
|
||
case SI, LI, LSI:
|
||
switch destination.Kind() {
|
||
case
|
||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
default:
|
||
return errCantAssignf("cannot assign integer to %v", destination)
|
||
}
|
||
case FP:
|
||
switch destination.Kind() {
|
||
case reflect.Float32, reflect.Float64:
|
||
default:
|
||
return errCantAssignf("cannot assign float to %v", destination)
|
||
}
|
||
case SBA, LBA:
|
||
if destination.Kind() != reflect.Slice {
|
||
return errCantAssignf("cannot assign byte array to %v", destination)
|
||
}
|
||
if destination.Elem() != reflect.TypeOf(byte(0)) {
|
||
return errCantAssignf("cannot convert %v to *[]byte", destination)
|
||
}
|
||
case OTA:
|
||
if destination.Kind() != reflect.Slice {
|
||
return errCantAssignf("cannot assign array to %v", destination)
|
||
}
|
||
case KTV:
|
||
if destination != reflect.TypeOf(dummyMap) {
|
||
return errCantAssignf("cannot assign table to %v", destination)
|
||
}
|
||
default:
|
||
return fmt.Errorf("unknown TN %d", tag.TN())
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// setInt expects a settable destination.
|
||
func setInt(destination reflect.Value, value int64, bytes int) {
|
||
switch {
|
||
case destination.CanInt():
|
||
destination.Set(reflect.ValueOf(int64(value)).Convert(destination.Type()))
|
||
case destination.CanUint():
|
||
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
|
||
case isTypeAny(destination.Type()):
|
||
switch {
|
||
case bytes > 4: destination.Set(reflect.ValueOf(int64(value)))
|
||
case bytes > 2: destination.Set(reflect.ValueOf(int32(value)))
|
||
case bytes > 1: destination.Set(reflect.ValueOf(int16(value)))
|
||
default: destination.Set(reflect.ValueOf(int8(value)))
|
||
}
|
||
default:
|
||
panic("setInt called on an unsupported type")
|
||
}
|
||
}
|
||
|
||
// setUint expects a settable destination.
|
||
func setUint(destination reflect.Value, value uint64, bytes int) {
|
||
switch {
|
||
case destination.CanInt():
|
||
destination.Set(reflect.ValueOf(int64(value)).Convert(destination.Type()))
|
||
case destination.CanUint():
|
||
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
|
||
case isTypeAny(destination.Type()):
|
||
switch {
|
||
case bytes > 4: destination.Set(reflect.ValueOf(uint64(value)))
|
||
case bytes > 2: destination.Set(reflect.ValueOf(uint32(value)))
|
||
case bytes > 1: destination.Set(reflect.ValueOf(uint16(value)))
|
||
default: destination.Set(reflect.ValueOf(uint8(value)))
|
||
}
|
||
default:
|
||
panic("setUint called on an unsupported type")
|
||
}
|
||
}
|
||
|
||
// setFloat expects a settable destination.
|
||
func setFloat(destination reflect.Value, value float64) {
|
||
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
|
||
}
|
||
|
||
// setByteArrayexpects a settable destination.
|
||
func setByteArray(destination reflect.Value, value []byte) {
|
||
destination.Set(reflect.ValueOf(value))
|
||
}
|
||
|
||
// decodeAndSetInt expects a settable destination.
|
||
func decodeAndSetInt(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) {
|
||
value, nn, err := decoder.ReadIntN(bytes)
|
||
n += nn; if err != nil { return n, err }
|
||
setInt(destination, value, bytes)
|
||
return n, nil
|
||
}
|
||
|
||
// decodeAndSetUint expects a settable destination.
|
||
func decodeAndSetUint(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 }
|
||
setUint(destination, value, bytes)
|
||
return n, nil
|
||
}
|
||
|
||
// 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 }
|
||
setFloat(destination, float64(value))
|
||
return n, nil
|
||
case 4:
|
||
value, nn, err := decoder.ReadFloat32()
|
||
n += nn; if err != nil { return n, err }
|
||
setFloat(destination, float64(value))
|
||
return n, nil
|
||
}
|
||
return n, errCantAssignf("unsupported bit width 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 LSI:
|
||
switch tag.CN() {
|
||
case 0: return reflect.TypeOf(int8(0)), nil
|
||
case 1: return reflect.TypeOf(int16(0)), nil
|
||
case 3: return reflect.TypeOf(int32(0)), nil
|
||
case 7: return reflect.TypeOf(int64(0)), nil
|
||
}
|
||
return nil, fmt.Errorf("unknown CN %d for LSI", 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.TypeOf(""), nil
|
||
case LBA: return reflect.TypeOf(""), 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())
|
||
}
|
||
|
||
// isTypeAny returns whether the given reflect.Type is an interface with no
|
||
// methods.
|
||
func isTypeAny(typ reflect.Type) bool {
|
||
return typ.Kind() == reflect.Interface && typ.NumMethod() == 0
|
||
}
|
||
|
||
// 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
|
||
currentTag := tag
|
||
for {
|
||
elem, populated, n, err := peekSliceOnce(decoder, currentTag, offset)
|
||
if err != nil { return 0, 0, err }
|
||
currentTag = elem
|
||
offset = n
|
||
dimension += 1
|
||
if elem.Is(OTA) {
|
||
if !populated {
|
||
// default to a large byte array, will be
|
||
// interpreted as a string.
|
||
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) - 1])
|
||
for index := lengthStart; index < lengthEnd; index += 1 {
|
||
if headerBytes[index] > 0 {
|
||
populated = true
|
||
break
|
||
}
|
||
}
|
||
n = elemTagEnd
|
||
|
||
return
|
||
}
|