285 lines
8.6 KiB
Go
285 lines
8.6 KiB
Go
package tape
|
|
|
|
import "fmt"
|
|
import "iter"
|
|
import "slices"
|
|
|
|
// encoding and decoding functions must not make any allocations
|
|
|
|
// ArrayPushFunc adds an item to an array that is being encoded.
|
|
type ArrayPushFunc func(value []byte) (n int, err error)
|
|
|
|
// ArrayPullFunc gets the next item of an array that is being decoded.
|
|
type ArrayPullFunc func() (value []byte, n int, err error)
|
|
|
|
// DecodePASTA decodes a packed single-type array.
|
|
func DecodePASTA(data []byte, itemLength int) ArrayPullFunc {
|
|
n := 0
|
|
return func() (value []byte, n_ int, err error) {
|
|
if n > len(data) - itemLength {
|
|
return nil, 0, fmt.Errorf("decoding PASTA: %w", ErrWrongBufferLength)
|
|
}
|
|
value = data[n:n + itemLength]
|
|
n += itemLength
|
|
return value, itemLength, nil
|
|
}
|
|
}
|
|
|
|
// DecodePASTAIter decodes a packed single-type array and returns it as an
|
|
// iterator.
|
|
func DecodePASTAIter(data []byte, itemLength int) iter.Seq[[]byte] {
|
|
return slices.Chunk(data, itemLength)
|
|
}
|
|
|
|
// EncodePASTA encodes a packed single-type array.
|
|
func EncodePASTA(data []byte, itemLength int) ArrayPushFunc {
|
|
n := 0
|
|
return func(value []byte) (n_ int, err error) {
|
|
if n > len(data) - itemLength {
|
|
return 0, fmt.Errorf("encoding PASTA: %w", ErrWrongBufferLength)
|
|
}
|
|
copy(data[n:], value)
|
|
n += itemLength
|
|
return itemLength, nil
|
|
}
|
|
}
|
|
|
|
// PASTASize returns the size of a packed single-type array.
|
|
func PASTASize(length, itemLength int) int {
|
|
return length * itemLength
|
|
}
|
|
|
|
// DecodeVILA encodes a variable item length array.
|
|
func DecodeVILA(data []byte) ArrayPullFunc {
|
|
n := 0
|
|
return func() (value []byte, n_ int, err error) {
|
|
if n >= len(data) { return nil, n_, fmt.Errorf("decoding VILA: %w", ErrWrongBufferLength) }
|
|
length, nn, err := DecodeGBEU[uint](data[n:])
|
|
n += nn
|
|
n_ += nn
|
|
if err != nil { return nil, n_, err }
|
|
|
|
if n > len(data) - int(length) {
|
|
return nil, n_, fmt.Errorf("decoding VILA: %w", ErrWrongBufferLength)
|
|
}
|
|
value = data[n:n + int(length)]
|
|
n += int(length)
|
|
n_ += int(length)
|
|
return value, int(length), nil
|
|
}
|
|
}
|
|
|
|
// DecodeVILAIter decodes a variable item length array and returns it as an
|
|
// iterator.
|
|
func DecodeVILAIter(data []byte) iter.Seq[[]byte] {
|
|
return func(yield func([]byte) bool) {
|
|
pull := DecodeVILA(data)
|
|
for {
|
|
value, _, err := pull()
|
|
if err != nil { return }
|
|
if !yield(value) { return }
|
|
}
|
|
}
|
|
}
|
|
|
|
// EncodeVILA encodes a variable item length array.
|
|
func EncodeVILA(data []byte) ArrayPushFunc {
|
|
n := 0
|
|
return func(value []byte) (n_ int, err error) {
|
|
if n >= len(data) { return n_, fmt.Errorf("encoding VILA: %w", ErrWrongBufferLength) }
|
|
nn, err := EncodeGBEU(data[n:], uint(len(value)))
|
|
n += nn
|
|
n_ += nn
|
|
if err != nil { return n, err }
|
|
|
|
if n >= len(data) { return n_, fmt.Errorf("encoding VILA: %w", ErrWrongBufferLength) }
|
|
nn = copy(data[n:], value)
|
|
n += nn
|
|
n_ += nn
|
|
if nn != len(value) { return n_, fmt.Errorf("encoding VILA: %w", ErrWrongBufferLength) }
|
|
return n_, err
|
|
}
|
|
}
|
|
|
|
// VILASize returns the size of a variable item length array.
|
|
func VILASize(items ...[]byte) int {
|
|
size := 0
|
|
for _, item := range items {
|
|
size += GBEUSize[uint](uint(len(item)))
|
|
size += len(item)
|
|
}
|
|
return size
|
|
}
|
|
|
|
// DecodeStringArray decodes a VILA string array from the given data.
|
|
func DecodeStringArray[T String](data []byte) (result []T, n int, err error) {
|
|
pull := DecodeVILA(data)
|
|
for {
|
|
item, nn, err := pull()
|
|
n += nn
|
|
if err != nil { return nil, n, err }
|
|
result = append(result, T(item))
|
|
}
|
|
return result, n, nil
|
|
}
|
|
|
|
// EncodeStringArray encodes a VILA string array into the given buffer.
|
|
func EncodeStringArray[T String](buffer []byte, value []T) (n int, err error) {
|
|
push := EncodeVILA(buffer)
|
|
for _, item := range value {
|
|
nn, err := push([]byte(item))
|
|
n += nn
|
|
if err != nil { return n, err }
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// StringArraySize returns the size of a VILA string array.
|
|
func StringArraySize[T String](value []T) int {
|
|
size := 0
|
|
for _, item := range value {
|
|
size += GBEUSize[uint](uint(len(item)))
|
|
size += len(item)
|
|
}
|
|
return size
|
|
}
|
|
|
|
// 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
|
|
}
|