package tape import "fmt" import "reflect" // EncodeAny encodes an "any" value. Returns an error if the underlying type is // unsupported. Supported types are: // // - int // - int // - uint // - uint // - string // - [] // - map[uint16] 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 // underlying type is unsupported. See [EncodeAny] for a list of supported // types. func TagAny(value any) (Tag, error) { // primitives switch value := value.(type) { case int, uint: return LI.WithCN(3), nil 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(7), nil case string: return bufferLenTag(len(value)), nil case []byte: return bufferLenTag(len(value)), nil } // aggregates reflectType := reflect.TypeOf(value) switch reflectType.Kind() { case reflect.Slice: return OTA.WithCN(reflect.ValueOf(value).Len()), nil case reflect.Array: return OTA.WithCN(reflectType.Len()), nil case reflect.Map: if reflectType.Key() == reflect.TypeOf(uint16(0)) { return OTA.WithCN(reflect.ValueOf(value).Len()), nil } return 0, fmt.Errorf("cannot encode map key %T, key must be uint16", 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 }