// 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 } }