admin管理员组

文章数量:1332971

I'm using FFM from first principles to access native structures, such as:

VkLayerProperties {
    char layerName[256];
    uint32_t specVersion;
    uint32_t implementationVersion;
    char description[256];
};

The following trivial code extracts the fields from a given off-heap memory address:

public void load(MemorySegment address) {
    var layout = MemoryLayout.structLayout(
        MemoryLayout.sequenceLayout(256, JAVA_BYTE).withName("layerName"),
        JAVA_INT.withName("specVersion"),
        JAVA_INT.withName("implementationVersion"),
        MemoryLayout.sequenceLayout(256, JAVA_BYTE).withName("description")
    );

    VarHandle handle = layout.varHandle(PathElement.groupElement("specVersion"));
    int specVersion = (int) handle.get(address, 0L);
    ....
}

This works fine for the primitive fields (and also for reference types). But how can the same pattern using the layout and path elements be used to create var handles to the array fields, i.e:

VarHandle layerName = layout.varHandle(...);   // <-- magic here please
byte[] layerName = (byte[]) handle.get(address, 0L);

I have tried every combination of the various element classes, tried the helpers in MethodHandles, etc. all without any luck.

Either the handle fails with a "not a value layout" error or the get fails (presumably) because the coordinates are wrong. I suspect I'm just being more than normally dumb.

However, if one looks at the equivalent code generated using jextract for this structure, lo and behold, it essentially doesn't use a var handle but just hard-codes the byte offsets and slices the memory:

public class VkLayerProperties {
    public static MemorySegment layerName$slice(MemorySegment seg) {
        return seg.asSlice(0, 256);
    }
}

Whereas all the non-array fields use handles to access the fields directly:

static final VarHandle const$1 = constants$53.const$0.varHandle(MemoryLayout.PathElement.groupElement("specVersion"));

public static int specVersion$get(MemorySegment seg) {
    return (int)constants$53.const$1.get(seg);
}

Obviously I could just use the same approach and calculate the byte offsets and sizes from the layout (or even just hard code them), but it seems like the using the path element framework was intended to be the preferred solution.

So:

  1. Is there a way to access array fields by deriving a var handle from structures memory layout?

  2. Why does the equivalent jextract circumvent the general approach and use a memory slice for these cases.

Are these two questions connected?

Note that "use jextract" is not an answer.

Thanks in advance for any suggestions or solutions.

I'm using FFM from first principles to access native structures, such as:

VkLayerProperties {
    char layerName[256];
    uint32_t specVersion;
    uint32_t implementationVersion;
    char description[256];
};

The following trivial code extracts the fields from a given off-heap memory address:

public void load(MemorySegment address) {
    var layout = MemoryLayout.structLayout(
        MemoryLayout.sequenceLayout(256, JAVA_BYTE).withName("layerName"),
        JAVA_INT.withName("specVersion"),
        JAVA_INT.withName("implementationVersion"),
        MemoryLayout.sequenceLayout(256, JAVA_BYTE).withName("description")
    );

    VarHandle handle = layout.varHandle(PathElement.groupElement("specVersion"));
    int specVersion = (int) handle.get(address, 0L);
    ....
}

This works fine for the primitive fields (and also for reference types). But how can the same pattern using the layout and path elements be used to create var handles to the array fields, i.e:

VarHandle layerName = layout.varHandle(...);   // <-- magic here please
byte[] layerName = (byte[]) handle.get(address, 0L);

I have tried every combination of the various element classes, tried the helpers in MethodHandles, etc. all without any luck.

Either the handle fails with a "not a value layout" error or the get fails (presumably) because the coordinates are wrong. I suspect I'm just being more than normally dumb.

However, if one looks at the equivalent code generated using jextract for this structure, lo and behold, it essentially doesn't use a var handle but just hard-codes the byte offsets and slices the memory:

public class VkLayerProperties {
    public static MemorySegment layerName$slice(MemorySegment seg) {
        return seg.asSlice(0, 256);
    }
}

Whereas all the non-array fields use handles to access the fields directly:

static final VarHandle const$1 = constants$53.const$0.varHandle(MemoryLayout.PathElement.groupElement("specVersion"));

public static int specVersion$get(MemorySegment seg) {
    return (int)constants$53.const$1.get(seg);
}

Obviously I could just use the same approach and calculate the byte offsets and sizes from the layout (or even just hard code them), but it seems like the using the path element framework was intended to be the preferred solution.

So:

  1. Is there a way to access array fields by deriving a var handle from structures memory layout?

  2. Why does the equivalent jextract circumvent the general approach and use a memory slice for these cases.

Are these two questions connected?

Note that "use jextract" is not an answer.

Thanks in advance for any suggestions or solutions.

Share Improve this question edited Nov 20, 2024 at 21:18 stridecolossus asked Nov 20, 2024 at 16:20 stridecolossusstridecolossus 1,56112 silver badges26 bronze badges 6
  • I think the handle-way is to use layout.sliceHandle. But if you know the types, I think it's just as easy to use segment.get(ValueLayout.JAVA_INT, layout.groupElement("specVersion")) instead of using VarHandle. It prevents auto-(un)boxing. – Rob Spoor Commented Nov 20, 2024 at 16:31
  • Note that you can also get rid of that 0L in handle.get(address, 0L) by wrapping the handle (once during creation) with MethodHandles.insertCoordinates(layout.varHandle(PathElement.groupElement("specVersion")), 1, 0L). – Rob Spoor Commented Nov 20, 2024 at 16:32
  • @RobSpoor I actually use the insertCoordinates trick but omitted it for brevity in the example code. The other suggestion for avoiding boxing is worth looking at though, cheers. – stridecolossus Commented Nov 20, 2024 at 17:05
  • 1 Maybe the previous FFM question helps – DuncG Commented Nov 20, 2024 at 17:35
  • 1 @DuncG That question isn't directly related to the issue I was facing, but it did point me towards documentation which probably is relevant. I'd somehow managed to miss this, it pays to ensure you're dealing with the most up-to-date docs because the FFM library API is still volatile ;) – stridecolossus Commented Nov 20, 2024 at 18:05
 |  Show 1 more comment

1 Answer 1

Reset to default 1

Is there a way to access array fields by deriving a var handle from structures memory layout?

No. This does not exist in the current FFM API.

VarHandles have all kinds of atomic access modes, such as compareAndExchange, that are not possible to implement for values larger than 8 bytes, due to hardware limitations.

Additionally, while you want a byte[] copy to be returned, another user might want a long[] (for improved alignment), or a MemorySegment (reference or copy), or to be able to provide a pre-allocated array instead. There is no one obvious choice here.

So, working out the details is left up to the user. Instead, as jextract does, you have to create a slice of the memory you want to access, and then if you want that to be copied into a newly-allocated byte[], you can call toArray(ValueLayout.JAVA_BYTE) on the resulting slice.

There are several ways to create a slice. As you've found out, jextract pre-computes the offset of the slice and calls asSlice with that offset. Alternatively, you can use MemoryLayout::byteOffset to derive the offset from a memory layout, then call asSlice with the resulting offset. Or, as Rob Spoor mentioned in the comments, use MemoryLayout::sliceHandle to create a method handle that will return a slice for the particular field.

本文标签: project panamaJava FFMObtaining a var handle to an array field of a structureStack Overflow