14 Commits

Author SHA1 Message Date
96c8d7924f tape: Test that integers of a user-defined type can be encoded 2025-08-12 08:21:19 -04:00
fdf0aa89a4 tape: Use reflection when encoding integers 2025-08-12 08:16:34 -04:00
1bded9852d tape: Dynamic tests put out more information 2025-08-12 08:16:21 -04:00
8beb9de256 Merge pull request 'encode-signedness' (#13) from encode-signedness into message-size-increase
Reviewed-on: #13
2025-08-11 19:10:56 -06:00
dc72cc2010 generate: Support LSI tags 2025-08-11 20:59:20 -04:00
0e03f84b8a generate: Update tests with new TNs 2025-08-11 20:59:10 -04:00
02196edf61 design: Change tag for signed PDL integers 2025-08-11 20:10:03 -04:00
1058615f6f tape: Do something when receiving an LSI tag 2025-08-11 18:40:56 -04:00
024edfa922 tape: Actually test decoding lol 2025-08-11 18:39:38 -04:00
fe973af99c tape: Test dynamic encoding and decoding of signed integers 2025-08-10 23:28:43 -04:00
52f0d6932e tape: Add encoding and decoding of signed integers 2025-08-10 23:28:25 -04:00
8e14a2c3f1 tape: Add LSI to Tag constants 2025-08-07 21:14:40 -04:00
4fbb70081a generate: Finish test sub-case for MessageNestedArray 2025-08-06 22:15:22 -04:00
a108e53cb6 Merge pull request 'branched-generated-encoder' (#9) from branched-generated-encoder into message-size-increase
Reviewed-on: #9
2025-08-06 19:11:08 -06:00
6 changed files with 178 additions and 60 deletions

View File

@@ -7,12 +7,12 @@ PDL allows defining a protocol using HOPP and TAPE.
| Syntax | TN | CN | Description | Syntax | TN | CN | Description
| ---------- | ------- | -: | ----------- | ---------- | ------- | -: | -----------
| I5 | SI | | | I5 | SI | |
| I8 | LI | 0 | | I8 | LSI | 0 |
| I16 | LI | 1 | | I16 | LSI | 1 |
| I32 | LI | 3 | | I32 | LSI | 3 |
| I64 | LI | 7 | | I64 | LSI | 7 |
| I128[^2] | LI | 15 | | I128[^2] | LSI | 15 |
| I256[^2] | LI | 31 | | I256[^2] | LSI | 31 |
| U5 | SI | | | U5 | SI | |
| U8 | LI | 0 | | U8 | LI | 0 |
| U16 | LI | 1 | | U16 | LI | 1 |

View File

@@ -307,8 +307,8 @@ func (this *Generator) generateMessage(method uint16, message Message) (n int, e
func (this *Generator) generateEncodeValue(typ Type, valueSource, tagSource string) (n int, err error) { func (this *Generator) generateEncodeValue(typ Type, valueSource, tagSource string) (n int, err error) {
switch typ := typ.(type) { switch typ := typ.(type) {
case TypeInt: case TypeInt:
// SI: (none) // SI: (none)
// LI: <value: IntN> // LI/LSI: <value: IntN>
if typ.Bits <= 5 { if typ.Bits <= 5 {
// SI stores the value in the tag, so we write nothing here // SI stores the value in the tag, so we write nothing here
break break
@@ -478,8 +478,8 @@ func (this *Generator) generateEncodeValue(typ Type, valueSource, tagSource stri
func (this *Generator) generateDecodeValue(typ Type, typeName, valueSource, tagSource string) (n int, err error) { func (this *Generator) generateDecodeValue(typ Type, typeName, valueSource, tagSource string) (n int, err error) {
switch typ := typ.(type) { switch typ := typ.(type) {
case TypeInt: case TypeInt:
// SI: (none) // SI: (none)
// LI: <value: IntN> // LI/LSI: <value: IntN>
if typ.Bits <= 5 { if typ.Bits <= 5 {
// SI stores the value in the tag // SI stores the value in the tag
nn, err := this.iprintf("*%s = uint8(%s.CN())\n", valueSource, tagSource) nn, err := this.iprintf("*%s = uint8(%s.CN())\n", valueSource, tagSource)
@@ -837,6 +837,9 @@ func (this *Generator) generateTag(typ Type, source string) (n int, err error) {
if typ.Bits <= 5 { if typ.Bits <= 5 {
nn, err := this.printf("tape.SI.WithCN(int(%s))", source) nn, err := this.printf("tape.SI.WithCN(int(%s))", source)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
} else if typ.Signed {
nn, err := this.printf("tape.LSI.WithCN(%d)", bitsToCN(typ.Bits))
n += nn; if err != nil { return n, err }
} else { } else {
nn, err := this.printf("tape.LI.WithCN(%d)", bitsToCN(typ.Bits)) nn, err := this.printf("tape.LI.WithCN(%d)", bitsToCN(typ.Bits))
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
@@ -881,6 +884,9 @@ func (this *Generator) generateTN(typ Type) (n int, err error) {
if typ.Bits <= 5 { if typ.Bits <= 5 {
nn, err := this.printf("tape.SI") nn, err := this.printf("tape.SI")
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
} else if typ.Signed {
nn, err := this.printf("tape.LSI")
n += nn; if err != nil { return n, err }
} else { } else {
nn, err := this.printf("tape.LI") nn, err := this.printf("tape.LI")
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }

View File

@@ -230,6 +230,26 @@ func TestGenerateRun(test *testing.T) {
Name: "NestedArray", Name: "NestedArray",
Type: TypeArray { Element: TypeArray { Element: TypeInt { Bits: 8 } } }, Type: TypeArray { Element: TypeArray { Element: TypeInt { Bits: 8 } } },
} }
protocol.Messages[0x0004] = Message {
Name: "Integers",
Type: TypeTableDefined {
Fields: map[uint16] Field {
0x0000: Field { Name: "U5", Type: TypeInt { Bits: 5 } },
0x0001: Field { Name: "U8", Type: TypeInt { Bits: 8 } },
0x0002: Field { Name: "U16", Type: TypeInt { Bits: 16 } },
0x0003: Field { Name: "U32", Type: TypeInt { Bits: 32 } },
0x0004: Field { Name: "U64", Type: TypeInt { Bits: 64 } },
0x0006: Field { Name: "I8", Type: TypeInt { Bits: 8, Signed: true } },
0x0007: Field { Name: "I16", Type: TypeInt { Bits: 16, Signed: true } },
0x0008: Field { Name: "I32", Type: TypeInt { Bits: 32, Signed: true } },
0x0009: Field { Name: "I64", Type: TypeInt { Bits: 64, Signed: true } },
0x000B: Field { Name: "NI8", Type: TypeInt { Bits: 8, Signed: true } },
0x000C: Field { Name: "NI16",Type: TypeInt { Bits: 16, Signed: true } },
0x000D: Field { Name: "NI32",Type: TypeInt { Bits: 32, Signed: true } },
0x000E: Field { Name: "NI64",Type: TypeInt { Bits: 64, Signed: true } },
},
},
}
protocol.Types["User"] = TypeTableDefined { protocol.Types["User"] = TypeTableDefined {
Fields: map[uint16] Field { Fields: map[uint16] Field {
0x0000: Field { Name: "Name", Type: TypeString { } }, 0x0000: Field { Name: "Name", Type: TypeString { } },
@@ -248,9 +268,9 @@ func TestGenerateRun(test *testing.T) {
} }
testEncode( testEncode(
&messageConnect, &messageConnect,
tu.S(0xC1, 0x02).AddVar( tu.S(0xE1, 0x02).AddVar(
[]byte { 0x00, 0x00, 0x66, 'r', 'a', 'r', 'i', 't', 'y' }, []byte { 0x00, 0x00, 0x86, 'r', 'a', 'r', 'i', 't', 'y' },
[]byte { 0x00, 0x01, 0x64, 'g', 'e', 'm', 's' }, []byte { 0x00, 0x01, 0x84, 'g', 'e', 'm', 's' },
)) ))
log.Println("MessageUserList") log.Println("MessageUserList")
messageUserList := MessageUserList { messageUserList := MessageUserList {
@@ -274,19 +294,19 @@ func TestGenerateRun(test *testing.T) {
} }
testEncode( testEncode(
&messageUserList, &messageUserList,
tu.S(0xC1, 0x01, 0x00, 0x00, tu.S(0xE1, 0x01, 0x00, 0x00,
0xA1, 0x03, 0xC1, 0xC1, 0x03, 0xE1,
).Add(0x03).AddVar( ).Add(0x03).AddVar(
[]byte { 0x00, 0x00, 0x66, 'r', 'a', 'r', 'i', 't', 'y' }, []byte { 0x00, 0x00, 0x86, 'r', 'a', 'r', 'i', 't', 'y' },
[]byte { 0x00, 0x01, 0x67, 'a', 's', 'd', 'j', 'a', 'd', 's' }, []byte { 0x00, 0x01, 0x87, 'a', 's', 'd', 'j', 'a', 'd', 's' },
[]byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x03, 0x24 }, []byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x03, 0x24 },
).Add(0x03).AddVar( ).Add(0x03).AddVar(
[]byte { 0x00, 0x00, 0x69, 'd', 'e', 'e', 'z', ' ', 'n', 'u', 't', 's' }, []byte { 0x00, 0x00, 0x89, 'd', 'e', 'e', 'z', ' ', 'n', 'u', 't', 's' },
[]byte { 0x00, 0x01, 0x64, 'l', 'o', 'g', 'y' }, []byte { 0x00, 0x01, 0x84, 'l', 'o', 'g', 'y' },
[]byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x80, 0x00 }, []byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x80, 0x00 },
).Add(0x03).AddVar( ).Add(0x03).AddVar(
[]byte { 0x00, 0x00, 0x69, 'c', 'r', 'e', 'e', 'k', 'f', 'l', 'o', 'w' }, []byte { 0x00, 0x00, 0x89, 'c', 'r', 'e', 'e', 'k', 'f', 'l', 'o', 'w' },
[]byte { 0x00, 0x01, 0x6C, 'i', 'm', ' ', 'c', 'r', 'e', 'e', 'k', 'f', []byte { 0x00, 0x01, 0x8C, 'i', 'm', ' ', 'c', 'r', 'e', 'e', 'k', 'f',
'l', 'o', 'w' }, 'l', 'o', 'w' },
[]byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x38, 0x94 }, []byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x38, 0x94 },
)) ))
@@ -300,12 +320,12 @@ func TestGenerateRun(test *testing.T) {
} }
testEncode( testEncode(
&messagePulse, &messagePulse,
tu.S(0xC1, 0x05).AddVar( tu.S(0xE1, 0x05).AddVar(
[]byte { 0x00, 0x00, 0x09 }, []byte { 0x00, 0x00, 0x09 },
[]byte { 0x00, 0x01, 0x21, 0xCA, 0xDF }, []byte { 0x00, 0x01, 0x41, 0xCA, 0xDF },
[]byte { 0x00, 0x02, 0x41, 0x51, 0xAC }, []byte { 0x00, 0x02, 0x61, 0x51, 0xAC },
[]byte { 0x00, 0x03, 0x43, 0x43, 0x93, 0x0C, 0xCD }, []byte { 0x00, 0x03, 0x63, 0x43, 0x93, 0x0C, 0xCD },
[]byte { 0x00, 0x04, 0x47, 0x41, 0xB6, 0xEE, 0x81, 0x28, 0x3C, 0x21, 0xE2 }, []byte { 0x00, 0x04, 0x67, 0x41, 0xB6, 0xEE, 0x81, 0x28, 0x3C, 0x21, 0xE2 },
)) ))
log.Println("MessageNestedArray") log.Println("MessageNestedArray")
uint8s := func(n int) []uint8 { uint8s := func(n int) []uint8 {
@@ -321,7 +341,46 @@ func TestGenerateRun(test *testing.T) {
} }
testEncode( testEncode(
&messageNestedArray, &messageNestedArray,
tu.S(0xA1, // TODO tu.S(0xC1, 0x02, 0xC1,
0x06, 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
35, 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC,
0xFD, 0xFE, 0xFF, 0xF0, 0xF1, 0xF2,
0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE,
0xFF, 0xF0, 0xF1, 0xF2, 0xF3))
log.Println("MessageIntegers")
messageIntegers := MessageIntegers {
U5: 0x13,
U8: 0xC9,
U16: 0x34C9,
U32: 0x10E134C9,
U64: 0x639109BC10E134C9,
I8: 0x35,
I16: 0x34C9,
I32: 0x10E134C9,
I64: 0x639109BC10E134C9,
NI8: -0x35,
NI16: -0x34C9,
NI32: -0x10E134C9,
NI64: -0x639109BC10E134C9,
}
testEncode(
&messageIntegers,
tu.S(0xE1, 13).AddVar(
[]byte { 0x00, 0x00, 0x13 },
[]byte { 0x00, 0x01, 0x20, 0xC9 },
[]byte { 0x00, 0x02, 0x21, 0x34, 0xC9 },
[]byte { 0x00, 0x03, 0x23, 0x10, 0xE1, 0x34, 0xC9 },
[]byte { 0x00, 0x04, 0x27, 0x63, 0x91, 0x09, 0xBC, 0x10, 0xE1, 0x34, 0xC9 },
[]byte { 0x00, 0x06, 0x40, 0x35 },
[]byte { 0x00, 0x07, 0x41, 0x34, 0xC9 },
[]byte { 0x00, 0x08, 0x43, 0x10, 0xE1, 0x34, 0xC9 },
[]byte { 0x00, 0x09, 0x47, 0x63, 0x91, 0x09, 0xBC, 0x10, 0xE1, 0x34, 0xC9 },
[]byte { 0x00, 0x0B, 0x40, 0xCB },
[]byte { 0x00, 0x0C, 0x41, 0xCB, 0x37 },
[]byte { 0x00, 0x0D, 0x43, 0xEF, 0x1E, 0xCB, 0x37 },
[]byte { 0x00, 0x0E, 0x47, 0x9C, 0x6E, 0xF6, 0x43, 0xEF, 0x1E, 0xCB, 0x37 },
)) ))
`) `)
} }

View File

@@ -99,6 +99,10 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err
if err != nil { return n, err } if err != nil { return n, err }
case LI: case LI:
// LI: <value: IntN> // LI: <value: IntN>
nn, err := decodeAndSetUint(decoder, destination, tag.CN() + 1)
n += nn; if err != nil { return n, err }
case LSI:
// LSI: <value: IntN>
nn, err := decodeAndSetInt(decoder, destination, tag.CN() + 1) nn, err := decodeAndSetInt(decoder, destination, tag.CN() + 1)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
case FP: case FP:
@@ -168,30 +172,40 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err
// underlying type is unsupported. See [EncodeAny] for a list of supported // underlying type is unsupported. See [EncodeAny] for a list of supported
// types. // types.
func TagAny(value any) (Tag, error) { func TagAny(value any) (Tag, error) {
// TODO use reflection for all of this to ignore type names return tagAny(reflect.ValueOf(value))
}
func tagAny(reflectValue reflect.Value) (Tag, error) {
// primitives // primitives
switch value := value.(type) { switch reflectValue.Kind() {
case int, uint: return LI.WithCN(3), nil case reflect.Int: return LSI.WithCN(3), nil
case int8, uint8: return LI.WithCN(0), nil case reflect.Int8: return LSI.WithCN(0), nil
case int16, uint16: return LI.WithCN(1), nil case reflect.Int16: return LSI.WithCN(1), nil
case int32, uint32: return LI.WithCN(3), nil case reflect.Int32: return LSI.WithCN(3), nil
case int64, uint64: return LI.WithCN(7), nil case reflect.Int64: return LSI.WithCN(7), nil
case string: return bufferLenTag(len(value)), nil case reflect.Uint: return LI.WithCN(3), nil
case []byte: return bufferLenTag(len(value)), nil case reflect.Uint8: return LI.WithCN(0), nil
case reflect.Uint16: return LI.WithCN(1), nil
case reflect.Uint32: return LI.WithCN(3), nil
case reflect.Uint64: return LI.WithCN(7), nil
case reflect.String: return bufferLenTag(reflectValue.Len()), nil
}
if reflectValue.CanConvert(reflect.TypeOf(dummyBuffer)) {
return bufferLenTag(reflectValue.Len()), nil
} }
// aggregates // aggregates
reflectType := reflect.TypeOf(value) reflectType := reflectValue.Type()
switch reflectType.Kind() { switch reflectType.Kind() {
case reflect.Slice: return OTA.WithCN(IntBytes(uint64(reflect.ValueOf(value).Len())) - 1), nil case reflect.Slice: return OTA.WithCN(IntBytes(uint64(reflectValue.Len())) - 1), nil
case reflect.Array: return OTA.WithCN(reflectType.Len()), nil case reflect.Array: return OTA.WithCN(reflectType.Len()), nil
case reflect.Map: case reflect.Map:
if reflectType.Key() == reflect.TypeOf(uint16(0)) { if reflectType.Key() == reflect.TypeOf(uint16(0)) {
return KTV.WithCN(IntBytes(uint64(reflect.ValueOf(value).Len())) - 1), nil return KTV.WithCN(IntBytes(uint64(reflectValue.Len())) - 1), nil
} }
return 0, fmt.Errorf("cannot encode map key %T, key must be uint16", value) return 0, fmt.Errorf("cannot encode map key %v, key must be uint16", reflectType.Key())
} }
return 0, fmt.Errorf("cannot get tag of type %T", value) return 0, fmt.Errorf("cannot get tag of type %v", reflectType)
} }
func encodeAnySlice(encoder *Encoder, value any, tag Tag) (n int, err error) { func encodeAnySlice(encoder *Encoder, value any, tag Tag) (n int, err error) {
@@ -200,11 +214,10 @@ func encodeAnySlice(encoder *Encoder, value any, tag Tag) (n int, err error) {
nn, err := encoder.WriteUintN(uint64(reflectValue.Len()), tag.CN() + 1) nn, err := encoder.WriteUintN(uint64(reflectValue.Len()), tag.CN() + 1)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
reflectType := reflect.TypeOf(value) reflectType := reflect.TypeOf(value)
oneTag, err := TagAny(reflect.Zero(reflectType.Elem()).Interface()) oneTag, err := tagAny(reflect.Zero(reflectType.Elem()))
if err != nil { return n, err } if err != nil { return n, err }
for index := 0; index < reflectValue.Len(); index += 1 { for index := 0; index < reflectValue.Len(); index += 1 {
item := reflectValue.Index(index).Interface() itemTag, err := tagAny(reflectValue.Index(index))
itemTag, err := TagAny(item)
if err != nil { return n, err } if err != nil { return n, err }
if itemTag.CN() > oneTag.CN() { oneTag = itemTag } if itemTag.CN() > oneTag.CN() { oneTag = itemTag }
} }
@@ -226,11 +239,12 @@ func encodeAnyMap(encoder *Encoder, value any, tag Tag) (n int, err error) {
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
iter := reflectValue.MapRange() iter := reflectValue.MapRange()
for iter.Next() { for iter.Next() {
key := iter.Key().Interface().(uint16) reflectValue := iter.Value().Elem()
value := iter.Value().Interface() key := iter.Key().Interface().(uint16)
value := reflectValue.Interface()
nn, err = encoder.WriteUint16(key) nn, err = encoder.WriteUint16(key)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
itemTag, err := TagAny(value) itemTag, err := tagAny(reflectValue)
if err != nil { return n, err } if err != nil { return n, err }
nn, err = encoder.WriteUint8(uint8(itemTag)) nn, err = encoder.WriteUint8(uint8(itemTag))
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
@@ -241,7 +255,7 @@ func encodeAnyMap(encoder *Encoder, value any, tag Tag) (n int, err error) {
} }
// setInt expects a settable destination. // setInt expects a settable destination.
func setInt(destination reflect.Value, value uint64) error { func setInt[T int64 | uint64](destination reflect.Value, value T) error {
switch { switch {
case destination.CanInt(): case destination.CanInt():
destination.Set(reflect.ValueOf(int64(value)).Convert(destination.Type())) destination.Set(reflect.ValueOf(int64(value)).Convert(destination.Type()))
@@ -277,6 +291,13 @@ func setByteArray(destination reflect.Value, value []byte) error {
// decodeAndSetInt expects a settable destination. // decodeAndSetInt expects a settable destination.
func decodeAndSetInt(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) { func decodeAndSetInt(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) {
value, nn, err := decoder.ReadIntN(bytes)
n += nn; if err != nil { return n, err }
return n, setInt(destination, value)
}
// decodeAndSetUint expects a settable destination.
func decodeAndSetUint(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) {
value, nn, err := decoder.ReadUintN(bytes) value, nn, err := decoder.ReadUintN(bytes)
n += nn; if err != nil { return n, err } n += nn; if err != nil { return n, err }
return n, setInt(destination, value) return n, setInt(destination, value)
@@ -319,6 +340,14 @@ func typeOf(decoder *Decoder, tag Tag) (reflect.Type, error) {
case 7: return reflect.TypeOf(uint64(0)), nil case 7: return reflect.TypeOf(uint64(0)), nil
} }
return nil, fmt.Errorf("unknown CN %d for LI", tag.CN()) return nil, fmt.Errorf("unknown CN %d for LI", tag.CN())
case LSI:
switch tag.CN() {
case 0: return reflect.TypeOf(int8(0)), nil
case 1: return reflect.TypeOf(int16(0)), nil
case 3: return reflect.TypeOf(int32(0)), nil
case 7: return reflect.TypeOf(int64(0)), nil
}
return nil, fmt.Errorf("unknown CN %d for LSI", tag.CN())
case FP: case FP:
switch tag.CN() { switch tag.CN() {
case 3: return reflect.TypeOf(float32(0)), nil case 3: return reflect.TypeOf(float32(0)), nil

View File

@@ -6,10 +6,12 @@ import "testing"
import "reflect" import "reflect"
import tu "git.tebibyte.media/sashakoshka/hopp/internal/testutil" import tu "git.tebibyte.media/sashakoshka/hopp/internal/testutil"
type userDefinedInteger int16
func TestEncodeAnyInt(test *testing.T) { func TestEncodeAnyInt(test *testing.T) {
err := testEncodeAny(test, uint8(0xCA), LI.WithCN(0), tu.S(0xCA)) err := testEncodeAny(test, uint8(0xCA), LI.WithCN(0), tu.S(0xCA))
if err != nil { test.Fatal(err) } if err != nil { test.Fatal(err) }
err = testEncodeAny(test, 400, LI.WithCN(3), tu.S( err = testEncodeAny(test, 400, LSI.WithCN(3), tu.S(
0, 0, 0x1, 0x90, 0, 0, 0x1, 0x90,
)) ))
if err != nil { test.Fatal(err) } if err != nil { test.Fatal(err) }
@@ -22,15 +24,17 @@ func TestEncodeAnyTable(test *testing.T) {
0x0000: "hi!", 0x0000: "hi!",
0xFFFF: []uint16 { 0xBEE5, 0x7777 }, 0xFFFF: []uint16 { 0xBEE5, 0x7777 },
0x1234: [][]uint16 { []uint16 { 0x5 }, []uint16 { 0x17, 0xAAAA} }, 0x1234: [][]uint16 { []uint16 { 0x5 }, []uint16 { 0x17, 0xAAAA} },
}, KTV.WithCN(0), tu.S(5).AddVar( 0x2345: [][]int16 { []int16 { 0x5 }, []int16 { 0x17, -0xAAA } },
0x3456: userDefinedInteger(0x3921),
}, KTV.WithCN(0), tu.S(7).AddVar(
[]byte { []byte {
0xF3, 0xB9, 0xF3, 0xB9,
byte(LI.WithCN(3)), byte(LSI.WithCN(3)),
0, 0, 0, 1, 0, 0, 0, 1,
}, },
[]byte { []byte {
0x01, 0x02, 0x01, 0x02,
byte(LI.WithCN(3)), byte(LSI.WithCN(3)),
0, 0, 0, 2, 0, 0, 0, 2,
}, },
[]byte { []byte {
@@ -52,6 +56,20 @@ func TestEncodeAnyTable(test *testing.T) {
0, 0x17, 0, 0x17,
0xAA, 0xAA, 0xAA, 0xAA,
}, },
[]byte {
0x23, 0x45,
byte(OTA.WithCN(0)), 2, byte(OTA.WithCN(0)),
1, byte(LSI.WithCN(1)),
0, 0x5,
2, byte(LSI.WithCN(1)),
0, 0x17,
0xF5, 0x56,
},
[]byte {
0x34, 0x56,
byte(LSI.WithCN(1)),
0x39, 0x21,
},
)) ))
if err != nil { test.Fatal(err) } if err != nil { test.Fatal(err) }
} }
@@ -60,6 +78,8 @@ func TestEncodeDecodeAnyTable(test *testing.T) {
err := testEncodeDecodeAny(test, map[uint16] any { err := testEncodeDecodeAny(test, map[uint16] any {
0xF3B9: uint32(1), 0xF3B9: uint32(1),
0x0102: uint32(2), 0x0102: uint32(2),
0x0103: int64(23432),
0x0104: int64(-88777),
0x0000: []byte("hi!"), 0x0000: []byte("hi!"),
0xFFFF: []uint16 { 0xBEE5, 0x7777 }, 0xFFFF: []uint16 { 0xBEE5, 0x7777 },
0x1234: [][]uint16 { []uint16 { 0x5 }, []uint16 { 0x17, 0xAAAA} }, 0x1234: [][]uint16 { []uint16 { 0x5 }, []uint16 { 0x17, 0xAAAA} },
@@ -154,10 +174,10 @@ func testEncodeAny(test *testing.T, value any, correctTag Tag, correctBytes tu.S
test.Log("got: ", tu.HexBytes(bytes)) test.Log("got: ", tu.HexBytes(bytes))
test.Log("correct:", correctBytes) test.Log("correct:", correctBytes)
if tag != correctTag { if tag != correctTag {
return fmt.Errorf("tag not equal") return fmt.Errorf("tag not equal: %v != %v", tag, correctTag)
} }
if ok, n := correctBytes.Check(bytes); !ok { if ok, n := correctBytes.Check(bytes); !ok {
return fmt.Errorf("bytes not equal: %d", n) return fmt.Errorf("bytes not equal at index %d", n)
} }
if n != len(bytes) { if n != len(bytes) {
return fmt.Errorf("n not equal: %d != %d", n, len(bytes)) return fmt.Errorf("n not equal: %d != %d", n, len(bytes))

View File

@@ -2,14 +2,17 @@ package tape
import "fmt" import "fmt"
// TODO: fix #7
type Tag byte; const ( type Tag byte; const (
SI Tag = 0 << 5 // Small integer SI Tag = 0 << 5 // Small integer
LI Tag = 1 << 5 // Large integer LI Tag = 1 << 5 // Large unsigned integer
FP Tag = 2 << 5 // Floating point LSI Tag = 2 << 5 // Large signed integer
SBA Tag = 3 << 5 // Small byte array FP Tag = 3 << 5 // Floating point
LBA Tag = 4 << 5 // Large byte array SBA Tag = 4 << 5 // Small byte array
OTA Tag = 5 << 5 // One-tag array LBA Tag = 5 << 5 // Large byte array
KTV Tag = 6 << 5 // Key-tag-value table OTA Tag = 6 << 5 // One-tag array
KTV Tag = 7 << 5 // Key-tag-value table
TNMask Tag = 0xE0 // The entire TN bitfield TNMask Tag = 0xE0 // The entire TN bitfield
CNMask Tag = 0x1F // The entire CN bitfield CNMask Tag = 0x1F // The entire CN bitfield
CNLimit Tag = 32 // All valid CNs are < CNLimit CNLimit Tag = 32 // All valid CNs are < CNLimit
@@ -40,6 +43,7 @@ func (tag Tag) String() string {
switch tag.WithoutCN() { switch tag.WithoutCN() {
case SI: tn = "SI" case SI: tn = "SI"
case LI: tn = "LI" case LI: tn = "LI"
case LSI: tn = "LSI"
case FP: tn = "FP" case FP: tn = "FP"
case SBA: tn = "SBA" case SBA: tn = "SBA"
case LBA: tn = "LBA" case LBA: tn = "LBA"