How can a VarHandle instance be created to access elements of a dynamically sized array (in the context of the Foreign Function and Memory API)?
package org.example;
import java.lang.foreign.*;
import java.lang.invoke.VarHandle;
public class ArrayVarHandle {
static final StructLayout coordinateLayout = MemoryLayout.structLayout(
ValueLayout.JAVA_INT.withName("x"),
ValueLayout.JAVA_INT.withName("y")
);
static final SequenceLayout arrayLayout = MemoryLayout.sequenceLayout(1, coordinateLayout);
static final VarHandle xInArrayVarHandle = arrayLayout.varHandle(
MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.groupElement("x")
);
public static void main(String[] args) {
try (var arena = Arena.ofConfined()) {
int count = 10;
var array = arena.allocate(coordinateLayout, count);
setXValues(array, count);
}
}
static void setXValues(MemorySegment array, int count) {
for (int i = 0; i < count; i++)
xInArrayVarHandle.set(array, 0, i, 100);
}
}
This code fails with the exception:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1
at org.example.ArrayVarHandle.setValues(ArrayVarHandle.java:30)
at org.example.ArrayVarHandle.main(ArrayVarHandle.java:24)
Obviously, it checks the boundaries of the layout arrayLayout
. It is declared with elementCount = 1
(first parameter).
If it is instead declared with elementCount = 999999
, the error changes:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Out of bound access on segment MemorySegment{ address: 0x6000014b8140, byteSize: 80 }; new offset = 0; new length = 7999992
at org.example.ArrayVarHandle.setValues(ArrayVarHandle.java:30)
at org.example.ArrayVarHandle.main(ArrayVarHandle.java:24)
So it checks the size of the layout against the size of the memory segment, and fails again.
Is it possible to create an array/sequence layout without a fixed size? Or create the VarHandle
differently?
In the above example, count
has a fixed value. But in the real application, the count is not known at compile-time. And the goal would be to create the VarHandle
instance once, and not on every call of the method.
It sounds like you're not looking for SequenceLayout
(which, as you've observed, is intended for use with fixed-size arrays), but instead you're looking for arrayElementVarHandle
, which is intended for variable-length arrays. You'd then allocate with arena.allocate(coordinateLayout, count)
.
This is explicitly addressed in this section of the documentation.