package tape // dont smoke reflection, kids!!!!!!!!! // totally reflectric, reflectrified, etc. this is probably souper slow but // certainly no slower than the built in json encoder i'd imagine. // TODO: add support for struct tags: `tape:"0000"`, tape:"0001"` so they can get // transformed into tables with a defined schema 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) { // TODO use reflection for all of this to ignore type names // 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 n, fmt.Errorf("cannot encode map key %T, key must be uint16", value) } return n, fmt.Errorf("cannot encode type %T", value) } // 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) } func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err error) { errWrongDestinationType := func(expected string) error { return fmt.Errorf( "expected %s destination, not %v", expected, destination) } if destination.Kind() != reflect.Pointer { return n, errWrongDestinationType("pointer") } switch tag.WithoutCN() { case SI: // SI: (none) err = setIntPtr(destination, uint64(tag.CN())) if err != nil { return n, err } case LI: // LI: nn, err := decodeAndSetIntPtr(decoder, destination, tag.CN() - 1) n += nn; if err != nil { return n, err } case FP: // FP: nn, err := decodeAndSetFloatPtr(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 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 case OTA: // OTA: * length, nn, err := decoder.ReadUintN(tag.CN() - 1) 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 elem := destination.Elem() if elem.Kind() == reflect.Struct && elem.Type().Name() == "unknownSlicePlaceholder" { needSet = true slice, err = skeletonValueSlice(oneTag, int(length)) if err != nil { return n, err } } else { slice = elem if slice.Kind() != reflect.Slice { return n, errWrongDestinationType("slice") } slice.SetLen(int(length)) } for index := range length { nn, err := decodeAny(decoder, slice.Index(int(index)), oneTag) n += nn; if err != nil { return n, err } } if needSet { destination.Elem().Set(slice) } case KTV: // KTV: ( )* table := destination.Elem() if table.Kind() != reflect.Map { return n, errWrongDestinationType("map") } typ := table.Type() if typ.Key().Kind() != reflect.Uint16 { return n, errWrongDestinationType("map[uint16]") } if typ.Elem() != reflect.TypeOf(any(nil)) { return n, errWrongDestinationType("map[uint16] any") } length, nn, err := decoder.ReadUintN(tag.CN() - 1) n += nn; if err != nil { return n, err } table.Clear() for _ = range length { key, nn, err := decoder.ReadUint16() n += nn; if err != nil { return n, err } itemTag, nn, err := decoder.ReadTag() n += nn; if err != nil { return n, err } value, err := skeletonValue(itemTag) if err != nil { return n, err } nn, err = decodeAny(decoder, value.Elem(), itemTag) n += nn; if err != nil { return n, err } table.SetMapIndex(reflect.ValueOf(key), value) } } return n, fmt.Errorf("unknown TN %d", tag.TN()) } // 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) { // TODO use reflection for all of this to ignore type names // 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() - 1), nil case reflect.Array: return OTA.WithCN(reflectType.Len()), nil case reflect.Map: if reflectType.Key() == reflect.TypeOf(uint16(0)) { return KTV.WithCN(IntBytes(uint64(reflect.ValueOf(value).Len())) - 1), 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 } func setIntPtr(destination reflect.Value, value uint64) error { elem := destination.Elem() if !elem.CanInt() { return fmt.Errorf("cannot assign integer to %T", elem.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()) } elem.Set(reflect.ValueOf(value).Convert(elem.Type())) return nil } func decodeAndSetIntPtr(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) } func decodeAndSetFloatPtr(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)) case 4: value, nn, err := decoder.ReadFloat32() n += nn; if err != nil { return n, err } return n, setFloatPtr(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 } func skeletonValue(tag Tag) (reflect.Value, error) { switch tag.WithoutCN() { case SI: value := uint8(0) return reflect.ValueOf(&value), 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 } return reflect.Value { }, 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 } 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: value := unknownSlicePlaceholder { }; return reflect.ValueOf(&value), nil case KTV: value := map[uint16] any { }; return reflect.ValueOf(&value), nil } return reflect.Value { }, fmt.Errorf("unknown TN %d", tag.TN()) } 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 } 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()) } // 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 { }