influxdb/tsdb/tsm1/batch_string.go

82 lines
1.9 KiB
Go

package tsm1
import (
"encoding/binary"
"fmt"
"unsafe"
"github.com/golang/snappy"
)
var (
errStringBatchDecodeInvalidStringLength = fmt.Errorf("StringBatchDecodeAll: invalid encoded string length")
errStringBatchDecodeLengthOverflow = fmt.Errorf("StringBatchDecodeAll: length overflow")
errStringBatchDecodeShortBuffer = fmt.Errorf("StringBatchDecodeAll: short buffer")
)
func StringBatchDecodeAll(b []byte, dst []string) ([]string, error) {
// First byte stores the encoding type, only have snappy format
// currently so ignore for now.
if len(b) > 0 {
var err error
// it is important that to note that `snappy.Decode` always returns
// a newly allocated slice as the final strings reference this slice
// directly.
b, err = snappy.Decode(nil, b[1:])
if err != nil {
return []string{}, fmt.Errorf("failed to decode string block: %v", err.Error())
}
} else {
return []string{}, nil
}
var (
i, l int
)
sz := cap(dst)
if sz == 0 {
sz = 64
dst = make([]string, sz)
} else {
dst = dst[:sz]
}
j := 0
for i < len(b) {
length, n := binary.Uvarint(b[i:])
if n <= 0 {
return []string{}, errStringBatchDecodeInvalidStringLength
}
// The length of this string plus the length of the variable byte encoded length
l = int(length) + n
lower := i + n
upper := lower + int(length)
if upper < lower {
return []string{}, errStringBatchDecodeLengthOverflow
}
if upper > len(b) {
return []string{}, errStringBatchDecodeShortBuffer
}
// NOTE: this optimization is critical for performance and to reduce
// allocations. This is just as "safe" as string.Builder, which
// returns a string mapped to the original byte slice
s := b[lower:upper]
val := *(*string)(unsafe.Pointer(&s))
if j < len(dst) {
dst[j] = val
} else {
dst = append(dst, val) // force a resize
dst = dst[:cap(dst)]
}
i += l
j++
}
return dst[:j], nil
}