tape: Implement encoding for "any" values
This commit is contained in:
parent
285e83d995
commit
c4407d9759
@ -13,8 +13,43 @@ import "reflect"
|
|||||||
// - string
|
// - string
|
||||||
// - []<supported type>
|
// - []<supported type>
|
||||||
// - map[uint16]<supported type>
|
// - map[uint16]<supported type>
|
||||||
func EncodeAny(encoder *Encoder, value any) (Tag, error) {
|
func EncodeAny(encoder *Encoder, value any, tag Tag) (n int, err error) {
|
||||||
// TODO
|
// 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
|
// 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 int8, uint8: return LI.WithCN(0), nil
|
||||||
case int16, uint16: return LI.WithCN(1), nil
|
case int16, uint16: return LI.WithCN(1), nil
|
||||||
case int32, uint32: return LI.WithCN(3), 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 string: return bufferLenTag(len(value)), nil
|
||||||
case []byte: 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 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