Skip to content

Commit 95b46e8

Browse files
dfa1claude
andcommitted
core: fix PType.valueLayout() for F32/F64
Use JAVA_FLOAT/DOUBLE_UNALIGNED for float ptypes instead of int/long layouts. Add PTypeIO bulk-copy helpers used by DeltaCodec and DictCodec to avoid per-element switch dispatch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 6fc84c7 commit 95b46e8

4 files changed

Lines changed: 64 additions & 36 deletions

File tree

core/src/main/java/io/github/dfa1/vortex/core/DType.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.github.dfa1.vortex.core;
22

33
import java.nio.ByteBuffer;
4-
import java.util.List;
54

65
/// Vortex logical data type. Strictly logical — defines value domain, not physical storage.
76
///

core/src/main/java/io/github/dfa1/vortex/core/PType.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.github.dfa1.vortex.core;
22

3-
import java.nio.ByteOrder;
43
import java.lang.foreign.ValueLayout;
4+
import java.nio.ByteOrder;
55

66
public enum PType {
77
U8, U16, U32, U64,
@@ -26,13 +26,16 @@ public boolean isSigned() {
2626
|| this == F16 || this == F32 || this == F64;
2727
}
2828

29+
/// Little-endian, unaligned `ValueLayout` for this ptype.
2930
public ValueLayout valueLayout() {
3031
return switch (this) {
31-
case I8, U8 -> ValueLayout.JAVA_BYTE;
32+
case I8, U8 -> ValueLayout.JAVA_BYTE;
3233
case I16, U16 -> ValueLayout.JAVA_SHORT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
33-
case I32, U32, F32 -> ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
34-
case I64, U64, F64 -> ValueLayout.JAVA_LONG_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
35-
default -> throw new UnsupportedOperationException("unsupported ptype: " + this);
34+
case I32, U32 -> ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
35+
case I64, U64 -> ValueLayout.JAVA_LONG_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
36+
case F32 -> ValueLayout.JAVA_FLOAT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
37+
case F64 -> ValueLayout.JAVA_DOUBLE_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
38+
case F16 -> throw new UnsupportedOperationException("F16 not supported"); // TODO: implement F16
3639
};
3740
}
3841
}

core/src/main/java/io/github/dfa1/vortex/encoding/DeltaCodec.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -203,20 +203,23 @@ private static long[] toLongs(Object data, PType ptype) {
203203
}
204204

