tape: Change how slice skeletons are generated, support nested OTAs

This commit is contained in:
Sasha Koshka 2025-06-29 10:27:40 -04:00
parent 4930215166
commit 81ac10508b

View File

@ -9,6 +9,8 @@ package tape
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:
//
@ -121,31 +123,20 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err
n += nn; if err != nil { return n, err }
oneTag, nn, err := decoder.ReadTag()
n += nn; if err != nil { return n, err }
var slice reflect.Value
needSet := false
if destination.Kind() == reflect.Struct && destination.Type() == unknownSlicePlaceholderType {
needSet = true
slice, err = skeletonValueSlice(oneTag, int(length))
if err != nil { return n, err }
slice = slice.Elem()
} else {
slice = destination
if slice.Kind() != reflect.Slice {
return n, errWrongDestinationType("slice")
}
slice.SetLen(int(length))
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, slice.Index(int(index)), oneTag)
nn, err := decodeAny(decoder, destination.Index(int(index)), oneTag)
n += nn; if err != nil { return n, err }
}
if needSet {
destination.Set(slice)
}
case KTV:
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
table := destination
var dummyMap map[uint16] any
if table.Type() != reflect.TypeOf(dummyMap) {
return n, errWrongDestinationType("map[uint16] any")
}
@ -305,65 +296,89 @@ func decodeAndSetFloat(decoder *Decoder, destination reflect.Value, bytes int) (
// 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:
value := uint8(0)
return reflect.ValueOf(&value), nil
return reflect.TypeOf(uint8(0)), nil
case LI:
switch tag.CN() {
case 0: value := uint8(0); return reflect.ValueOf(&value), nil
case 1: value := uint16(0); return reflect.ValueOf(&value), nil
case 3: value := uint32(0); return reflect.ValueOf(&value), nil
case 7: value := uint64(0); return reflect.ValueOf(&value), nil
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 reflect.Value { }, fmt.Errorf("unknown CN %d for LI", tag.CN())
return nil, fmt.Errorf("unknown CN %d for LI", tag.CN())
case FP:
switch tag.CN() {
case 3: value := float32(0); return reflect.ValueOf(&value), nil
case 7: value := float64(0); return reflect.ValueOf(&value), nil
case 3: return reflect.TypeOf(float32(0)), nil
case 7: return reflect.TypeOf(float64(0)), nil
}
return reflect.Value { }, fmt.Errorf("unknown CN %d for FP", tag.CN())
case SBA: value := []byte { }; return reflect.ValueOf(&value), nil
case LBA: value := []byte { }; return reflect.ValueOf(&value), nil
case OTA: return skeletonValueSlice(decoder, tag)
case KTV: value := map[uint16] any { }; return reflect.ValueOf(&value), 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 reflect.Value { }, fmt.Errorf("unknown TN %d", tag.TN())
return nil, fmt.Errorf("unknown TN %d", tag.TN())
}
// skeletonValueSlice returns a pointer value. In order for it to be set, it
// must be dereferenced using Elem().
func skeletonValueSlice(decoder *Decoder, tag Tag) (reflect.Value, error) {
// TODO
}
// TODO: delete fucntion below
// skeletonValueSlice returns a pointer value. In order for it to be set, it
// must be dereferenced using Elem().
func skeletonValueSlice(tag Tag, length int) (reflect.Value, error) {
switch tag.WithoutCN() {
case SI:
value := make([]uint8, length)
return reflect.ValueOf(&value), nil
case LI:
switch tag.CN() {
case 0: value := make([]uint8, length); return reflect.ValueOf(&value), nil
case 1: value := make([]uint16, length); return reflect.ValueOf(&value), nil
case 3: value := make([]uint32, length); return reflect.ValueOf(&value), nil
case 7: value := make([]uint64, length); return reflect.ValueOf(&value), nil
// 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
}
return reflect.Value { }, fmt.Errorf("unknown CN %d for LI OTA", tag.CN())
case FP:
switch tag.CN() {
case 3: value := make([]float32, length); return reflect.ValueOf(&value), nil
case 7: value := make([]float64, length); return reflect.ValueOf(&value), nil
}
return reflect.Value { }, fmt.Errorf("unknown CN %d for FP OTA", tag.CN())
case SBA: value := make([][]byte, length); return reflect.ValueOf(&value), nil
case LBA: value := make([][]byte, length); return reflect.ValueOf(&value), nil
case OTA: value := make([]any, length); return reflect.ValueOf(&value), nil
case KTV: value := make([]map[uint16] any, length); return reflect.ValueOf(&value), nil
}
return reflect.Value { }, fmt.Errorf("unknown TN %d", tag.TN())
}
// 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
}