Compare commits
5 Commits
23c3efa845
...
717754644c
Author | SHA1 | Date | |
---|---|---|---|
717754644c | |||
134daacc03 | |||
a927b9519e | |||
32df336c3e | |||
2b3a53052f |
148
tape/array.go
148
tape/array.go
@ -6,64 +6,142 @@ import "slices"
|
|||||||
|
|
||||||
// encoding and decoding functions must not make any allocations
|
// 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)
|
return slices.Chunk(data, itemLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
func EncodeArray(data []byte, items ...[]byte) (n int, err error) {
|
// EncodePASTA encodes a packed single-type array.
|
||||||
for _, item := range items {
|
func EncodePASTA(data []byte, itemLength int) ArrayPushFunc {
|
||||||
if n >= len(data) { return n, ErrWrongBufferLength }
|
n := 0
|
||||||
copy(data[n:], item)
|
return func(value []byte) (n_ int, err error) {
|
||||||
n += len(item)
|
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
|
return length * itemLength
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeStringArray decodes a packed string array from the given data.
|
// DecodeVILA encodes a variable item length array.
|
||||||
func DecodeStringArray[T String](data []byte) (result []T, n int, err error) {
|
func DecodeVILA(data []byte) ArrayPullFunc {
|
||||||
for len(data) > 0 {
|
n := 0
|
||||||
if len(data) < 2 { return nil, n, fmt.Errorf("decoding []string: %w", ErrWrongBufferLength) }
|
return func() (value []byte, n_ int, err error) {
|
||||||
itemSize16, nn, _ := DecodeI16[uint16](data[:2])
|
if n >= len(data) { return nil, n_, fmt.Errorf("decoding VILA: %w", ErrWrongBufferLength) }
|
||||||
itemSize := int(itemSize16)
|
length, nn, err := DecodeGBEU[uint](data[n:])
|
||||||
n += nn
|
n += nn
|
||||||
data = data[nn:]
|
n_ += nn
|
||||||
if len(data) < itemSize { return nil, n, fmt.Errorf("decoding []string: %w", ErrWrongBufferLength) }
|
if err != nil { return nil, n_, err }
|
||||||
result = append(result, T(data[:itemSize]))
|
|
||||||
data = data[itemSize:]
|
if n > len(data) - int(length) {
|
||||||
n += itemSize
|
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
|
return result, n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeStringArray encodes a packed string array into the given buffer.
|
// EncodeStringArray encodes a VILA string array into the given buffer.
|
||||||
func EncodeStringArray[T String](buffer []byte, value []T) (n int, err error) {
|
func EncodeStringArray[T String](buffer []byte, value []T) (n int, err error) {
|
||||||
|
push := EncodeVILA(buffer)
|
||||||
for _, item := range value {
|
for _, item := range value {
|
||||||
length, err := StringSize(item)
|
nn, err := push([]byte(item))
|
||||||
|
n += nn
|
||||||
if err != nil { return n, err }
|
if err != nil { return n, err }
|
||||||
if len(buffer) < 2 + length { return n, fmt.Errorf("encoding []string: %w", ErrWrongBufferLength) }
|
|
||||||
EncodeI16(buffer[:2], uint16(length))
|
|
||||||
buffer = buffer[2:]
|
|
||||||
copy(buffer, item)
|
|
||||||
buffer = buffer[length:]
|
|
||||||
n += 2 + length
|
|
||||||
}
|
}
|
||||||
if len(buffer) > 0 { return n, fmt.Errorf("encoding []string: %w", ErrWrongBufferLength) }
|
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringArraySize returns the size of a packed string array. Returns 0 and an
|
// StringArraySize returns the size of a VILA string array.
|
||||||
// error if the size is too large.
|
func StringArraySize[T String](value []T) int {
|
||||||
func StringArraySize[T String](value []T) (int, error) {
|
size := 0
|
||||||
total := 0
|
|
||||||
for _, item := range value {
|
for _, item := range value {
|
||||||
total += 2 + len(item)
|
size += GBEUSize[uint](uint(len(item)))
|
||||||
|
size += len(item)
|
||||||
}
|
}
|
||||||
if total > dataMaxSize { return 0, ErrDataTooLarge }
|
return size
|
||||||
return total, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeI8Array decodes a packed array of 8 bit integers from the given data.
|
// DecodeI8Array decodes a packed array of 8 bit integers from the given data.
|
||||||
|
@ -28,10 +28,10 @@ type Int16 interface { ~uint16 | ~int16 }
|
|||||||
type Int32 interface { ~uint32 | ~int32 }
|
type Int32 interface { ~uint32 | ~int32 }
|
||||||
// Int64 is any 64-bit integer.
|
// Int64 is any 64-bit integer.
|
||||||
type Int64 interface { ~uint64 | ~int64 }
|
type Int64 interface { ~uint64 | ~int64 }
|
||||||
// UInt is any unsigned integer.
|
// Uint is any unsigned integer.
|
||||||
type UInt interface { ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 }
|
type Uint interface { ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 }
|
||||||
// String is any string.
|
// String is any string.
|
||||||
type String interface { ~string }
|
type String interface { ~string | ~[]uint8 }
|
||||||
|
|
||||||
// DecodeI8 decodes an 8 bit integer from the given data.
|
// DecodeI8 decodes an 8 bit integer from the given data.
|
||||||
func DecodeI8[T Int8](data []byte) (value T, n int, err error) {
|
func DecodeI8[T Int8](data []byte) (value T, n int, err error) {
|
||||||
@ -107,7 +107,7 @@ func EncodeI64[T Int64](buffer []byte, value T) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DecodeGBEU decodes an 8 to 64 bit growing integer into the given buffer.
|
// 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) {
|
func DecodeGBEU[T Uint](data []byte) (value T, n int, err error) {
|
||||||
var fullValue uint64
|
var fullValue uint64
|
||||||
for _, chunk := range data {
|
for _, chunk := range data {
|
||||||
fullValue *= 0x80
|
fullValue *= 0x80
|
||||||
@ -122,7 +122,7 @@ func DecodeGBEU[T UInt](data []byte) (value T, n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EncodeGBEU encodes an 8 to 64 bit growing integer into a given buffer.
|
// 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) {
|
func EncodeGBEU[T Uint] (buffer []byte, value T) (n int, err error) {
|
||||||
window := (GBEUSize(value) - 1) * 7
|
window := (GBEUSize(value) - 1) * 7
|
||||||
|
|
||||||
index := 0
|
index := 0
|
||||||
@ -142,7 +142,7 @@ func EncodeGBEU[T UInt] (buffer []byte, value T) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GBEUSize returns the size (in octets) of a GBEU integer.
|
// GBEUSize returns the size (in octets) of a GBEU integer.
|
||||||
func GBEUSize[T UInt] (value T) int {
|
func GBEUSize[T Uint] (value T) int {
|
||||||
length := 0
|
length := 0
|
||||||
for {
|
for {
|
||||||
value >>= 7
|
value >>= 7
|
||||||
|
Loading…
Reference in New Issue
Block a user