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" var dummyMap map[uint16] any // 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 } return n, nil } // 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) { reflectDestination := reflect.ValueOf(destination) if reflectDestination.Kind() != reflect.Pointer { return n, fmt.Errorf("expected pointer destination, not %v", destination) } return decodeAny(decoder, reflectDestination.Elem(), tag) } // 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 { } var unknownSlicePlaceholderType = reflect.TypeOf(unknownSlicePlaceholder { }) // decodeAny is internal to [DecodeAny]. It takes in an addressable // [reflect.Value] as the destination. func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err error) { errWrongDestinationType := func(expected string) error { panic(fmt.Errorf( // return fmt.Errorf( "expected %s destination, not %v", expected, destination)) } switch tag.WithoutCN() { case SI: // SI: (none) err = setInt(destination, uint64(tag.CN())) if err != nil { return n, err } case LI: // LI: nn, err := decodeAndSetInt(decoder, destination, tag.CN() + 1) n += nn; if err != nil { return n, err } case FP: // FP: nn, err := decodeAndSetFloat(decoder, destination, tag.CN() + 1) n += nn; if err != nil { return n, err } case SBA: // SBA: * buffer := make([]byte, tag.CN()) nn, err := decoder.Read(buffer) n += nn; if err != nil { return n, err } err = setByteArray(destination, buffer) if err != nil { return n, err } case LBA: // LBA: * 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 } err = setByteArray(destination, buffer) if err != nil { return n, err } 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 } 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, destination.Index(int(index)), oneTag) n += nn; if err != nil { return n, err } } case KTV: // KTV: ( )* table := destination if table.Type() != reflect.TypeOf(dummyMap) { 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(decoder, 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) } default: return n, fmt.Errorf("unknown TN %d", tag.TN()) } return n, nil } // 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(IntBytes(uint64(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) 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 } // setInt expects a settable destination. func setInt(destination reflect.Value, value uint64) error { switch { case destination.CanInt(): destination.Set(reflect.ValueOf(int64(value)).Convert(destination.Type())) case destination.CanUint(): destination.Set(reflect.ValueOf(value).Convert(destination.Type())) default: return fmt.Errorf("cannot assign integer to %T", destination.Interface()) } return nil } // setInt expects a settable destination. func setFloat(destination reflect.Value, value float64) error { if !destination.CanFloat() { return fmt.Errorf("cannot assign float to %T", destination.Interface()) } destination.Set(reflect.ValueOf(value).Convert(destination.Type())) return nil } // setByteArrayexpects a settable destination. func setByteArray(destination reflect.Value, value []byte) error { typ := destination.Type() if typ.Kind() != reflect.Slice { return fmt.Errorf("cannot assign %T to ", value) } if typ.Elem() != reflect.TypeOf(byte(0)) { return fmt.Errorf("cannot convert %T to *[]byte", value) } destination.Set(reflect.ValueOf(value)) return nil } // decodeAndSetInt expects a settable destination. func decodeAndSetInt(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, setInt(destination, value) } // decodeAndSetInt expects a settable destination. func decodeAndSetFloat(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, setFloat(destination, float64(value)) case 4: value, nn, err := decoder.ReadFloat32() n += nn; if err != nil { return n, err } return n, setFloat(destination, float64(value)) } return n, fmt.Errorf("cannot decode float%d", bytes * 8) } // 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: return reflect.TypeOf(uint8(0)), nil case LI: switch tag.CN() { 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 nil, fmt.Errorf("unknown CN %d for LI", tag.CN()) case FP: switch tag.CN() { case 3: return reflect.TypeOf(float32(0)), nil case 7: return reflect.TypeOf(float64(0)), 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 nil, fmt.Errorf("unknown TN %d", tag.TN()) } // 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 } } } // 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 }