Add sparse & compressed search index blocks#41
Merged
Conversation
A stream can contain search indexes only. This means that blocks are referenced into another stream. Adds chunk type 0x46: Compressed Block Search Table Allows storing search indexes with entropy compression. This will allow higher precision tables to be stored without loosing precision - other than the size increase from fewer collisions. We allow for flexibility in the encoder while always keeping decoding fast. `huff0` is about 500MB/s encoding and 2000MB/s decoding per thread and doesn't rely on any special CPU to be reasonably fast. Adds chunk type 0x47: Remote Block Reference
There was a problem hiding this comment.
Pull request overview
This PR extends the MinLZ block search specification to support (1) “index-only” streams that reference blocks in another stream, and (2) entropy-compressed per-block search tables to reduce index size while keeping decoding fast.
Changes:
- Document “Search Index only Streams” that replace block payloads with Remote Block Reference chunks (0x47).
- Add a new “Compressed Block Search Table” chunk (0x46) that stores search-table bits compressed with huff0 and includes a checksum.
- Specify the Remote Block Reference (0x47) payload format, including support for multiple referenced blocks via relative offsets.
Comments suppressed due to low confidence (1)
SPEC_SEARCH.md:152
- This sentence appears to have a grammatical error: "This block replaces a block that and indicates the offset of the block in the stream." Please fix the wording (e.g., remove the stray "that") and clarify what stream the offset is relative to (the current index-only stream vs the referenced data stream).
This block replaces a block that and indicates the offset of the block in the stream.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
00bddcd to
49decfd
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 18 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (2)
writer.go:1226
- appendSearchTableEitherChunk discards the error from appendSearchTableCompressedChunk. If the 0x46 encoder fails unexpectedly, this will silently emit a 0x45 chunk and lose the error. Consider handling the error (e.g., set writer error state via w.err(err) and return the 0x45 chunk only when err == nil && !ok).
func (w *Writer) appendSearchTableEitherChunk(dst []byte, reductions uint8, table []byte) []byte {
if w.searchCfg.compression != nil && w.searchCfg.compression.enabled {
e := cstEncoderPool.Get().(*cstEncoder)
out, ok, _ := appendSearchTableCompressedChunk(dst, w.searchCfg, reductions, table, e)
cstEncoderPool.Put(e)
search_compressed.go:157
- CompressedSearchSkipPct's doc comment says the default is 5.0, but WithCompression() initializes skipPctTimes100 from cstDefaultSkipPctTimes100 (currently 1000 = 10.0%), and the CLI default flag is also 10.0. Please align the documentation with the actual default behavior.
// CompressedSearchSkipPct sets the popcount-band half-width (in percent) below
// which compression is skipped. The default is 5.0.
func CompressedSearchSkipPct(pct float64) CompressedSearchOption {
harshavardhana
approved these changes
May 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A stream can contain search indexes only. This means that blocks are referenced into another stream.
Adds chunk type 0x46: Compressed Block Search Table
Allows storing search indexes with entropy compression.
This will allow higher precision tables to be stored without loosing precision - other than the size increase from fewer collisions.
We allow for flexibility in the encoder while always keeping decoding fast.
huff0is about 500MB/s encoding and 2000MB/s decoding per thread and doesn't rely on any special CPU to be reasonably fast.Adds chunk type "0x47: Remote Block Reference" to spec.
New Blocks
2.2 Compressed Block Search Table (chunk type 0x46, skippable)
The Compressed Block Search Table is an optional chunk that will come before a block that represents the contents.
See Section 2.1 on how to decode table information until the huff0 section.
The index bits are split into huff0 blocks.
The first value (h0_bs) is the log2 of the uncompressed huff0 block size in bytes.
The bitmap size (n) must be divisible by the huff0 block size.
The maximum huff0 block size is 128KiB (h0_bs = 17).
The minimum size is 32 bytes (h0_bs = 5).
Tables for huff0 blocks are stored separately. There can be up to 16 tables per block.
The encoder is allowed to reuse tables for different huff0 blocks.
The encoded huff0 tables follow. This is similar to zstd, rfc 8878.
Table sizes are self-contained, but h0_tc must be read.
The number of blocks can be calculated as
n / (1 << h0_bs).For each block the table index is specified by the h0_ti value and decoded as follows:
If h0_ti is <= 15, the compressed size follows as a uvarint - and the compressed data itself.
Note the compressed streams are always 4 streams interleaved,
also named 4X in zstd.
An uncompressed block (h0_ti = 16) and RLE (h0_ti = 17) will not have any size,
since that can be inferred.
RLE in this context means "single value repeated for the entire block" and can only be used for that.
2.2.1 Sparse Bit Table
A sparse bit table can be used for very sparsely populated blocks.
Like huff0 block, this starts with a uvarint encoded length in bytes.
The block contains byte-encoded distances between bits.
To decode, read single bytes. Each byte is the distance to the next set bit.
Bits are counted from the least significant bit.
If the byte is 255, add 255 to the distance and read the following byte.
When the final byte has been read, there are no more bits.
The distance is not expected to reach the end of the block.
2.3 Remote Block Reference (chunk type 0x47, skippable)
When generating search indexes from an existing stream or you, for
another reason, want to separate the search index from the data,
you can use the Remote Block Reference chunk type to indicate a remote block.
This block replaces a block that and indicates the offset of the block in the stream.
If more values are present, it indicates additional blocks without indexes between.
These are stored as relative offsets from the current block.
The block offsets must be in strictly ascending order.