Big nasty commit to add code generation for encoding

This commit is contained in:
2025-06-20 15:05:58 -04:00
parent a1f297e5b5
commit ce503c4689
9 changed files with 786 additions and 35 deletions

48
tape/dynamic.go Normal file
View File

@@ -0,0 +1,48 @@
package tape
import "fmt"
import "reflect"
import "git.tebibyte.media/sashakoshka/hopp/codec"
// EncodeAny encodes an "any" value. Returns an error if the underlying type is
// unsupported. Supported types are:
//
// - int
// - int<N>
// - uint
// - uint<N>
// - string
// - []<supported type>
// - map[uint16]<supported type>
func EncodeAny(encoder *codec.Encoder, value any) (Tag, error) {
// TODO
}
// 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(8), 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 encode type %T", value)
}

45
tape/tag.go Normal file
View File

@@ -0,0 +1,45 @@
package tape
import "git.tebibyte.media/sashakoshka/hopp/codec"
type Tag byte; const (
SI Tag = 0 << 5 // Small integer
LI Tag = 1 << 5 // Large integer
FP Tag = 2 << 5 // Floating point
SBA Tag = 3 << 5 // Small byte array
LBA Tag = 4 << 5 // Large byte array
OTA Tag = 5 << 5 // One-tag array
KTV Tag = 6 << 5 // Key-tag-value table
TNMask Tag = 0xE0 // The entire TN bitfield
CNMask Tag = 0x20 // The entire CN bitfield
CNLimit Tag = 32 // All valid CNs are < CNLimit
)
func (tag Tag) TN() int {
return int(tag >> 5)
}
func (tag Tag) CN() int {
return int(tag & CNMask)
}
func (tag Tag) WithCN(cn int) Tag {
return (tag & TNMask) | Tag(cn % 32)
}
func (tag Tag) Is(other Tag) bool {
return tag.TN() == other.TN()
}
// BufferTag returns the appropriate tag for a buffer.
func BufferTag(value []byte) Tag {
return bufferLenTag(len(value))
}
func bufferLenTag(length int) Tag {
if length < int(CNLimit) {
return SBA.WithCN(length)
} else {
return LBA.WithCN(codec.IntBytes(uint64(length)))
}
}