提问人:swaechter 提问时间:11/8/2023 最后编辑:swaechter 更新时间:11/9/2023 访问量:72
如何通过 Java java.lang.foreign API 访问本机字节数组
How to access a native byte array via Java java.lang.foreign API
问:
我想通过 FFI/Panama 从 Java 访问 PKCS11 库。假设我们在 Windows x64 下具有以下打包组布局:
GroupLayout groupLayout = MemoryLayout.structLayout(
MemoryLayout.sequenceLayout(64, JAVA_BYTE).withName("slotDescription"),
MemoryLayout.sequenceLayout(32, JAVA_BYTE).withName("manufacturerId"),
JAVA_INT_UNALIGNED.withName("flags"),
MemoryLayout.structLayout(
JAVA_BYTE.withName("major"),
JAVA_BYTE.withName("minor")
).withName("hardwareVersion"),
MemoryLayout.structLayout(
JAVA_BYTE.withName("major"),
JAVA_BYTE.withName("minor")
).withName("firmwareVersion")
).withName("CK_SLOT_INFO");
naiv 方法是编写一个辅助方法:
public static String getString(MemorySegment memorySegment, int offset, int length) {
byte[] slicedData = new byte[length];
MemorySegment slicedMemorySegment = memorySegment.asSlice(offset, length);
slicedMemorySegment.asByteBuffer().get(slicedData);
return new String(slicedData);
}
然后用偏移量和长度调用它:
String manufacturerId = MemorySegmentUtils.getString(memorySegment, 64, 32);
由于 PKCS11 使用不同的填充/填充,因此我不想对这些偏移量和长度进行硬编码。使用 java.lang.foreign API 对 C 字符串使用 Java VarHandle 中提到的类似是行不通的,因为 char 字符串是固定的,而不是以零结尾的。getUtf8String
那么我如何使用 a 来读取这些字节:MethodHandle
MethodHandle methodHandle = groupLayout.sliceHandle(MemoryLayout.PathElement.groupElement("manufacturerId"));
// What to do now?
String manufacturerId = ???
答:
1赞
swaechter
11/8/2023
#1
解决方案如下所示:
public static String getFixedString(MemorySegment memorySegment, GroupLayout groupLayout, String name) throws Throwable {
MethodHandle methodHandle = groupLayout.sliceHandle(MemoryLayout.PathElement.groupElement(name));
MemorySegment namedMemorySegment = (MemorySegment) methodHandle.invokeExact(memorySegment);
byte[] namedData = namedMemorySegment.toArray(ValueLayout.JAVA_BYTE);
return new String(namedData);
}
评论
5赞
Jorn Vernee
11/8/2023
FWIW,您可以替换为:getBytes
namedMemorySegment.toArray(ValueLayout.JAVA_BYTE)
6赞
Holger
11/8/2023
如果要从切片句柄中获得优势,则应将它们存储在字段中,而不是为每个操作(重新)获取它们。static final
0赞
swaechter
11/8/2023
非常感谢@JornVernee - 我知道有更简单的方法!
0赞
swaechter
11/8/2023
@Holger 是的,确实如此。我将把每个组模板 + 方法句柄调用重构到自己的平台依赖类中。目前,我仍处于“试用”阶段。但 FFI 确实令人印象深刻。非常适合替换依赖于平台的 JNI 代码:)
3赞
Jorn Vernee
11/8/2023
@swaechter 不可以,布局取决于平台。要确定您需要哪种布局,您需要一个 C/C++ 编译器,该编译器未与 Java 运行时捆绑在一起(但它与 jextract 捆绑在一起)。我建议使用 2 种不同的布局,每个平台一个,然后根据您运行的平台选择一个或另一个,然后从该布局派生所有句柄。
评论