Skip to content

Commit 32a35e0

Browse files
dfa1claude
andcommitted
refactor(reader): replace ArraySegments.trySegment with Array.segmentIfPresent()
The deprecated ArraySegments class held a single static probe (trySegment) behind the dict zip-bomb guard. Replace it with an instance method on the Array interface: default Optional<MemorySegment> segmentIfPresent() // empty by default overridden by the segment-backed types to return their existing buffer, and by MaskedArray to delegate to its inner data. ScanIterator calls codes.segmentIfPresent(). Because each override now reads its own field directly, the package-private accessors that existed only to feed the central switch are gone: Materialized*.buffer() (8) and GenericArray.buffer(int). bytesSegment() (5 decoder callers) and the LazyDecimalArray buf record component stay; their overrides reuse them. ArraySegments is deleted. Naming: segmentIfPresent() reads as the non-allocating counterpart to materialize(arena) — returns the buffer only when one already exists, empty otherwise (no allocate/decode). core 223, reader 638 green; full reactor compiles; javadoc + checkstyle clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0ff4399 commit 32a35e0

17 files changed

Lines changed: 179 additions & 105 deletions

reader/src/main/java/io/github/dfa1/vortex/reader/ScanIterator.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import io.github.dfa1.vortex.core.VortexException;
66
import io.github.dfa1.vortex.encoding.EncodingId;
77
import io.github.dfa1.vortex.reader.array.Array;
8-
import io.github.dfa1.vortex.reader.array.ArraySegments;
98
import io.github.dfa1.vortex.reader.array.BoolArray;
109
import io.github.dfa1.vortex.reader.array.ByteArray;
1110
import io.github.dfa1.vortex.reader.array.ChunkedBoolArray;
@@ -641,11 +640,8 @@ private Array decodeDictLayout(Layout dictLayout, DType dtype, SegmentAllocator
641640
/// @param codes the decoded codes array
642641
/// @param codesPType code ptype reported by the dict layout metadata
643642
/// @param n claimed dict row count
644-
// ArraySegments is deprecated-for-removal; this guard is its only caller and moves to
645-
// the decode-limits layer with it.
646-
@SuppressWarnings("removal")
647643
private static void validateDictCodesCapacity(Array codes, PType codesPType, long n) {
648-
Optional<MemorySegment> maybeSeg = ArraySegments.trySegment(codes);
644+
Optional<MemorySegment> maybeSeg = codes.segmentIfPresent();
649645
if (maybeSeg.isEmpty()) {
650646
return;
651647
}

reader/src/main/java/io/github/dfa1/vortex/reader/array/Array.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.lang.foreign.MemorySegment;
66
import java.lang.foreign.SegmentAllocator;
7+
import java.util.Optional;
78

89
/// Decoded columnar data. Concrete subtypes specialise element access for the JIT;
910
/// each covers a specific dtype family.
@@ -77,4 +78,24 @@ static Array limited(Array arr, long rows) {
7778
}
7879
return arr.length() <= rows ? arr : arr.limited(rows);
7980
}
81+
82+
/// Returns this array's primary backing segment if it is already segment-backed,
83+
/// otherwise empty — a non-allocating probe.
84+
///
85+
/// Unlike [#materialize(java.lang.foreign.SegmentAllocator)], this never allocates or
86+
/// decodes: lazy and composite arrays return empty rather than being materialised. The
87+
/// default is empty; segment-backed types (the `Materialized*` records, `VarBinArray`,
88+
/// `GenericArray`, `LazyDecimalArray`) override to return their existing buffer, and
89+
/// [MaskedArray] delegates to its inner data. The scan layer's dictionary zip-bomb guard
90+
/// uses it to inspect a codes buffer's real size without expanding an oversized claimed
91+
/// row count.
92+
///
93+
/// **Vortex-internal.** Application code should prefer the typed accessors on concrete
94+
/// subtypes ([LongArray#getLong(long)], [IntArray#getInt(long)], …) or
95+
/// [#materialize(java.lang.foreign.SegmentAllocator)].
96+
///
97+
/// @return the primary [MemorySegment], or empty if this array has no segment backing
98+
default Optional<MemorySegment> segmentIfPresent() {
99+
return Optional.empty();
100+
}
80101
}

