Compare commits

..

8 Commits

8 changed files with 585 additions and 538 deletions

206
tape/array.go Normal file
View File

@ -0,0 +1,206 @@
package tape
import "fmt"
import "iter"
import "slices"
// encoding and decoding functions must not make any allocations
func DecodeArray(data []byte, itemLength int) iter.Seq[[]byte] {
return slices.Chunk(data, itemLength)
}
func EncodeArray(data []byte, items ...[]byte) (n int, err error) {
for _, item := range items {
if n >= len(data) { return n, ErrWrongBufferLength }
copy(data[n:], item)
n += len(item)
}
return n, nil
}
func ArraySize(length, itemLength int) int {
return length * itemLength
}
// DecodeStringArray decodes a packed string array from the given data.
func DecodeStringArray[T String](data []byte) (result []T, n int, err error) {
for len(data) > 0 {
if len(data) < 2 { return nil, n, fmt.Errorf("decoding []string: %w", ErrWrongBufferLength) }
itemSize16, nn, _ := DecodeI16[uint16](data[:2])
itemSize := int(itemSize16)
n += nn
data = data[nn:]
if len(data) < itemSize { return nil, n, fmt.Errorf("decoding []string: %w", ErrWrongBufferLength) }
result = append(result, T(data[:itemSize]))
data = data[itemSize:]
n += itemSize
}
return result, n, nil
}
// EncodeStringArray encodes a packed string array into the given buffer.
func EncodeStringArray[T String](buffer []byte, value []T) (n int, err error) {
for _, item := range value {
length, err := StringSize(item)
if err != nil { return n, err }
if len(buffer) < 2 + length { return n, fmt.Errorf("encoding []string: %w", ErrWrongBufferLength) }
EncodeI16(buffer[:2], uint16(length))
buffer = buffer[2:]
copy(buffer, item)
buffer = buffer[length:]
n += 2 + length
}
if len(buffer) > 0 { return n, fmt.Errorf("encoding []string: %w", ErrWrongBufferLength) }
return n, nil
}
// StringArraySize returns the size of a packed string array. Returns 0 and an
// error if the size is too large.
func StringArraySize[T String](value []T) (int, error) {
total := 0
for _, item := range value {
total += 2 + len(item)
}
if total > dataMaxSize { return 0, ErrDataTooLarge }
return total, nil
}
// DecodeI8Array decodes a packed array of 8 bit integers from the given data.
func DecodeI8Array[T Int8](data []byte) (result []T, n int, err error) {
result = make([]T, len(data))
for index, item := range data {
result[index] = T(item)
}
return result, len(data), nil
}
// EncodeI8Array encodes a packed array of 8 bit integers into the given buffer.
func EncodeI8Array[T Int8](buffer []byte, value []T) (n int, err error) {
if len(buffer) != len(value) { return 0, fmt.Errorf("encoding []int8: %w", ErrWrongBufferLength) }
for index, item := range value {
buffer[index] = byte(item)
}
return len(buffer), nil
}
// I8ArraySize returns the size of a packed 8 bit integer array. Returns 0 and
// an error if the size is too large.
func I8ArraySize[T Int8](value []T) (int, error) {
total := len(value)
if total > dataMaxSize { return 0, ErrDataTooLarge }
return total, nil
}
// DecodeI16Array decodes a packed array of 16 bit integers from the given data.
func DecodeI16Array[T Int16](data []byte) (value []T, n int, err error) {
if len(data) % 2 != 0 { return nil, 0, fmt.Errorf("decoding []int16: %w", ErrWrongBufferLength) }
length := len(data) / 2
result := make([]T, length)
for index := range length {
offset := index * 2
result[index] = T(data[offset]) << 8 | T(data[offset + 1])
}
return result, len(data) / 2, nil
}
// EncodeI16Array encodes a packed array of 16 bit integers into the given buffer.
func EncodeI16Array[T Int16](buffer []byte, value []T) (n int, err error) {
if len(buffer) != len(value) * 2 { return 0, fmt.Errorf("encoding []int16: %w", ErrWrongBufferLength) }
for _, item := range value {
buffer[0] = byte(item >> 8)
buffer[1] = byte(item)
buffer = buffer[2:]
}
return len(value) * 2, nil
}
// I16ArraySize returns the size of a packed 16 bit integer array. Returns 0 and
// an error if the size is too large.
func I16ArraySize[T Int16](value []T) (int, error) {
total := len(value) * 2
if total > dataMaxSize { return 0, ErrDataTooLarge }
return total, nil
}
// DecodeI32Array decodes a packed array of 32 bit integers from the given data.
func DecodeI32Array[T Int32](data []byte) (value []T, n int, err error) {
if len(data) % 4 != 0 { return nil, 0, fmt.Errorf("decoding []int32: %w", ErrWrongBufferLength) }
length := len(data) / 4
result := make([]T, length)
for index := range length {
offset := index * 4
result[index] =
T(data[offset + 0]) << 24 |
T(data[offset + 1]) << 16 |
T(data[offset + 2]) << 8 |
T(data[offset + 3])
}
return result, len(data) / 4, nil
}
// EncodeI32Array encodes a packed array of 32 bit integers into the given buffer.
func EncodeI32Array[T Int32](buffer []byte, value []T) (n int, err error) {
if len(buffer) != len(value) * 4 { return 0, fmt.Errorf("encoding []int32: %w", ErrWrongBufferLength) }
for _, item := range value {
buffer[0] = byte(item >> 24)
buffer[1] = byte(item >> 16)
buffer[2] = byte(item >> 8)
buffer[3] = byte(item)
buffer = buffer[4:]
}
return len(value) * 4, nil
}
// I32ArraySize returns the size of a packed 32 bit integer array. Returns 0 and
// an error if the size is too large.
func I32ArraySize[T Int32](value []T) (int, error) {
total := len(value) * 4
if total > dataMaxSize { return 0, ErrDataTooLarge }
return total, nil
}
// DecodeI64Array decodes a packed array of 32 bit integers from the given data.
func DecodeI64Array[T Int64](data []byte) (value []T, n int, err error) {
if len(data) % 8 != 0 { return nil, 0, fmt.Errorf("decoding []int64: %w", ErrWrongBufferLength) }
length := len(data) / 8
result := make([]T, length)
for index := range length {
offset := index * 8
result[index] =
T(data[offset + 0]) << 56 |
T(data[offset + 1]) << 48 |
T(data[offset + 2]) << 40 |
T(data[offset + 3]) << 32 |
T(data[offset + 4]) << 24 |
T(data[offset + 5]) << 16 |
T(data[offset + 6]) << 8 |
T(data[offset + 7])
}
return result, len(data) / 8, nil
}
// EncodeI64Array encodes a packed array of 64 bit integers into the given buffer.
func EncodeI64Array[T Int64](buffer []byte, value []T) (n int, err error) {
if len(buffer) != len(value) * 8 { return 0, fmt.Errorf("encoding []int64: %w", ErrWrongBufferLength) }
for _, item := range value {
buffer[0] = byte(item >> 56)
buffer[1] = byte(item >> 48)
buffer[2] = byte(item >> 40)
buffer[3] = byte(item >> 32)
buffer[4] = byte(item >> 24)
buffer[5] = byte(item >> 16)
buffer[6] = byte(item >> 8)
buffer[7] = byte(item)
buffer = buffer[8:]
}
return len(value) * 8, nil
}
// I64ArraySize returns the size of a packed 64 bit integer array. Returns 0 and
// an error if the size is too large.
func I64ArraySize[T Int64](value []T) (int, error) {
total := len(value) * 8
if total > dataMaxSize { return 0, ErrDataTooLarge }
return total, nil
}

