如何定义 swig 类型映射以将 unsigned char* 返回到 java

I have a Java Application that calls a c library for performing crypto functions. This is a custom library implemented in c that we need to use from some Java programs. I need a way to define a SWIG typemap that will allow me call a function passing bytearray from Java and treat it as an unsigned character pointer in C function where c function fills data and returns it to java

Excerpt of my present unhappy interface file is as follows

%module  CryptoFacade

%pointer_functions(int, intp);
%pointer_functions(unsigned char, unsigned_charp);

int enCrypt(char* clearText, int clearLen,unsigned char* retCipherText, int *retCipherLen);


And Excerpt from my unhappy Java code is as follows. In the code below I expected that the call to enCrypt function will give me a buffer but it gives me a "short" as per generated code. (See comments in the code)


class MainLoader {
static {
     System.loadLibrary("dccasecuJ"); //Load my crypto library
}

 public static void main(String[] args) {

 // Define the parameters to be passed by reference 
 SWIGTYPE_p_int retCipherLen=CryptoFacade.new_intp();
 SWIGTYPE_p_unsigned_char retCipherText =
            CryptoFacade.new_unsigned_charp();

CryptoFacade myFacade=new CryptoFacade();   

// Call crypto library function. First two are value parameters, next two are return
myFacade.enCrypt("STRING-TO-ENCRYPT", 17, retCipherText, retCipherLen);

// The length I get back in fourth parameter is just fine 
int gotLen= CryptoFacade.intp_value(retCipherLen); 

//The value I get for the Ciphertext though is a "short" ... no good 
// I need a byte[] in java that has the ciphertext
short gotText= CryptoFacade.unsigned_charp_value(retCipherText);

I guess I should change my interface definition to something like below where I make my third parameter a jbytearray and then I got to implement a typemap that will copy the contents pointed to by unsigned character pointer in C program to a java bytearray.

I am perfectly fine if I have to specify the length of content to 256 bytes, because handling arbitrary lengths may be tricky.

Can someone point me to place where I can find such a typemap (I am new to SWIG and have no experience in writing typemaps)

%module  CryptoFacade

%pointer_functions(int, intp);
%pointer_functions(unsigned char, unsigned_charp);

int enCrypt(char* clearText, int clearLen, jbyteArray retCipherText, int *retCipherLen);

解决方案

The easiest way to do work with byte arrays in Java is to use %array_class (or %array_functions) which is like %pointer_functions but for an entire array, not just a single element. I put together a complete example for you, using this header file as a test:

inline void foo(unsigned char *bytearr) {
  bytearr[0] = 1;
  bytearr[1] = 2;
  bytearr[2] = 3;
  bytearr[3] = 100;
}

We can wrap this with SWIG with:

%module test

%{
#include "test.h"
%}

%include <carrays.i>
%array_class(unsigned char,ByteArr);

%include "test.h"

// Emit Java code to automatically load the shared library
%pragma(java) jniclasscode=%{
  static {
    try {
        System.loadLibrary("test");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. 
" + e);
      System.exit(1);
    }
  }
%}

I also put together some Java to exercise this function:

public class run {
  public static void main(String[] argv) {
    ByteArr arr = new ByteArr(4); // Initial size 4
    // You could set some values before passing in if you wanted to.
    test.foo(arr.cast());
    System.out.println(arr.getitem(0) + ", " + arr.getitem(1) + ", " + arr.getitem(2) + ", " + arr.getitem(3));
  }
}

which compiled and ran. Note that unsigned char in C or C++ is represented in Java as short - Java doesn't have any unsigned types, so the smallest type which fits the range 0-255 is a short. byte doesn't cover it by default. (You could play games re-mapping unsigned char to byte in other ways but that's far from intuitive so the default is not to).

You can do more advanced things if you want. For example if you know the size of the array you can use arrays_java.i. This example creates typemaps using JNI for returning an array of unsigned char. This example shows how to pass an array in to a function using JNI typemaps. (It's using long instead of byte, but the it's literally a search and replace to change that).

相关文章