312 lines
9.8 KiB
Go
312 lines
9.8 KiB
Go
// Package tape implements Table Pair Encoding.
|
|
package tape
|
|
|
|
import "fmt"
|
|
|
|
const dataMaxSize = 0xFFFF
|
|
const uint16Max = 0xFFFF
|
|
|
|
// Error enumerates common errors in this package.
|
|
type Error string; const (
|
|
ErrWrongBufferLength Error = "wrong buffer length"
|
|
ErrDataTooLarge Error = "data too large"
|
|
)
|
|
|
|
// Error implements the error interface.
|
|
func (err Error) Error() string {
|
|
return string(err)
|
|
}
|
|
|
|
// Int8 is any 8-bit integer.
|
|
type Int8 interface { ~uint8 | ~int8 }
|
|
// Int16 is any 16-bit integer.
|
|
type Int16 interface { ~uint16 | ~int16 }
|
|
// Int32 is any 32-bit integer.
|
|
type Int32 interface { ~uint32 | ~int32 }
|
|
// Int64 is any 64-bit integer.
|
|
type Int64 interface { ~uint64 | ~int64 }
|
|
// String is any string.
|
|
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
|
|
}
|
|
|
|
// 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) }
|
|
buffer[0] = byte(value)
|
|
return 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
|
|
}
|
|
|
|
// 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) }
|
|
buffer[0] = byte(value >> 8)
|
|
buffer[1] = byte(value)
|
|
return 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) }
|
|
return T(data[0]) << 24 |
|
|
T(data[1]) << 16 |
|
|
T(data[2]) << 8 |
|
|
T(data[3]), 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) }
|
|
buffer[0] = byte(value >> 24)
|
|
buffer[1] = byte(value >> 16)
|
|
buffer[2] = byte(value >> 8)
|
|
buffer[3] = byte(value)
|
|
return 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) }
|
|
return T(data[0]) << 56 |
|
|
T(data[1]) << 48 |
|
|
T(data[2]) << 40 |
|
|
T(data[3]) << 32 |
|
|
T(data[4]) << 24 |
|
|
T(data[5]) << 16 |
|
|
T(data[6]) << 8 |
|
|
T(data[7]), 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) }
|
|
buffer[0] = byte(value >> 56)
|
|
buffer[1] = byte(value >> 48)
|
|
buffer[2] = byte(value >> 40)
|
|
buffer[3] = byte(value >> 32)
|
|
buffer[4] = byte(value >> 24)
|
|
buffer[5] = byte(value >> 16)
|
|
buffer[6] = byte(value >> 8)
|
|
buffer[7] = byte(value)
|
|
return nil
|
|
}
|
|
|
|
// DecodeString decodes a string from the given data.
|
|
func DecodeString[T String](data []byte) (T, error) {
|
|
return T(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
|
|
}
|
|
|
|
// StringSize returns the size of a string. Returns 0 and an error if the size
|
|
// is too large.
|
|
func StringSize[T String](value T) (int, error) {
|
|
if len(value) > dataMaxSize { return 0, ErrDataTooLarge }
|
|
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) {
|
|
if n < uint16Max && n >= 0 {
|
|
return uint16(n), true
|
|
} else {
|
|
return 0, false
|
|
}
|
|
}
|