Expand description
OnPair: dictionary-based short-string compression for fast random access.
Rust port of the algorithm described in arXiv:2508.02280. OnPair replaces recurring substrings (“tokens”) with fixed-width integer codes into a dictionary; decoding is a gather-copy, so individual rows decode in isolation at no extra cost.
§Layout
A compressed Column is a CompactDictionary (token bytes + offsets), a
code stream (one Token per emitted token), and a row layer (offsets into
the code stream). Borrow it as a ColumnView — or build a view directly
from buffers deserialized from storage — and decode into a caller-owned buffer:
the whole column with ColumnView::decompress_into, one row with
ColumnView::decompress_row_into, or decode_into over a reusable
WideDictionary. The caller owns buffer sizing: size it from
ColumnView::decoded_len or ColumnView::row_decoded_len (plus
DECODE_PADDING).
§Examples
use onpair::{Column, DECODE_PADDING, DEFAULT_CONFIG};
use std::mem::MaybeUninit;
// Compress an Arrow (bytes, offsets) value pair.
let bytes = b"catdogcat";
let offsets: [u32; 4] = [0, 3, 6, 9];
let col = Column::compress(bytes, &offsets, DEFAULT_CONFIG).unwrap();
let view = col.view();
// Random-access a single row into a caller buffer — no full decode, no
// wide-table build. Size it from the row's decoded length plus DECODE_PADDING.
let mut row = vec![MaybeUninit::uninit(); view.row_decoded_len(1) + DECODE_PADDING];
// SAFETY: `col` is valid by construction and `row` is sized as required.
let n = unsafe { view.decompress_row_into(1, &mut row) };
assert_eq!(unsafe { std::slice::from_raw_parts(row.as_ptr().cast::<u8>(), n) }, b"dog");
// Bulk-decode the whole column into a caller buffer, sized from the decoded length.
let mut buf = vec![MaybeUninit::uninit(); view.decoded_len() + DECODE_PADDING];
// SAFETY: `col` is freshly compressed (valid by construction) and `buf` is sized.
let n = unsafe { view.decompress_into(&mut buf) };
let out = unsafe { std::slice::from_raw_parts(buf.as_ptr().cast::<u8>(), n) };
assert_eq!(out, b"catdogcat");The trained encoder is also available directly via Parser, to reuse one
dictionary across several corpora.
Modules§
- search
- Compressed-domain search: equality, prefix, and substring queries answered directly over the code stream, without decoding rows back to bytes.
Structs§
- Column
- Owned compressed column, produced by
Column::compress/Parser::parse. Self-contained: it carries its own dictionary, so it decodes without reference to the training corpus. - Column
View - Borrowed,
Copyview over a compressed column — obtained from aColumnor built directly from buffers deserialized from storage. - Compact
Dictionary - Owned compact dictionary — trusted: holding one is a proof that its buffers satisfy the invariants in this module’s documentation.
- Compact
Dictionary View - Borrowed,
Copy, trusted view over a compact dictionary’s buffers. - Config
- Training configuration. See
DEFAULT_CONFIGfor a reasonable starting point. - MaxDict
Bits - Training-time upper bound on the dictionary: at most
2^bitstokens. - Output
TooSmall try_decode_into’soutcould not hold the decoded bytes. Nothing was written past the end ofout; size it fromdecoded_lenand retry (the checked decode needs noDECODE_PADDINGslack — seetry_decode_into).- Parser
- A trained encoder. Holds the
CompactDictionary(cloned into eachColumnso columns are self-contained) and a crate-private, encode-side longest-prefix matcher built from it. - Threshold
- Dynamic-threshold sample fraction. Validated to
(0.0, 1.0]at construction, so aThresholdalways holds an in-range value. - Token
Range - Inclusive token-id range
[begin, last]over the sorted dictionary. - Wide
Dictionary - Owned wide dictionary:
num_tokensrows ofMAX_TOKEN_SIZEbytes plus per-token lengths. - Wide
Dictionary View - Borrowed,
Copy, trusted view over a wide dictionary’s buffers.
Enums§
- Error
- Error returned by the public training and encoding API.
- Invalid
Column - A violation found while validating compressed buffers.
Constants§
- DECODE_
PADDING - Trailing bytes a
decode_intooutput buffer needs beyond the decoded length. The decoder over-stores a fixedMAX_TOKEN_SIZE-byte chunk for the final token — up toMAX_TOKEN_SIZE - 1bytes past the logical end — so an output buffer is sized asdecoded_len + DECODE_PADDING. - DEFAULT_
CONFIG - Reasonable starting point: dictionary capped at 4 096 tokens, dynamic threshold sampling 15 %, and deterministic sampling with seed 42.
- MAX_
TOKEN_ SIZE - Maximum byte length of any dictionary token, and the fixed width the decoder reads per token.
Traits§
- Dictionary
- An owned dictionary that can lend its borrowed
DictionaryView. - Dictionary
View - A borrowed trusted dictionary’s token-read interface, abstracted over the
layout (
CompactDictionaryVieworWideDictionaryView). - Offset
- Width of a row offset. Sealed to
u32andu64to match the Arrow binary and large-binary layouts.
Functions§
- code_
bits_ for_ num_ tokens - Minimum code width needed to address
num_tokensdistinct tokens:ceil(log2(num_tokens)). - compress
- Compress an Arrow
(bytes, offsets)value pair end-to-end. Equivalent toParser::train(..)?.parse(..), but validates the offsets once instead of in both the train and parse steps.offsetshasn + 1entries. - decode_
into ⚠ - Decode
codesagainst an already-validateddictintoout, returning the bytes written. It over-reads a fixed 16 bytes per token, trusting the dictionary’s offsets/lengths (validated up front). Each code is bounds-checked in the loop (a near-free, predicted-not-taken branch); an out-of-range code panics withInvalidColumn::CodeOutOfRange. - decoded_
len - Exact decoded byte length of
codesagainstdict(the sum of token lengths) — sizes a decode buffer. - try_
decode_ into - Decode
codesagainst an already-validateddictintoout— the safe, buffer-checked counterpart ofdecode_into. ReturnsOk(bytes_written), orErr(OutputTooSmall)ifoutcannot hold the result (in which case nothing is written past the end ofout). Never reads or writes out of bounds.
Type Aliases§
- Token
- Identifies a dictionary token: a token id, equivalently a code in a
column’s code stream. Named
Tokenfor simplicity.