The current IVCSC/IVCSR format has no byte-level index. major_ptr indexes value-groups, not bytes — row_indptr = value_ptr[major_ptr] gets you to a group boundary, but locating cell N's bytes still requires decoding (or, via _ivcsc's self-synchronizing LEB128 realignment, scanning) through everything before it. The format is an archive today, and not a queryable store. This means random or metadata-driven cell selection can't skip straight to the bytes it needs. The parallel-decode machinery in _ivcsc.py (_realign_forward, _group_chunk_boundaries) is real engineering, but it exists specifically because there's no byte index; a real offset index would let self-contained records replace that machinery rather than need it.
A separate, related design lesson worth encoding up front: the addressing quantum and the compression quantum are not the same knob. Using one chunk size for both (both the byte-level unit callers seek by, and the unit Blosc2 compresses) costs read throughput — large chunks defeat Blosc2's internal blocking. They need to be independently tunable.
Proposed scope
- A per-major-slice (or per-superchunk) byte-offset index, stored alongside
packed_indices, cheap enough to keep in memory even at atlas scale (a few bytes per cell should be enough). - Two independent size parameters: an addressing quantum (cells per superchunk, tuned for locality under the read patterns real callers use, can be random vs. sample-clustered) and a compression quantum (kept small, tuned for decode throughput, likely close to what the current format's incidental chunking already gets right).
- A decision on whether cell ordering (clustering cells by a query key) is a format-level concern (a stored permutation/sort key) or purely a caller concern. This affects whether the format needs to store anything beyond raw byte offsets.
- Explicitly scope what "v2" means for compatibility: new files only, or a migration path for existing v1 (current-format) files.
Acceptance criteria
Implementation details
- Implement the byte-offset index and two-quantum layout from above in
_ivcsc.py/_io.py. - Create a frozen conformance fixture which can be some small, checked-in file (or generator) that every future change to the codec must keep reading correctly.
- Version negotiation:
read_h5ad/read_zarr detect v1 vs. v2 (via the existing encoding-version attribute pattern already used in _io.py) and dispatch accordingly; v1 files keep reading without requiring a re-export. Alternatively, decide that all v1 files should be converted (no backwards compatibility). - Update
write_h5ad/write_zarr's format= parameter (or add a new value) so callers can opt into v2 explicitly.
Acceptance criteria
References
src/vsparse/_ivcsc.py (_realign_forward, _group_chunk_boundaries),
src/vsparse/_io.py.
The current IVCSC/IVCSR format has no byte-level index.
major_ptrindexes value-groups, not bytes —row_indptr = value_ptr[major_ptr]gets you to a group boundary, but locating cell N's bytes still requires decoding (or, via_ivcsc's self-synchronizing LEB128 realignment, scanning) through everything before it. The format is an archive today, and not a queryable store. This means random or metadata-driven cell selection can't skip straight to the bytes it needs. The parallel-decode machinery in_ivcsc.py(_realign_forward,_group_chunk_boundaries) is real engineering, but it exists specifically because there's no byte index; a real offset index would let self-contained records replace that machinery rather than need it.A separate, related design lesson worth encoding up front: the addressing quantum and the compression quantum are not the same knob. Using one chunk size for both (both the byte-level unit callers seek by, and the unit Blosc2 compresses) costs read throughput — large chunks defeat Blosc2's internal blocking. They need to be independently tunable.
Proposed scope
packed_indices, cheap enough to keep in memory even at atlas scale (a few bytes per cell should be enough).Acceptance criteria
index layout, the two-quantum split, and the cell-ordering question.
Implementation details
_ivcsc.py/_io.py.read_h5ad/read_zarrdetect v1 vs. v2 (via the existingencoding-versionattribute pattern already used in_io.py) and dispatch accordingly; v1 files keep reading without requiring a re-export. Alternatively, decide that all v1 files should be converted (no backwards compatibility).write_h5ad/write_zarr'sformat=parameter (or add a new value) so callers can opt into v2 explicitly.Acceptance criteria
shipped format.
References
src/vsparse/_ivcsc.py(_realign_forward,_group_chunk_boundaries),src/vsparse/_io.py.