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