10 Commits

6 changed files with 150 additions and 45 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:
@@ -171,13 +175,18 @@ func TagAny(value any) (Tag, error) {
// TODO use reflection for all of this to ignore type names // TODO use reflection for all of this to ignore type names
// primitives // primitives
switch value := value.(type) { switch value := value.(type) {
case int, uint: return LI.WithCN(3), nil case int: return LSI.WithCN(3), nil
case int8, uint8: return LI.WithCN(0), nil case int8: return LSI.WithCN(0), nil
case int16, uint16: return LI.WithCN(1), nil case int16: return LSI.WithCN(1), nil
case int32, uint32: return LI.WithCN(3), nil case int32: return LSI.WithCN(3), nil
case int64, uint64: return LI.WithCN(7), nil case int64: return LSI.WithCN(7), nil
case string: return bufferLenTag(len(value)), nil case uint: return LI.WithCN(3), nil
case []byte: return bufferLenTag(len(value)), 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 // aggregates
@@ -241,7 +250,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 +286,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 +335,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

@@ -9,7 +9,7 @@ import tu "git.tebibyte.media/sashakoshka/hopp/internal/testutil"
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 +22,16 @@ 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 } },
}, KTV.WithCN(0), tu.S(6).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 +53,15 @@ 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,
},
)) ))
if err != nil { test.Fatal(err) } if err != nil { test.Fatal(err) }
} }
@@ -60,6 +70,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} },

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"