181 lines
5.4 KiB
Go
181 lines
5.4 KiB
Go
// Package tape implements Table Pair Encoding.
|
|
package tape
|
|
|
|
import "fmt"
|
|
|
|
// encoding and decoding functions must not make any allocations
|
|
|
|
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"
|
|
ErrGBEUNotTerminated Error = "GBEU not terminated"
|
|
)
|
|
|
|
// 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 }
|
|
// UInt is any unsigned integer.
|
|
type UInt interface { ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 }
|
|
// String is any string.
|
|
type String interface { ~string }
|
|
|
|
// DecodeI8 decodes an 8 bit integer from the given data.
|
|
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) (n int, err error) {
|
|
if len(buffer) != 1 { return 0, fmt.Errorf("encoding int8: %w", ErrWrongBufferLength) }
|
|
buffer[0] = byte(value)
|
|
return 1, nil
|
|
}
|
|
|
|
// DecodeI16 decodes a 16 bit integer from the given data.
|
|
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) (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 2, nil
|
|
}
|
|
|
|
// DecodeI32 decodes a 32 bit integer from the given data.
|
|
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]), 4, nil
|
|
}
|
|
|
|
// EncodeI32 encodes a 32 bit integer into the given buffer.
|
|
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 4, nil
|
|
}
|
|
|
|
// DecodeI64 decodes a 64 bit integer from the given data.
|
|
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 |
|
|
T(data[3]) << 32 |
|
|
T(data[4]) << 24 |
|
|
T(data[5]) << 16 |
|
|
T(data[6]) << 8 |
|
|
T(data[7]), 8, nil
|
|
}
|
|
|
|
// EncodeI64 encodes a 64 bit integer into the given buffer.
|
|
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)
|
|
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 8, nil
|
|
}
|
|
|
|
// DecodeGBEU decodes an 8 to 64 bit growing integer into the given buffer.
|
|
func DecodeGBEU[T UInt](data []byte) (value T, n int, err error) {
|
|
var fullValue uint64
|
|
for _, chunk := range data {
|
|
fullValue *= 0x80
|
|
fullValue += uint64(chunk & 0x7F)
|
|
ccb := chunk >> 7
|
|
if ccb == 0 {
|
|
return T(fullValue), n, nil
|
|
}
|
|
n += 1
|
|
}
|
|
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) (n int, err error) {
|
|
window := (GBEUSize(value) - 1) * 7
|
|
|
|
index := 0
|
|
for window >= 0 {
|
|
if index >= len(buffer) { return index, fmt.Errorf("encoding GBEU: %w", ErrWrongBufferLength) }
|
|
|
|
chunk := uint8(value >> window) & 0x7F
|
|
if window > 0 {
|
|
chunk |= 0x80
|
|
}
|
|
buffer[index] = chunk
|
|
|
|
index += 1
|
|
window -= 7
|
|
}
|
|
return index, nil
|
|
}
|
|
|
|
// GBEUSize returns the size (in octets) of a GBEU integer.
|
|
func GBEUSize[T UInt] (value T) int {
|
|
length := 0
|
|
for {
|
|
value >>= 7
|
|
length ++
|
|
if value == 0 { return length }
|
|
}
|
|
}
|
|
|
|
// DecodeString decodes a string from the given data.
|
|
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) (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
|
|
// is too large.
|
|
func StringSize[T String](value T) (int, error) {
|
|
if len(value) > dataMaxSize { return 0, ErrDataTooLarge }
|
|
return len(value), 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
|
|
}
|
|
}
|