131
tape/array_test.go Normal file
View File

@ -0,0 +1,131 @@
package tape
// import "fmt"
import "slices"
// import "errors"
import "testing"
import "math/rand"
func TestI8Array(test *testing.T) {
var buffer [64]byte
_, err := EncodeI8Array[uint8](buffer[:], []uint8 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int8: wrong buffer length") { test.Fatal(err) }
_, err = EncodeI8Array[uint8](buffer[:0], []uint8 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int8: wrong buffer length") { test.Fatal(err) }
_, _, err = DecodeI8Array[uint8](buffer[:])
if err != nil { test.Fatal(err) }
_, _, err = DecodeI8Array[uint8](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
array := randInts[uint8](rand.Intn(16))
length, _ := I8ArraySize(array)
if length != len(array) { test.Fatalf("%d != %d", length, len(array)) }
_, err := EncodeI8Array[uint8](buffer[:length], array)
if err != nil { test.Fatal(err) }
decoded, _, err := DecodeI8Array[uint8](buffer[:length])
if err != nil { test.Fatal(err) }
if !slices.Equal(decoded, array) {
test.Fatalf("%v != %v", decoded, array)
}
}
}
func TestI16Array(test *testing.T) {
var buffer [128]byte
_, err := EncodeI16Array[uint16](buffer[:], []uint16 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int16: wrong buffer length") { test.Fatal(err) }
_, err = EncodeI16Array[uint16](buffer[:0], []uint16 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int16: wrong buffer length") { test.Fatal(err) }
_, _, err = DecodeI16Array[uint16](buffer[:])
if err != nil { test.Fatal(err) }
_, _, err = DecodeI16Array[uint16](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
array := randInts[uint16](rand.Intn(16))
length, _ := I16ArraySize(array)
if length != 2 * len(array) { test.Fatalf("%d != %d", length, 2 * len(array)) }
_, err := EncodeI16Array[uint16](buffer[:length], array)
if err != nil { test.Fatal(err) }
decoded, _, err := DecodeI16Array[uint16](buffer[:length])
if err != nil { test.Fatal(err) }
if !slices.Equal(decoded, array) {
test.Fatalf("%v != %v", decoded, array)
}
}
}
func TestI32Array(test *testing.T) {
var buffer [256]byte
_, err := EncodeI32Array[uint32](buffer[:], []uint32 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int32: wrong buffer length") { test.Fatal(err) }
_, err = EncodeI32Array[uint32](buffer[:0], []uint32 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int32: wrong buffer length") { test.Fatal(err) }
_, _, err = DecodeI32Array[uint32](buffer[:])
if err != nil { test.Fatal(err) }
_, _, err = DecodeI32Array[uint32](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
array := randInts[uint32](rand.Intn(16))
length, _ := I32ArraySize(array)
if length != 4 * len(array) { test.Fatalf("%d != %d", length, 4 * len(array)) }
_, err := EncodeI32Array[uint32](buffer[:length], array)
if err != nil { test.Fatal(err) }
decoded, _, err := DecodeI32Array[uint32](buffer[:length])
if err != nil { test.Fatal(err) }
if !slices.Equal(decoded, array) {
test.Fatalf("%v != %v", decoded, array)
}
}
}
func TestI64Array(test *testing.T) {
var buffer [512]byte
_, err := EncodeI64Array[uint64](buffer[:], []uint64 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int64: wrong buffer length") { test.Fatal(err) }
_, err = EncodeI64Array[uint64](buffer[:0], []uint64 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int64: wrong buffer length") { test.Fatal(err) }
_, _, err = DecodeI64Array[uint64](buffer[:])
if err != nil { test.Fatal(err) }
_, _, err = DecodeI64Array[uint64](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
array := randInts[uint64](rand.Intn(16))
length, _ := I64ArraySize(array)
if length != 8 * len(array) { test.Fatalf("%d != %d", length, 8 * len(array)) }
_, err := EncodeI64Array[uint64](buffer[:length], array)
if err != nil { test.Fatal(err) }
decoded, _, err := DecodeI64Array[uint64](buffer[:length])
if err != nil { test.Fatal(err) }
if !slices.Equal(decoded, array) {
test.Fatalf("%v != %v", decoded, array)
}
}
}
func TestStringArray(test *testing.T) {
var buffer [8192]byte
_, err := EncodeStringArray[string](buffer[:], []string { "0", "4", "50", "19" })
if !errIs(err, ErrWrongBufferLength, "encoding []string: wrong buffer length") { test.Fatal(err) }
_, err = EncodeStringArray[string](buffer[:0], []string { "0", "4", "50", "19" })
if !errIs(err, ErrWrongBufferLength, "encoding []string: wrong buffer length") { test.Fatal(err) }
_, _, err = DecodeStringArray[string](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
array := randStrings[string](rand.Intn(16), 16)
length, _ := StringArraySize(array)
// TODO test length
_, err := EncodeStringArray[string](buffer[:length], array)
if err != nil { test.Fatal(err) }
decoded, _, err := DecodeStringArray[string](buffer[:length])
if err != nil { test.Fatal(err) }
if !slices.Equal(decoded, array) {
test.Fatalf("%v != %v", decoded, array)
}
}
}

View File

@ -1,83 +0,0 @@
package tape
import "iter"
// DecodePairs decodes message tag/value pairs from a byte slice. It returns an
// iterator over all pairs, where the first value is the tag and the second is
// the value. If data yielded by the iterator is retained, it must be copied
// first.
func DecodePairs(data []byte) (iter.Seq2[uint16, []byte], error) {
// determine section bounds
if len(data) < 2 { return nil, ErrDataTooLarge }
length16, _ := DecodeI16[uint16](data[0:2])
data = data[2:]
length := int(length16)
headerSize := length * 4
if len(data) < headerSize { return nil, ErrDataTooLarge }
valuesData := data[headerSize:]
// ensure the value buffer is big enough
var valuesSize int
for index := range length {
offset := index * 4
end, _ := DecodeI16[uint16](data[offset + 2:offset + 4])
valuesSize = int(end)
}
if valuesSize > len(valuesData) {
return nil, ErrDataTooLarge
}
// return iterator
return func(yield func(uint16, []byte) bool) {
start := uint16(0)
for index := range length {
offset := index * 4
key , _ := DecodeI16[uint16](data[offset + 0:offset + 2])
end, _ := DecodeI16[uint16](data[offset + 2:offset + 4])
// if nextValuesOffset < len(valuesData) {
if !yield(key, valuesData[start:end]) {
return
}
// } else {
// if !yield(key, nil) {
// return
// }
// }
start = end
}
}, nil
}
// EncodePairs encodes message tag/value pairs into a byte slice.
func EncodePairs(pairs map[uint16] []byte) ([]byte, error) {
// determine section bounds
headerSize := 2 + len(pairs) * 4
valuesSize := 0
for _, value := range pairs {
valuesSize += len(value)
}
// generate data
buffer := make([]byte, headerSize + valuesSize)
length16, ok := U16CastSafe(len(pairs))
if !ok { return nil, ErrDataTooLarge }
EncodeI16[uint16](buffer[0:2], length16)
index := 0
end := headerSize
for key, value := range pairs {
start := end
end += len(value)
tagOffset := 2 + index * 4
end16, ok := U16CastSafe(end - headerSize)
if !ok { return nil, ErrDataTooLarge }
// write tag and length
EncodeI16[uint16](buffer[tagOffset + 0:tagOffset + 2], key)
EncodeI16[uint16](buffer[tagOffset + 2:tagOffset + 4], end16)
// write value
copy(buffer[start:end], value)
index ++
}
return buffer, nil
}

View File

@ -1,62 +0,0 @@
package tape
import "slices"
import "testing"
func TestDecodePairs(test *testing.T) {
pairs := map[uint16] []byte {
3894: []byte("foo"),
7: []byte("br"),
}
got, err := DecodePairs([]byte {
0, 2,
0, 7, 0, 2,
15, 54, 0, 5,
98, 114,
102, 111, 111})
if err != nil { test.Fatal(err) }
length := 0
for key, value := range got {
test.Log(key, value)
if !slices.Equal(pairs[key], value) { test.Fatal("not equal") }
length ++
}
test.Log("length")
if length != len(pairs) { test.Fatal("wrong length") }
}
func TestEncodePairs(test *testing.T) {
pairs := map[uint16] []byte {
3894: []byte("foo"),
7: []byte("br"),
}
got, err := EncodePairs(pairs)
if err != nil { test.Fatal(err) }
test.Log(got)
valid := slices.Equal(got, []byte {
0, 2,
15, 54, 0, 3,
0, 7, 0, 5,
102, 111, 111,
98, 114}) ||
slices.Equal(got, []byte {
0, 2,
0, 7, 0, 2,
15, 54, 0, 5,
98, 114,
102, 111, 111})
if !valid { test.Fatal("not equal") }
}
func FuzzDecodePairs(fuzz *testing.F) {
fuzz.Add([]byte {
0, 2,
0, 7, 0, 2,
15, 54, 0, 5,
98, 114,
102, 111, 111})
fuzz.Fuzz(func(t *testing.T, buffer []byte) {
// ensure it does not panic :P
DecodePairs(buffer)
})
}

56
tape/table.go Normal file
View File

@ -0,0 +1,56 @@
package tape
// encoding and decoding functions must not make any allocations
type TablePushFunc func(tag uint16, value []byte) (n int, err error)
type TablePullFunc func() (tag uint16, value []byte, n int, err error)
func DecodeTable(data []byte) TablePullFunc {
return func() (tag uint16, value []byte, n int, err error) {
tag, nn, err := DecodeI16[uint16](data[n:])
if err != nil { return tag, value, n, err }
n += nn
length, nn, err := DecodeGBEU[uint64](data[n:])
if err != nil { return tag, value, n, err }
n += nn
value = data[n:n + int(length)]
n += int(length)
return tag, value, n, err
}
}
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))]
}

View File

@ -3,6 +3,8 @@ package tape
import "fmt"
// encoding and decoding functions must not make any allocations
const dataMaxSize = 0xFFFF
const uint16Max = 0xFFFF
@ -32,54 +34,54 @@ type UInt interface { ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 }
type String interface { ~string }
// DecodeI8 decodes an 8 bit integer from the given data.
func DecodeI8[T Int8](data []byte) (T, error) {
if len(data) != 1 { return 0, fmt.Errorf("decoding int8: %w", ErrWrongBufferLength) }
return T(data[0]), nil
func DecodeI8[T Int8](data []byte) (value T, n int, err error) {
if len(data) < 1 { return 0, 0, fmt.Errorf("decoding int8: %w", ErrWrongBufferLength) }
return T(data[0]), 1, nil
}
// EncodeI8 encodes an 8 bit integer into the given buffer.
func EncodeI8[T Int8](buffer []byte, value T) error {
if len(buffer) != 1 { return fmt.Errorf("encoding int8: %w", ErrWrongBufferLength) }
func EncodeI8[T Int8](buffer []byte, value T) (n int, err error) {
if len(buffer) < 1 { return 0, fmt.Errorf("encoding int8: %w", ErrWrongBufferLength) }
buffer[0] = byte(value)
return nil
return 1, nil
}
// DecodeI16 decodes a 16 bit integer from the given data.
func DecodeI16[T Int16](data []byte) (T, error) {
if len(data) != 2 { return 0, fmt.Errorf("decoding int16: %w", ErrWrongBufferLength) }
return T(data[0]) << 8 | T(data[1]), nil
func DecodeI16[T Int16](data []byte) (value T, n int, err error) {
if len(data) < 2 { return 0, 0, fmt.Errorf("decoding int16: %w", ErrWrongBufferLength) }
return T(data[0]) << 8 | T(data[1]), 2, nil
}
// EncodeI16 encodes a 16 bit integer into the given buffer.
func EncodeI16[T Int16](buffer []byte, value T) error {
if len(buffer) != 2 { return fmt.Errorf("encoding int16: %w", ErrWrongBufferLength) }
func EncodeI16[T Int16](buffer []byte, value T) (n int, err error) {
if len(buffer) < 2 { return 0, fmt.Errorf("encoding int16: %w", ErrWrongBufferLength) }
buffer[0] = byte(value >> 8)
buffer[1] = byte(value)
return nil
return 2, nil
}
// DecodeI32 decodes a 32 bit integer from the given data.
func DecodeI32[T Int32](data []byte) (T, error) {
if len(data) != 4 { return 0, fmt.Errorf("decoding int32: %w", ErrWrongBufferLength) }
func DecodeI32[T Int32](data []byte) (value T, n int, err error) {
if len(data) < 4 { return 0, 0, fmt.Errorf("decoding int32: %w", ErrWrongBufferLength) }
return T(data[0]) << 24 |
T(data[1]) << 16 |
T(data[2]) << 8 |
T(data[3]), nil
T(data[3]), 4, nil
}
// EncodeI32 encodes a 32 bit integer into the given buffer.
func EncodeI32[T Int32](buffer []byte, value T) error {
if len(buffer) != 4 { return fmt.Errorf("encoding int32: %w", ErrWrongBufferLength) }
func EncodeI32[T Int32](buffer []byte, value T) (n int, err error) {
if len(buffer) < 4 { return 0, fmt.Errorf("encoding int32: %w", ErrWrongBufferLength) }
buffer[0] = byte(value >> 24)
buffer[1] = byte(value >> 16)
buffer[2] = byte(value >> 8)
buffer[3] = byte(value)
return nil
return 4, nil
}
// DecodeI64 decodes a 64 bit integer from the given data.
func DecodeI64[T Int64](data []byte) (T, error) {
if len(data) != 8 { return 0, fmt.Errorf("decoding int64: %w", ErrWrongBufferLength) }
func DecodeI64[T Int64](data []byte) (value T, n int, err error) {
if len(data) < 8 { return 0, 0, fmt.Errorf("decoding int64: %w", ErrWrongBufferLength) }
return T(data[0]) << 56 |
T(data[1]) << 48 |
T(data[2]) << 40 |
@ -87,12 +89,12 @@ func DecodeI64[T Int64](data []byte) (T, error) {
T(data[4]) << 24 |
T(data[5]) << 16 |
T(data[6]) << 8 |
T(data[7]), nil
T(data[7]), 8, nil
}
// EncodeI64 encodes a 64 bit integer into the given buffer.
func EncodeI64[T Int64](buffer []byte, value T) error {
if len(buffer) != 8 { return fmt.Errorf("encoding int64: %w", ErrWrongBufferLength) }
func EncodeI64[T Int64](buffer []byte, value T) (n int, err error) {
if len(buffer) < 8 { return 0, fmt.Errorf("encoding int64: %w", ErrWrongBufferLength) }
buffer[0] = byte(value >> 56)
buffer[1] = byte(value >> 48)
buffer[2] = byte(value >> 40)
@ -101,30 +103,31 @@ func EncodeI64[T Int64](buffer []byte, value T) error {
buffer[5] = byte(value >> 16)
buffer[6] = byte(value >> 8)
buffer[7] = byte(value)
return nil
return 8, nil
}
// DecodeGBEU decodes an 8 to 64 bit growing integer into the given buffer.
func DecodeGBEU[T UInt](data []byte) (T, error) {
var value uint64
func DecodeGBEU[T UInt](data []byte) (value T, n int, err error) {
var fullValue uint64
for _, chunk := range data {
value *= 0x80
value += uint64(chunk & 0x7F)
fullValue *= 0x80
fullValue += uint64(chunk & 0x7F)
ccb := chunk >> 7
if ccb == 0 {
return T(value), nil
return T(fullValue), n, nil
}
n += 1
}
return 0, fmt.Errorf("decoding GBEU: %w", ErrGBEUNotTerminated)
return 0, n, fmt.Errorf("decoding GBEU: %w", ErrGBEUNotTerminated)
}
// EncodeGBEU encodes an 8 to 64 bit growing integer into a given buffer.
func EncodeGBEU[T UInt] (buffer []byte, value T) (error) {
func EncodeGBEU[T UInt] (buffer []byte, value T) (n int, err error) {
window := (GBEUSize(value) - 1) * 7
index := 0
for window >= 0 {
if index >= len(buffer) { return fmt.Errorf("encoding GBEU: %w", ErrWrongBufferLength) }
if index >= len(buffer) { return index, fmt.Errorf("encoding GBEU: %w", ErrWrongBufferLength) }
chunk := uint8(value >> window) & 0x7F
if window > 0 {
@ -135,7 +138,7 @@ func EncodeGBEU[T UInt] (buffer []byte, value T) (error) {
index += 1
window -= 7
}
return nil
return index, nil
}
// GBEUSize returns the size (in octets) of a GBEU integer.
@ -149,15 +152,14 @@ func GBEUSize[T UInt] (value T) int {
}
// DecodeString decodes a string from the given data.
func DecodeString[T String](data []byte) (T, error) {
return T(data), nil
func DecodeString[T String](data []byte) (value T, n int, err error) {
return T(data), len(data), nil
}
// EncodeString encodes a string into the given buffer.
func EncodeString[T String](data []byte, value T) error {
if len(data) != len(value) { return fmt.Errorf("encoding string: %w", ErrWrongBufferLength) }
copy(data, value)
return nil
func EncodeString[T String](data []byte, value T) (n int, err error) {
if len(data) != len(value) { return 0, fmt.Errorf("encoding string: %w", ErrWrongBufferLength) }
return copy(data, value), nil
}
// StringSize returns the size of a string. Returns 0 and an error if the size
@ -167,186 +169,6 @@ func StringSize[T String](value T) (int, error) {
return len(value), nil
}
// DecodeStringArray decodes a packed string array from the given data.
func DecodeStringArray[T String](data []byte) ([]T, error) {
result := []T { }
for len(data) > 0 {
if len(data) < 2 { return nil, fmt.Errorf("decoding []string: %w", ErrWrongBufferLength) }
itemSize16, _ := DecodeI16[uint16](data[:2])
itemSize := int(itemSize16)
data = data[2:]
if len(data) < itemSize { return nil, fmt.Errorf("decoding []string: %w", ErrWrongBufferLength) }
result = append(result, T(data[:itemSize]))
data = data[itemSize:]
}
return result, nil
}
// EncodeStringArray encodes a packed string array into the given buffer.
func EncodeStringArray[T String](buffer []byte, value []T) error {
for _, item := range value {
length, err := StringSize(item)
if err != nil { return err }
if len(buffer) < 2 + length { return fmt.Errorf("encoding []string: %w", ErrWrongBufferLength) }
EncodeI16(buffer[:2], uint16(length))
buffer = buffer[2:]
copy(buffer, item)
buffer = buffer[length:]
}
if len(buffer) > 0 { return fmt.Errorf("encoding []string: %w", ErrWrongBufferLength) }
return nil
}
// StringArraySize returns the size of a packed string array. Returns 0 and an
// error if the size is too large.
func StringArraySize[T String](value []T) (int, error) {
total := 0
for _, item := range value {
total += 2 + len(item)
}
if total > dataMaxSize { return 0, ErrDataTooLarge }
return total, nil
}
// DecodeI8Array decodes a packed array of 8 bit integers from the given data.
func DecodeI8Array[T Int8](data []byte) ([]T, error) {
result := make([]T, len(data))
for index, item := range data {
result[index] = T(item)
}
return result, nil
}
// EncodeI8Array encodes a packed array of 8 bit integers into the given buffer.
func EncodeI8Array[T Int8](buffer []byte, value []T) error {
if len(buffer) != len(value) { return fmt.Errorf("encoding []int8: %w", ErrWrongBufferLength) }
for index, item := range value {
buffer[index] = byte(item)
}
return nil
}
// I8ArraySize returns the size of a packed 8 bit integer array. Returns 0 and
// an error if the size is too large.
func I8ArraySize[T Int8](value []T) (int, error) {
total := len(value)
if total > dataMaxSize { return 0, ErrDataTooLarge }
return total, nil
}
// DecodeI16Array decodes a packed array of 16 bit integers from the given data.
func DecodeI16Array[T Int16](data []byte) ([]T, error) {
if len(data) % 2 != 0 { return nil, fmt.Errorf("decoding []int16: %w", ErrWrongBufferLength) }
length := len(data) / 2
result := make([]T, length)
for index := range length {
offset := index * 2
result[index] = T(data[offset]) << 8 | T(data[offset + 1])
}
return result, nil
}
// EncodeI16Array encodes a packed array of 16 bit integers into the given buffer.
func EncodeI16Array[T Int16](buffer []byte, value []T) error {
if len(buffer) != len(value) * 2 { return fmt.Errorf("encoding []int16: %w", ErrWrongBufferLength) }
for _, item := range value {
buffer[0] = byte(item >> 8)
buffer[1] = byte(item)
buffer = buffer[2:]
}
return nil
}
// I16ArraySize returns the size of a packed 16 bit integer array. Returns 0 and
// an error if the size is too large.
func I16ArraySize[T Int16](value []T) (int, error) {
total := len(value) * 2
if total > dataMaxSize { return 0, ErrDataTooLarge }
return total, nil
}
// DecodeI32Array decodes a packed array of 32 bit integers from the given data.
func DecodeI32Array[T Int32](data []byte) ([]T, error) {
if len(data) % 4 != 0 { return nil, fmt.Errorf("decoding []int32: %w", ErrWrongBufferLength) }
length := len(data) / 4
result := make([]T, length)
for index := range length {
offset := index * 4
result[index] =
T(data[offset + 0]) << 24 |
T(data[offset + 1]) << 16 |
T(data[offset + 2]) << 8 |
T(data[offset + 3])
}
return result, nil
}
// EncodeI32Array encodes a packed array of 32 bit integers into the given buffer.
func EncodeI32Array[T Int32](buffer []byte, value []T) error {
if len(buffer) != len(value) * 4 { return fmt.Errorf("encoding []int32: %w", ErrWrongBufferLength) }
for _, item := range value {
buffer[0] = byte(item >> 24)
buffer[1] = byte(item >> 16)
buffer[2] = byte(item >> 8)
buffer[3] = byte(item)
buffer = buffer[4:]
}
return nil
}
// I32ArraySize returns the size of a packed 32 bit integer array. Returns 0 and
// an error if the size is too large.
func I32ArraySize[T Int32](value []T) (int, error) {
total := len(value) * 4
if total > dataMaxSize { return 0, ErrDataTooLarge }
return total, nil
}
// DecodeI64Array decodes a packed array of 32 bit integers from the given data.
func DecodeI64Array[T Int64](data []byte) ([]T, error) {
if len(data) % 8 != 0 { return nil, fmt.Errorf("decoding []int64: %w", ErrWrongBufferLength) }
length := len(data) / 8
result := make([]T, length)
for index := range length {
offset := index * 8
result[index] =
T(data[offset + 0]) << 56 |
T(data[offset + 1]) << 48 |
T(data[offset + 2]) << 40 |
T(data[offset + 3]) << 32 |
T(data[offset + 4]) << 24 |
T(data[offset + 5]) << 16 |
T(data[offset + 6]) << 8 |
T(data[offset + 7])
}
return result, nil
}
// EncodeI64Array encodes a packed array of 64 bit integers into the given buffer.
func EncodeI64Array[T Int64](buffer []byte, value []T) error {
if len(buffer) != len(value) * 8 { return fmt.Errorf("encoding []int64: %w", ErrWrongBufferLength) }
for _, item := range value {
buffer[0] = byte(item >> 56)
buffer[1] = byte(item >> 48)
buffer[2] = byte(item >> 40)
buffer[3] = byte(item >> 32)
buffer[4] = byte(item >> 24)
buffer[5] = byte(item >> 16)
buffer[6] = byte(item >> 8)
buffer[7] = byte(item)
buffer = buffer[8:]
}
return nil
}
// I64ArraySize returns the size of a packed 64 bit integer array. Returns 0 and
// an error if the size is too large.
func I64ArraySize[T Int64](value []T) (int, error) {
total := len(value) * 8
if total > dataMaxSize { return 0, ErrDataTooLarge }
return total, nil
}
// U16CastSafe safely casts an integer to a uint16. If an overflow or underflow
// occurs, it will return (0, false).
func U16CastSafe(n int) (uint16, bool) {

View File

@ -11,19 +11,19 @@ const randStringBytes = "-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU
func TestI8(test *testing.T) {
var buffer [16]byte
err := EncodeI8[uint8](buffer[:], 5)
_, err := EncodeI8[uint8](buffer[:], 5)
if err != nil { test.Fatal(err) }
_, err = EncodeI8[uint8](buffer[:0], 5)
if err.Error() != "encoding int8: wrong buffer length" { test.Fatal(err) }
err = EncodeI8[uint8](buffer[:0], 5)
if err.Error() != "encoding int8: wrong buffer length" { test.Fatal(err) }
_, err = DecodeI8[uint8](buffer[:])
if err.Error() != "decoding int8: wrong buffer length" { test.Fatal(err) }
_, err = DecodeI8[uint8](buffer[:0])
_, _, err = DecodeI8[uint8](buffer[:])
if err != nil { test.Fatal(err) }
_, _, err = DecodeI8[uint8](buffer[:0])
if err.Error() != "decoding int8: wrong buffer length" { test.Fatal(err) }
for number := range uint8(255) {
err := EncodeI8[uint8](buffer[:1], number)
_, err := EncodeI8[uint8](buffer[:1], number)
if err != nil { test.Fatal(err) }
decoded, err := DecodeI8[uint8](buffer[:1])
decoded, _, err := DecodeI8[uint8](buffer[:1])
if err != nil { test.Fatal(err) }
if decoded != number {
test.Fatalf("%d != %d", decoded, number)
@ -33,20 +33,20 @@ func TestI8(test *testing.T) {
func TestI16(test *testing.T) {
var buffer [16]byte
err := EncodeI16[uint16](buffer[:], 5)
_, err := EncodeI16[uint16](buffer[:], 5)
if err != nil { test.Fatal(err) }
_, err = EncodeI16[uint16](buffer[:0], 5)
if err.Error() != "encoding int16: wrong buffer length" { test.Fatal(err) }
err = EncodeI16[uint16](buffer[:0], 5)
if err.Error() != "encoding int16: wrong buffer length" { test.Fatal(err) }
_, err = DecodeI16[uint16](buffer[:])
if err.Error() != "decoding int16: wrong buffer length" { test.Fatal(err) }
_, err = DecodeI16[uint16](buffer[:0])
_, _, err = DecodeI16[uint16](buffer[:])
if err != nil { test.Fatal(err) }
_, _, err = DecodeI16[uint16](buffer[:0])
if err.Error() != "decoding int16: wrong buffer length" { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
number := uint16(rand.Int())
err := EncodeI16[uint16](buffer[:2], number)
_, err := EncodeI16[uint16](buffer[:2], number)
if err != nil { test.Fatal(err) }
decoded, err := DecodeI16[uint16](buffer[:2])
decoded, _, err := DecodeI16[uint16](buffer[:2])
if err != nil { test.Fatal(err) }
if decoded != number {
test.Fatalf("%d != %d", decoded, number)
@ -56,20 +56,20 @@ func TestI16(test *testing.T) {
func TestI32(test *testing.T) {
var buffer [16]byte
err := EncodeI32[uint32](buffer[:], 5)
_, err := EncodeI32[uint32](buffer[:], 5)
if err != nil { test.Fatal(err) }
_, err = EncodeI32[uint32](buffer[:0], 5)
if err.Error() != "encoding int32: wrong buffer length" { test.Fatal(err) }
err = EncodeI32[uint32](buffer[:0], 5)
if err.Error() != "encoding int32: wrong buffer length" { test.Fatal(err) }
_, err = DecodeI32[uint32](buffer[:])
if err.Error() != "decoding int32: wrong buffer length" { test.Fatal(err) }
_, err = DecodeI32[uint32](buffer[:0])
_, _, err = DecodeI32[uint32](buffer[:])
if err != nil { test.Fatal(err) }
_, _, err = DecodeI32[uint32](buffer[:0])
if err.Error() != "decoding int32: wrong buffer length" { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
number := uint32(rand.Int())
err := EncodeI32[uint32](buffer[:4], number)
_, err := EncodeI32[uint32](buffer[:4], number)
if err != nil { test.Fatal(err) }
decoded, err := DecodeI32[uint32](buffer[:4])
decoded, _, err := DecodeI32[uint32](buffer[:4])
if err != nil { test.Fatal(err) }
if decoded != number {
test.Fatalf("%d != %d", decoded, number)
@ -79,20 +79,20 @@ func TestI32(test *testing.T) {
func TestI64(test *testing.T) {
var buffer [16]byte
err := EncodeI64[uint64](buffer[:], 5)
_, err := EncodeI64[uint64](buffer[:], 5)
if err != nil { test.Fatal(err) }
_, err = EncodeI64[uint64](buffer[:0], 5)
if err.Error() != "encoding int64: wrong buffer length" { test.Fatal(err) }
err = EncodeI64[uint64](buffer[:0], 5)
if err.Error() != "encoding int64: wrong buffer length" { test.Fatal(err) }
_, err = DecodeI64[uint64](buffer[:])
if err.Error() != "decoding int64: wrong buffer length" { test.Fatal(err) }
_, err = DecodeI64[uint64](buffer[:0])
_, _, err = DecodeI64[uint64](buffer[:])
if err != nil { test.Fatal(err) }
_, _, err = DecodeI64[uint64](buffer[:0])
if err.Error() != "decoding int64: wrong buffer length" { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
number := uint64(rand.Int())
err := EncodeI64[uint64](buffer[:8], number)
_, err := EncodeI64[uint64](buffer[:8], number)
if err != nil { test.Fatal(err) }
decoded, err := DecodeI64[uint64](buffer[:8])
decoded, _, err := DecodeI64[uint64](buffer[:8])
if err != nil { test.Fatal(err) }
if decoded != number {
test.Fatalf("%d != %d", decoded, number)
@ -107,17 +107,17 @@ func TestGBEU(test *testing.T) {
255, 255, 255, 255,
255, 255, 255, 255,
}
err := EncodeGBEU[uint64](buffer[:0], 5)
_, err := EncodeGBEU[uint64](buffer[:0], 5)
if err == nil { test.Fatal("no error") }
if err.Error() != "encoding GBEU: wrong buffer length" { test.Fatal(err) }
err = EncodeGBEU[uint64](buffer[:2], 5555555)
_, err = EncodeGBEU[uint64](buffer[:2], 5555555)
if err == nil { test.Fatal("no error") }
if err.Error() != "encoding GBEU: wrong buffer length" { test.Fatal(err) }
_, err = DecodeGBEU[uint64](buffer[:0])
_, _, err = DecodeGBEU[uint64](buffer[:0])
if err == nil { test.Fatal("no error") }
if err.Error() != "decoding GBEU: GBEU not terminated" { test.Fatal(err) }
err = EncodeGBEU[uint64](buffer[:], 0x97)
_, err = EncodeGBEU[uint64](buffer[:], 0x97)
if err != nil { test.Fatal(err) }
if correct, got := []byte { 0x81, 0x17 }, buffer[:2]; !slices.Equal(correct, got) {
message := "not equal:"
@ -126,13 +126,13 @@ func TestGBEU(test *testing.T) {
}
test.Fatal(message)
}
decoded, err := DecodeGBEU[uint64](buffer[:])
decoded, _, err := DecodeGBEU[uint64](buffer[:])
if err != nil { test.Fatal(err) }
if correct, got := uint64(0x97), decoded; correct != got {
test.Fatalf("not equal: %x", got)
}
err = EncodeGBEU[uint64](buffer[:], 0x123456)
_, err = EncodeGBEU[uint64](buffer[:], 0x123456)
if err != nil { test.Fatal(err) }
if correct, got := []byte { 0xc8, 0xe8, 0x56 }, buffer[:3]; !slices.Equal(correct, got) {
message := "not equal:"
@ -141,14 +141,14 @@ func TestGBEU(test *testing.T) {
}
test.Fatal(message)
}
decoded, err = DecodeGBEU[uint64](buffer[:])
decoded, _, err = DecodeGBEU[uint64](buffer[:])
if err != nil { test.Fatal(err) }
if correct, got := uint64(0x123456), decoded; correct != got {
test.Fatalf("not equal: %x", got)
}
maxGBEU64 := []byte { 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F }
err = EncodeGBEU[uint64](buffer[:], 0xFFFFFFFFFFFFFFFF)
_, err = EncodeGBEU[uint64](buffer[:], 0xFFFFFFFFFFFFFFFF)
if err != nil { test.Fatal(err) }
if correct, got := maxGBEU64, buffer[:10]; !slices.Equal(correct, got) {
message := "not equal:"
@ -157,18 +157,18 @@ func TestGBEU(test *testing.T) {
}
test.Fatal(message)
}
decoded, err = DecodeGBEU[uint64](buffer[:])
decoded, _, err = DecodeGBEU[uint64](buffer[:])
if err != nil { test.Fatal(err) }
if correct, got := uint64(0xFFFFFFFFFFFFFFFF), decoded; correct != got {
test.Fatalf("not equal: %x", got)
}
err = EncodeGBEU[uint64](buffer[:], 11)
_, err = EncodeGBEU[uint64](buffer[:], 11)
if err != nil { test.Fatal(err) }
if correct, got := byte(0xb), buffer[0]; correct != got {
test.Fatal("not equal:", got)
}
decoded, err = DecodeGBEU[uint64](buffer[:])
decoded, _, err = DecodeGBEU[uint64](buffer[:])
if err != nil { test.Fatal(err) }
if correct, got := uint64(0xb), decoded; correct != got {
test.Fatalf("not equal: %x", got)
@ -177,9 +177,9 @@ func TestGBEU(test *testing.T) {
for _ = range largeNumberNTestRounds {
buffer = [16]byte { }
number := uint64(rand.Int())
err := EncodeGBEU[uint64](buffer[:], number)
_, err := EncodeGBEU[uint64](buffer[:], number)
if err != nil { test.Fatal(err) }
decoded, err := DecodeGBEU[uint64](buffer[:])
decoded, _, err := DecodeGBEU[uint64](buffer[:])
if err != nil { test.Fatal(err) }
if decoded != number {
test.Error("not equal:")
@ -210,21 +210,21 @@ func TestGBEUSize(test *testing.T) {
func TestString(test *testing.T) {
var buffer [16]byte
err := EncodeString[string](buffer[:], "hello")
_, err := EncodeString[string](buffer[:], "hello")
if !errIs(err, ErrWrongBufferLength, "encoding string: wrong buffer length") { test.Fatal(err) }
err = EncodeString[string](buffer[:0], "hello")
_, err = EncodeString[string](buffer[:0], "hello")
if !errIs(err, ErrWrongBufferLength, "encoding string: wrong buffer length") { test.Fatal(err) }
_, err = DecodeString[string](buffer[:])
_, _, err = DecodeString[string](buffer[:])
if err != nil { test.Fatal(err) }
_, err = DecodeString[string](buffer[:0])
_, _, err = DecodeString[string](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
length := rand.Intn(16)
str := randString(length)
err := EncodeString[string](buffer[:length], str)
_, err := EncodeString[string](buffer[:length], str)
if err != nil { test.Fatal(err) }
decoded, err := DecodeString[string](buffer[:length])
decoded, _, err := DecodeString[string](buffer[:length])
if err != nil { test.Fatal(err) }
if decoded != str {
test.Fatalf("%s != %s", decoded, str)
@ -232,129 +232,6 @@ func TestString(test *testing.T) {
}
}
func TestI8Array(test *testing.T) {
var buffer [64]byte
err := EncodeI8Array[uint8](buffer[:], []uint8 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int8: wrong buffer length") { test.Fatal(err) }
err = EncodeI8Array[uint8](buffer[:0], []uint8 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int8: wrong buffer length") { test.Fatal(err) }
_, err = DecodeI8Array[uint8](buffer[:])
if err != nil { test.Fatal(err) }
_, err = DecodeI8Array[uint8](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
array := randInts[uint8](rand.Intn(16))
length, _ := I8ArraySize(array)
if length != len(array) { test.Fatalf("%d != %d", length, len(array)) }
err := EncodeI8Array[uint8](buffer[:length], array)
if err != nil { test.Fatal(err) }
decoded, err := DecodeI8Array[uint8](buffer[:length])
if err != nil { test.Fatal(err) }
if !slices.Equal(decoded, array) {
test.Fatalf("%v != %v", decoded, array)
}
}
}
func TestI16Array(test *testing.T) {
var buffer [128]byte
err := EncodeI16Array[uint16](buffer[:], []uint16 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int16: wrong buffer length") { test.Fatal(err) }
err = EncodeI16Array[uint16](buffer[:0], []uint16 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int16: wrong buffer length") { test.Fatal(err) }
_, err = DecodeI16Array[uint16](buffer[:])
if err != nil { test.Fatal(err) }
_, err = DecodeI16Array[uint16](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
array := randInts[uint16](rand.Intn(16))
length, _ := I16ArraySize(array)
if length != 2 * len(array) { test.Fatalf("%d != %d", length, 2 * len(array)) }
err := EncodeI16Array[uint16](buffer[:length], array)
if err != nil { test.Fatal(err) }
decoded, err := DecodeI16Array[uint16](buffer[:length])
if err != nil { test.Fatal(err) }
if !slices.Equal(decoded, array) {
test.Fatalf("%v != %v", decoded, array)
}
}
}
func TestI32Array(test *testing.T) {
var buffer [256]byte
err := EncodeI32Array[uint32](buffer[:], []uint32 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int32: wrong buffer length") { test.Fatal(err) }
err = EncodeI32Array[uint32](buffer[:0], []uint32 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int32: wrong buffer length") { test.Fatal(err) }
_, err = DecodeI32Array[uint32](buffer[:])
if err != nil { test.Fatal(err) }
_, err = DecodeI32Array[uint32](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
array := randInts[uint32](rand.Intn(16))
length, _ := I32ArraySize(array)
if length != 4 * len(array) { test.Fatalf("%d != %d", length, 4 * len(array)) }
err := EncodeI32Array[uint32](buffer[:length], array)
if err != nil { test.Fatal(err) }
decoded, err := DecodeI32Array[uint32](buffer[:length])
if err != nil { test.Fatal(err) }
if !slices.Equal(decoded, array) {
test.Fatalf("%v != %v", decoded, array)
}
}
}
func TestI64Array(test *testing.T) {
var buffer [512]byte
err := EncodeI64Array[uint64](buffer[:], []uint64 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int64: wrong buffer length") { test.Fatal(err) }
err = EncodeI64Array[uint64](buffer[:0], []uint64 { 0, 4, 50, 19 })
if !errIs(err, ErrWrongBufferLength, "encoding []int64: wrong buffer length") { test.Fatal(err) }
_, err = DecodeI64Array[uint64](buffer[:])
if err != nil { test.Fatal(err) }
_, err = DecodeI64Array[uint64](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
array := randInts[uint64](rand.Intn(16))
length, _ := I64ArraySize(array)
if length != 8 * len(array) { test.Fatalf("%d != %d", length, 8 * len(array)) }
err := EncodeI64Array[uint64](buffer[:length], array)
if err != nil { test.Fatal(err) }
decoded, err := DecodeI64Array[uint64](buffer[:length])
if err != nil { test.Fatal(err) }
if !slices.Equal(decoded, array) {
test.Fatalf("%v != %v", decoded, array)
}
}
}
func TestStringArray(test *testing.T) {
var buffer [8192]byte
err := EncodeStringArray[string](buffer[:], []string { "0", "4", "50", "19" })
if !errIs(err, ErrWrongBufferLength, "encoding []string: wrong buffer length") { test.Fatal(err) }
err = EncodeStringArray[string](buffer[:0], []string { "0", "4", "50", "19" })
if !errIs(err, ErrWrongBufferLength, "encoding []string: wrong buffer length") { test.Fatal(err) }
_, err = DecodeStringArray[string](buffer[:0])
if err != nil { test.Fatal(err) }
for _ = range largeNumberNTestRounds {
array := randStrings[string](rand.Intn(16), 16)
length, _ := StringArraySize(array)
// TODO test length
err := EncodeStringArray[string](buffer[:length], array)
if err != nil { test.Fatal(err) }
decoded, err := DecodeStringArray[string](buffer[:length])
if err != nil { test.Fatal(err) }
if !slices.Equal(decoded, array) {
test.Fatalf("%v != %v", decoded, array)
}
}
}
func TestU16CastSafe(test *testing.T) {
number, ok := U16CastSafe(90_000)
if ok { test.Fatalf("false positive: %v, %v", number, ok) }