7

So I need to use JNI to call a C function from java. I've been able to successfully do this when passing in different data types (create the native variables, header file, shared library, blah blah), but can't get it to work with a byte array. Here's my C function:

#include <stdio.h>
void encrypt(int size, unsigned char *buffer);
void decrypt(int size, unsigned char *buffer);

void encrypt(int size, unsigned char *buffer){
    for(int i=0; i<size; i++){
        unsigned char c = buffer[i];
        printf("%c",c);
    }
}
void decrypt(int size, unsigned char *buffer){
    for(int i=0; i<size; i++){
        unsigned char c = buffer[i];
        printf("%c",c);
    }
}

And here's my java code (I understand that after making a header file from this, I have to replace the C function declarations by the JNI code in the header file)

class Tester{
    public native void encrypt(int size, char *buffer);
    public native void decrypt(int size, char *buffer);
    static{
    System.loadLibrary("buffer");
    {
    public static void main(String[] args){
        Tester test = new Tester();
        String hello = "hello";
        byte[] byteHello = hello.getBytes();
        test.encrypt(5,byteHello);
        test.decrypt(5,byteHello);
    }
}

I get that the char* type isn't supported in Java and that's why I'm getting an error trying to compile. Perhaps I should change the type to char[] in Java? Anyways, my goal is to be able to pass a byte array in Java into my C function, iterate through the byte array, and print out each element.

2
  • The java char and C char types are incompatible, it would probably be better to pass the byte[] to C and then convert each element on demand Commented May 24, 2016 at 21:08
  • "How can I use Java Native Interface to pass a byte array into a C function which takes a char* as an argument?" - you can't, but you can pass a byte array into a C function which takes a Java byte array as an argument. Commented May 24, 2016 at 21:15

1 Answer 1

7

The Java char and C char types are incompatible, it would probably be better to pass the byte[] to C and then convert each element on demand.


Something along these lines:

Main.java:

//...Code to load library...

public static void main(String[] args) {
    passBytes("hello".getBytes());
}

public static native void passBytes(byte[] bytes);

Main.c:

#include "Main.h" // Main.h is generated

JNIEXPORT void JNICALL Java_Main_passBytes
  (JNIEnv *env, jclass clazz, jbyteArray array) {
    unsigned char* buffer = (*env)->GetByteArrayElements(env, array, NULL);
    jsize size = (*env)->GetArrayLength(env, array);

    for(int i = 0; i < size; i++) {
        printf("%c", buffer[i]);
    }

    (*env)->ReleaseByteArrayElements(env, array, buffer, JNI_ABORT);
 }

jbyteArray is nothing more then a stub type, defined in jni.h:

struct _jobject;
typedef struct _jobject *jobject;
typedef jobject jarray;
typedef jarray jbyteArray;

It doesn't actually contain any data. It is more or less just a memory adress.

To get the elements from it we pass it to GetByteArrayElements (since the type was byte[]) which can than ask the VM to retrieve the elements in a C style array. (It might or might not make a copy. See doc)

The same is done for the array length.

To tell the VM we're done with the array. We call ReleaseArrayElements.

Also, jbyte is defined as signed char, so just using the result of GetByteArrayElements as an unsigend char* instead of jbyte* is safe in this case.

Sign up to request clarification or add additional context in comments.

11 Comments

A bit more explanation would make this a better answer. A copy from your comment would be a good start, but it would also be worth mentioning that a Java array as received by a native method is not a C array. You have to use the appropriate JNI function to obtain from it an actual C array (as you demonstrate), and that puts some additional requirements on the overall native method implementation.
@JohnBollinger Thanks for the suggestion. I kind of assumed that OP already knew most of this. But any ways, I sometimes forget other people will read this too ;)
The importance of the Release* functions is to unpin the objects in the JVM heap. The array elements are not necessarily copied by Get* and so JNI_ABORT does not necessarily avoid modifying the JVM array; It only saves time if there was a copy..
@JornVernee Hey, thanks a lot! This works, and I am effectively able to print and change the values inside the memory buffer. Is there any chance you could answer my very similar question regarding calling a C library from python? You seem to know what you're talking about. Thanks again.
@Mr.Pickles No sorry, I don't know much about python.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.