Philippe Peter Philippe Peter - 4 years ago 297
Java Question

JNA two dimensional arrays

I try to call a short** in C with JNA.

The C looks like this:

void compute(short** in, int row, int col) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
printf("in[%d][%d] = %d\n", i,j, in[i][j]);
}
}
}



  • Passing a short[][] from JNA doesn't work.

  • The JNA documentation say "To map a native multi-dimensional array, use a single-dimensional Java array" but it doesn't work. When calling

    'nativeLib.compute(new short[] { 1, 2, 3, 4 }, 2, 2);
    I get: java.lang.Error: Invalid memory access at com.sun.jna.Native.invokeVoid(Native Method)

  • It seems that a PointerByReference is needed, I tried to fill the PointerByReference with PointerByReference that contains short values but it doesn't work:

    Pointer pointerOfArray = new Memory(row * col * Native.getNativeSize(Short.TYPE));
    for(int i=0;i<row;i++) {

    Pointer pointer = new Memory(col * Native.getNativeSize(Short.TYPE));
    for(int j=0;j<col;j++) {
    pointer.setShort(j*Native.getNativeSize(Short.TYPE), in[i][j]);
    }
    pointerOfArray.setPointer(i*row*Native.getNativeSize(Short.TYPE), pointer);
    }

  • I also tried:

    Pointer pointer = new Memory(4*Short.SIZE);

    Pointer pointer1 = new Memory(2*Short.SIZE);
    pointer1.setShort(0,(short)1);
    pointer1.setShort(Short.SIZE,(short)2);

    Pointer pointer2 = new Memory(2*Short.SIZE);
    pointer2.setShort(0,(short)3);
    pointer2.setShort(Short.SIZE,(short)4);

    pointer.setPointer(0, pointer1);
    pointer.setPointer(2*Short.SIZE, pointer2);

    nativeLib.compute(new PointerByReference(pointer), 2,2);



But I get
in[0][0] = 3184
in[0][1] = 10460
in[1][0] = 3344
in[1][1] = 10460


Does anyone have an idea? I can't change the C signature, I have to deal with this short**

Thanks a lot.

Solution

I finnaly succed! doing this:

short[][] in = {
{1,2,3},
{4,5,6},
};

Pointer[] data = new Pointer[in.length];
for(int i=0;i<in.length;i++) {
data[i] = new Memory(2*Short.SIZE);
data[i].write(0, in[i], 0,in[0].length);
}

nativeLib.compute(data, in.length,in[0].length);


With result:

in[0][0] = 1
in[0][1] = 2
in[0][2] = 3
in[1][0] = 4
in[1][1] = 5
in[1][2] = 6


Thanks a lot!

Answer Source

JNA only handles single dimension arrays.

There are two approaches to handling two dimensional arrays.

If you have constant row and column length, which it appears from your code that you do, you simply define the JNA array with total length row*col, and then use (rowIndex * col + colIndex) to fetch the result.

The other approach is to define an array type, and have a pointer to it. You can then create an array of pointers (Pointer[]); each will point to the first "column" of a new row.

The Invalid Memory Access error indicates you have not properly allocated the Native memory, and gives you a strong hint at the answer: you cannot simply pass a primitive array as a parameter.

The new short[] {1, 2, 3, 4} doesn't work here, because you haven't allocated native-side memory to support the java memory for that array. Either the array needs to be part of a structure (which allocates memory for each of its members), or you need to allocate the memory yourself, such as with the Memory class.

You were on the right track with that memory allocation you did by using the Memory class.

We know in C, the short** in is expecting a contiguous block of two-byte values. For a 2x2 array there should be four values and 8 total bytes, with the pointer to the first byte passed to the function. So this code is correct:

Pointer pointerOfArray = new Memory(row * col * Native.getNativeSize(Short.TYPE));

However, your allocation of new Memory inside the loop below isn't appropriate. You're allocating yet more memory, and not in the same location as the original allocation. You can create new pointers to point to various points within the existing memory allocation, but that is over-complicating things.

You have just created an 8-byte block of memory which matches the memory your C code expects, so you just need to set the values in that memory appropriately. If you use Pointer.set*(), the offset needs to be row * # of columns + column.

Here's how to do it, filling the memory with the 2D array:

for(int i = 0; i < row; i++) {
   for(int j = 0; j < col; j++) {
        pointerOfArray.setShort((col * i + j) * Native.getNativeSize(Short.TYPE), in[i][j]);
    }
}

If you have already converted your 2D array to a 1D array, you can write it directly using Pointer.write() e.g.,

pointerOfArray.write(0, new short[] {1, 2, 3, 4}, 0, 4);

Then you would pass pointerOfArray to the native C for in.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download