diff --git a/tape/array.go b/tape/array.go index 1d933e3..00cdffb 100644 --- a/tape/array.go +++ b/tape/array.go @@ -6,23 +6,90 @@ import "slices" // encoding and decoding functions must not make any allocations -func DecodeArray(data []byte, itemLength int) iter.Seq[[]byte] { +// 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) } -func EncodeArray(data []byte, items ...[]byte) (n int, err error) { - for _, item := range items { - if n >= len(data) { return n, ErrWrongBufferLength } - copy(data[n:], item) - n += len(item) +// 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 } - return n, nil } -func ArraySize(length, itemLength int) int { +// 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 + } +} + +// EncodeVILA encodes a variable item length array. +func EncodeVILA(data []byte, items ...[]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 + } +} + +// TODO: Decode, encode, size for string array needs to use VILA + // DecodeStringArray decodes a packed string array from the given data. func DecodeStringArray[T String](data []byte) (result []T, n int, err error) { for len(data) > 0 {