Skip to content

Add bounded direct QWTB and HQWB builders - #39

Open
eljeffeg wants to merge 15 commits into
rossanoventurini:mainfrom
eljeffeg:codex/direct-qwt-builders
Open

Add bounded direct QWTB and HQWB builders#39
eljeffeg wants to merge 15 commits into
rossanoventurini:mainfrom
eljeffeg:codex/direct-qwt-builders

Conversation

@eljeffeg

@eljeffeg eljeffeg commented Aug 3, 2026

Copy link
Copy Markdown

Summary

Adds budget-aware, file-backed construction for canonical QWTB and HQWB byte layouts.

This lets callers build large QWT256 and Huffman-QWT256 payloads without first materializing a row-sized heap wavelet tree. Input is a replayable little-endian u32 sequence; construction emits one level at a time and stable-partitions through bounded scratch files.

What this adds

  • write_qwt256_u32_direct for canonical QWTB output
  • write_hqwt256_u32_direct for canonical HQWB output
  • explicit buffer and scratch-disk budgets with preflight enforcement
  • conservative final-output upper-bound helpers for outer container budgeting
  • build statistics covering rows, levels, output bytes, and peak scratch bytes
  • per-level data, rank support, select samples, directories, and alignment owned by QWT
  • sync-before-install and automatic success/failure scratch cleanup
  • deterministic Huffman code generation shared by heap and direct construction
  • exact-byte parity and access/rank/select differential tests across empty, single-symbol, skewed, multi-level, and 256/2,048/8,192 boundaries

The API is intended for storage engines that need bounded external construction while preserving QWT's canonical byte format and mmap compatibility.

Dependency

This is a stacked PR and depends on #32:

#32

PR #32 introduces the first-class QWTB/HQWB byte layouts and zero-copy borrowed views that these direct writers emit. Please review and merge #32 first; this PR's effective diff against main will shrink after it lands.

Verification

  • cargo test --all-features: 255 passed
  • cargo clippy --all-targets --all-features -- -D warnings: clean
  • git diff --check: clean

eljeffeg added 15 commits July 21, 2026 14:46
Foundation for PR rossanoventurini#2 zero-copy I/O:

- src/bytes/{mod,error,util}.rs: LayoutError, LE/align/cast helpers,
  QWTB/HQWB magic constants (container encode/decode comes next)
- QVector::from_raw_parts, SuperblockPlain::from_counters,
  RSSupportPlain::from_parts, RSQVector::from_parts
- QWaveletTree::from_parts / HuffQWaveletTree::from_parts
  (reject WITH_PREFETCH_SUPPORT in v1)
- Round-trip tests: disassemble via accessors → reassemble via
  from_parts → equal get/rank/select (plain + Huffman)

No feature flags, no new dependencies. serde path unchanged.
Canonical little-endian container for plain QWaveletTree (magic QWTB):
- 32-byte header + 128-byte level directory entries
- 64-aligned DataLine / SuperblockPlain payloads
- select samples and n_occs_smaller per level
- empty-tree convention (n_levels=0, sentinel default qv)

QWaveletTree::from_parts now takes explicit n_levels so empty trees
round-trip cleanly. Owned from_bytes copies POD via copy_pod_slice so
heap Vec<u8> need not be 64-byte aligned (zero-copy views still require
alignment via cast_slice).

Tests: get/rank/select round-trip, empty tree, bad magic, truncated.
Full suite: 100 passed.
Canonical little-endian container for HuffQWaveletTree (magic HQWB):
- 32-byte header with encode_len / decode_n_buckets
- 136-byte level directory (QWTB 128 B + level_len u64)
- 8-aligned encode LUT (content:u32, len:u32) and length-prefixed
  decode buckets (content:u32 + symbol:u64 zero-extended)
- 64-aligned DataLine / SuperblockPlain payloads per level

HuffQWaveletTree::from_parts normalizes empty-tree sentinels
(n_levels=0 with default qv + lens=[0]).

Tests: get/rank/select round-trip, empty tree, bad magic, large-σ
uneven freqs. Full suite green.
Add borrowing views over QWTB/HQWB blobs that cast POD level payloads
in place (DataLine, SuperblockPlain, select samples) and implement
AccessUnsigned/RankUnsigned/SelectUnsigned matching the owned trees.

- RSQVectorView: per-level get/rank/rank_all/select over borrowed slices
- QwtView: plain QWTB open + wavelet-matrix queries
- HqwtView: HQWB open; owns small Huffman LUTs, borrows level payloads
- AlignedBytes: helper to 64-align heap blobs for cast_slice (mmap is
  naturally page-aligned)

Requires a 64-byte-aligned base buffer; rejects Misaligned otherwise.
Make DataLine.words / SuperblockPlain.counters and owned-layout
accessors (data_lines, superblocks, select_samples, n_occs_smaller,
position_bits, qvector, rs_support) pub(crate). External mmap clients
should use QwtView/HqwtView and RSQVectorView POD accessors instead.
Remove leaked references to the private PR2 design doc from public
module docs. Add checked_region for untrusted header offset/size math
on the QWTB/HQWB decode paths (and HqwtView), and make align_up
overflow-safe.
Use is_multiple_of, iterator-style fill loops, size_of_val, and add
missing # Safety docs so src/bytes is clean under clippy.
Fix remaining pre-existing clippy lints outside bytes/: empty-line-after-doc,
legacy numeric constants, needless_range_loop, len_without_is_empty, and
intentional inverted Range tests via std::ops::Range { start, end }.
Document the QWTB/HQWB owned and zero-copy APIs in README and on the
public to_bytes/from_bytes entry points. Add PR2_BODY.md as draft PR text.
Add extract_range (ascending multiset), extract_range_distinct(+_into),
and get_and_rank(+_unchecked) to both QWaveletTree and the zero-copy
QwtView. Partition uses rank_all at both endpoints; singleton ranges
short-circuit to a single remaining-path descent.

View APIs omit prefetch_info (not on RSQVectorView). Differential tests
assert view extract/get_and_rank match the owned heap tree, and heap
tests cover sort/sort+dedup equivalence to per-row get.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant