Search code examples
jna

JNA 2 Dimensional Arrays


JNA 2 Dimensional Arrays

My C function is

    void func(void** bufs, int numBufs);

Tye C-code is expecting an array of pointers to arrays of bytes. func() knows the length of each of the byte arrays and fills them with data.

What is the JNA signature for this?

I have wrestled with this seemingly simple problem for two days and not cracked it.

On the Java side I have DirectBuffer bufs[] and the intention is for the C function to populate bufs[] with data.

I had expected that I could declare the JNA signature as

    public static native boolean func(Pointer[] bufs, int numBufs);

and then construct a Java array of Pointers each Pointer being new Pointer(db.address());

But whilst I can construct the java array of Pointers I get the error:

java.lang.IllegalArgumentException: class [Lcom.sun.jna.Pointer; is not a supported argument type (in method func in class SomeLib)

I have experimented at length and am getting nowhere. I have looked at all the JNA examples on StackOver flow, but none quite fit.

I am using using JNA via Maven

        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.3.1</version>
        </dependency>

Any help gratefully appreciated.


Solution

  • You're close. Unfortunately the mapping of non-primitive arrays to C only works one way... you can map retrieved memory to the Java array, but not send a Java array to C (except for primitives).

    The reason for this restriction is that in C, arrays are contiguous blocks of memory. To access the second element of an array, you just offset by a number of bytes equal to the size of the first element. But the argument you pass to a C function is simply a pointer to the beginning of the array.

    So your mapping of func() should use a Pointer for the array argument.

    You didn't describe how you constructed your Pointer[] array, but allocating each with a new Pointer() call will produce pointers scattered across the native memory rather than contiguous.

    There are basically two approaches to ensuring you have contiguous memory, depending on the level of abstraction you want.

    One low-level approach is to create a Memory object, allocating enough room for your Pointer array (probably new Memory(Native.POINTER_SIZE * numBufs)) and then using setPointer() with the appropriate multiple of Native.POINTER_SIZE offset to map your array to the Memory object. Then pass the Memory object to the C function.

    A higher level approach is to wrap the Pointer in a JNA Structure, using the Structure.toArray() method to do that contiguous array allocation for you. So you could have this:

    public class Foo extends Structure {
        public Pointer bar; 
    }
    

    And then create the array:

    Foo[] array = (Foo[]) new Foo().toArray(numBufs);
    

    At this point you have a (contiguous) array of native memory mapped to JNA's Pointer type. Now you just need to assign these Pointers-to-pointers to the pointers attached to your data:

    for (int i = 0; i < numBufs; i++) {
        // Assign Pointer to corresponding DirectBuffer
        array[i].bar = bufs[i];
    }
    

    Then you should be able to pass the Array to C by passing the first element's pointer:

    func(array[0].bar, numBufs);
    // or equivalently
    func(array[0].getPointer(), numBufs);