Files
ingot/internal/chunkenc/bstream.go
T
david 06a95597e9 chunkenc
Bitstream primitives (bstream) and XOR encoder/decoder following the
Prometheus TSDB variant of the Gorilla scheme. Delta-of-delta timestamps
with 14/17/20/64-bit buckets, XOR-compressed float64 values. Decoder is
total: arbitrary bytes produce values or ErrShortStream.
2026-07-04 11:42:06 -04:00

145 lines
2.9 KiB
Go

// Chunk encoding follows Prometheus tsdb/chunkenc. See /NOTICE.md.
package chunkenc
import (
"errors"
)
var ErrShortStream = errors.New("chunkenc: unexpected end of stream")
type bstream struct {
stream []byte
count uint8
}
func (b *bstream) bytes() []byte {
return b.stream
}
func (b *bstream) writeBit(bit bool) {
if b.count == 0 {
b.stream = append(b.stream, 0)
b.count = 8
}
i := len(b.stream) - 1
if bit {
b.stream[i] |= 1 << (b.count - 1)
}
b.count--
}
func (b *bstream) writeBits(u uint64, nbits int) {
// Byte-aligned fast path from the top of the value down.
u <<= 64 - uint(nbits)
for nbits >= 8 {
b.writeByte(byte(u >> 56))
u <<= 8
nbits -= 8
}
for nbits > 0 {
b.writeBit((u >> 63) == 1)
u <<= 1
nbits--
}
}
func (b *bstream) writeByte(byt byte) {
if b.count == 0 {
b.stream = append(b.stream, 0)
b.count = 8
}
i := len(b.stream) - 1
// Split across the partial last byte and a fresh one.
b.stream[i] |= byt >> (8 - b.count)
b.stream = append(b.stream, 0)
i++
b.stream[i] = byt << b.count
// count unchanged: same number of free bits, now in the new last byte
}
type bstreamReader struct {
stream []byte
off int // byte offset of next unread byte
buf uint64 // bit buffer, MSB-first
valid uint8 //valid bits remaining in buf
}
func newBReader(b []byte) bstreamReader {
return bstreamReader{stream: b}
}
// loadNextBuffer refills buf with up to 8 bytes. Returns false at EOF.
func (r *bstreamReader) loadNextBuffer(nbits uint8) bool {
if r.off >= len(r.stream) {
return false
}
// Fast path: 8 full bytes available.
if r.off+8 <= len(r.stream) {
r.buf = uint64(r.stream[r.off])<<56 |
uint64(r.stream[r.off+1])<<48 |
uint64(r.stream[r.off+2])<<40 |
uint64(r.stream[r.off+3])<<32 |
uint64(r.stream[r.off+4])<<24 |
uint64(r.stream[r.off+5])<<16 |
uint64(r.stream[r.off+6])<<8 |
uint64(r.stream[r.off+7])
r.off += 8
r.valid = 64
return true
}
// Tail: load what's left, left-aligned.
n := len(r.stream) - r.off
r.buf = 0
for i := 0; i < n; i++ {
r.buf |= uint64(r.stream[r.off+i]) << (56 - 8*uint(i))
}
r.off += n
r.valid = uint8(n * 8)
return true
}
func (r *bstreamReader) readBit() (bool, error) {
if r.valid == 0 {
if !r.loadNextBuffer(1) {
return false, ErrShortStream
}
}
bit := r.buf&(1<<63) != 0
r.buf <<= 1
r.valid--
return bit, nil
}
func (r *bstreamReader) readBits(nbits int) (uint64, error) {
if nbits == 0 {
return 0, nil
}
var v uint64
remaining := uint8(nbits)
for remaining > 0 {
if r.valid == 0 {
if !r.loadNextBuffer(remaining) {
return 0, ErrShortStream
}
}
take := remaining
if take > r.valid {
take = r.valid
}
v = (v << take) | (r.buf >> (64 - take))
r.buf <<= take
r.valid -= take
remaining -= take
}
return v, nil
}
func (r *bstreamReader) readByte() (byte, error) {
v, err := r.readBits(8)
return byte(v), err
}