Java JNA:如何在结构中指定可变长度(0+;)数组?

Java JNA:如何在结构中指定可变长度(0+;)数组?,java,arrays,structure,jna,Java,Arrays,Structure,Jna,示例结构,其中count是数组中的字节数,可以是0。我想在Java中分配新实例,并读取本机分配的实例 公共类VarArray扩展结构{ 公共字节dummy0; 公众短消息1; 公共整数计数; 公共字节[]数组; } 在结构中不允许使用数组=新字节[0] 如果计数为0,则声明默认值array=new byte[1]将从未分配的地址读取 删除数组字段可以读取,因为我可以从指针偏移量Structure.size()访问字节。[编辑:不正确,取决于填充]。但是,对于分配新实例,我需要手动确定字段大小和

示例结构,其中
count
是数组中的字节数,可以是0。我想在Java中分配新实例,并读取本机分配的实例

公共类VarArray扩展结构{
公共字节dummy0;
公众短消息1;
公共整数计数;
公共字节[]数组;
}
结构中不允许使用
数组=新字节[0]
如果计数为0,则声明默认值
array=new byte[1]
将从未分配的地址读取

删除
数组
字段可以读取,因为我可以从指针偏移量
Structure.size()访问字节。
[编辑:不正确,取决于填充]。但是,对于分配新实例,我需要手动确定字段大小和对齐填充,以便分配正确的内存大小

我有一个使用两种类型的解决方案—一种是不带
array
的,用于本机分配和0计数的Java分配实例,另一种是带有
array
的子类型,用于Java分配的1+计数实例。这似乎有些臃肿,尤其是对于所需的锅炉板代码

有更好的办法吗?
或者是一种简单的方法来计算字段大小和对齐方式,这样一种类型就足够了

导入java.util.array;
导入java.util.List;
导入com.sun.jna.Pointer;
导入com.sun.jna.Structure;
公共类JnaStructTester{
/**
*对于本机分配的和0计数的JNA分配实例。
*/
公共静态类VarArray扩展结构{
公共字节dummy0;
公众短消息1;
公共整数计数;
公共VarArray(){}
公共变量数组(指针p){
超级(p);
}
公共字节[]getArray(){
字节[]数组=新字节[计数];
如果(计数>0){
int offset=size();
getPointer().read(偏移量、数组、0、计数);
}
返回数组;
}
@凌驾
受保护列表getFieldOrder(){
返回列表(“dummy0”、“dummy1”、“count”);
}
}
/**
*对于1+个JNA分配的实例。
*/
公共静态类VarArrayX扩展了VarArray{
公共字节[]数组;
公共VarArrayX(){}
@凌驾
公共字节[]getArray(){
返回数组;
}
@凌驾
受保护列表getFieldOrder(){
返回列表(“dummy0”、“dummy1”、“count”、“array”);
}
}
公共静态void main(字符串[]args){
var va0=新的VarArrayX();
va0.dummy0=(字节)0xef;
va0.dummy1=(短)0xabcd;
va0.count=7;
va0.array=新字节[]{1,2,3,4,5,6,7};
va0.write();
var va1=新的VarArray();
va1.dummy0=(字节)0xab;
va1.dummy1=(短)0xcdef;
va1.write();
打印(新指针(Pointer.nativeValue(va0.getPointer()));
打印(新指针(Pointer.nativeValue(va1.getPointer()));
}
专用静态无效打印(指针p){
var va=新的VarArray(p);
va.read();
系统输出打印项次(va);
System.out.println(“byte[]array=“+Arrays.toString(va.getArray()));
System.out.println();
}
}
输出:

JnaStructTester$VarArray(native@0x7fb6835524b0) (8 bytes) {
  byte dummy0@0=ffffffef
  short dummy1@2=ffffabcd
  int count@4=7
}
byte[] array=[1, 2, 3, 4, 5, 6, 7]

JnaStructTester$VarArray(native@0x7fb683551210) (8 bytes) {
  byte dummy0@0=ffffffab
  short dummy1@2=ffffcdef
  int count@4=0
}
byte[] array=[]
JnaStructTester$VarArray(native@0x7fd85cf1ffb0) (12 bytes) {
  short dummy0@0=4321
  int dummy1@4=abcdef
  byte count@8=7
  byte array[7]@9=[B@4f2410ac
}
byte[] array=[1, 2, 3, 4, 5, 6, 7]

JnaStructTester$VarArray(native@0x7fd85cf20690) (12 bytes) {
  short dummy0@0=4321
  int dummy1@4=abcdef
  byte count@8=0
  byte array[0]@9=[B@722c41f4
}
byte[] array=[]
(我使用的是相当旧的JNA版本4.2.2)

更新(2020-01-07) 感谢Daniel Widdis的建议,这里有一个很好的解决方案

无法根据数组是否为空动态修改字段列表。当不包括变量数组字段时,布局被静态缓存,因此具有非空数组的未来实例将失败。相反:

  • ensureAllocated()
    中调整数组,以避免空数组错误
  • writeField()
    仅在数组非空时写入字段
  • readField()
    根据已读取的计数设置数组大小,如果计数为0,则跳过读取数组
  • 通过在
    readField()
    中将数组设置为正确的大小,将自动填充整个数组,无需手动创建

    导入java.util.array;
    导入java.util.List;
    导入com.sun.jna.Pointer;
    导入com.sun.jna.Structure;
    公共类JnaStructTester{
    公共静态类VarArray扩展结构{
    公共短消息0;
    公共部门1;
    公共字节计数;
    公共字节[]数组=新字节[0];
    公共VarArray(){}
    公共变量数组(字节[]数组){
    this.count=(字节)array.length;
    this.array=数组;
    }
    公共变量数组(指针p){
    超级(p);
    }
    @凌驾
    重新分配的受保护的空{
    如果(计数==0)数组=新字节[1];
    super.重新分配();
    如果(计数==0)数组=新字节[0];
    }
    @凌驾
    受保护的void writeField(StructField StructField){
    if(structField.name.equals(“数组”)&&count==0)返回;
    super.writeField(structField);
    }
    @凌驾
    受保护对象读取字段(StructField StructField){
    if(structField.name.equals(“数组”)){
    数组=新字节[计数];
    if(count==0)返回null;
    }
    返回super.readField(structField);
    }
    @凌驾
    受保护列表getFieldOrder(){
    返回列表(“dummy0”、“dummy1”、“count”、“array”);
    }
    }
    公共静态void main(字符串[]args){
    var va0=新的VARARY(新字节[]{1,2,3,4,5,6,7});
    va0.dummy0=0x4321;
    va0.dummy1=0xabcdef;
    va0.write();
    var va1=新的VarArray();
    va1.dummy0=0x4321;
    va1.dummy1=0xabcdef