From 8e14a2c3f1e4b9a15484f34d6abc802995234219 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Thu, 7 Aug 2025 21:14:40 -0400 Subject: [PATCH 1/8] tape: Add LSI to Tag constants --- tape/tag.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tape/tag.go b/tape/tag.go index 9a89f38..f4001f2 100644 --- a/tape/tag.go +++ b/tape/tag.go @@ -2,14 +2,17 @@ package tape import "fmt" +// TODO: fix #7 + 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 + LI Tag = 1 << 5 // Large unsigned integer + LSI Tag = 2 << 5 // Large signed integer + FP Tag = 3 << 5 // Floating point + SBA Tag = 4 << 5 // Small byte array + LBA Tag = 5 << 5 // Large byte array + OTA Tag = 6 << 5 // One-tag array + KTV Tag = 7 << 5 // Key-tag-value table TNMask Tag = 0xE0 // The entire TN bitfield CNMask Tag = 0x1F // The entire CN bitfield CNLimit Tag = 32 // All valid CNs are < CNLimit @@ -40,6 +43,7 @@ func (tag Tag) String() string { switch tag.WithoutCN() { case SI: tn = "SI" case LI: tn = "LI" + case LSI: tn = "LSI" case FP: tn = "FP" case SBA: tn = "SBA" case LBA: tn = "LBA" From 52f0d6932e941cfaf31fe3eb72428de36dd123fa Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Sun, 10 Aug 2025 23:28:25 -0400 Subject: [PATCH 2/8] tape: Add encoding and decoding of signed integers --- tape/dynamic.go | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tape/dynamic.go b/tape/dynamic.go index dd89055..ab78e01 100644 --- a/tape/dynamic.go +++ b/tape/dynamic.go @@ -99,6 +99,10 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err if err != nil { return n, err } case LI: // LI: + nn, err := decodeAndSetUint(decoder, destination, tag.CN() + 1) + n += nn; if err != nil { return n, err } + case LSI: + // LSI: nn, err := decodeAndSetInt(decoder, destination, tag.CN() + 1) n += nn; if err != nil { return n, err } case FP: @@ -171,13 +175,18 @@ 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 + case int: return LSI.WithCN(3), nil + case int8: return LSI.WithCN(0), nil + case int16: return LSI.WithCN(1), nil + case int32: return LSI.WithCN(3), nil + case int64: return LSI.WithCN(7), nil + case uint: return LI.WithCN(3), nil + case uint8: return LI.WithCN(0), nil + case uint16: return LI.WithCN(1), nil + case uint32: return LI.WithCN(3), nil + case uint64: return LI.WithCN(7), nil + case string: return bufferLenTag(len(value)), nil + case []byte: return bufferLenTag(len(value)), nil } // aggregates @@ -241,7 +250,7 @@ func encodeAnyMap(encoder *Encoder, value any, tag Tag) (n int, err error) { } // 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 { case destination.CanInt(): destination.Set(reflect.ValueOf(int64(value)).Convert(destination.Type())) @@ -277,6 +286,13 @@ func setByteArray(destination reflect.Value, value []byte) error { // decodeAndSetInt expects a settable destination. 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) n += nn; if err != nil { return n, err } return n, setInt(destination, value) From fe973af99c0af10afb615c5c0ad2cbf812bd8e97 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Sun, 10 Aug 2025 23:28:43 -0400 Subject: [PATCH 3/8] tape: Test dynamic encoding and decoding of signed integers --- tape/dynamic_test.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tape/dynamic_test.go b/tape/dynamic_test.go index 19575bd..d942757 100644 --- a/tape/dynamic_test.go +++ b/tape/dynamic_test.go @@ -9,7 +9,7 @@ import tu "git.tebibyte.media/sashakoshka/hopp/internal/testutil" func TestEncodeAnyInt(test *testing.T) { err := testEncodeAny(test, uint8(0xCA), LI.WithCN(0), tu.S(0xCA)) 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, )) if err != nil { test.Fatal(err) } @@ -22,15 +22,16 @@ func TestEncodeAnyTable(test *testing.T) { 0x0000: "hi!", 0xFFFF: []uint16 { 0xBEE5, 0x7777 }, 0x1234: [][]uint16 { []uint16 { 0x5 }, []uint16 { 0x17, 0xAAAA} }, - }, KTV.WithCN(0), tu.S(5).AddVar( + 0x2345: [][]int16 { []int16 { 0x5 }, []int16 { 0x17, -0xAAA } }, + }, KTV.WithCN(0), tu.S(6).AddVar( []byte { 0xF3, 0xB9, - byte(LI.WithCN(3)), + byte(LSI.WithCN(3)), 0, 0, 0, 1, }, []byte { 0x01, 0x02, - byte(LI.WithCN(3)), + byte(LSI.WithCN(3)), 0, 0, 0, 2, }, []byte { @@ -52,6 +53,15 @@ func TestEncodeAnyTable(test *testing.T) { 0, 0x17, 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, + }, )) if err != nil { test.Fatal(err) } } From 024edfa92245a3f77dba2cb1c4e9fecd1c1ceee9 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Mon, 11 Aug 2025 18:39:38 -0400 Subject: [PATCH 4/8] tape: Actually test decoding lol --- tape/dynamic_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tape/dynamic_test.go b/tape/dynamic_test.go index d942757..81918f0 100644 --- a/tape/dynamic_test.go +++ b/tape/dynamic_test.go @@ -70,6 +70,8 @@ func TestEncodeDecodeAnyTable(test *testing.T) { err := testEncodeDecodeAny(test, map[uint16] any { 0xF3B9: uint32(1), 0x0102: uint32(2), + 0x0103: int64(23432), + 0x0104: int64(-88777), 0x0000: []byte("hi!"), 0xFFFF: []uint16 { 0xBEE5, 0x7777 }, 0x1234: [][]uint16 { []uint16 { 0x5 }, []uint16 { 0x17, 0xAAAA} }, From 1058615f6fccb41dc6fb153388c065f565c75cf0 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Mon, 11 Aug 2025 18:40:56 -0400 Subject: [PATCH 5/8] tape: Do something when receiving an LSI tag --- tape/dynamic.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tape/dynamic.go b/tape/dynamic.go index ab78e01..86fdb1a 100644 --- a/tape/dynamic.go +++ b/tape/dynamic.go @@ -335,6 +335,14 @@ func typeOf(decoder *Decoder, tag Tag) (reflect.Type, error) { case 7: return reflect.TypeOf(uint64(0)), nil } 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: switch tag.CN() { case 3: return reflect.TypeOf(float32(0)), nil From 02196edf610365d86f288fa369ffe57531cdf765 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Mon, 11 Aug 2025 20:10:03 -0400 Subject: [PATCH 6/8] design: Change tag for signed PDL integers --- design/pdl-language.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/design/pdl-language.md b/design/pdl-language.md index 2eb9754..27195bd 100644 --- a/design/pdl-language.md +++ b/design/pdl-language.md @@ -7,12 +7,12 @@ PDL allows defining a protocol using HOPP and TAPE. | Syntax | TN | CN | Description | ---------- | ------- | -: | ----------- | I5 | SI | | -| I8 | LI | 0 | -| I16 | LI | 1 | -| I32 | LI | 3 | -| I64 | LI | 7 | -| I128[^2] | LI | 15 | -| I256[^2] | LI | 31 | +| I8 | LSI | 0 | +| I16 | LSI | 1 | +| I32 | LSI | 3 | +| I64 | LSI | 7 | +| I128[^2] | LSI | 15 | +| I256[^2] | LSI | 31 | | U5 | SI | | | U8 | LI | 0 | | U16 | LI | 1 | From 0e03f84b8a1f8412d6a0eae315294593bf5e3ad1 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Mon, 11 Aug 2025 20:59:10 -0400 Subject: [PATCH 7/8] generate: Update tests with new TNs --- generate/generate_test.go | 87 +++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/generate/generate_test.go b/generate/generate_test.go index 943886f..1acac06 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -230,6 +230,26 @@ func TestGenerateRun(test *testing.T) { Name: "NestedArray", 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 { Fields: map[uint16] Field { 0x0000: Field { Name: "Name", Type: TypeString { } }, @@ -248,9 +268,9 @@ func TestGenerateRun(test *testing.T) { } testEncode( &messageConnect, - tu.S(0xC1, 0x02).AddVar( - []byte { 0x00, 0x00, 0x66, 'r', 'a', 'r', 'i', 't', 'y' }, - []byte { 0x00, 0x01, 0x64, 'g', 'e', 'm', 's' }, + tu.S(0xE1, 0x02).AddVar( + []byte { 0x00, 0x00, 0x86, 'r', 'a', 'r', 'i', 't', 'y' }, + []byte { 0x00, 0x01, 0x84, 'g', 'e', 'm', 's' }, )) log.Println("MessageUserList") messageUserList := MessageUserList { @@ -274,19 +294,19 @@ func TestGenerateRun(test *testing.T) { } testEncode( &messageUserList, - tu.S(0xC1, 0x01, 0x00, 0x00, - 0xA1, 0x03, 0xC1, + tu.S(0xE1, 0x01, 0x00, 0x00, + 0xC1, 0x03, 0xE1, ).Add(0x03).AddVar( - []byte { 0x00, 0x00, 0x66, 'r', 'a', 'r', 'i', 't', 'y' }, - []byte { 0x00, 0x01, 0x67, 'a', 's', 'd', 'j', 'a', 'd', 's' }, + []byte { 0x00, 0x00, 0x86, 'r', 'a', 'r', 'i', 't', 'y' }, + []byte { 0x00, 0x01, 0x87, 'a', 's', 'd', 'j', 'a', 'd', 's' }, []byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x03, 0x24 }, ).Add(0x03).AddVar( - []byte { 0x00, 0x00, 0x69, 'd', 'e', 'e', 'z', ' ', 'n', 'u', 't', 's' }, - []byte { 0x00, 0x01, 0x64, 'l', 'o', 'g', 'y' }, + []byte { 0x00, 0x00, 0x89, 'd', 'e', 'e', 'z', ' ', 'n', 'u', 't', 's' }, + []byte { 0x00, 0x01, 0x84, 'l', 'o', 'g', 'y' }, []byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x80, 0x00 }, ).Add(0x03).AddVar( - []byte { 0x00, 0x00, 0x69, 'c', 'r', 'e', 'e', 'k', 'f', 'l', 'o', 'w' }, - []byte { 0x00, 0x01, 0x6C, 'i', 'm', ' ', 'c', 'r', 'e', 'e', 'k', 'f', + []byte { 0x00, 0x00, 0x89, 'c', 'r', 'e', 'e', 'k', 'f', 'l', 'o', 'w' }, + []byte { 0x00, 0x01, 0x8C, 'i', 'm', ' ', 'c', 'r', 'e', 'e', 'k', 'f', 'l', 'o', 'w' }, []byte { 0x00, 0x02, 0x23, 0x00, 0x00, 0x38, 0x94 }, )) @@ -300,12 +320,12 @@ func TestGenerateRun(test *testing.T) { } testEncode( &messagePulse, - tu.S(0xC1, 0x05).AddVar( + tu.S(0xE1, 0x05).AddVar( []byte { 0x00, 0x00, 0x09 }, - []byte { 0x00, 0x01, 0x21, 0xCA, 0xDF }, - []byte { 0x00, 0x02, 0x41, 0x51, 0xAC }, - []byte { 0x00, 0x03, 0x43, 0x43, 0x93, 0x0C, 0xCD }, - []byte { 0x00, 0x04, 0x47, 0x41, 0xB6, 0xEE, 0x81, 0x28, 0x3C, 0x21, 0xE2 }, + []byte { 0x00, 0x01, 0x41, 0xCA, 0xDF }, + []byte { 0x00, 0x02, 0x61, 0x51, 0xAC }, + []byte { 0x00, 0x03, 0x63, 0x43, 0x93, 0x0C, 0xCD }, + []byte { 0x00, 0x04, 0x67, 0x41, 0xB6, 0xEE, 0x81, 0x28, 0x3C, 0x21, 0xE2 }, )) log.Println("MessageNestedArray") uint8s := func(n int) []uint8 { @@ -321,7 +341,7 @@ func TestGenerateRun(test *testing.T) { } testEncode( &messageNestedArray, - tu.S(0xA1, 0x02, 0xA1, + 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, @@ -329,5 +349,38 @@ func TestGenerateRun(test *testing.T) { 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 }, + )) `) } From dc72cc2010bf7f1d2828a156e44fc6783a8e45ad Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Mon, 11 Aug 2025 20:59:20 -0400 Subject: [PATCH 8/8] generate: Support LSI tags --- generate/generate.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/generate/generate.go b/generate/generate.go index 0f21e37..7d6aa1b 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -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) { switch typ := typ.(type) { case TypeInt: - // SI: (none) - // LI: + // SI: (none) + // LI/LSI: if typ.Bits <= 5 { // SI stores the value in the tag, so we write nothing here 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) { switch typ := typ.(type) { case TypeInt: - // SI: (none) - // LI: + // SI: (none) + // LI/LSI: if typ.Bits <= 5 { // SI stores the value in the tag 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 { nn, err := this.printf("tape.SI.WithCN(int(%s))", source) 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 { nn, err := this.printf("tape.LI.WithCN(%d)", bitsToCN(typ.Bits)) 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 { nn, err := this.printf("tape.SI") 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 { nn, err := this.printf("tape.LI") n += nn; if err != nil { return n, err }