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 }