How to use a ByteBuffer return from C++ to Java

不羁岁月 提交于 2019-12-13 16:43:14

问题


This is almost a repeat of question in How can I make Swig correctly wrap a char* buffer that is modified in C as a Java Something-or-other? ...
Instead of Stringbuffer if I were to use a bytebuffer, what would be the change in the typemap ?


回答1:


I've put together an example of how you might do this using the following header file/function as a test:

#include <stdio.h>

static void foo(char *buf, int len) {
  while(len--)
    putchar(*buf++);
}

My solution was to modify this answer such that the proxy takes a ByteBuffer and converts it into a byte[] for us to pass to the JNI code, which then converts it into a pointer + length combination for us.

%module test

%{
#include "test.h"
%}

%typemap(jtype) (char *buf, int len) "byte[]"
%typemap(jstype) (char *buf, int len) "java.nio.ByteBuffer"
%typemap(jni) (char *buf, int len) "jbyteArray"
%typemap(javain,pre="    byte[] temp$javainput = new byte[$javainput.capacity()];"
                    "    $javainput.get(temp$javainput);")
        (char *buf, int len) "temp$javainput"

%typemap(in,numinputs=1) (char *buf, int len) {
  $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL);
  $2 = JCALL1(GetArrayLength, jenv, $input);
}

%typemap(freearg) (const signed char *arr, size_t sz) {
  // Or use  0 instead of ABORT to keep changes if it was a copy
  JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT); 
}

%include "test.h"

The new bit here is in the javain typemap, allocating a temporary byte[] and then using get to fill it. There is actually an array() function that if the ByteBuffer you're using supports you should use instead, i.e. the typemap should be just:

%typemap(javain) (char *buf, int len) "$javainput.array()"

if your implementation supports it (the method is optional and may throw UnsuportedOperationException).

In actual fact this can be simplified further with SWIG 2.0, from the previous referenced question since we are expecting the type to always be byte we can use a built int typemap from SWIG 2.0 to simplify our interface which now becomes:

%module test

%{
#include "test.h"
%}

%apply (char *STRING, size_t LENGTH) { (char *buf, int len) }
%typemap(javain) (char *buf, int len) "$javainput.array()"
%typemap(jstype) (char *buf, int len) "java.nio.ByteBuffer"

%include "test.h"

I tested all three versions of this with the following Java:

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("test");
    byte value[] = "hello world\n".getBytes();
    java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(value);
    test.foo(buf);
  }
}

To be safe with the possibly unsupported array() what you probably want to do is add a try/catch in a function with a pragma:

%pragma(java) modulecode = %{
  private static byte[] buf2bytearr(java.nio.ByteBuffer buf) {
    try {
      return buf.array();
    }
    catch (UnsupportedOperationException e) {
      byte arr[] = new byte[buf.capacity()];
      buf.get(arr);
      return arr;
    }
  }
%}

and then modify the typemap to use that:

%typemap(javain) (char *buf, int len) "buf2bytearr($javainput)"


来源:https://stackoverflow.com/questions/11618267/how-to-use-a-bytebuffer-return-from-c-to-java

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!