Skip to content

feat(writer): cascade run-end ends and values (issue #410) - #435

Merged
dfa1 merged 1 commit into
mainfrom
feat/410-runend-cascade
Oct 3, 2026
Merged

dfa1 merged 1 commit into
mainfrom
feat/410-runend-cascade

Conversation

@dfa1

@dfa1 dfa1 commented Oct 3, 2026

Copy link
Copy Markdown
Owner

Part of #410: the "should RunEnd/RLE/ZigZag/Delta cascade?" follow-up, starting with RunEnd.

RunEndEncodingEncoder was terminal, so its ends and values were always stored raw. It now overrides encodeCascade and hands both to the compressor as open ChildSlots, with Rust's RunEndScheme exclusions declared on each slot:

child data excluded
ends (wire 0) strictly increasing, all distinct vortex.dict, vortex.runend, fastlanes.rle, vortex.sparse
values (wire 1) adjacent values always differ vortex.runend

Same [ends, values] wire shape as the terminal encode.

Rust's ancestor rule (no RunEnd on a Dict's values pool) has nothing to attach to here: primitive Dict keeps its values as an owned buffer, and the Utf8 pool never reaches RunEnd, which accepts integers only.

Effect (FileSizeComparisonIntegrationTest, main → this PR):

main PR
FileSizeComparison (Java) 1,174,359 1,161,559 (−1.09%, Java/JNI 0.95× → 0.94×)
GlobalDictComparison 2,487,928 2,466,808 (−0.85%)
ZstdComparison (zstd) 1,090,351 1,090,351

vortex-jni reads the cascaded files: every interop suite is green (367 integration tests), plus 1,922 writer and 1,592 reader unit tests.

Dedup: adds PrimitiveArrays.fromLongsArray, the inverse of toLongs. It replaces RunEnd's private widen/narrow (widen was a copy of toLongs) and Patched's private fromLongs.

Tests: RunEndEncodingEncoderTest.Cascade (run split, per-slot exclusions, U8 carrier narrowing, empty input) and PrimitiveArraysTest (fromLongsArray inverts toLongs at every integer width's extremes).

🤖 Generated with Claude Code

RunEnd was a terminal encoding: its ends and values were stored raw, so the
upstream RunEndScheme exclusion rules had nothing to attach to. It now
overrides encodeCascade and hands both buffers to the compressor as open
child slots, with Rust's per-child exclusions:
- ends (strictly increasing, all distinct): Dict, RunEnd, RLE, Sparse barred
- values (adjacent always differ): RunEnd barred

Same [ends, values] wire shape as the terminal path. FileSizeComparison
drops 1,174,359 -> 1,161,559 bytes (-1.09%, Java/JNI 0.95x -> 0.94x) and
GlobalDictComparison 2,487,928 -> 2,466,808 (-0.85%); vortex-jni reads the
cascaded files (all interop suites green).

Also adds PrimitiveArrays.fromLongsArray, the inverse of toLongs, replacing
RunEnd's private widen/narrow and Patched's private fromLongs copies.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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