Skip to content

Direct Buffers

Eugene Gershnik edited this page Apr 19, 2023 · 3 revisions

SimpleJNI supports passing data between Java and C++ via java.nio direct buffers.

It declares a jByteBuffer type to represent java.nio.ByteBuffer. When passed a jByteBuffer from Java to a native method you can access its contents as a STL-compatible array of type T via java_direct_buffer<T> template.

The template parameter T can be as simple as uint8_t to access individual bytes or be some other type depending of what kind of data you store in the buffer. Here is an example of accessing individual bytes

void JNICALL someNativeMethod(JNIEnv * env, jByteBuffer buffer)
{
    java_direct_buffer<uint8_t> cpp_buffer(env, buffer);

    jlong size = cpp_buffer();
    uint8_t x = cpp_buffer[3];
    cpp_buffer[3] = 7;

    for(uint8_t val: elements)
    {
        std::cout << val << ' '; 
    }

    uint8_t * data = cpp_buffer.data();
}

The interface of java_direct_buffer closely mimics std::vector or std::array and it can be used in exactly the same way.

You can also create direct buffers on C++ side from any contiguous piece of memory. Keep in mind that once you have given the buffer to Java you must ensure that memory remains valid as long as Java can access it. Unfortunately Java and JNI provide no built-in way to know when the direct buffer is finalized so if you allocate dynamic memory to share with Java you will need to coordinate its lifetime tracking with Java side on your own. Alternatively, if the buffer size is known in advance and not huge you can simply use a static buffer.

To create java_direct_buffer that refers to C++ memory area you simply construct it with a pointer and a size

static uint8_t bytes[] = { 1, 2 }; 
java_direct_buffer<uint8_t> cpp_buffer(bytes, jlong(std::size(bytes)));

You can access such buffer as above populating it or modifying its content.

To create jByteBuffer object from it you need to invoke to_java method.

JNIEnv * env = ...;
java_local_ref<jByteBuffer> buffer = cpp_buffer.to_java(env);

The resultant jByteBuffer can then be passed to Java.