reader/src/main/java/io/github/dfa1/vortex/reader/array/ArraySegments.java

Lines changed: 0 additions & 58 deletions
This file was deleted.

reader/src/main/java/io/github/dfa1/vortex/reader/array/GenericArray.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.math.BigDecimal;
1010
import java.math.BigInteger;
1111
import java.nio.ByteOrder;
12+
import java.util.Optional;
1213

1314
/// Fallback [Array] for dtypes that lack a dedicated concrete subtype.
1415
///
@@ -74,10 +75,6 @@ public GenericArray limited(long rows) {
7475
return new GenericArray(dtype, rows, buffers, children);
7576
}
7677

77-
MemorySegment buffer(int i) {
78-
return buffers[i];
79-
}
80-
8178
/// Returns the primary (index 0) raw buffer directly — no copy or allocation.
8279
///
8380
/// @param arena unused; the existing buffer is returned as-is
@@ -87,6 +84,14 @@ public MemorySegment materialize(SegmentAllocator arena) {
8784
return buffers[0];
8885
}
8986

87+
/// Returns the primary (index 0) raw buffer — already materialised, no allocation.
88+
///
89+
/// @return the first backing [MemorySegment]
90+
@Override
91+
public Optional<MemorySegment> segmentIfPresent() {
92+
return Optional.of(buffers[0]);
93+
}
94+
9095
/// Decodes the decimal value at row `i` from a single-buffer layout.
9196
///
9297
/// The buffer holds one little-endian two's-complement integer per row. Element

reader/src/main/java/io/github/dfa1/vortex/reader/array/LazyDecimalArray.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.math.BigDecimal;
1010
import java.math.BigInteger;
1111
import java.nio.ByteOrder;
12+
import java.util.Optional;
1213

1314
/// Lazy `vortex.decimal` array.
1415
///
@@ -82,4 +83,12 @@ public Array limited(long rows) {
8283
public MemorySegment materialize(SegmentAllocator arena) {
8384
return buf;
8485
}
86+
87+
/// Returns the backing buffer directly — already materialised, no allocation.
88+
///
89+
/// @return the backing little-endian two's-complement segment
90+
@Override
91+
public Optional<MemorySegment> segmentIfPresent() {
92+
return Optional.of(buf);
93+
}
8594
}

reader/src/main/java/io/github/dfa1/vortex/reader/array/MaskedArray.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.lang.foreign.MemorySegment;
66
import java.lang.foreign.SegmentAllocator;
7+
import java.util.Optional;
78

89
/// Decoded `vortex.masked` array: a non-nullable child paired with an optional validity bitmap.
910
///
@@ -66,14 +67,21 @@ public Array limited(long rows) {
6667
}
6768

6869
/// Materialises the inner (data) payload, ignoring the validity mask — the
69-
/// segment returned is the data buffer only. This matches the prior
70-
/// `ArraySegments` behaviour of unwrapping a masked array to its inner data;
71-
/// callers that need validity must read [#validity()] separately.
70+
/// segment returned is the data buffer only. Unwraps to the inner array's own
71+
/// materialisation; callers that need validity must read [#validity()] separately.
7272
///
7373
/// @param arena allocator used to materialise lazy inner variants
7474
/// @return the inner payload's primary [MemorySegment]
7575
@Override
7676
public MemorySegment materialize(SegmentAllocator arena) {
7777
return child.materialize(arena);
7878
}
79+
80+
/// Probes the inner (data) payload's backing segment, ignoring the validity mask.
81+
///
82+
/// @return the inner array's segment if segment-backed, otherwise empty
83+
@Override
84+
public Optional<MemorySegment> segmentIfPresent() {
85+
return child.segmentIfPresent();
86+
}
7987
}

reader/src/main/java/io/github/dfa1/vortex/reader/array/MaterializedBoolArray.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.lang.foreign.MemorySegment;
77
import java.lang.foreign.SegmentAllocator;
88
import java.lang.foreign.ValueLayout;
9+
import java.util.Optional;
910

1011
/// Buffer-backed [BoolArray] — the fallback used when an encoding decoder
1112
/// either materialises the output eagerly or has no lazy variant of its own.
@@ -36,10 +37,6 @@ public long length() {
3637
return length;
3738
}
3839

