Shaping C-like structs into memory segments – Foreign (Function) Memory API

0 Comments

146. Shaping C-like structs into memory segments

Let’s consider the C-like struct from the following figure:

Figure 7.12 – A C-like structure

So, in Figure 7.12, we have a C-like struct named point to shape an (x, y) pair of double values. Moreover, we have 5 such pairs declared under the name pointarr. We can try to shape a memory segment to fit this model as follows (arena is an instance of Arena):

MemorySegment segment = MemorySegment.allocateNative(
  2 * ValueLayout.JAVA_DOUBLE.byteSize() * 5,
  ValueLayout.JAVA_DOUBLE.byteAlignment(), arena.scope());

Next, we should set (x, y) pairs into this segment. For this, we can visualize it as follows:

Figure 7.13 – Memory segment to store (x, y) pairs

Based on this diagram, we can easily come up with the following snippet of code for setting the (x, y) pairs:

for (int i = 0; i < 5; i++) {
  segment.setAtIndex(
    ValueLayout.JAVA_DOUBLE, i * 2, Math.random());
  segment.setAtIndex(
    ValueLayout.JAVA_DOUBLE, i * 2 + 1, Math.random());
}

But, another approach consists of using the StructLayout.

Introducing StructLayout

A StructLayout is a group layout. In this layout, the members (other memory layouts) are laid out one after the other exactly as in a C struct. This means that we can shape our C-like struct by laying out two ValueLayout.JAVA_DOUBLE as follows:

StructLayout struct = MemoryLayout.structLayout(                          
  ValueLayout.JAVA_DOUBLE.withName(“x”),                               
  ValueLayout.JAVA_DOUBLE.withName(“y”));

But, we have 5 pairs of (x, y), so we need to nest this StructLayout in a SequenceLayout containing 5 StructLayout as follows:

SequenceLayout struct
  = MemoryLayout.sequenceLayout(5,
      MemoryLayout.structLayout(
        ValueLayout.JAVA_DOUBLE.withName(“x”),
        ValueLayout.JAVA_DOUBLE.withName(“y”)));

Next, as we already know from Problem x, we need to define the proper layout paths via PathElement and get back the VarHandle. We need a VarHandle for x and one for y. Notice in the following code how we point them out via their names:

// VarHandle[varType=double,
// coord=[interface java.lang.foreign.MemorySegment, long]]
VarHandle xHandle = struct.varHandle(
  PathElement.sequenceElement(),
  PathElement.groupElement(“x”));
// VarHandle[varType=double,
// coord=[interface java.lang.foreign.MemorySegment, long]]
VarHandle yHandle = struct.varHandle(
  PathElement.sequenceElement(),
  PathElement.groupElement(“y”));

Finally, we can use these VarHandle and the element count for setting the data as follows:

try (Arena arena = Arena.openConfined()) {
  MemorySegment segment = arena.allocate(struct);
  for (int i = 0; i < struct.elementCount(); i++) {
    xHandle.set(segment, i, Math.random());
    yHandle.set(segment, i, Math.random());
  }
  …

Getting the data is straightforward:

  for (int i = 0; i < struct.elementCount(); i++) {
    System.out.printf(“\nx = %.2f”, xHandle.get(segment, i));
    System.out.printf(“\ny = %.2f”, yHandle.get(segment, i));
  }
}

Challenge yourself to implement this example via ValueLayout.JAVA_DOUBLE.arrayElementVarHandle(int… shape).


Leave a Reply

Your email address will not be published. Required fields are marked *