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

Posted

技术标签:

【中文标题】用于包装无符号二进制数据的 SWIG 技术【英文标题】:SWIG Technique to Wrap Unsigned Binary Data 【发布时间】:2012-03-29 21:47:20 【问题描述】:

我有一个 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");
            
        

【问题讨论】:

那么你想从 C 返回一个已知大小的数组到 Java 吗?如果返回指针,如何传达大小?函数的第二个“输出”,例如通过指针unsigned char *getData(size_t *out_length); // stores size of returned data in out_length? @awoodland - 我为这个问题添加了更多上下文,因为我太笼统了。我添加了一个 C 示例,说明如何从 C 中使用 getBinaryFromRow。从 Java 中,我想模仿 C 示例并调用 getBinaryFromRow。我确实知道长度输出参数中的长度。 while 循环继续读取,直到不再有二进制数据。长度参数是每次循环迭代的字节数。我不需要在 Java 端编写文件,但如果 api/wrapping 有效,这是一个很好的测试。我可以使用 byte[] 或 SWIG 中最简单的任何结构。 getBinaryFromRow() 的声明是什么,我认为这是您关心的示例 @awoodland - unsigned char * getBinaryFromRow(struct result_row_t *row, int32_t *length) 这是我要包装的函数。 我正在研究这个有/没有赏金的答案。不过我需要一些时间来写它:) 【参考方案1】:

我创建了一个测试用例,反映了您正在尝试做的事情(我认为):

#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 数组。完成后,它会设置一个名为length 的临时size_t,它将用于调用真正的C 函数并存储结果。 (在看到 this answer to another question 之前,我没有看到 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 - 这对你有用还是你想让我详细说明一下? 我仍在尝试将其应用于未签名的 C 函数。我会为您提供一些后续问题,但我会在提问之前弄清楚它们。 你在底部简要提到了内存所有权,对于这个例子,你会使用 %newobject 和 %typemap(newfree) char * "free($1);";在这里? @c12,我认为这可行,但我不是 100% 确定 JNI 函数本身的所有权语义是什么,这也取决于您要包装的函数。你#ll希望比char *更具体。【参考方案2】:

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

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

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

【讨论】:

以上是关于用于包装无符号二进制数据的 SWIG 技术的主要内容,如果未能解决你的问题,请参考以下文章

C#Winform基础 无符号二进制数(整数)转换为八进制数

C#Winform基础 无符号二进制数(整数)转换为十进制数

C#Winform基础 八进制数转换为无符号二进制数(整数,正数)

C#Winform基础 十六进制数转换为八进制数(整数,无符号)

C#Winform基础 八进制数转换为十进制数(无符号,整数,正数)

C#Winform基础 无符号二进制数(整数)转换为十六进制(小大写版本)