205205
private static MemorySegment fromLongs(long[] longs, PType ptype) {
206-
int n = longs.length;
207-
int elemSize = ptype.byteSize();
208-
byte[] bytes = new byte[n * elemSize];
209-
ByteBuffer bb = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
210-
for (long v : longs) {
211-
switch (ptype) {
212-
case I8, U8 -> bb.put((byte) v);
213-
case I16, U16 -> bb.putShort((short) v);
214-
case I32, U32 -> bb.putInt((int) v);
215-
case I64, U64 -> bb.putLong(v);
216-
default -> throw new UnsupportedOperationException("unsupported ptype: " + ptype);
217-
}
206+
// Fast path: 64-bit target — bulk byte-order copy, no per-element narrowing.
207+
if (ptype == PType.I64 || ptype == PType.U64) {
208+
byte[] bytes = new byte[longs.length * 8];
209+
MemorySegment dst = MemorySegment.ofArray(bytes);
210+
MemorySegment.copy(MemorySegment.ofArray(longs), ValueLayout.JAVA_LONG, 0L,
211+
dst, ptype.valueLayout(), 0L, longs.length);
212+
return dst;
213+
}
214+
// Narrowing path: per-element MethodHandle setter from PTypeIO.
215+
int n = longs.length;
216+
long elemSize = ptype.byteSize();
217+
byte[] bytes = new byte[(int) (n * elemSize)];
218+
MemorySegment seg = MemorySegment.ofArray(bytes);
219+
for (int i = 0; i < n; i++) {
220+
PTypeIO.set(seg, i * elemSize, ptype, longs[i]);
218221
}
219-
return MemorySegment.ofArray(bytes);
222+
return seg;
220223
}
221224

222225
// ── Stats helpers ─────────────────────────────────────────────────────────

core/src/main/java/io/github/dfa1/vortex/encoding/DictCodec.java

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,12 @@ public EncodeResult encode(DType dtype, Object data) {
5151
PType codePType = codePType(dictSize);
5252
int codeBytes = codePType.byteSize();
5353

54-
// Values buffer: unique values in insertion order
55-
ByteBuffer valuesBuf = ByteBuffer.allocate(dictSize * ptype.byteSize())
56-
.order(ByteOrder.LITTLE_ENDIAN);
57-
for (Object v : valueMap.keySet()) {
58-
writeElement(valuesBuf, ptype, v);
59-
}
60-
valuesBuf.flip();
54+
// Values buffer: unique values in insertion order.
55+
// Materialize as typed primitive array, then bulk-copy with byte-order conversion
56+
// via `MemorySegment.copy` — avoids per-element `switch (ptype)` dispatch.
57+
Object uniqueArray = buildUniqueArray(ptype, valueMap.keySet(), dictSize);
58+
ByteBuffer valuesBuf = PTypeIO.copyArray(ptype, uniqueArray, dictSize)
59+
.asByteBuffer().order(ByteOrder.LITTLE_ENDIAN);
6160

6261
// Codes buffer: per-row index into values
6362
ByteBuffer codesBuf = ByteBuffer.allocate(len * codeBytes)
@@ -194,16 +193,40 @@ private static Object readElement(Object data, PType ptype, int i) {
194193
};
195194
}
196195

197-
private static void writeElement(ByteBuffer buf, PType ptype, Object v) {
198-
switch (ptype) {
199-
case I8, U8 -> buf.put((Byte) v);
200-
case I16, U16 -> buf.putShort((Short) v);
201-
case I32, U32 -> buf.putInt((Integer) v);
202-
case I64, U64 -> buf.putLong((Long) v);
203-
case F32 -> buf.putFloat((Float) v);
204-
case F64 -> buf.putDouble((Double) v);
205-
case F16 -> throw new UnsupportedOperationException("F16 not supported");
206-
}
196+
private static Object buildUniqueArray(PType ptype, Iterable<Object> uniques, int dictSize) {
197+
return switch (ptype) {
198+
case I8, U8 -> {
199+
byte[] a = new byte[dictSize]; int i = 0;
200+
for (Object v : uniques) { a[i++] = (Byte) v; }
201+
yield a;
202+
}
203+
case I16, U16 -> {
204+
short[] a = new short[dictSize]; int i = 0;
205+
for (Object v : uniques) { a[i++] = (Short) v; }
206+
yield a;
207+
}
208+
case I32, U32 -> {
209+
int[] a = new int[dictSize]; int i = 0;
210+
for (Object v : uniques) { a[i++] = (Integer) v; }
211+
yield a;
212+
}
213+
case I64, U64 -> {
214+
long[] a = new long[dictSize]; int i = 0;
215+
for (Object v : uniques) { a[i++] = (Long) v; }
216+
yield a;
217+
}
218+
case F32 -> {
219+
float[] a = new float[dictSize]; int i = 0;
220+
for (Object v : uniques) { a[i++] = (Float) v; }
221+
yield a;
222+
}
223+
case F64 -> {
224+
double[] a = new double[dictSize]; int i = 0;
225+
for (Object v : uniques) { a[i++] = (Double) v; }
226+
yield a;
227+
}
228+
case F16 -> throw new UnsupportedOperationException("F16 not supported"); // TODO: implement F16
229+
};
207230
}
208231

209232
private static void writeCode(ByteBuffer buf, PType codePType, int code) {

0 commit comments

Comments
 (0)