问题
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