v0.8.0
Read and write Vortex Variant (semi-structured, JSON-shaped) columns from Java. Internally, transform encodings now decode lazily, trimming per-decode allocation. This release also hardens the reader's bounds handling on untrusted input (ADR 0003 Phase E), fixes CSV-import memory blow-ups on large files, and lifts test coverage to 80% with all Sonar ratings at A.
Added
- Writer:
vortex.variantencoder. Encodes a variant column as the canonicalvortex.variantcontainer overcore_storage— an all-equal column becomes a singlevortex.constant, a row-varying column avortex.chunkedof per-run constants — with an optional row-aligned typedshreddedchild recorded inVariantMetadata.shredded_dtype. Input isVariantData(List<Scalar>)with.constant(n, v)/.shredded(...)factories. Java↔Rust (JNI) round-trip verified for constant, row-varying, and shredded columns. Scalar values only — arbitrary nested objects needvortex.parquet.variant(deferred, ADR 0014). (35da529d, e4e44980, 4566dca0) - Reader: variant columns now decode Java-side.
ConstantEncodingDecoderandChunkedEncodingDecoderhandleDType.Variant(materialising the inner-typed array);VariantEncodingDecoderwraps the result asVariantArray, exposingcoreStorage()andshredded(). (76e4c741, 4566dca0)
Security
- Reader bounds hardening (ADR 0003 Phase E): untrusted offsets/lengths from file metadata now flow through a typed
IoBoundshelper that throwsVortexExceptioninstead of a rawIndexOutOfBoundsException, and hand-rolled index guards were replaced withObjects.checkIndex. A crafted flat-segment file can no longer trip an unchecked array access during decode. (e9af80d6, 3bcd9881, a5ce8380)
Fixed
- CSV import: large files no longer OOM. The importer now streams rows in a single pass (buffering only the first chunk for schema inference) and disables the global-dictionary pass by default, which previously accumulated every distinct value in memory. (d5280ae2, 0b6784b5, 62863616)
- CLI:
IoWorker.runAndAwaitdecremented its in-flight counter after signaling completion, so a caller readingpending()right after it returned could still see the task counted; the counter is now decremented before the await returns. Theview/tuicommands also close the openedVortexHandleon every error path (openOnWorkerreturnsOptional). (95c06b1a, 27446d81) - Reader:
BoolArray.materializemasked the accumulator byte before the bit-set OR, removing a sign-promotion footgun in the packed-bitmap write. (bc8e9d4e)
Changed
- Decode shape: transform encodings now decode lazy-only. The eager
Materialized*Arrayfallbacks were removed fromvortex.zigzag(all PTypes + broadcast, cd59fefa),fastlanes.for(all integer PTypes, d7953e1f),vortex.alp(broadcast-without-patches, deab8067),vortex.constant(Decimal →LazyConstantDecimalArray, a6a9611e),vortex.runend(Bool →LazyRunEndBoolArray, 0bbcb81f),vortex.sparse(Bool →LazySparseBoolArray, db2e955b), andfastlanes.rle(validity →OffsetBoolArray, empty →LazyConstantXxxArray, 5e83a5c3). Decompression encodings (bitpacked,pco,zstd,fsst,delta,patched), the primitive base, thevortex.dictencoding-level path, and thevortex.alppatches path stay Materialized by design. See ADR 0015. - Breaking — sealed
Arraypermits changed.DecimalArrayis now anon-sealedfamily interface (decimal arrays moved fromimplements Arraytoimplements DecimalArray), so decimal joins the per-dtype family layer. Downstream exhaustiveswitchoverArraymust add acase DecimalArray. (a6a9611e) - Breaking —
ArrayAPI.Array.truncate(rows)renamed toArray.limited(rows)and made an abstract operation implemented by every array (composites slice their children); raw-segment access moved off theArraySegmentsutility ontoArray.materialize(SegmentAllocator)andArray.segmentIfPresent(). (87ab65e2, 4d9ac1f8, 332b067e, 32a35e03) - CSV import reports progress every 10K rows instead of per-chunk. (07a056e7)
Removed
- Breaking —
EmptyArrayremoved from the sealedArraypermits. It was never emitted by the reader (empties are zero-length typed arrays in their own family) and broke the dtype→family invariant (EmptyArray(I64)was not aLongArray). Represent an empty column as a zero-length array of the appropriate family. (3a4dcdfa)
Documentation
- ADR 0016: captures
vortex-arrowbridge interop options (separate module / Arrow C-Data / none); deferred until a concrete downstream need. (a6126f29)
Tests
- Test coverage raised from ~74% to 80% — the lazy/chunked/dict/run-end/sparse array families,
ChunkImpl, and several decoders (DecimalEncodingDecoder,DictEncodingDecoder,ParquetImporter) reached full line + branch coverage. SonarCloud quality gate green: reliability, security, and maintainability all at A, zero bugs and vulnerabilities.