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
62 changes: 49 additions & 13 deletions codegen/src/main/java/ObjectLayerGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ private record ClassSpec(String objectKey, String interfaceName, String superInt
private List<StructField> matchFields;
private int matchStride;

/** Size in bytes of the {@code TBox} struct, the stride of a {@code TBox *} array. */
private int tboxStride;

// -------------------------------------------------------------------------
// Entry point
// -------------------------------------------------------------------------
Expand Down Expand Up @@ -102,6 +105,7 @@ private void init(JsonNode root) {
spanSize = structSize(root, "Span");
matchFields = structFields(root, "Match");
matchStride = structSize(root, "Match");
tboxStride = structSize(root, "TBox");
}

private void run(ClassSpec spec, JsonNode root, Path out, String inputPath) throws IOException {
Expand Down Expand Up @@ -174,23 +178,51 @@ private static int structSize(JsonNode root, String name) {
int offset = 0;
int widest = 1;
for (JsonNode f : structOf(root, name).path("fields")) {
String cType = f.path("cType").asText().trim();
int bracket = cType.indexOf('[');
int size;
int align;
if (bracket >= 0) { // a fixed-size array field, e.g. char[4]
int count = Integer.parseInt(cType.substring(bracket + 1, cType.indexOf(']')).trim());
align = scalarBytes(cType.substring(0, bracket).trim());
size = count * align;
} else {
size = align = scalarBytes(cType);
}
widest = Math.max(widest, align);
offset = (offset + align - 1) / align * align + size;
int[] sa = sizeAlign(root, f.path("cType").asText().trim());
widest = Math.max(widest, sa[1]);
offset = (offset + sa[1] - 1) / sa[1] * sa[1] + sa[0];
}
return (offset + widest - 1) / widest * widest;
}

/** The alignment in bytes of a catalog struct: the widest alignment among its fields. */
private static int structAlign(JsonNode root, String name) {
int widest = 1;
for (JsonNode f : structOf(root, name).path("fields")) {
widest = Math.max(widest, sizeAlign(root, f.path("cType").asText().trim())[1]);
}
return widest;
}

/**
* The {@code {size, alignment}} in bytes of a struct field's C type: a scalar, a fixed-size array, or
* a nested catalog struct (a TBox nests two Spans), resolved recursively.
*/
private static int[] sizeAlign(JsonNode root, String cType) {
cType = cType.replace("const ", "").trim();
int bracket = cType.indexOf('[');
if (bracket >= 0) { // a fixed-size array field, e.g. char[4]
int[] base = sizeAlign(root, cType.substring(0, bracket).trim());
int count = Integer.parseInt(cType.substring(bracket + 1, cType.indexOf(']')).trim());
return new int[]{base[0] * count, base[1]};
}
if (isStruct(root, cType)) {
return new int[]{structSize(root, cType), structAlign(root, cType)};
}
int size = scalarBytes(cType);
return new int[]{size, size};
}

/** Whether the catalog defines a struct of this name. */
private static boolean isStruct(JsonNode root, String name) {
for (JsonNode s : root.path("structs")) {
if (s.path("name").asText().equals(name)) {
return true;
}
}
return false;
}

/**
* The scalar fields of a catalog struct with their byte offsets, for folding an array of that struct
* into a record. A fixed-size array field (padding) contributes to the offsets but is not a record
Expand Down Expand Up @@ -368,6 +400,7 @@ && cleanType(fn.path("returnType").path("c").asText()).equals("Temporal *")) {
String arrayKind = arrayElement != null ? "objectArray"
: retC.equals("TimestampTz *") ? "scalarArray"
: retC.equals("Span *") ? "spanArray"
: retC.equals("TBox *") ? "tboxArray"
: retC.equals("Match *") ? "matchArray"
: null;
int last = params.size() - 1;
Expand All @@ -390,6 +423,7 @@ && cleanType(fn.path("returnType").path("c").asText()).equals("Temporal *")) {
case "objectArray" -> "java.util.List<Temporal>";
case "scalarArray" -> "java.util.List<java.time.OffsetDateTime>";
case "spanArray" -> "java.util.List<types.collections.time.tstzspan>";
case "tboxArray" -> "java.util.List<types.boxes.TBox>";
default -> "java.util.List<Match>";
};
return new Method(ooName, fnName, rt, arrayKind, arrayElement, foldArgs);
Expand Down Expand Up @@ -774,6 +808,8 @@ private String generateMethod(Method m) {
+ "_array.getLong((long) _i * Long.BYTES))");
case "spanArray" -> arrayFoldBody(m, "new types.collections.time.tstzspan("
+ "GeneratedFunctions.span_copy(_array.slice((long) _i * " + spanSize + ")))");
case "tboxArray" -> arrayFoldBody(m, "new types.boxes.TBox("
+ "GeneratedFunctions.tbox_copy(_array.slice((long) _i * " + tboxStride + ")))");
case "matchArray" -> arrayFoldBody(m, matchElementExpr());
case "split" -> splitFoldBody(m);
default -> "\t\treturn " + invocation + ";\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package types.temporal.generated;

import functions.GeneratedFunctions;
import jnr.ffi.Memory;
import jnr.ffi.Pointer;
import jnr.ffi.Runtime;
import org.junit.jupiter.api.Test;
import types.basic.tfloat.TFloatSeq;
import types.boxes.TBox;
Expand All @@ -10,6 +12,8 @@
import types.temporal.Temporal;
import types.temporal.TemporalType;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
Expand Down Expand Up @@ -82,4 +86,32 @@ void boxAndValueSpanRestrictionsMatchTheLibrary() {
assertEquals(id(GeneratedFunctions.tnumber_minus_spanset(p, spanset.get_inner())),
id(g.minusSpanset(spanset)));
}

@Test
void tboxArraysMatchTheLibrary() {
TFloatSeq a = new TFloatSeq("[1@2019-09-01, 3@2019-09-02, 2@2019-09-03]");
Pointer p = a.getInner();
GeneratedTNumber g = gen(a);
Runtime rt = Runtime.getSystemRuntime();
int tboxBytes = 56; // sizeof(TBox) on the 64-bit targets: two 24-byte Spans plus a padded int16

// tboxes() folds the contiguous TBox array into TBox wrappers; compare each to the raw element.
Pointer c = Memory.allocate(rt, Integer.BYTES);
Pointer arr = GeneratedFunctions.tnumber_tboxes(p, c);
List<TBox> boxes = g.tboxes();
assertEquals(c.getInt(0), boxes.size());
for (int i = 0; i < boxes.size(); i++) {
assertEquals(GeneratedFunctions.tbox_out(arr.slice((long) i * tboxBytes), 15),
GeneratedFunctions.tbox_out(boxes.get(i).get_inner(), 15));
}

// The split-into-N and split-each-N variants fold the same way.
Pointer c2 = Memory.allocate(rt, Integer.BYTES);
GeneratedFunctions.tnumber_split_n_tboxes(p, 2, c2);
assertEquals(c2.getInt(0), g.splitNTboxes(2).size());

Pointer c3 = Memory.allocate(rt, Integer.BYTES);
GeneratedFunctions.tnumber_split_each_n_tboxes(p, 2, c3);
assertEquals(c3.getInt(0), g.splitEachNTboxes(2).size());
}
}
Loading