tape: Implement encoding for "any" values
This commit is contained in:
parent
285e83d995
commit
c4407d9759
@ -13,8 +13,43 @@ import "reflect"
|
||||
// - string
|
||||
// - []<supported type>
|
||||
// - map[uint16]<supported type>
|
||||
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: <length: UN> <elementTag: tape.Tag> <values>*
|
||||
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: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
||||
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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user