diff --git a/tape/dynamic.go b/tape/dynamic.go index fd35dfd..6dc0380 100644 --- a/tape/dynamic.go +++ b/tape/dynamic.go @@ -13,8 +13,43 @@ import "reflect" // - string // - [] // - map[uint16] -func EncodeAny(encoder *Encoder, value any) (Tag, error) { - // TODO +func EncodeAny(encoder *Encoder, value any, tag Tag) (n int, err error) { + // primitives + switch value := value.(type) { + case int: return encoder.WriteInt32(int32(value)) + case uint: return encoder.WriteUint32(uint32(value)) + case int8: return encoder.WriteInt8(value) + case uint8: return encoder.WriteUint8(value) + case int16: return encoder.WriteInt16(value) + case uint16: return encoder.WriteUint16(value) + case int32: return encoder.WriteInt32(value) + case uint32: return encoder.WriteUint32(value) + case int64: return encoder.WriteInt64(value) + case uint64: return encoder.WriteUint64(value) + case string: return EncodeAny(encoder, []byte(value), tag) + case []byte: + if tag.Is(LBA) { + nn, err := encoder.WriteUintN(uint64(len(value)), tag.CN() + 1) + n += nn; if err != nil { return n, err } + } + nn, err := encoder.Write(value) + n += nn; if err != nil { return n, err } + } + + // aggregates + reflectType := reflect.TypeOf(value) + switch reflectType.Kind() { + case reflect.Slice: + return encodeAnySlice(encoder, value, tag) + case reflect.Array: + return encodeAnySlice(encoder, reflect.ValueOf(value).Slice(0, reflectType.Len()).Interface(), tag) + case reflect.Map: + if reflectType.Key() == reflect.TypeOf(uint16(0)) { + return encodeAnyMap(encoder, value, tag) + } + return 0, fmt.Errorf("cannot encode map key %T, key must be uint16", value) + } + return 0, fmt.Errorf("cannot encode type %T", value) } // TagAny returns the correct tag for an "any" value. Returns an error if the @@ -27,7 +62,7 @@ func TagAny(value any) (Tag, error) { case int8, uint8: return LI.WithCN(0), nil case int16, uint16: return LI.WithCN(1), nil case int32, uint32: return LI.WithCN(3), nil - case int64, uint64: return LI.WithCN(8), nil + case int64, uint64: return LI.WithCN(7), nil case string: return bufferLenTag(len(value)), nil case []byte: return bufferLenTag(len(value)), nil } @@ -43,5 +78,51 @@ func TagAny(value any) (Tag, error) { } return 0, fmt.Errorf("cannot encode map key %T, key must be uint16", value) } - return 0, fmt.Errorf("cannot encode type %T", value) + return 0, fmt.Errorf("cannot get tag of type %T", value) +} + +func encodeAnySlice(encoder *Encoder, value any, tag Tag) (n int, err error) { + // OTA: * + reflectValue := reflect.ValueOf(value) + nn, err := encoder.WriteUintN(uint64(reflectValue.Len()), tag.CN() + 1) + n += nn; if err != nil { return n, err } + reflectType := reflect.TypeOf(value) + oneTag, err := TagAny(reflect.Zero(reflectType.Elem()).Interface()) + if err != nil { return n, err } + for index := 0; index <= reflectValue.Len(); index += 1 { + item := reflectValue.Index(index).Interface() + itemTag, err := TagAny(item) + if err != nil { return n, err } + if itemTag.CN() > oneTag.CN() { oneTag = itemTag } + } + if oneTag.Is(SBA) { oneTag += 1 << 5 } + nn, err = encoder.WriteUint8(uint8(oneTag)) + n += nn; if err != nil { return n, err } + for index := 0; index <= reflectValue.Len(); index += 1 { + item := reflectValue.Index(index).Interface() + nn, err = EncodeAny(encoder, item, oneTag) + n += nn; if err != nil { return n, err } + } + return n, err +} + +func encodeAnyMap(encoder *Encoder, value any, tag Tag) (n int, err error) { + // KTV: ( )* + reflectValue := reflect.ValueOf(value) + nn, err := encoder.WriteUintN(uint64(reflectValue.Len()), tag.CN() + 1) + n += nn; if err != nil { return n, err } + iter := reflectValue.MapRange() + for iter.Next() { + key := iter.Key().Interface().(uint16) + value := iter.Value().Interface() + nn, err = encoder.WriteUint16(key) + n += nn; if err != nil { return n, err } + itemTag, err := TagAny(value) + n += nn; if err != nil { return n, err } + nn, err = encoder.WriteUint8(uint8(itemTag)) + n += nn; if err != nil { return n, err } + nn, err = EncodeAny(encoder, value, itemTag) + n += nn; if err != nil { return n, err } + } + return n, nil }