diff --git a/tape/dynamic.go b/tape/dynamic.go index 7e7333b..e56da67 100644 --- a/tape/dynamic.go +++ b/tape/dynamic.go @@ -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: ( )* 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 }