将 JNA 结构的集合传递给本机方法

str*_*sus 5 java jna

问题

我试图将一组 JNA 结构传递给本机方法,但事实证明它非常繁琐:

假设我们有一个结构:

class MyStructure extends Structure {
    // fields...
}
Run Code Online (Sandbox Code Playgroud)

和 JNA 接口中的一个方法:

void pass(MyStructure[] data);
Run Code Online (Sandbox Code Playgroud)

它映射到本机方法:

void pass(const MYStructure* data);
Run Code Online (Sandbox Code Playgroud)

现在的复杂之处在于应用程序正在动态构建这些结构的集合,即我们不是在处理静态数组,而是在处理类似这样的事情:

class Builder {
    private final Collection<MyStructure> list = new ArrayList<>();

    // Add some data
    public void add(MyStructure entry) {
        list.add(entry);
    }

    // Pass the data to the native library
    public void pass() {
        // TODO
    }
}
Run Code Online (Sandbox Code Playgroud)

pass()方法的一个简单实现可能是:

MyStructure[] array = list.toArray(MyStucture[]::new);
api.pass(array);
Run Code Online (Sandbox Code Playgroud)

libJNA 库接口在哪里)。

当然这不起作用,因为数组不是连续的内存块 - 足够公平。

垃圾解决方案#1

一种解决方案是从结构实例分配一个 JNA 数组并逐个字段填充它:

MYStructure[] array = (MyStructure[]) new MyStructure().toArray(size);
for(int n = 0; n < array.length; ++n) {
    array[n].field = list.get(n).field;
    // other fields...
}
Run Code Online (Sandbox Code Playgroud)

这保证了数组由连续的内存组成。但是我们必须实现数据的逐个字段副本(我们已经在列表中填充了它)——这对于一个简单的结构来说是可以的,但是我正在处理的一些数据有几十个字段,指向进一步嵌套数组等的结构。基本上这种方法是不可行的。

垃圾解决方案#2

另一种选择是将数据集合转换为一个简单的 JNA 指针,大致如下:

MyStructure[] array = list.toArray(MyStructure[]::new);
int size = array[0].size();
Memory mem = new Memory(array.length * size);
for(int n = 0; n < array.length; ++n) {
    if(array[n] != null) {
        array[n].write();
        byte[] bytes = array[n].getPointer().getByteArray(0, size);
        mem.write(n * size, bytes, 0, bytes.length);
    }
}
Run Code Online (Sandbox Code Playgroud)

此解决方案是通用的,因此我们也可以将其应用于其他结构。但是我们必须将方法签名更改为 ,Pointer而不是MyStructure[]使代码更钝,更少的自我记录和更难测试。此外,我们可能会使用第三方库,而这甚至可能不是一种选择。

(注意我刚才在这里问了一个类似的问题,但没有得到满意的答案,我想我会再试一次,我会删除旧的/回答两个)。

概括

基本上我期待/希望有这样的事情:

MyStructure[] array = MyStructure.magicContiguousMemoryBlock(list.toArray());
Run Code Online (Sandbox Code Playgroud)

类似于 JNA 助手类为StringArray字符串数组提供的方式:

StringArray array = new StringArray(new String[]{...});
Run Code Online (Sandbox Code Playgroud)

但据我所知,不存在这样的“魔法”。有没有另一种更简单、更“JNA”的方式来做到这一点?必须为我们基本上已经拥有的数据分配一个逐字节的副本似乎非常愚蠢(并且可能不正确)!

我还有其他选择吗?感激地接受任何指示(双关语)。