84 lines
2.2 KiB
Go
84 lines
2.2 KiB
Go
package tape
|
|
|
|
import "iter"
|
|
|
|
// DecodePairs decodes message tag/value pairs from a byte slice. It returns an
|
|
// iterator over all pairs, where the first value is the tag and the second is
|
|
// the value. If data yielded by the iterator is retained, it must be copied
|
|
// first.
|
|
func DecodePairs(data []byte) (iter.Seq2[uint16, []byte], error) {
|
|
// determine section bounds
|
|
if len(data) < 2 { return nil, ErrDataTooLarge }
|
|
length16, _ := DecodeI16[uint16](data[0:2])
|
|
data = data[2:]
|
|
length := int(length16)
|
|
headerSize := length * 4
|
|
if len(data) < headerSize { return nil, ErrDataTooLarge }
|
|
valuesData := data[headerSize:]
|
|
|
|
// ensure the value buffer is big enough
|
|
var valuesSize int
|
|
for index := range length {
|
|
offset := index * 4
|
|
end, _ := DecodeI16[uint16](data[offset + 2:offset + 4])
|
|
valuesSize = int(end)
|
|
}
|
|
if valuesSize > len(valuesData) {
|
|
return nil, ErrDataTooLarge
|
|
}
|
|
|
|
// return iterator
|
|
return func(yield func(uint16, []byte) bool) {
|
|
start := uint16(0)
|
|
for index := range length {
|
|
offset := index * 4
|
|
key , _ := DecodeI16[uint16](data[offset + 0:offset + 2])
|
|
end, _ := DecodeI16[uint16](data[offset + 2:offset + 4])
|
|
// if nextValuesOffset < len(valuesData) {
|
|
if !yield(key, valuesData[start:end]) {
|
|
return
|
|
}
|
|
// } else {
|
|
// if !yield(key, nil) {
|
|
// return
|
|
// }
|
|
// }
|
|
start = end
|
|
}
|
|
}, nil
|
|
}
|
|
|
|
// EncodePairs encodes message tag/value pairs into a byte slice.
|
|
func EncodePairs(pairs map[uint16] []byte) ([]byte, error) {
|
|
// determine section bounds
|
|
headerSize := 2 + len(pairs) * 4
|
|
valuesSize := 0
|
|
for _, value := range pairs {
|
|
valuesSize += len(value)
|
|
}
|
|
|
|
// generate data
|
|
buffer := make([]byte, headerSize + valuesSize)
|
|
length16, ok := U16CastSafe(len(pairs))
|
|
if !ok { return nil, ErrDataTooLarge }
|
|
EncodeI16[uint16](buffer[0:2], length16)
|
|
index := 0
|
|
end := headerSize
|
|
for key, value := range pairs {
|
|
start := end
|
|
end += len(value)
|
|
tagOffset := 2 + index * 4
|
|
end16, ok := U16CastSafe(end - headerSize)
|
|
if !ok { return nil, ErrDataTooLarge }
|
|
|
|
// write tag and length
|
|
EncodeI16[uint16](buffer[tagOffset + 0:tagOffset + 2], key)
|
|
EncodeI16[uint16](buffer[tagOffset + 2:tagOffset + 4], end16)
|
|
|
|
// write value
|
|
copy(buffer[start:end], value)
|
|
index ++
|
|
}
|
|
return buffer, nil
|
|
}
|