SWIG技术包装无符号二进制数据

时间:2012-03-29 21:47:20

标签: swig

我有一个C函数,它返回一个表示二进制数据的unsigned char *。我在文档中注意到SWIG有一个很好的类型映射来处理二进制数据作为C函数的输入,但是当C函数返回二进制数据及其无符号时呢?有什么想法吗?

swig.i:

%apply (char *STRING, size_t LENGTH) { (const char data[], size_t len) }
%inline %{
void binaryChar1(const char data[], size_t len) {
  printf("len: %d data: ", len);
  for (size_t i=0; i<len; ++i)
    printf("%x ", data[i]);
  printf("\n");
}
%}

的java:

byte[] data = "hi\0jk".getBytes();
example.binaryChar1(data);

C示例:

 enw_resultrow_t *result_row = getResultRow();
 unsigned char *blob;
 while ((blob = getBinaryFromRow(result_row, &length))) {
            char fname[32];
            FILE *fp;
            i++;
            snprintf (fname, sizeof(fname), "FileXYZ", i);
            printf ("Blob from %d:%s is saved in %s has %d bytes\n", i, 
                    aSender?inet_ntoa(aSender->sin_addr):"???", fname, length);
            if ((fp = fopen (fname, "w"))) {
                l = fwrite (blob, sizeof (unsigned char), length, fp);
                printf("Successfully wrote %d bytes to file\n", l);
                fclose (fp);
            } else {
                printf("Error writing file");
            }
        }

2 个答案:

答案 0 :(得分:5)

我创建了一个测试用例,反映了你想要做的事情(我认为):

#include <stdlib.h>

enum thing {
  ONE=1,
  TWO=2, 
  THREE=3
};

static signed char *get_data(enum thing t, size_t *len) {
  *len = (size_t)t;
  signed char *ret = malloc(sizeof(signed char) * (*len));
  for (size_t i = 0; i < *len; ++i) {
    ret[i] = i;
  }
  return ret;
}

要包裹get_data(),我使用了以下界面:

%module test

%{
#include "test.h"
%}

%typemap(jni) signed char *get_data "jbyteArray"
%typemap(jtype) signed char *get_data "byte[]"
%typemap(jstype) signed char *get_data "byte[]"
%typemap(javaout) signed char *get_data {
  return $jnicall;
}

%typemap(in,numinputs=0,noblock=1) size_t *len { 
  size_t length=0;
  $1 = &length;
}

%typemap(out) signed char *get_data {
  $result = JCALL1(NewByteArray, jenv, length);
  JCALL4(SetByteArrayRegion, jenv, $result, 0, length, $1);
}

%include "test.h"

基本上这样做是将get_data函数的返回类型设置为Java数组,直接从JNI代码一直到SWIG代理。完成后,它会设置一个名为size_t的临时length,用于调用真实的C函数并存储结果。 (在我看到this answer to another question之前我没有看到noblock,它告诉SWIG不要使typemap参数独立,因此意味着给定函数只能有一个size_t *len参数,如果你很好奇,看看它对生成的包装器代码的作用)。

一旦设置完毕,剩下的就是使用JNI调用分配一个数组并将一些值复制到其中。

我测试了这个:

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("test");
    byte[] test1 = test.get_data(thing.ONE);
    System.out.println(test1.length);
    System.out.println(test1 + ": " + test1[0]);

    byte[] test2 = test.get_data(thing.TWO);
    System.out.println(test2.length);
    System.out.println(test2 + ": " + test2[0] + ", " + test2[1]);

    byte[] test3 = test.get_data(thing.THREE);
    System.out.println(test3.length);
    System.out.println(test3 + ": " + test3[0] + ", " + test3[1] + ", " + test3[2]);

  }
}

然后给出了:

1
[B@525483cd: 0
2
[B@2a9931f5: 0, 1
3
[B@2f9ee1ac: 0, 1, 2

我让自己变成了signed char,这让我有点作弊。如果你想让它没有签名,你需要使用强制转换(请注意最好丢失符号)或short / int并进行适当的转换。

请注意真实代码中的内存所有权。

答案 1 :(得分:1)

我认为你不需要实现自己的机制。 swig提供了一个名为'cdata.i'的模块。 您应该在接口定义文件中包含它。

包含此内容后,它会提供两个函数cdata()和memmove()。给定void *和二进制数据的长度,cdata()将其转换为目标语言的字符串类型。 memmove()是相反的。给定一个字符串类型,它会将字符串的内容(包括嵌入的空字节)复制到C void *类型中。

使用此模块处理二进制数据变得非常简单。 我希望这就是你所需要的。