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

7
我有一个返回表示二进制数据的unsigned char*的C函数。我注意到在SWIG文档中有一个很好的typemap可以处理二进制数据作为输入到C函数,但是当C函数返回二进制数据且它是unsigned时怎么办?有什么建议吗? 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");
            }
        }

你想从C语言返回一个已知大小的数组到Java中?如果你返回指针,如何传递数组大小信息呢?可以通过函数的第二个“输出”参数来传递,例如使用指针 unsigned char *getData(size_t *out_length); // 将返回数据的大小存储在out_length中 - Flexo
@awoodland - 我添加了更多的上下文到问题中,因为我之前太笼统了。我添加了一个C示例,展示了如何从C中使用getBinaryFromRow。从Java中,我想要模仿C示例并调用getBinaryFromRow。我知道长度,因为它在长度输出参数中。while循环会一直读取,直到没有二进制数据为止。长度参数是每个循环迭代的字节数。我不需要在Java端写文件,但如果API/封装工作正常,这是一个很好的测试。在SWIG中,我可以使用byte[]或任何最容易的结构。 - c12
“getBinaryFromRow()” 的声明是什么?我猜这是你关心的例子。 - Flexo
@awoodland - 这是我正在尝试包装的函数:unsigned char * getBinaryFromRow(struct result_row_t *row, int32_t *length)。 - c12
我正在为此问题撰写答案,无论是否有悬赏。不过我需要一些时间来撰写它 :) - Flexo
@awoodland - 听起来不错,我觉得这是值得悬赏的 :) - c12
2个回答

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函数的返回类型从JNI代码一直到SWIG代理设置为Java数组。完成后,它会设置一个临时的size_t叫做length,用于调用真正的C函数并存储结果。(在看到另一个问题的答案之前,我从未见过noblock,它告诉SWIG不要让类型映射参数独立,因此对于给定函数只能有一个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

在你的实际代码中要小心内存所有权。


@c12 - 这个对你有用吗?还是需要我详细解释一下? - Flexo
我仍在尝试将其应用于无符号C函数。我会有一些后续问题需要问你,但我正在努力弄清楚它们。 - c12
你在底部简要提到了内存所有权,对于这个例子,你会使用 %newobject 和 %typemap(newfree) char * "free($1);"; 吗? - c12
@c12,我认为这会起作用,但我不确定JNI函数本身的所有权语义是什么,这也取决于您要包装的函数。您需要比仅使用“char *”更具体。 - Flexo

1

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

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

使用这个模块处理二进制数据变得更加简单。 我希望这就是你需要的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接