SWIG Technique, чтобы обернуть неподписанные двоичные данные - PullRequest
7 голосов
/ 30 марта 2012

У меня есть функция C, которая возвращает беззнаковый символ *, представляющий двоичные данные.В документации я заметил, что 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 ]

5 голосов
/ 02 апреля 2012

Я создал тестовый пример, который отражает то, что вы пытаетесь сделать (я думаю):

#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 и сохранения результата. (Я не видел 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 с соответствующим преобразованием.

Будьте осторожны с владением памятью в вашем реальном коде.

1 голос
/ 17 мая 2012

Я думаю, вам не нужно реализовывать собственный механизм.Swig предоставляет модуль с именем «cdata.i».Вы должны включить это в файл определения интерфейса.

Как только вы включите это, он даст две функции cdata () и memmove ().Учитывая пустоту * и длину двоичных данных, cdata () преобразует их в строковый тип целевого языка.memmove () наоборот.учитывая тип строки, он скопирует содержимое строки (включая внедренные нулевые байты) в тип C void *.

Обработка двоичных данных становится намного проще с этим модулем.Я надеюсь, что это то, что вам нужно.

...