Manipulation of ByteBuffer from JNI

gonzus picture gonzus · Jul 4, 2012 · Viewed 13.8k times · Source

I need to pass a (direct) ByteBuffer to native functions that will read/write from/into the buffer. Once these operations are completed, I would like to access the ByteBuffer from Java code using the regular functions; in particular, limit() and position() should reflect the current state of the buffer.

Since JNI will use GetDirectBufferAddress() to directly access the underlying buffer, I am assuming I should call flip () / limit() / position() after I am done reading / writing. However, I have been unable to make this work. For example, after I read a couple of bytes into the buffer from C, and set its limit and position accordingly, I cannot query those bytes from Java; Java's idea of the buffer limit and position are not consistent with what I did in the C code.

Can anybody point me to a working example of this? Thanks in advance.

Answer

maba picture maba · Jul 4, 2012

You could let your native method return the number of written bytes. Then update the ByteBuffer accordingly on the Java side.

public class SomeClass {
    /**
     * @param buffer input/output buffer
     * @return number of bytes written to buffer
     */
    private native int nativeMethod(ByteBuffer buffer);

    public void myMethod() {
        ByteBuffer buffer = ByteBuffer.allocateDirect(100);
        int written = nativeMethod(buffer);
        if (written > 0) {
            buffer.limit(written);
        }
        ...
    }
}

Edit

Trying out to set the limit value on the C side is working too so I don't know why you have problems with it.

Naive implementation (nothing cached etc.):

static jint nativeMethod(JNIEnv *env, jobject obj, jobject buffer) {
    jclass cls = env->GetObjectClass(buffer);
    jmethodID mid = env->GetMethodID(cls, "limit", "(I)Ljava/nio/Buffer;");
    char *buf = (char*)env->GetDirectBufferAddress(buffer);
    jlong capacity = env->GetDirectBufferCapacity(buffer);
    int written = 0;

    // Do something spectacular with the buffer...

    env->CallObjectMethod(buffer, mid, written);
    return written;
}

The limit is set on the buffer when inspecting on Java side.