我想通过 FFI/Panama 从 Java 访问 PKCS11 库。假设我们在 Windows x64 下有以下 1 字节打包组布局:
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");
一个简单的方法是编写一个辅助方法:
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 使用不同的打包/填充,我不想对这些偏移量和长度进行硬编码。使用
getUtf8String
(如 Java VarHandle 中提到的使用 java.lang.foreign API 来处理 C 字符串)不起作用,因为字符字符串是固定的且不是以零结尾的。
那么我如何使用
MethodHandle
来读取这些字节:
MethodHandle methodHandle = groupLayout.sliceHandle(MemoryLayout.PathElement.groupElement("manufacturerId"));
// What to do now?
String manufacturerId = ???
解决方案如下所示:
String slotDescription = MemorySegmentUtils.getFixedString(memorySegment, groupLayout, "slotDescription");
String manufacturerId = MemorySegmentUtils.getFixedString(memorySegment, groupLayout, "manufacturerId");
以及辅助方法:
public static byte[] getBytes(MemorySegment memorySegment) {
byte[] data = new byte[(int) memorySegment.byteSize()];
memorySegment.asByteBuffer().get(data);
return data;
}
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);
return new String(MemorySegmentUtils.getBytes(namedMemorySegment));
}