tape: Change how slice skeletons are generated, support nested OTAs
This commit is contained in:
parent
4930215166
commit
81ac10508b
147
tape/dynamic.go
147
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: <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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user