diff --git a/tape/dynamic.go b/tape/dynamic.go index 2c53dba..6cd9faf 100644 --- a/tape/dynamic.go +++ b/tape/dynamic.go @@ -63,53 +63,58 @@ func EncodeAny(encoder *Encoder, value any, tag Tag) (n int, err error) { // 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) { - return decodeAny(decoder, reflect.ValueOf(destination), tag) + 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( + panic(fmt.Errorf( + // return fmt.Errorf( "expected %s destination, not %v", - expected, destination) - //) - } - - if destination.Kind() != reflect.Pointer { - return n, errWrongDestinationType("pointer") + expected, destination)) } switch tag.WithoutCN() { case SI: // SI: (none) - err = setIntPtr(destination, uint64(tag.CN())) + err = setInt(destination, uint64(tag.CN())) if err != nil { return n, err } case LI: // LI: - nn, err := decodeAndSetIntPtr(decoder, destination, tag.CN() + 1) + nn, err := decodeAndSetInt(decoder, destination, tag.CN() + 1) n += nn; if err != nil { return n, err } case FP: // FP: - nn, err := decodeAndSetFloatPtr(decoder, destination, tag.CN() + 1) + nn, err := decodeAndSetFloat(decoder, destination, tag.CN() + 1) n += nn; if err != nil { return n, err } case SBA: // SBA: * - destination, err := asByteArrayPtr(destination) - if err != nil { return n, err } buffer := make([]byte, tag.CN()) nn, err := decoder.Read(buffer) n += nn; if err != nil { return n, err } - *destination = buffer + err = setByteArray(destination, buffer) + if err != nil { return n, err } case LBA: // LBA: * - destination, err := asByteArrayPtr(destination) - if err != nil { return n, err } 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 } - *destination = buffer + err = setByteArray(destination, buffer) + if err != nil { return n, err } case OTA: // OTA: * length, nn, err := decoder.ReadUintN(tag.CN() + 1) @@ -118,13 +123,13 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err n += nn; if err != nil { return n, err } var slice reflect.Value needSet := false - elem := destination.Elem() - if elem.Kind() == reflect.Struct && elem.Type().Name() == "unknownSlicePlaceholder" { + 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 = elem + slice = destination if slice.Kind() != reflect.Slice { return n, errWrongDestinationType("slice") } @@ -135,11 +140,11 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err n += nn; if err != nil { return n, err } } if needSet { - destination.Elem().Set(slice) + destination.Set(slice) } case KTV: // KTV: ( )* - table := destination.Elem() + table := destination var dummyMap map[uint16] any if table.Type() != reflect.TypeOf(dummyMap) { return n, errWrongDestinationType("map[uint16] any") @@ -154,7 +159,7 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err n += nn; if err != nil { return n, err } value, err := skeletonValue(itemTag) if err != nil { return n, err } - nn, err = decodeAny(decoder, value, itemTag) + nn, err = decodeAny(decoder, value.Elem(), itemTag) n += nn; if err != nil { return n, err } table.SetMapIndex(reflect.ValueOf(key), value) } @@ -240,58 +245,65 @@ func encodeAnyMap(encoder *Encoder, value any, tag Tag) (n int, err error) { return n, nil } -func setIntPtr(destination reflect.Value, value uint64) error { - elem := destination.Elem() - if !elem.CanInt() { - return fmt.Errorf("cannot assign integer to %T", elem.Interface()) +// 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()) } - elem.Set(reflect.ValueOf(value).Convert(elem.Type())) return nil } -func setFloatPtr(destination reflect.Value, value float64) error { - elem := destination.Elem() - if !elem.CanFloat() { - return fmt.Errorf("cannot assign float to %T", elem.Interface()) +// 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()) } - elem.Set(reflect.ValueOf(value).Convert(elem.Type())) + destination.Set(reflect.ValueOf(value).Convert(destination.Type())) return nil } -func decodeAndSetIntPtr(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) { +// 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, setIntPtr(destination, value) + return n, setInt(destination, value) } -func decodeAndSetFloatPtr(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) { +// 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, setFloatPtr(destination, float64(value)) + return n, setFloat(destination, float64(value)) case 4: value, nn, err := decoder.ReadFloat32() n += nn; if err != nil { return n, err } - return n, setFloatPtr(destination, float64(value)) + return n, setFloat(destination, float64(value)) } return n, fmt.Errorf("cannot decode float%d", bytes * 8) } -func asByteArrayPtr(value reflect.Value) (*[]byte, error) { - typ := value.Type() - if typ.Kind() != reflect.Pointer { - return nil, fmt.Errorf("cannot convert %T to pointer", value) - } - if typ.Elem().Kind() != reflect.Slice { - return nil, fmt.Errorf("cannot convert %T to slice pointer", value) - } - if typ.Elem().Elem() != reflect.TypeOf(byte(0)) { - return nil, fmt.Errorf("cannot convert %T to *[]byte", value) - } - return value.Convert(reflect.PtrTo(reflect.SliceOf(reflect.TypeOf(byte(0))))).Interface().(*[]byte), nil -} - +// skeletonValue returns a pointer value. In order for it to be set, it must be +// dereferenced using Elem(). func skeletonValue(tag Tag) (reflect.Value, error) { switch tag.WithoutCN() { case SI: @@ -319,6 +331,8 @@ func skeletonValue(tag Tag) (reflect.Value, error) { return reflect.Value { }, 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(tag Tag, length int) (reflect.Value, error) { switch tag.WithoutCN() { case SI: @@ -345,8 +359,3 @@ func skeletonValueSlice(tag Tag, length int) (reflect.Value, error) { } return reflect.Value { }, fmt.Errorf("unknown TN %d", tag.TN()) } - -// 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 { }