39-
MemorySegment buffer() {
40-
return buffer;
41-
}
42-
4340
/// Returns the backing buffer directly — already an LSB-first packed bitmap,
4441
/// matching the format produced by [BoolArray#materialize(SegmentAllocator)],
4542
/// so no copy or allocation is needed.
@@ -51,6 +48,11 @@ public MemorySegment materialize(SegmentAllocator arena) {
5148
return buffer;
5249
}
5350

51+
@Override
52+
public Optional<MemorySegment> segmentIfPresent() {
53+
return Optional.of(buffer);
54+
}
55+
5456
@Override
5557
public boolean getBoolean(long i) {
5658
byte b = buffer.get(ValueLayout.JAVA_BYTE, i >>> 3);

reader/src/main/java/io/github/dfa1/vortex/reader/array/MaterializedByteArray.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.lang.foreign.MemorySegment;
88
import java.lang.foreign.SegmentAllocator;
99
import java.lang.foreign.ValueLayout;
10+
import java.util.Optional;
1011
import java.util.function.LongBinaryOperator;
1112

1213
/// Buffer-backed [ByteArray] — the fallback used when an encoding decoder
@@ -40,10 +41,6 @@ public long length() {
4041
return length;
4142
}
4243

43-
MemorySegment buffer() {
44-
return buffer;
45-
}
46-
4744
/// Returns the backing buffer directly — already a contiguous one-byte-per-element
4845
/// segment, so no copy or allocation is needed.
4946
///
@@ -54,6 +51,11 @@ public MemorySegment materialize(SegmentAllocator arena) {
5451
return buffer;
5552
}
5653

54+
@Override
55+
public Optional<MemorySegment> segmentIfPresent() {
56+
return Optional.of(buffer);
57+
}
58+
5759
@Override
5860
public byte getByte(long i) {
5961
return buffer.get(ValueLayout.JAVA_BYTE, length == elementCount ? i : i % elementCount);

reader/src/main/java/io/github/dfa1/vortex/reader/array/MaterializedDoubleArray.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.lang.foreign.MemorySegment;
77
import java.lang.foreign.SegmentAllocator;
8+
import java.util.Optional;
89
import java.util.function.DoubleBinaryOperator;
910
import java.util.function.DoubleConsumer;
1011

@@ -39,10 +40,6 @@ public long length() {
3940
return length;
4041
}
4142

42-
MemorySegment buffer() {
43-
return buffer;
44-
}
45-
4643
/// Returns the backing buffer directly — already a contiguous little-endian
4744
/// `f64` segment, so no copy or allocation is needed.
4845
///
@@ -53,6 +50,11 @@ public MemorySegment materialize(SegmentAllocator arena) {
5350
return buffer;
5451
}
5552

53+
@Override
54+
public Optional<MemorySegment> segmentIfPresent() {
55+
return Optional.of(buffer);
56+
}
57+
5658
@Override
5759
public double getDouble(long i) {
5860
return buffer.getAtIndex(PTypeIO.LE_DOUBLE, length == elementCount ? i : i % elementCount);

reader/src/main/java/io/github/dfa1/vortex/reader/array/MaterializedFloat16Array.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import java.lang.foreign.MemorySegment;
88
import java.lang.foreign.SegmentAllocator;
9+
import java.util.Optional;
910

1011
/// Buffer-backed [Float16Array] — the fallback used when an encoding decoder
1112
/// either materialises the output eagerly or has no lazy variant of its own.
@@ -36,10 +37,6 @@ public long length() {
3637
return length;
3738
}
3839

39-
MemorySegment buffer() {
40-
return buffer;
41-
}
42-
4340
/// Returns the backing buffer directly — already a contiguous little-endian
4441
/// half-precision segment (2 bytes per element), so no copy or allocation is needed.
4542
///
@@ -50,6 +47,11 @@ public MemorySegment materialize(SegmentAllocator arena) {
5047
return buffer;
5148
}
5249

50+
@Override
51+
public Optional<MemorySegment> segmentIfPresent() {
52+
return Optional.of(buffer);
53+
}
54+
5355
@Override
5456
public float getFloat(long i) {
5557
return Float.float16ToFloat(buffer.getAtIndex(PTypeIO.LE_SHORT, i));

0 commit comments

Comments
 (0)