JNA Arrays and Pointer

I am currently working on a project that requires me to receive a call in Java from the C library. I basically call a C function that takes a pointer to a function, then the C function uses the function pointer as a callback. I am using JNA to pass a Java object (now I will call it a Callback object) as a callback function. The callback object has a single method that gets a C structure (called "Frame") that contains an array of 16 elements, as well as other variables. I wrapped this structure in a Java class in the standard JNA way, for example:

class Frame extends Structure { public short port; public short flags; public Pointer name // this is a pointer to the byte array public int rateDivisor; } 

The callback mechanism works great! The callback object gets the Frame object from C, but when I try to get an array of bytes from the Pointer using name.getByteArray(0, 16) , the application crashes with an Access Violation Exception. However, if I replaced the pointer with a byte array:

 class Frame extends Structure { public short port; public short flags; public byte[] name = new byte[16]; public int rateDivisor; } 

Then the code works fine! That is why I do not want to use this code. Every time I call a function, the returned frame is actually the same object (it is just reused), but the byte array is a new object. This callback function is called many times per second, which causes the garbage collector to go crazy, swallowing thousands of temporary arrays. This project is performance critical, so I really don't want any temporary objects in this part of the application.

I assume that using the byte array in the Frame class, I force JNA to create a new copy of the C byte array. What I want to do is point the pointer to the C byte array, which eliminates the need to copy data, so experimenting with JNA Pointer

My question is: why can't I get an array of bytes from a pointer? Any help would be greatly appreciated :)

(Note. The fact that this makes it even more difficult is that I do not have access to the source C code!)

+4
source share
3 answers

The reason is probably just as simple: the name is not a pointer to an analogue of C.

Let me show you two examples:

WITH

 typedef struct{ char * name; }MyCStruct; 

maps to Java:

 class MyJStruct extends Structure{ Pointer name; } 

But in this other scenario (in which I think you ended up):

WITH

 typedef struct{ char name[16]; }MyCStruct; 

maps to Java:

 class MyJStruct extends Structure{ byte[] name = new byte[16]; } 

In the first case, the C structure contains a pointer, and sizeof the size of the pointer (4 or 8 bits depending on 32/64 bits), in the second case, it contains the entire array, which means that the size is 16 bytes.

This explains the difference between the two Java mappings: JNA needs to know how to read the structure fields, and also explains why you are name.getByteArray(0, 16) error when calling name.getByteArray(0, 16) , as it expresses the following:

  • take the first 4 bytes after the flags field
  • treat it as a pointer
  • go to ram to the specified address and read 16 bytes

Which is a failure, since the indicated memory zone is probably inaccessible to the program.

You can check it yourself on C: if sizeof(struct Frame) is 24, then the structure stores the whole array, if it is 12 (or 16), then it contains a pointer to 32 bits (or a pointer to 64 bits)

The official documentation on this is pretty clear, but hard to find, so here you go:

http://twall.github.com/jna/3.3.0/javadoc/overview-summary.html

you will find it in the Nested Arrays section, and I really recommend that you read the section just below the " Variable-Size Structures "

hope this helps

considers

+5
source

Change the input parameter of your callback to a pointer. Save your own personal Pointer-> Frame card (with or without weak links) and create a new frame based on the input pointer, if it does not already exist.

eg.

 Map frames = new HashMap<Pointer,Frame>(); void callback(Pointer p) { Frame f = frames.get(p); if (f == null) { f = new Frame(p); frames.put(p, f); } // do whatever... } 
+2
source

(New answer to meet StackTrace expectations, see comment on my previous answer)

Well, I'm not sure if there is a good and clean way to do this ... so I will point out a good game called Memory and Pointer game

Keep in mind that you may need these methods (C):

 Frame nativeLibFunc(...); //1 Frame * nativeLibFunc(...); //2 void nativeLibFunc(Frame f); //3 void nativeLibFunc(Frame * fptr);//4 

The worst case, in my opinion, will be the third, since you cannot do this without the normal comparison explained earlier. The next worst case is the first, since you will not get a place in RAM, but a direct result at the top of the stack, so reading it requires you to trick JNA into being able to retrieve the pointer and read what happens after the memory zone you want to skip ( Java):

 class MyMicroFrame extends Structure { public short port; public short flags; //public byte[] name = new byte[16]; ==> commented out, ignored //public int rateDivisor; ==> commented out, read manually, see below } //... MyMicroFrame mmf = NativeLib.INSTANCE.nativeLibFunc(...); int rateDivisor = mmf.getPointer().getInt(20); 

I think you have an idea. if your structure is larger, just divide it into small parts, avoiding the zones you want to skip in order to rejoin them, manually moving to the memory indicated by the first part.

Things to consider when working this way:

  • 32/64 bit pointer size - 4 or 8 bytes see com.sun.jna.Native.POINTER_SIZE
  • Alignment of the structure that I have not tested on this answer
+1
source

All Articles