tape: Add table encoding/decoding functions

This commit is contained in:
Sasha Koshka 2025-05-18 15:50:24 -04:00
parent 4f3b256821
commit 3d8a012477
2 changed files with 159 additions and 0 deletions

59
tape/table.go Normal file
View File

@ -0,0 +1,59 @@
package tape
import "iter"
// encoding and decoding functions must not make any allocations
type TablePushFunc func(tag uint16, value []byte) (n int, err error)
func DecodeTable(data []byte) iter.Seq2[uint16, []byte] {
return func(yield func(tag uint16, value []byte) bool) {
n := 0
for {
tag, nn, err := DecodeI16[uint16](data[n:])
if err != nil { return }
n += nn
length, nn, err := DecodeGBEU[uint64](data[n:])
if err != nil { return }
n += nn
value := data[n:n + int(length)]
yield(tag, value)
n += int(length)
}
}
}
func EncodeTable(data []byte) TablePushFunc {
return func(tag uint16, value []byte) (n int, err error) {
if n >= len(data) { return n, ErrWrongBufferLength }
nn, err := EncodeI16(data[n:], uint16(tag))
if err != nil { return n, err }
n += nn
if n >= len(data) { return n, ErrWrongBufferLength }
nn, err = EncodeGBEU(data[n:], uint(len(value)))
if err != nil { return n, err }
n += nn
if n >= len(data) { return n, ErrWrongBufferLength }
nn = copy(data[n:], value)
n += nn
if nn < len(value) {
return n, ErrWrongBufferLength
}
if n >= len(data) { return n, ErrWrongBufferLength }
data = data[n:]
return n, nil
}
}
func TableSize(itemLengths ...int) int {
sum := 0
for _, length := range itemLengths {
sum += GBEUSize(uint(length)) + length
}
return sum
}

100
tape/table_test.go Normal file
View File

@ -0,0 +1,100 @@
package tape
import "fmt"
import "slices"
// import "errors"
import "testing"
// import "math/rand"
var longText =
`Curious, I started off in that direction, only for Prism to stop me. "Wrong way, Void. It's right over here." He trotted over to a door to the left of us. It was marked with the number '4004'. He took a key out of his saddlebags, unlocked it, and pushed it open. "You know, some say this suite is haunted. They call the ghost that lives here the 'Spirit of 4004'. Ooooh!" He made paddling motions in the air with his hooves.`
func TestTable(test *testing.T) {
item5 := []byte("hello")
item7 := []byte("world")
item0 := []byte(longText)
item3249 := []byte { 0x0, 0x1, 0x2, 0x3, 0xA0, 0x5 }
buffer := [512]byte { }
push := EncodeTable(buffer[:])
_, err := push(5, item5)
if err != nil { test.Fatal(err)}
_, err = push(7, item7)
if err != nil { test.Fatal(err)}
_, err = push(0, item0)
if err != nil { test.Fatal(err)}
_, err = push(3249, item3249)
if err != nil { test.Fatal(err)}
test.Logf("len of longText: %d 0x%X", len(longText), len(longText))
correct := []byte("\x00\x05\x05hello\x00\x07\x05world\x00\x00\x83\x28" + longText)
if got := buffer[:len(correct)]; !slices.Equal(got, correct) {
if !compareHexArray(test, correct, got) {
test.FailNow()
}
}
}
func TestTableSmall(test *testing.T) {
item2 := []byte("hello")
item3249 := []byte { 0x0, 0x1, 0x2, 0x3, 0xA0, 0x5 }
buffer := [64]byte { }
push := EncodeTable(buffer[:])
_, err := push(2, item2)
if err != nil { test.Fatal(err) }
_, err = push(3249, item3249)
if err != nil { test.Fatal(err) }
correct := []byte("\x00\x02\x05hello\x0C\xB1\x06\x00\x01\x02\x03\xA0\x05")
if got := buffer[:len(correct)]; !slices.Equal(got, correct) {
if !compareHexArray(test, correct, got) {
test.FailNow()
}
}
}
func dumpHexArray(data []byte) (message string) {
for _, item := range data {
message = fmt.Sprintf("%s %02X", message, item)
}
return message
}
func compareHexArray(test *testing.T, correct, got []byte) bool {
index := 0
for {
if index >= len(correct) {
if index < len(got) {
test.Log("correct longer than got")
test.Log("got: ", dumpHexArray(got))
test.Log("correct:", dumpHexArray(correct))
return false
}
}
if index >= len(got) {
if index < len(correct) {
test.Log("got longer than correct")
test.Log("got: ", dumpHexArray(got))
test.Log("correct:", dumpHexArray(correct))
return false
}
}
if correct[index] != got[index] {
test.Log("not equal")
test.Log("got: ", dumpHexArray(got))
test.Log("correct:", dumpHexArray(correct))
partLow := index - 8
partHigh := index + 8
test.Log("got part ", dumpHexArray(safeSlice(got, partLow, partHigh)))
test.Log("correct part", dumpHexArray(safeSlice(correct, partLow, partHigh)))
return false
}
index ++
}
return true
}
func safeSlice[T any](slice []T, low, high int) []T {
return slice[max(low, 0):min(high, len(slice))]
}