Compare commits
12 Commits
encode-sig
...
52aa07a98f
| Author | SHA1 | Date | |
|---|---|---|---|
| 52aa07a98f | |||
| 94041f2abc | |||
| 423f547da3 | |||
| 9278bdcb43 | |||
| 0acf44886a | |||
| a4da33536c | |||
| 2180d29615 | |||
| 2209763666 | |||
| 96c8d7924f | |||
| fdf0aa89a4 | |||
| 1bded9852d | |||
| 8beb9de256 |
@@ -1,151 +1,15 @@
|
|||||||
package generate
|
package generate
|
||||||
|
|
||||||
// import "fmt"
|
// import "fmt"
|
||||||
import "strings"
|
|
||||||
import "testing"
|
import "testing"
|
||||||
import "git.tebibyte.media/sashakoshka/goparse"
|
|
||||||
|
|
||||||
var testGenerateCorrect =
|
// TODO: once everything has been ironed out, test that the public API of the
|
||||||
`package protocol
|
// generator is equal to something specific
|
||||||
|
|
||||||
/* # Do not edit this package by hand!
|
var exampleProtocol = defaultProtocol()
|
||||||
*
|
|
||||||
* This file was automatically generated by the Holanet PDL compiler. The
|
|
||||||
* source file is located at input.pdl
|
|
||||||
* Please edit that file instead, and re-compile it to this location.
|
|
||||||
*
|
|
||||||
* HOPP, TAPE, METADAPT, PDL/0 (c) 2025 holanet.xyz
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "git.tebibyte.media/sashakoshka/hopp/tape"
|
func init() {
|
||||||
|
exampleProtocol.Messages[0x0000] = Message {
|
||||||
// Table is a KTV table with an undefined schema.
|
|
||||||
type Table map[uint16] any
|
|
||||||
|
|
||||||
// Message is any message that can be sent along this protocol.
|
|
||||||
type Message interface {
|
|
||||||
tape.Encodable
|
|
||||||
tape.Decodable
|
|
||||||
|
|
||||||
// Method returns the method code of the message.
|
|
||||||
Method() uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// User represents the protocol data type User.
|
|
||||||
type User struct {
|
|
||||||
Name string
|
|
||||||
Bio string
|
|
||||||
Followers uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeValue encodes the value of this type without the tag. The value is
|
|
||||||
// encoded according to the parameters specified by the tag, if possible.
|
|
||||||
func (this *User) EncodeValue(encoder *tape.Encoder) (n int, err error) {
|
|
||||||
nn, err := tape.WriteTableHeader(2)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
nn, err := encoder.WriteUint16(0x0000)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
nn, err := tape.WriteString(encoder, this.Name)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
nn, err := encoder.WriteUint16(0x0001)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
nn, err := tape.WriteString(encoder, this.Bio)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode replaces the data in this User with information from the decoder.
|
|
||||||
func (this *User) Decode(decoder *tape.Decoder) (n int, err error) {
|
|
||||||
pull, nn, err := tape.ReadTableHeader(decoder)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
|
|
||||||
for {
|
|
||||||
key, tag, end, nn, err := pull()
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
if end { break }
|
|
||||||
|
|
||||||
switch key {
|
|
||||||
case 0x0000:
|
|
||||||
value, nn, err := tape.ReadString(decoder)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
this.Name = value
|
|
||||||
case 0x0001:
|
|
||||||
value, nn, err := tape.ReadString(decoder)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
this.Bio = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageConnect represents the protocol message M0000 Connect.
|
|
||||||
type MessageConnect struct {
|
|
||||||
Name string
|
|
||||||
Password string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method returns the method code, M0000.
|
|
||||||
func (this *MessageConnect) Method() uint16 {
|
|
||||||
return 0x0000
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode encodes the message to the encoder.
|
|
||||||
func (this *MessageConnect) Encode(encoder *tape.Encoder) (n int, err error) {
|
|
||||||
nn, err := tape.WriteTableHeader(2)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
nn, err := encoder.WriteUint16(0x0000)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
nn, err := tape.WriteString(encoder, this.Name)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
nn, err := encoder.WriteUint16(0x0001)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
nn, err := tape.WriteString(encoder, this.Password)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode replaces the data in this message with information from the decoder.
|
|
||||||
func (this *MessageConnect) Decode(decoder *tape.Decoder) (n int, err error) {
|
|
||||||
pull, nn, err := tape.ReadTableHeader(decoder)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
|
|
||||||
for {
|
|
||||||
key, tag, end, nn, err := pull()
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
if end { break }
|
|
||||||
|
|
||||||
switch key {
|
|
||||||
case 0x0000:
|
|
||||||
value, nn, err := tape.ReadString(decoder)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
this.Name = value
|
|
||||||
case 0x0001:
|
|
||||||
value, nn, err := tape.ReadString(decoder)
|
|
||||||
n += nn; if err != nil { return n, err }
|
|
||||||
this.Password = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageUserList represents the protocol message M0001 UserList.
|
|
||||||
type MessageUserList struct {
|
|
||||||
Users []User
|
|
||||||
}
|
|
||||||
|
|
||||||
// Method returns the method code, M0001.
|
|
||||||
func (this *MessageUserList) Method() uint16 {
|
|
||||||
return 0x0001
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO methods
|
|
||||||
`
|
|
||||||
|
|
||||||
func TestGenerate(test *testing.T) {
|
|
||||||
protocol := defaultProtocol()
|
|
||||||
protocol.Messages[0x0000] = Message {
|
|
||||||
Name: "Connect",
|
Name: "Connect",
|
||||||
Type: TypeTableDefined {
|
Type: TypeTableDefined {
|
||||||
Fields: map[uint16] Field {
|
Fields: map[uint16] Field {
|
||||||
@@ -154,7 +18,7 @@ func TestGenerate(test *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
protocol.Messages[0x0001] = Message {
|
exampleProtocol.Messages[0x0001] = Message {
|
||||||
Name: "UserList",
|
Name: "UserList",
|
||||||
Type: TypeTableDefined {
|
Type: TypeTableDefined {
|
||||||
Fields: map[uint16] Field {
|
Fields: map[uint16] Field {
|
||||||
@@ -162,59 +26,7 @@ func TestGenerate(test *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
protocol.Types["User"] = TypeTableDefined {
|
exampleProtocol.Messages[0x0002] = Message {
|
||||||
Fields: map[uint16] Field {
|
|
||||||
0x0000: Field { Name: "Name", Type: TypeString { } },
|
|
||||||
0x0001: Field { Name: "Bio", Type: TypeString { } },
|
|
||||||
0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
correct := testGenerateCorrect
|
|
||||||
|
|
||||||
builder := strings.Builder { }
|
|
||||||
generator := Generator { Output: &builder }
|
|
||||||
/* TODO test n: */ _, err := generator.Generate(&protocol)
|
|
||||||
if err != nil { test.Fatal(parse.Format(err)) }
|
|
||||||
got := builder.String()
|
|
||||||
|
|
||||||
test.Log("CORRECT:")
|
|
||||||
test.Log(correct)
|
|
||||||
test.Log("GOT:")
|
|
||||||
test.Log(got)
|
|
||||||
|
|
||||||
if correct != got {
|
|
||||||
test.Error("not equal")
|
|
||||||
for index := range min(len(correct), len(got)) {
|
|
||||||
if correct[index] == got[index] { continue }
|
|
||||||
test.Log("C:", correct[max(0, index - 8):min(len(correct), index + 8)])
|
|
||||||
test.Log("G:", got[max(0, index - 8):min(len(got), index + 8)])
|
|
||||||
break
|
|
||||||
}
|
|
||||||
test.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGenerateRun(test *testing.T) {
|
|
||||||
protocol := defaultProtocol()
|
|
||||||
protocol.Messages[0x0000] = Message {
|
|
||||||
Name: "Connect",
|
|
||||||
Type: TypeTableDefined {
|
|
||||||
Fields: map[uint16] Field {
|
|
||||||
0x0000: Field { Name: "Name", Type: TypeString { } },
|
|
||||||
0x0001: Field { Name: "Password", Type: TypeString { } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
protocol.Messages[0x0001] = Message {
|
|
||||||
Name: "UserList",
|
|
||||||
Type: TypeTableDefined {
|
|
||||||
Fields: map[uint16] Field {
|
|
||||||
0x0000: Field { Name: "Users", Type: TypeArray { Element: TypeNamed { Name: "User" } } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
protocol.Messages[0x0002] = Message {
|
|
||||||
Name: "Pulse",
|
Name: "Pulse",
|
||||||
Type: TypeTableDefined {
|
Type: TypeTableDefined {
|
||||||
Fields: map[uint16] Field {
|
Fields: map[uint16] Field {
|
||||||
@@ -226,11 +38,11 @@ func TestGenerateRun(test *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
protocol.Messages[0x0003] = Message {
|
exampleProtocol.Messages[0x0003] = Message {
|
||||||
Name: "NestedArray",
|
Name: "NestedArray",
|
||||||
Type: TypeArray { Element: TypeArray { Element: TypeInt { Bits: 8 } } },
|
Type: TypeArray { Element: TypeArray { Element: TypeInt { Bits: 8 } } },
|
||||||
}
|
}
|
||||||
protocol.Messages[0x0004] = Message {
|
exampleProtocol.Messages[0x0004] = Message {
|
||||||
Name: "Integers",
|
Name: "Integers",
|
||||||
Type: TypeTableDefined {
|
Type: TypeTableDefined {
|
||||||
Fields: map[uint16] Field {
|
Fields: map[uint16] Field {
|
||||||
@@ -250,14 +62,17 @@ func TestGenerateRun(test *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
protocol.Types["User"] = TypeTableDefined {
|
exampleProtocol.Types["User"] = TypeTableDefined {
|
||||||
Fields: map[uint16] Field {
|
Fields: map[uint16] Field {
|
||||||
0x0000: Field { Name: "Name", Type: TypeString { } },
|
0x0000: Field { Name: "Name", Type: TypeString { } },
|
||||||
0x0001: Field { Name: "Bio", Type: TypeString { } },
|
0x0001: Field { Name: "Bio", Type: TypeString { } },
|
||||||
0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } },
|
0x0002: Field { Name: "Followers", Type: TypeInt { Bits: 32 } },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
testGenerateRun(test, &protocol, `
|
}
|
||||||
|
|
||||||
|
func TestGenerateRunEncode(test *testing.T) {
|
||||||
|
testGenerateRun(test, &exampleProtocol, "encode", `
|
||||||
// imports
|
// imports
|
||||||
`, `
|
`, `
|
||||||
// test case
|
// test case
|
||||||
@@ -384,3 +199,131 @@ func TestGenerateRun(test *testing.T) {
|
|||||||
))
|
))
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGenerateRunDecode(test *testing.T) {
|
||||||
|
testGenerateRun(test, &exampleProtocol, "decode", `
|
||||||
|
// imports
|
||||||
|
`, `
|
||||||
|
log.Println("MessageConnect")
|
||||||
|
messageConnect := MessageConnect {
|
||||||
|
Name: "rarity",
|
||||||
|
Password: "gems",
|
||||||
|
}
|
||||||
|
testDecode(
|
||||||
|
&messageConnect,
|
||||||
|
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 {
|
||||||
|
Users: []User {
|
||||||
|
User {
|
||||||
|
Name: "rarity",
|
||||||
|
Bio: "asdjads",
|
||||||
|
Followers: 0x324,
|
||||||
|
},
|
||||||
|
User {
|
||||||
|
Name: "deez nuts",
|
||||||
|
Bio: "logy",
|
||||||
|
Followers: 0x8000,
|
||||||
|
},
|
||||||
|
User {
|
||||||
|
Name: "creekflow",
|
||||||
|
Bio: "im creekflow",
|
||||||
|
Followers: 0x3894,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testEncode(
|
||||||
|
&messageUserList,
|
||||||
|
tu.S(0xE1, 0x01, 0x00, 0x00,
|
||||||
|
0xC1, 0x03, 0xE1,
|
||||||
|
).Add(0x03).AddVar(
|
||||||
|
[]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, 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, 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 },
|
||||||
|
))
|
||||||
|
log.Println("MessagePulse")
|
||||||
|
messagePulse := MessagePulse {
|
||||||
|
Index: 9,
|
||||||
|
Offset: -0x3521,
|
||||||
|
X: 45.389,
|
||||||
|
Y: 294.1,
|
||||||
|
Z: 384729384.234892034,
|
||||||
|
}
|
||||||
|
testEncode(
|
||||||
|
&messagePulse,
|
||||||
|
tu.S(0xE1, 0x05).AddVar(
|
||||||
|
[]byte { 0x00, 0x00, 0x09 },
|
||||||
|
[]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 {
|
||||||
|
array := make([]uint8, n)
|
||||||
|
for index := range array {
|
||||||
|
array[index] = uint8(index + 1) | 0xF0
|
||||||
|
}
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
messageNestedArray := MessageNestedArray {
|
||||||
|
uint8s(6),
|
||||||
|
uint8s(35),
|
||||||
|
}
|
||||||
|
testEncode(
|
||||||
|
&messageNestedArray,
|
||||||
|
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 },
|
||||||
|
))
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import "os/exec"
|
|||||||
import "testing"
|
import "testing"
|
||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
|
|
||||||
func testGenerateRun(test *testing.T, protocol *Protocol, imports string, testCase string) {
|
func testGenerateRun(test *testing.T, protocol *Protocol, title, imports, testCase string) {
|
||||||
// reset data directory
|
// reset data directory
|
||||||
dir := "test/generate-run"
|
dir := filepath.Join("test", title)
|
||||||
err := os.RemoveAll(dir)
|
err := os.RemoveAll(dir)
|
||||||
if err != nil { test.Fatal(err) }
|
if err != nil { test.Fatal(err) }
|
||||||
err = os.MkdirAll(dir, 0750)
|
err = os.MkdirAll(dir, 0750)
|
||||||
@@ -34,6 +34,7 @@ func testGenerateRun(test *testing.T, protocol *Protocol, imports string, testCa
|
|||||||
imports = `
|
imports = `
|
||||||
import "log"
|
import "log"
|
||||||
import "bytes"
|
import "bytes"
|
||||||
|
import "reflect"
|
||||||
import "git.tebibyte.media/sashakoshka/hopp/tape"
|
import "git.tebibyte.media/sashakoshka/hopp/tape"
|
||||||
import tu "git.tebibyte.media/sashakoshka/hopp/internal/testutil"
|
import tu "git.tebibyte.media/sashakoshka/hopp/internal/testutil"
|
||||||
` + imports
|
` + imports
|
||||||
@@ -56,13 +57,34 @@ func testGenerateRun(test *testing.T, protocol *Protocol, imports string, testCa
|
|||||||
log.Fatalln("not equal at", n)
|
log.Fatalln("not equal at", n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testDecode(correct Message, data any) {
|
||||||
|
var flat []byte
|
||||||
|
switch data := data.(type) {
|
||||||
|
case []byte: flat = data
|
||||||
|
case tu.Snake: flat = data.Flatten()
|
||||||
|
}
|
||||||
|
message := reflect.New(reflect.ValueOf(correct).Elem().Type()).Interface().(Message)
|
||||||
|
log.Println("before: ", message)
|
||||||
|
decoder := tape.NewDecoder(bytes.NewBuffer(flat))
|
||||||
|
n, err := message.Decode(decoder)
|
||||||
|
if err != nil { log.Fatalf("at %d: %v\n", n, err) }
|
||||||
|
log.Println("got: ", message)
|
||||||
|
log.Println("correct:", correct)
|
||||||
|
if n != len(flat) {
|
||||||
|
log.Fatalf("n incorrect: %d != %d\n", n, len(flat))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(message, correct) {
|
||||||
|
log.Fatalln("not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
`
|
`
|
||||||
fmt.Fprintf(
|
fmt.Fprintf(
|
||||||
mainFile, "package main\n%s\nfunc main() {\n%s\n%s\n%s\n}\n%s",
|
mainFile, "package main\n%s\nfunc main() {\n%s\n%s\n%s\n}\n%s",
|
||||||
imports, setup, testCase, teardown, static)
|
imports, setup, testCase, teardown, static)
|
||||||
|
|
||||||
// build and run test
|
// build and run test
|
||||||
command := exec.Command("go", "run", "./generate/test/generate-run")
|
command := exec.Command("go", "run", "./" + filepath.Join("generate", dir))
|
||||||
workingDirAbs, err := filepath.Abs("..")
|
workingDirAbs, err := filepath.Abs("..")
|
||||||
if err != nil { test.Fatal(err) }
|
if err != nil { test.Fatal(err) }
|
||||||
command.Dir = workingDirAbs
|
command.Dir = workingDirAbs
|
||||||
|
|||||||
@@ -64,6 +64,18 @@ func (sn Snake) Check(data []byte) (ok bool, n int) {
|
|||||||
return true, n
|
return true, n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flatten returns the snake flattened to a byte array. The result of this
|
||||||
|
// function always satisfies the snake.
|
||||||
|
func (sn Snake) Flatten() []byte {
|
||||||
|
flat := []byte { }
|
||||||
|
for _, sector := range sn {
|
||||||
|
for _, variation := range sector {
|
||||||
|
flat = append(flat, variation...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flat
|
||||||
|
}
|
||||||
|
|
||||||
func (sn Snake) String() string {
|
func (sn Snake) String() string {
|
||||||
if len(sn) == 0 || len(sn[0]) == 0 || len(sn[0][0]) == 0 {
|
if len(sn) == 0 || len(sn[0]) == 0 || len(sn[0][0]) == 0 {
|
||||||
return "EMPTY"
|
return "EMPTY"
|
||||||
|
|||||||
200
tape/dynamic.go
200
tape/dynamic.go
@@ -14,6 +14,14 @@ import "reflect"
|
|||||||
var dummyMap map[uint16] any
|
var dummyMap map[uint16] any
|
||||||
var dummyBuffer []byte
|
var dummyBuffer []byte
|
||||||
|
|
||||||
|
type errCantAssign string
|
||||||
|
func (err errCantAssign) Error() string {
|
||||||
|
return string(err)
|
||||||
|
}
|
||||||
|
func errCantAssignf(format string, v ...any) errCantAssign {
|
||||||
|
return errCantAssign(fmt.Sprintf(format, v...))
|
||||||
|
}
|
||||||
|
|
||||||
// EncodeAny encodes an "any" value. Returns an error if the underlying type is
|
// EncodeAny encodes an "any" value. Returns an error if the underlying type is
|
||||||
// unsupported. Supported types are:
|
// unsupported. Supported types are:
|
||||||
//
|
//
|
||||||
@@ -83,20 +91,34 @@ type unknownSlicePlaceholder struct { }
|
|||||||
var unknownSlicePlaceholderType = reflect.TypeOf(unknownSlicePlaceholder { })
|
var unknownSlicePlaceholderType = reflect.TypeOf(unknownSlicePlaceholder { })
|
||||||
|
|
||||||
// decodeAny is internal to [DecodeAny]. It takes in an addressable
|
// decodeAny is internal to [DecodeAny]. It takes in an addressable
|
||||||
// [reflect.Value] as the destination.
|
// [reflect.Value] as the destination. If the decoded value cannot fit in the
|
||||||
|
// destination, it skims over the payload, leaves the destination empty, and
|
||||||
|
// returns without an error.
|
||||||
func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err error) {
|
func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err error) {
|
||||||
errWrongDestinationType := func(expected string) error {
|
n, err = decodeAnyOrError(decoder, destination, tag)
|
||||||
panic(fmt.Errorf(
|
if _, ok := err.(errCantAssign); ok {
|
||||||
// return fmt.Errorf(
|
if n > 0 { panic(fmt.Sprintf("decodeAnyOrError decoded more than it should: %d", n)) }
|
||||||
"expected %s destination, not %v",
|
nn, err := Skim(decoder, tag)
|
||||||
expected, destination))
|
n += nn; if err != nil { return n, err }
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeAnyOrError is internal to [decodeAny]. It takes in an addressable
|
||||||
|
// [reflect.Value] as the destination. If the decoded value cannot fit in the
|
||||||
|
// destination, it decodes nothing and returns an error of type errCantAssign,
|
||||||
|
// except for the case of a mismatched OTA element tag, wherein it will skim
|
||||||
|
// over the rest of the payload, leave the destination empty, and return without
|
||||||
|
// an error.
|
||||||
|
func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err error) {
|
||||||
|
err = canSet(destination.Type(), tag)
|
||||||
|
if err != nil { return n, err }
|
||||||
|
|
||||||
switch tag.WithoutCN() {
|
switch tag.WithoutCN() {
|
||||||
case SI:
|
case SI:
|
||||||
// SI: (none)
|
// SI: (none)
|
||||||
err = setInt(destination, uint64(tag.CN()))
|
setInt(destination, uint64(tag.CN()))
|
||||||
if err != nil { return n, err }
|
|
||||||
case LI:
|
case LI:
|
||||||
// LI: <value: IntN>
|
// LI: <value: IntN>
|
||||||
nn, err := decodeAndSetUint(decoder, destination, tag.CN() + 1)
|
nn, err := decodeAndSetUint(decoder, destination, tag.CN() + 1)
|
||||||
@@ -114,8 +136,7 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err
|
|||||||
buffer := make([]byte, tag.CN())
|
buffer := make([]byte, tag.CN())
|
||||||
nn, err := decoder.Read(buffer)
|
nn, err := decoder.Read(buffer)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
err = setByteArray(destination, buffer)
|
setByteArray(destination, buffer)
|
||||||
if err != nil { return n, err }
|
|
||||||
case LBA:
|
case LBA:
|
||||||
// LBA: <length: UN> <data: U8>*
|
// LBA: <length: UN> <data: U8>*
|
||||||
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||||||
@@ -123,34 +144,42 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err
|
|||||||
buffer := make([]byte, length)
|
buffer := make([]byte, length)
|
||||||
nn, err = decoder.Read(buffer)
|
nn, err = decoder.Read(buffer)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
err = setByteArray(destination, buffer)
|
setByteArray(destination, buffer)
|
||||||
if err != nil { return n, err }
|
|
||||||
case OTA:
|
case OTA:
|
||||||
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
||||||
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
oneTag, nn, err := decoder.ReadTag()
|
oneTag, nn, err := decoder.ReadTag()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
if destination.Kind() != reflect.Slice {
|
|
||||||
return n, errWrongDestinationType("slice")
|
|
||||||
}
|
|
||||||
if destination.Cap() < int(length) {
|
if destination.Cap() < int(length) {
|
||||||
destination.Grow(int(length) - destination.Cap())
|
destination.Grow(int(length) - destination.Cap())
|
||||||
}
|
}
|
||||||
|
// skip the rest of the array if the one tag doesn't
|
||||||
|
// match up with the destination
|
||||||
|
err = canSet(destination.Type().Elem(), oneTag)
|
||||||
|
if _, ok := err.(errCantAssign); ok {
|
||||||
|
for _ = range length {
|
||||||
|
nn, err := Skim(decoder, oneTag)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil { return n, err }
|
||||||
destination.SetLen(int(length))
|
destination.SetLen(int(length))
|
||||||
for index := range length {
|
for index := range length {
|
||||||
nn, err := decodeAny(decoder, destination.Index(int(index)), oneTag)
|
nn, err := decodeAny(decoder, destination.Index(int(index)), oneTag)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn
|
||||||
|
if _, ok := err.(errCantAssign); ok {
|
||||||
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case KTV:
|
case KTV:
|
||||||
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
||||||
table := destination
|
|
||||||
if table.Type() != reflect.TypeOf(dummyMap) {
|
|
||||||
return n, errWrongDestinationType("map[uint16] any")
|
|
||||||
}
|
|
||||||
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
table.Clear()
|
destination.Clear()
|
||||||
for _ = range length {
|
for _ = range length {
|
||||||
key, nn, err := decoder.ReadUint16()
|
key, nn, err := decoder.ReadUint16()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
@@ -160,7 +189,7 @@ func decodeAny(decoder *Decoder, destination reflect.Value, tag Tag) (n int, err
|
|||||||
if err != nil { return n, err }
|
if err != nil { return n, err }
|
||||||
nn, err = decodeAny(decoder, value.Elem(), itemTag)
|
nn, err = decodeAny(decoder, value.Elem(), itemTag)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
table.SetMapIndex(reflect.ValueOf(key), value.Elem())
|
destination.SetMapIndex(reflect.ValueOf(key), value.Elem())
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return n, fmt.Errorf("unknown TN %d", tag.TN())
|
return n, fmt.Errorf("unknown TN %d", tag.TN())
|
||||||
@@ -172,35 +201,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: return LSI.WithCN(3), nil
|
case reflect.Int: return LSI.WithCN(3), nil
|
||||||
case int8: return LSI.WithCN(0), nil
|
case reflect.Int8: return LSI.WithCN(0), nil
|
||||||
case int16: return LSI.WithCN(1), nil
|
case reflect.Int16: return LSI.WithCN(1), nil
|
||||||
case int32: return LSI.WithCN(3), nil
|
case reflect.Int32: return LSI.WithCN(3), nil
|
||||||
case int64: return LSI.WithCN(7), nil
|
case reflect.Int64: return LSI.WithCN(7), nil
|
||||||
case uint: return LI.WithCN(3), nil
|
case reflect.Uint: return LI.WithCN(3), nil
|
||||||
case uint8: return LI.WithCN(0), nil
|
case reflect.Uint8: return LI.WithCN(0), nil
|
||||||
case uint16: return LI.WithCN(1), nil
|
case reflect.Uint16: return LI.WithCN(1), nil
|
||||||
case uint32: return LI.WithCN(3), nil
|
case reflect.Uint32: return LI.WithCN(3), nil
|
||||||
case uint64: return LI.WithCN(7), nil
|
case reflect.Uint64: return LI.WithCN(7), nil
|
||||||
case string: return bufferLenTag(len(value)), nil
|
case reflect.String: return bufferLenTag(reflectValue.Len()), nil
|
||||||
case []byte: return bufferLenTag(len(value)), 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) {
|
||||||
@@ -209,11 +243,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 }
|
||||||
}
|
}
|
||||||
@@ -235,11 +268,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 }
|
||||||
@@ -249,53 +283,79 @@ func encodeAnyMap(encoder *Encoder, value any, tag Tag) (n int, err error) {
|
|||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func canSet(destination reflect.Type, tag Tag) error {
|
||||||
|
switch tag.WithoutCN() {
|
||||||
|
case SI, LI, LSI:
|
||||||
|
switch destination.Kind() {
|
||||||
|
case
|
||||||
|
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||||
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
default:
|
||||||
|
return errCantAssignf("cannot assign integer to %v", destination)
|
||||||
|
}
|
||||||
|
case FP:
|
||||||
|
switch destination.Kind() {
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
default:
|
||||||
|
return errCantAssignf("cannot assign float to %v", destination)
|
||||||
|
}
|
||||||
|
case SBA, LBA:
|
||||||
|
if destination.Kind() != reflect.Slice {
|
||||||
|
return errCantAssignf("cannot assign byte array to %v", destination)
|
||||||
|
}
|
||||||
|
if destination.Elem() != reflect.TypeOf(byte(0)) {
|
||||||
|
return errCantAssignf("cannot convert %v to *[]byte", destination)
|
||||||
|
}
|
||||||
|
case OTA:
|
||||||
|
if destination.Kind() != reflect.Slice {
|
||||||
|
return errCantAssignf("cannot assign array to %v", destination)
|
||||||
|
}
|
||||||
|
case KTV:
|
||||||
|
if destination != reflect.TypeOf(dummyMap) {
|
||||||
|
return errCantAssignf("cannot assign table to %v", destination)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown TN %d", tag.TN())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// setInt expects a settable destination.
|
// setInt expects a settable destination.
|
||||||
func setInt[T int64 | uint64](destination reflect.Value, value T) error {
|
func setInt[T int64 | uint64](destination reflect.Value, value T) {
|
||||||
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()))
|
||||||
case destination.CanUint():
|
case destination.CanUint():
|
||||||
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
|
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("cannot assign integer to %T", destination.Interface())
|
panic("setInt called on an unsupported type")
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setFloat expects a settable destination.
|
// setFloat expects a settable destination.
|
||||||
func setFloat(destination reflect.Value, value float64) error {
|
func setFloat(destination reflect.Value, value float64) {
|
||||||
if !destination.CanFloat() {
|
|
||||||
return fmt.Errorf("cannot assign float to %T", destination.Interface())
|
|
||||||
}
|
|
||||||
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
|
destination.Set(reflect.ValueOf(value).Convert(destination.Type()))
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setByteArrayexpects a settable destination.
|
// setByteArrayexpects a settable destination.
|
||||||
func setByteArray(destination reflect.Value, value []byte) error {
|
func setByteArray(destination reflect.Value, value []byte) {
|
||||||
typ := destination.Type()
|
|
||||||
if typ.Kind() != reflect.Slice {
|
|
||||||
return fmt.Errorf("cannot assign %T to ", value)
|
|
||||||
}
|
|
||||||
if typ.Elem() != reflect.TypeOf(byte(0)) {
|
|
||||||
return fmt.Errorf("cannot convert %T to *[]byte", value)
|
|
||||||
}
|
|
||||||
destination.Set(reflect.ValueOf(value))
|
destination.Set(reflect.ValueOf(value))
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
value, nn, err := decoder.ReadIntN(bytes)
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
return n, setInt(destination, value)
|
setInt(destination, value)
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeAndSetUint expects a settable destination.
|
// decodeAndSetUint expects a settable destination.
|
||||||
func decodeAndSetUint(decoder *Decoder, destination reflect.Value, bytes int) (n int, err error) {
|
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)
|
setInt(destination, value)
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeAndSetInt expects a settable destination.
|
// decodeAndSetInt expects a settable destination.
|
||||||
@@ -304,13 +364,15 @@ func decodeAndSetFloat(decoder *Decoder, destination reflect.Value, bytes int) (
|
|||||||
case 8:
|
case 8:
|
||||||
value, nn, err := decoder.ReadFloat64()
|
value, nn, err := decoder.ReadFloat64()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
return n, setFloat(destination, float64(value))
|
setFloat(destination, float64(value))
|
||||||
|
return n, nil
|
||||||
case 4:
|
case 4:
|
||||||
value, nn, err := decoder.ReadFloat32()
|
value, nn, err := decoder.ReadFloat32()
|
||||||
n += nn; if err != nil { return n, err }
|
n += nn; if err != nil { return n, err }
|
||||||
return n, setFloat(destination, float64(value))
|
setFloat(destination, float64(value))
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
return n, fmt.Errorf("cannot decode float%d", bytes * 8)
|
return n, errCantAssignf("unsupported bit width float%d", bytes * 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
// skeletonValue returns a pointer value. In order for it to be set, it must be
|
// skeletonValue returns a pointer value. In order for it to be set, it must be
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ 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) }
|
||||||
@@ -23,7 +25,8 @@ func TestEncodeAnyTable(test *testing.T) {
|
|||||||
0xFFFF: []uint16 { 0xBEE5, 0x7777 },
|
0xFFFF: []uint16 { 0xBEE5, 0x7777 },
|
||||||
0x1234: [][]uint16 { []uint16 { 0x5 }, []uint16 { 0x17, 0xAAAA} },
|
0x1234: [][]uint16 { []uint16 { 0x5 }, []uint16 { 0x17, 0xAAAA} },
|
||||||
0x2345: [][]int16 { []int16 { 0x5 }, []int16 { 0x17, -0xAAA } },
|
0x2345: [][]int16 { []int16 { 0x5 }, []int16 { 0x17, -0xAAA } },
|
||||||
}, KTV.WithCN(0), tu.S(6).AddVar(
|
0x3456: userDefinedInteger(0x3921),
|
||||||
|
}, KTV.WithCN(0), tu.S(7).AddVar(
|
||||||
[]byte {
|
[]byte {
|
||||||
0xF3, 0xB9,
|
0xF3, 0xB9,
|
||||||
byte(LSI.WithCN(3)),
|
byte(LSI.WithCN(3)),
|
||||||
@@ -62,10 +65,110 @@ func TestEncodeAnyTable(test *testing.T) {
|
|||||||
0, 0x17,
|
0, 0x17,
|
||||||
0xF5, 0x56,
|
0xF5, 0x56,
|
||||||
},
|
},
|
||||||
|
[]byte {
|
||||||
|
0x34, 0x56,
|
||||||
|
byte(LSI.WithCN(1)),
|
||||||
|
0x39, 0x21,
|
||||||
|
},
|
||||||
))
|
))
|
||||||
if err != nil { test.Fatal(err) }
|
if err != nil { test.Fatal(err) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecodeWrongType(test *testing.T) {
|
||||||
|
datas := [][]byte {
|
||||||
|
/* int8 */ []byte { byte(LSI.WithCN(0)), 0x45 },
|
||||||
|
/* int16 */ []byte { byte(LSI.WithCN(1)), 0x45, 0x67 },
|
||||||
|
/* int32 */ []byte { byte(LSI.WithCN(3)), 0x45, 0x67, 0x89, 0xAB },
|
||||||
|
/* int64 */ []byte { byte(LSI.WithCN(7)), 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 },
|
||||||
|
/* uint5 */ []byte { byte(SI.WithCN(12)) },
|
||||||
|
/* uint8 */ []byte { byte(LI.WithCN(0)), 0x45 },
|
||||||
|
/* uint16 */ []byte { byte(LI.WithCN(1)), 0x45, 0x67 },
|
||||||
|
/* uint32 */ []byte { byte(LI.WithCN(3)), 0x45, 0x67, 0x89, 0xAB },
|
||||||
|
/* uint64 */ []byte { byte(LI.WithCN(7)), 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 },
|
||||||
|
/* string */ []byte { byte(SBA.WithCN(7)), 'p', 'u', 'p', 'e', 'v', 'e', 'r' },
|
||||||
|
/* []byte */ []byte { byte(SBA.WithCN(5)), 'b', 'l', 'a', 'r', 'g' },
|
||||||
|
/* []string */ []byte {
|
||||||
|
byte(OTA.WithCN(0)), 2, byte(LBA.WithCN(0)),
|
||||||
|
0x08, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23,
|
||||||
|
0x05, 0x11, 0x11, 0x11, 0x11, 0x11,
|
||||||
|
},
|
||||||
|
/* map[uint16] any */ []byte {
|
||||||
|
byte(KTV.WithCN(0)), 2,
|
||||||
|
0x02, 0x23, byte(LSI.WithCN(1)), 0x45, 0x67,
|
||||||
|
0x02, 0x23, byte(LI.WithCN(3)), 0x45, 0x67, 0x89, 0xAB,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, data := range datas {
|
||||||
|
test.Logf("data %2d %v [%s]", index, Tag(data[0]), tu.HexBytes(data[1:]))
|
||||||
|
// integers should only assign to other integers
|
||||||
|
if index > 8 {
|
||||||
|
cas := func(destination any) {
|
||||||
|
n, err := DecodeAny(NewDecoder(bytes.NewBuffer(data[1:])), destination, Tag(data[0]))
|
||||||
|
if err != nil { test.Fatalf("error: %v | n: %d", err, n) }
|
||||||
|
reflectValue := reflect.ValueOf(destination).Elem()
|
||||||
|
if reflectValue.CanInt() {
|
||||||
|
if reflectValue.Int() != 0 {
|
||||||
|
test.Fatalf("destination not zero: %v", reflectValue.Elem().Interface())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if reflectValue.Uint() != 0 {
|
||||||
|
test.Fatalf("destination not zero: %v", reflectValue.Elem().Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n != len(data) - 1 {
|
||||||
|
test.Fatalf("n not equal: %d != %d", n, len(data) - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test.Log("- int8")
|
||||||
|
{ var dest int8; cas(&dest) }
|
||||||
|
test.Log("- int16")
|
||||||
|
{ var dest int16; cas(&dest) }
|
||||||
|
test.Log("- int32")
|
||||||
|
{ var dest int32; cas(&dest) }
|
||||||
|
test.Log("- int64")
|
||||||
|
{ var dest int64; cas(&dest) }
|
||||||
|
test.Log("- uint8")
|
||||||
|
{ var dest uint8; cas(&dest) }
|
||||||
|
test.Log("- uint16")
|
||||||
|
{ var dest uint16; cas(&dest) }
|
||||||
|
test.Log("- uint32")
|
||||||
|
{ var dest uint32; cas(&dest) }
|
||||||
|
test.Log("- uint64")
|
||||||
|
{ var dest uint64; cas(&dest) }
|
||||||
|
}
|
||||||
|
arrayCase := func(destination any) {
|
||||||
|
n, err := DecodeAny(NewDecoder(bytes.NewBuffer(data[1:])), destination, Tag(data[0]))
|
||||||
|
if err != nil { test.Fatalf("error: %v | n: %d", err, n) }
|
||||||
|
reflectDestination := reflect.ValueOf(destination)
|
||||||
|
reflectValue := reflectDestination.Elem()
|
||||||
|
if reflectValue.Len() != 0 {
|
||||||
|
test.Fatalf("len(destination) not zero: %v", reflectValue.Interface())
|
||||||
|
}
|
||||||
|
if n != len(data) - 1 {
|
||||||
|
test.Fatalf("n not equal: %d != %d", n, len(data) - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SBA/LBA types should only assign to other SBA/LBA types
|
||||||
|
if index != 9 && index != 10 {
|
||||||
|
test.Log("- string")
|
||||||
|
{ var dest string; arrayCase(&dest) }
|
||||||
|
test.Log("- []byte")
|
||||||
|
{ var dest []byte; arrayCase(&dest) }
|
||||||
|
}
|
||||||
|
// arrays should only assign to other arrays
|
||||||
|
if index != 11 {
|
||||||
|
test.Log("- []string")
|
||||||
|
{ var dest []string; arrayCase(&dest) }
|
||||||
|
}
|
||||||
|
// tables should only assign to other tables
|
||||||
|
if index != 12 {
|
||||||
|
test.Log("- map[uint16] any")
|
||||||
|
{ var dest = map[uint16] any { }; arrayCase(&dest) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEncodeDecodeAnyTable(test *testing.T) {
|
func TestEncodeDecodeAnyTable(test *testing.T) {
|
||||||
err := testEncodeDecodeAny(test, map[uint16] any {
|
err := testEncodeDecodeAny(test, map[uint16] any {
|
||||||
0xF3B9: uint32(1),
|
0xF3B9: uint32(1),
|
||||||
@@ -166,10 +269,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))
|
||||||
|
|||||||
54
tape/skim.go
Normal file
54
tape/skim.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package tape
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Skim uses up data from a decoder to "skim" over one value (and all else
|
||||||
|
// contained within it) without actually putting the data anywhere.
|
||||||
|
func Skim(decoder *Decoder, tag Tag) (n int, err error) {
|
||||||
|
switch tag.WithoutCN() {
|
||||||
|
case SI:
|
||||||
|
// SI: (none)
|
||||||
|
return n, nil
|
||||||
|
case LI, LSI, FP:
|
||||||
|
// LI: <value: IntN>
|
||||||
|
// LSI: <value: IntN>
|
||||||
|
// FP: <value: FloatN>
|
||||||
|
nn, err := decoder.Discard(tag.CN() + 1)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
case SBA:
|
||||||
|
// SBA: <data: U8>*
|
||||||
|
nn, err := decoder.Discard(tag.CN())
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
case LBA:
|
||||||
|
// LBA: <length: UN> <data: U8>*
|
||||||
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = decoder.Discard(int(length))
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
case OTA:
|
||||||
|
// OTA: <length: UN> <elementTag: tape.Tag> <values>*
|
||||||
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
oneTag, nn, err := decoder.ReadTag()
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
for _ = range length {
|
||||||
|
nn, err := Skim(decoder, oneTag)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
}
|
||||||
|
case KTV:
|
||||||
|
// KTV: <length: UN> (<key: U16> <tag: Tag> <value>)*
|
||||||
|
length, nn, err := decoder.ReadUintN(tag.CN() + 1)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
for _ = range length {
|
||||||
|
nn, err := decoder.Discard(2)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
itemTag, nn, err := decoder.ReadTag()
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
nn, err = Skim(decoder, itemTag)
|
||||||
|
n += nn; if err != nil { return n, err }
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return n, fmt.Errorf("unknown TN %d", tag.TN())
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
137
tape/skim_test.go
Normal file
137
tape/skim_test.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package tape
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestSkimInteger(test *testing.T) {
|
||||||
|
data := []byte {
|
||||||
|
0x12, 0x45, 0x23, 0xF9,
|
||||||
|
}
|
||||||
|
mainDataLen := len(data)
|
||||||
|
// extra junk
|
||||||
|
data = append(data, 0x00, 0x01, 0x02, 0x03,)
|
||||||
|
|
||||||
|
n, err := Skim(NewDecoder(bytes.NewBuffer(data)), LI.WithCN(3))
|
||||||
|
if err != nil {
|
||||||
|
test.Fatal(err)
|
||||||
|
}
|
||||||
|
if got, correct := n, mainDataLen; got != correct {
|
||||||
|
test.Fatalf("n not equal: %d != %d", got, correct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSkimArray(test *testing.T) {
|
||||||
|
data := []byte {
|
||||||
|
2, byte(LI.WithCN(1)),
|
||||||
|
0xBE, 0xE5, 0x77, 0x77,
|
||||||
|
}
|
||||||
|
mainDataLen := len(data)
|
||||||
|
// extra junk
|
||||||
|
data = append(data, 0x00, 0x01, 0x02, 0x03,)
|
||||||
|
|
||||||
|
n, err := Skim(NewDecoder(bytes.NewBuffer(data)), OTA.WithCN(0))
|
||||||
|
if err != nil {
|
||||||
|
test.Fatal(err)
|
||||||
|
}
|
||||||
|
if got, correct := n, mainDataLen; got != correct {
|
||||||
|
test.Fatalf("n not equal: %d != %d", got, correct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSkimNestedArray(test *testing.T) {
|
||||||
|
data := []byte {
|
||||||
|
2, byte(OTA.WithCN(0)),
|
||||||
|
1, byte(LSI.WithCN(1)),
|
||||||
|
0, 0x5,
|
||||||
|
2, byte(LSI.WithCN(1)),
|
||||||
|
0, 0x17,
|
||||||
|
0xF5, 0x56,
|
||||||
|
}
|
||||||
|
mainDataLen := len(data)
|
||||||
|
// extra junk
|
||||||
|
data = append(data, 0x00, 0x01, 0x02, 0x03,)
|
||||||
|
|
||||||
|
n, err := Skim(NewDecoder(bytes.NewBuffer(data)), OTA.WithCN(0))
|
||||||
|
if err != nil {
|
||||||
|
test.Fatal(err)
|
||||||
|
}
|
||||||
|
if got, correct := n, mainDataLen; got != correct {
|
||||||
|
test.Fatalf("n not equal: %d != %d", got, correct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSkimTable(test *testing.T) {
|
||||||
|
data := []byte {
|
||||||
|
2,
|
||||||
|
0xF3, 0xB9,
|
||||||
|
byte(LSI.WithCN(3)),
|
||||||
|
0, 0, 0, 1,
|
||||||
|
|
||||||
|
0x01, 0x02,
|
||||||
|
byte(LSI.WithCN(3)),
|
||||||
|
0, 0, 0, 2,
|
||||||
|
}
|
||||||
|
mainDataLen := len(data)
|
||||||
|
// extra junk
|
||||||
|
data = append(data, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03)
|
||||||
|
|
||||||
|
n, err := Skim(NewDecoder(bytes.NewBuffer(data)), KTV.WithCN(0))
|
||||||
|
if got, correct := n, mainDataLen; got != correct {
|
||||||
|
test.Fatalf("n not equal: %d != %d ... (%d)", got, correct, len(data))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
test.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSkimTableComplex(test *testing.T) {
|
||||||
|
data := []byte {
|
||||||
|
7,
|
||||||
|
0xF3, 0xB9,
|
||||||
|
byte(LSI.WithCN(3)),
|
||||||
|
0, 0, 0, 1,
|
||||||
|
|
||||||
|
0x01, 0x02,
|
||||||
|
byte(LSI.WithCN(3)),
|
||||||
|
0, 0, 0, 2,
|
||||||
|
|
||||||
|
0, 0,
|
||||||
|
byte(SBA.WithCN(3)),
|
||||||
|
'h', 'i', '!',
|
||||||
|
|
||||||
|
0xFF, 0xFF,
|
||||||
|
byte(OTA.WithCN(0)), 2, byte(LI.WithCN(1)),
|
||||||
|
0xBE, 0xE5, 0x77, 0x77,
|
||||||
|
|
||||||
|
0x12, 0x34,
|
||||||
|
byte(OTA.WithCN(0)), 2, byte(OTA.WithCN(0)),
|
||||||
|
1, byte(LI.WithCN(1)),
|
||||||
|
0, 0x5,
|
||||||
|
2, byte(LI.WithCN(1)),
|
||||||
|
0, 0x17,
|
||||||
|
0xAA, 0xAA,
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
0x34, 0x56,
|
||||||
|
byte(LSI.WithCN(1)),
|
||||||
|
0x39, 0x21,
|
||||||
|
}
|
||||||
|
mainDataLen := len(data)
|
||||||
|
// extra junk
|
||||||
|
data = append(data, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03)
|
||||||
|
|
||||||
|
n, err := Skim(NewDecoder(bytes.NewBuffer(data)), KTV.WithCN(0))
|
||||||
|
if got, correct := n, mainDataLen; got != correct {
|
||||||
|
test.Fatalf("n not equal: %d != %d ... (%d)", got, correct, len(data))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
test.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user