From 56c376cd4e889233cc9566e24606a1ffffabb15c Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Sun, 12 Oct 2025 11:17:41 -0400 Subject: [PATCH] tape: Decode OTAs into any, and allow assignment of SBA/LBA to string --- tape/dynamic.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tape/dynamic.go b/tape/dynamic.go index 9712b14..52f833d 100644 --- a/tape/dynamic.go +++ b/tape/dynamic.go @@ -101,6 +101,7 @@ func EncodeAny(encoder *Encoder, value any, tag Tag) (n int, err error) { // 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. +// The head of the decoder must be at the start of the payload. func DecodeAnyInto(decoder *Decoder, destination any, tag Tag) (n int, err error) { reflectDestination := reflect.ValueOf(destination) if reflectDestination.Kind() != reflect.Pointer { @@ -110,7 +111,8 @@ func DecodeAnyInto(decoder *Decoder, destination any, tag Tag) (n int, err error } // DecodeAny is like [DecodeAnyInto], but it automatically creates the -// destination from the tag and data. +// destination from the tag and data. The head of the decoder must be at the +// start of the payload. func DecodeAny(decoder *Decoder, tag Tag) (value any, n int, err error) { destination, err := skeletonValue(decoder, tag) if err != nil { return nil, n, err } @@ -128,7 +130,8 @@ 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. +// returns without an error. The head of the decoder must be at the start of the +// payload. func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err error) { n, err = decodeAnyOrError(decoder, destination, tag) if _, ok := err.(errCantAssign); ok { @@ -145,7 +148,7 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err // 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. +// an error. The head of the decoder must be at the start of the payload. func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err error) { err = canSet(destination.Type(), tag) if err != nil { return n, err } @@ -189,6 +192,13 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i setString(destination, string(buffer)) case OTA: // OTA: * + if isTypeAny(destination.Type()) { + // need a skeleton value if we are assigning to any. + value, err := skeletonValue(decoder, tag) + if err != nil { return n, err } + destination.Set(value) + destination = value.Elem() + } length, nn, err := decoder.ReadUintN(tag.CN() + 1) n += nn; if err != nil { return n, err } if length > uint64(MaxStructureLength) { @@ -356,6 +366,7 @@ func canSet(destination reflect.Type, tag Tag) error { return errCantAssignf("cannot assign float to %v", destination) } case SBA, LBA: + if destination.Kind() == reflect.String { return nil } if destination.Kind() != reflect.Slice { return errCantAssignf("cannot assign byte array to %v", destination) } @@ -463,7 +474,8 @@ 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(). +// dereferenced using Elem(). The head of the decoder must be at the start of +// the payload. func skeletonValue(decoder *Decoder, tag Tag) (reflect.Value, error) { typ, err := typeOf(decoder, tag) if err != nil { return reflect.Value { }, err } @@ -471,7 +483,8 @@ func skeletonValue(decoder *Decoder, tag Tag) (reflect.Value, error) { } // typeOf returns the type of the current tag being decoded. It does not use up -// the decoder, it only peeks. +// the decoder, it only peeks. The head of the decoder must be at the start of +// the payload. func typeOf(decoder *Decoder, tag Tag) (reflect.Type, error) { switch tag.WithoutCN() { case SI: