Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JNA two dimensional arrays

Tags:

java

jna

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!

like image 884
Philippe Peter Avatar asked Feb 04 '17 16:02

Philippe Peter


1 Answers

JNA only handles single dimension arrays.

And, technically, so does C. A short * could be a 1d, 2d, or 3d array. You wouldn't know unless you knew the internals. Only by reading documentation do you know that the function is expecting a 2D array. All you're really doing is passing a pointer to the first element of the array (total length row*col), and then use (rowIndex * col + colIndex) to fetch the result. In JNA you'd just use a 1D array to match.

In this case, however, you have a short ** so you know you have a 1D array of pointers, each pointer pointing to a 1D array of shorts. In JNA, you create an array of pointers (Pointer[]) for the first *; each will point to the first "column" of a new row (the second *).

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. You must allocate its memory, either by using the Memory class or by including the array as part of a Structure.

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. You were on the right track with that memory allocation you did by using the Memory class.

In C, the short** in is expecting an array of pointers. So you should start out by declaring an array of pointers:

Pointer[] p = new Pointer[row];

Then you'll set the pointers for each row, allocating memory:

p[0] = new Memory(col * Native.getNativeSize(Short.TYPE));
p[1] = new Memory(col * Native.getNativeSize(Short.TYPE));

Now, you can write your array values. You could iterate over the columns with offset and setShort() but you can also write directly using Pointer.write() e.g.,

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

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

like image 185
Daniel Widdis Avatar answered Nov 15 '22 03:11

Daniel Widdis