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 "fmt"
import "reflect" import "reflect"
var dummyMap map[uint16] any
// EncodeAny encodes an "any" value. Returns an error if the underlying type is // EncodeAny encodes an "any" value. Returns an error if the underlying type is
// unsupported. Supported types are: // 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 } n += nn; if err != nil { return n, err }
oneTag, nn, err := decoder.ReadTag() oneTag, nn, err := decoder.ReadTag()
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
var slice reflect.Value if destination.Kind() != reflect.Slice {
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") return n, errWrongDestinationType("slice")
} }
slice.SetLen(int(length)) if destination.Cap() < int(length) {
destination.Grow(destination.Cap() - int(length))
} }
destination.SetLen(int(length))
for index := range 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 } n += nn; if err != nil { return n, err }
} }
if needSet {
destination.Set(slice)
}
case KTV: case KTV:
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)* // KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
table := destination table := destination
var dummyMap map[uint16] any
if table.Type() != reflect.TypeOf(dummyMap) { if table.Type() != reflect.TypeOf(dummyMap) {
return n, errWrongDestinationType("map[uint16] any") 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 // skeletonValue returns a pointer value. In order for it to be set, it must be
// dereferenced using Elem(). // dereferenced using Elem().
func skeletonValue(decoder *Decoder, tag Tag) (reflect.Value, error) { 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() { switch tag.WithoutCN() {
case SI: case SI:
value := uint8(0) return reflect.TypeOf(uint8(0)), nil
return reflect.ValueOf(&value), nil
case LI: case LI:
switch tag.CN() { switch tag.CN() {
case 0: value := uint8(0); return reflect.ValueOf(&value), nil case 0: return reflect.TypeOf(uint8(0)), nil
case 1: value := uint16(0); return reflect.ValueOf(&value), nil case 1: return reflect.TypeOf(uint16(0)), nil
case 3: value := uint32(0); return reflect.ValueOf(&value), nil case 3: return reflect.TypeOf(uint32(0)), nil
case 7: value := uint64(0); return reflect.ValueOf(&value), 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: case FP:
switch tag.CN() { switch tag.CN() {
case 3: value := float32(0); return reflect.ValueOf(&value), nil case 3: return reflect.TypeOf(float32(0)), nil
case 7: value := float64(0); return reflect.ValueOf(&value), nil case 7: return reflect.TypeOf(float64(0)), nil
} }
return reflect.Value { }, fmt.Errorf("unknown CN %d for FP", tag.CN()) return nil, fmt.Errorf("unknown CN %d for FP", tag.CN())
case SBA: value := []byte { }; return reflect.ValueOf(&value), nil case SBA: return reflect.SliceOf(reflect.TypeOf(byte(0))), nil
case LBA: value := []byte { }; return reflect.ValueOf(&value), nil case LBA: return reflect.SliceOf(reflect.TypeOf(byte(0))), nil
case OTA: return skeletonValueSlice(decoder, tag) case OTA:
case KTV: value := map[uint16] any { }; return reflect.ValueOf(&value), nil 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 reflect.Value { }, fmt.Errorf("unknown TN %d", tag.TN()) return typ, nil
case KTV: return reflect.TypeOf(dummyMap), nil
}
return nil, fmt.Errorf("unknown TN %d", tag.TN())
} }
// skeletonValueSlice returns a pointer value. In order for it to be set, it // peekSlice returns the element tag and dimension count of the OTA currently
// must be dereferenced using Elem(). // being decoded. It does not use up the decoder, it only peeks.
func skeletonValueSlice(decoder *Decoder, tag Tag) (reflect.Value, error) { func peekSlice(decoder *Decoder, tag Tag) (Tag, int, error) {
// TODO 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
}
}
} }
// TODO: delete fucntion below // 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
// skeletonValueSlice returns a pointer value. In order for it to be set, it headerBytes, err := decoder.Peek(elemTagEnd)
// must be dereferenced using Elem(). if err != nil { return 0, false, 0, err }
func skeletonValueSlice(tag Tag, length int) (reflect.Value, error) {
switch tag.WithoutCN() { elem = Tag(headerBytes[len(headerBytes)])
case SI: for index := lengthStart; index < lengthEnd; index += 1 {
value := make([]uint8, length) if headerBytes[index] > 0 {
return reflect.ValueOf(&value), nil populated = true
case LI: break
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
} }
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()) n = elemTagEnd
case SBA: value := make([][]byte, length); return reflect.ValueOf(&value), nil
case LBA: value := make([][]byte, length); return reflect.ValueOf(&value), nil return
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())
} }