bitstream

package
v0.28.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 2, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package bitstream implements a zero-alloc, MSB-first bit stream reader and writer.

It is the foundation of every codec in encoding/chunk (delta-of-delta, Gorilla XOR, T64, dictionary, scaled-decimal+nearest-delta): DoubleDelta and Gorilla live or die on a correct, fast bit packer (DESIGN.md §10, §14 M0). The reader is modeled on the Prometheus/dgryski bstream (an 8-byte refill buffer with a valid-bit count); the writer is append-style (Writer.WriteBits appends to a caller-owned []byte).

All hot-path methods are leaf functions kept small for inlining. The Writer never allocates beyond the append growth of its backing slice; callers may [Reset] it onto a pooled buffer. The Reader takes a []byte view and performs no allocation after construction.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func LeadingZeros64

func LeadingZeros64(u uint64) int

LeadingZeros64 is a convenience for codecs that need the bit-width of a value; it returns bits.LeadingZeros64(u). Exposed so codecs don't import math/bits directly.

Types

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader is an MSB-first bit reader over a []byte view. It performs no allocation after construction. The design (an 8-byte refill buffer with a valid-bit count) is modeled on the Prometheus/dgryski bstream; [loadNextBuffer] refills 8 bytes at a time so most reads are a shift-and-mask against [Reader.buffer] with no bounds check.

func NewReader

func NewReader(b []byte) *Reader

NewReader returns a Reader over b. The Reader aliases b; callers must not mutate b until the Reader is done. A zero-length b yields a Reader that returns io.EOF from every read.

func (*Reader) AlignToByte

func (r *Reader) AlignToByte()

AlignToByte discards the buffered bits up to the next byte boundary (so the next read begins at a byte boundary in the refill buffer).

func (*Reader) Buffered added in v0.10.0

func (r *Reader) Buffered() uint8

Buffered returns the number of bits currently available without a stream refill. A decoder of a short variable-length prefix takes a fast Peek/Skip path when Buffered() >= the prefix width and falls back to bit-at-a-time ReadBit only at the rare buffer boundary — avoiding a function call per bit on the common path.

func (*Reader) ConsumedBytes

func (r *Reader) ConsumedBytes() int

ConsumedBytes reports how many bytes of the source stream have been fully consumed (every bit read). It is the byte offset at which the next byte-aligned field or column stream begins. Callers that Writer.PadToByte at the end of encoding can use this to slice the source for the next column.

func (*Reader) Peek added in v0.10.0

func (r *Reader) Peek(nbits uint8) uint64

Peek returns the next nbits bits (right-justified) without consuming them. The caller must ensure 1 <= nbits <= Buffered() (typically by checking Buffered() first).

func (*Reader) ReadBit

func (r *Reader) ReadBit() (bool, error)

ReadBit reads one bit.

func (*Reader) ReadBits

func (r *Reader) ReadBits(nbits uint8) (uint64, error)

ReadBits reads nbits bits (nbits in [0,64]) and returns them in the low nbits of a uint64. Returns io.EOF if the stream is exhausted.

func (*Reader) ReadByte

func (r *Reader) ReadByte() (byte, error)

ReadByte reads a full byte (8 bits).

func (*Reader) ReadBytes

func (r *Reader) ReadBytes(p []byte) error

ReadBytes reads n bytes into p (which must have length n) when the reader is byte-aligned, which is the common case after [AlignToByte] or a whole-byte field. When not aligned, it falls back to per-byte [ReadByte]. This is the bulk read path for dictionary entries and row-id arrays.

func (*Reader) ReadBytesView

func (r *Reader) ReadBytesView(n int) ([]byte, error)

ReadBytesView returns the next n byte-aligned bytes as a view into the source stream. The returned slice aliases the reader input and is valid until that input is mutated or released.

func (*Reader) ReadUvarint

func (r *Reader) ReadUvarint() (uint64, error)

ReadUvarint reads an unsigned varint (7 bits per byte, continuation in the high bit), matching Writer.WriteUvarint and encoding/binary Uvarint. Direct method calls (not io.ByteReader) avoid the receiver escaping to the heap.

func (*Reader) ReadVarint

func (r *Reader) ReadVarint() (int64, error)

ReadVarint reads a zig-zag varint, matching Writer.WriteVarint and encoding/binary Varint.

func (*Reader) Remaining

func (r *Reader) Remaining() int

Remaining reports the number of bytes still unread, including the partial byte held in the refill buffer. It is approximate (rounds the buffered bits up to a byte).

func (*Reader) Reset

func (r *Reader) Reset(b []byte)

Reset re-binds r onto b, discarding prior state. Cheaper than allocating a new Reader; use with a pooled *Reader.

func (*Reader) Skip added in v0.10.0

func (r *Reader) Skip(n uint8)

Skip consumes n bits previously observed via Peek. The caller must ensure n <= Buffered().

