Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ private record Row(String decoderClass, String encoderClass, boolean decode, boo
}

private static final Pattern TABLE_ROW = Pattern.compile(
"^\\|\\s*`([a-z][a-z0-9._]*)`\\s*\\|\\s*`?([A-Za-z0-9]*)`?\\s*\\|\\s*`?([A-Za-z0-9]*)`?\\s*"
+ "\\|\\s*([✅❌])\\s*\\|\\s*([✅❌])\\s*\\|",
"^\\|\\s*+`([a-z][a-z0-9._]*)`\\s*+\\|\\s*+`?([A-Za-z0-9]*+)`?\\s*+\\|\\s*+`?([A-Za-z0-9]*+)`?\\s*+"
+ "\\|\\s*+([✅❌])\\s*+\\|\\s*+([✅❌])\\s*+\\|",
Pattern.MULTILINE);

private static Map<String, Row> documented;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ public CascadeStep encodeCascade(DType dtype, Object data, EncodeContext ctx) {
/// the cascade into Dict+FoR+BitPacked instead of a clean ALP→BitPacked chain.
private static int[] findExponentsF64(double[] values) {
int n = values.length;
if (n == 0) {
return new int[]{0, 0};
}
int sampleLen = Math.min(SAMPLE_SIZE, n);
double[] sample = new double[sampleLen];
long stride = Math.max(1, (long) n / sampleLen);
Expand Down Expand Up @@ -256,6 +259,9 @@ private static CascadeStep encodeCascadeF64(double[] values, EncodeContext ctx)
/// Size-based exponent search for F32. See [#findExponentsF64(double[])] for cost model.
private static int[] findExponentsF32(float[] values) {
int n = values.length;
if (n == 0) {
return new int[]{0, 0};
}
int sampleLen = Math.min(SAMPLE_SIZE, n);
float[] sample = new float[sampleLen];
long stride = Math.max(1, (long) n / sampleLen);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/// Write-only encoder for `vortex.fsst`.
///
Expand Down Expand Up @@ -123,6 +125,47 @@ private record Fsst(
MemorySegment symBuf, MemorySegment symLenBuf, MemorySegment compBuf,
byte[] metaBytes, int[] uncompLens, int[] codesOffsets,
PType uncompLenPType, PType codesOffPType, int n) {

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Fsst other)) {
return false;
}
return n == other.n
&& Objects.equals(symBuf, other.symBuf)
&& Objects.equals(symLenBuf, other.symLenBuf)
&& Objects.equals(compBuf, other.compBuf)
&& Arrays.equals(metaBytes, other.metaBytes)
&& Arrays.equals(uncompLens, other.uncompLens)
&& Arrays.equals(codesOffsets, other.codesOffsets)
&& uncompLenPType == other.uncompLenPType
&& codesOffPType == other.codesOffPType;
}

@Override
public int hashCode() {
int result = Objects.hash(symBuf, symLenBuf, compBuf, uncompLenPType, codesOffPType, n);
result = 31 * result + Arrays.hashCode(metaBytes);
result = 31 * result + Arrays.hashCode(uncompLens);
result = 31 * result + Arrays.hashCode(codesOffsets);
return result;
}

@Override
public String toString() {
return "Fsst[symBuf=" + symBuf
+ ", symLenBuf=" + symLenBuf
+ ", compBuf=" + compBuf
+ ", metaBytes=" + Arrays.toString(metaBytes)
+ ", uncompLens=" + Arrays.toString(uncompLens)
+ ", codesOffsets=" + Arrays.toString(codesOffsets)
+ ", uncompLenPType=" + uncompLenPType
+ ", codesOffPType=" + codesOffPType
+ ", n=" + n + "]";
}
}

private static Fsst compress(String[] strings, Arena arena) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,49 @@ void encode_f64_metadata_expE_isNonZero() throws Exception {

assertThat(meta.exp_e()).isGreaterThan(0);
}

@Test
void encode_f64_empty_doesNotThrow() {
// Given — an all-zero-bit-pattern chunk (e.g. an all-null column) reaching this
// encoder via SparseEncodingEncoder's cascade can strip every value, leaving n=0
// (regression: division by zero in findExponentsF64's sample stride)
double[] values = {};

// When
EncodeResult result = ENCODER.encode(DTypes.F64, values, EncodeTestHelper.testCtx());

// Then
assertThat(result.statsMin()).isNull();
assertThat(result.statsMax()).isNull();
}

@Test
void encode_f32_empty_doesNotThrow() {
// Given
float[] values = {};

// When
EncodeResult result = ENCODER.encode(DTypes.F32, values, EncodeTestHelper.testCtx());

// Then
assertThat(result.statsMin()).isNull();
assertThat(result.statsMax()).isNull();
}

@Test
void encodeCascade_f64_empty_doesNotThrow() {
// Given — the actual crash path: CascadingCompressor calling encodeCascade directly
// with a zero-length child array
double[] values = {};

// When
CascadeStep result = ENCODER.encodeCascade(DTypes.F64, values, EncodeTestHelper.testCtx());

// Then
assertThat(result.applicable()).isTrue();
assertThat(result.statsMin()).isNull();
assertThat(result.statsMax()).isNull();
}
}

/// Property-based round-trip. ALP is a **lossless** codec: every value either fits the
Expand Down