func (*Reader) SkipBits

func (r *Reader) SkipBits(nbits uint8) error

SkipBits discards nbits bits. nbits must be in [0,64].

type Writer

type Writer struct {
	// contains filtered or unexported fields
}

Writer is an MSB-first bit writer that appends to a caller-owned []byte. It is the foundation of every encoder in encoding/chunk (DESIGN.md §10, §14 M0).

Zero-alloc discipline: Writer.WriteBits and friends only grow the backing slice via append; there is no per-write allocation. [Reset] re-binds a Writer onto a pooled buffer so callers can reuse a *Writer from a sync.Pool:

w := pool.Get().(*Writer)
w.Reset(buf[:0])
defer pool.Put(w)

The bit order is MSB-first: the first bit written occupies the high bit of the first byte. Partial trailing bits are flushed into the last byte, high-justified. [Bytes] returns the consumed prefix; the Writer is then ready for more writes (remaining low bits of the last byte are preserved across [Bytes]).

func NewWriter

func NewWriter(buf []byte) *Writer

NewWriter binds w onto buf for writing. buf is taken as the initial backing slice (its existing content is preserved and writing continues after it). Pass nil or a zero-length slice to start empty. The capacity of buf determines the first growth.

func (*Writer) AppendBytes

func (w *Writer) AppendBytes(n int) []byte

AppendBytes reserves n byte-aligned bytes in the stream and returns the writable tail. The returned slice aliases the writer buffer until the next write.

func (*Writer) AppendString

func (w *Writer) AppendString(s string)

AppendString appends a string's bytes directly when byte-aligned, with no copy (append([]byte, string...) is a compiler intrinsic). Falls back to per-byte [WriteByte] when not aligned.

func (*Writer) AppendTo

func (w *Writer) AppendTo(dst []byte) []byte

AppendTo returns the written bytes as a newly-owned copy (for when the caller needs a stable slice independent of the backing buffer). Allocates.

func (*Writer) Bytes

func (w *Writer) Bytes() []byte

Bytes returns the bytes consumed so far, including the partial last byte. It is safe to call mid-stream; subsequent writes continue into the unused low bits of the last returned byte. The returned slice aliases the backing buffer.

func (*Writer) Len

func (w *Writer) Len() int

Len returns the number of bytes consumed so far (including a partial trailing byte).

func (*Writer) PadToByte

func (w *Writer) PadToByte()

PadToByte pads the stream with zero bits up to the next byte boundary. After this, Writer.Len increases by at most one and [Bytes] is byte-aligned.

func (*Writer) Reset

func (w *Writer) Reset(buf []byte)

Reset re-binds w onto buf, discarding any previous state. The bit position resets to the start of buf (any existing bytes in buf are kept as-is and writing continues after them only if [Reset] is called with a non-empty buf — but count is always reset to 0, so the first new write begins a fresh byte after the existing content). Use this to reuse a *Writer from a sync.Pool onto a pooled buffer.

func (*Writer) WriteBit

func (w *Writer) WriteBit(bit bool)

WriteBit appends a single bit (MSB-first).

func (*Writer) WriteBits

func (w *Writer) WriteBits(u uint64, nbits int)

WriteBits appends the nbits least-significant bits of u, MSB-first within the field. nbits must be in [0, 64]; WriteBits(0, 0) is a no-op.

This is the fast inline path (modeled on Prometheus writeBitsFast): it fills the partial last byte first, then writes whole bytes directly, then any trailing partial byte — avoiding per-byte call overhead.

func (*Writer) WriteByte

func (w *Writer) WriteByte(b byte) error

WriteByte appends a full byte. It conforms to io.ByteWriter (always returns a nil error) so a *Writer can be passed to helpers that write byte-by-byte; the append-style bit methods ([WriteBits], [WriteBit]) do not return an error because they cannot fail.

func (*Writer) WriteBytes

func (w *Writer) WriteBytes(p []byte)

WriteBytes appends a slice of bytes. When the writer is byte-aligned (the common case after [PadToByte] or a whole-byte field), this is a single append — the fast path for bulk byte writes like dictionary entries and row-id arrays. When not aligned, it falls back to per-byte [WriteByte].

Go's `append([]byte, string...)` is a compiler special case that avoids a copy, so callers may pass a string directly: w.WriteBytes([]byte(s)) is still a copy, but w.stream = append(w.stream, s...) is not. For the zero-copy string path, use Writer.AppendString instead.

func (*Writer) WriteUvarint

func (w *Writer) WriteUvarint(u uint64)

WriteUvarint appends u as an unsigned varint (7 bits per byte, continuation bit in the high bit), matching encoding/binary Uvarint. Max 10 bytes.

func (*Writer) WriteVarint

func (w *Writer) WriteVarint(i int64)

WriteVarint appends i as a zig-zag varint, matching encoding/binary Varint.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL