java - 使用 SWIG 将字节数组转换为 C 结构体

标签 java swig

我在 C 中有以下结构:

typedef struct KFMutableBytes {
    uint8_t * _Nullable bytes;
    size_t length;
    const size_t capacity;
} KFMutableBytes;

该结构作为指针传递给函数,如下所示:

KFMutableBytes bytes = ...;
someFunc(&bytes);

该函数写入bytes.bytes直至bytes.capacity,并将写入的长度存储在bytes.length中。

到目前为止我已经得到了这个:

%typemap(jni) KFMutableBytes * "jbyteArray"
%typemap(jtype) KFMutableBytes * "byte[]"
%typemap(jstype) KFMutableBytes * "byte[]"
%typemap(in) KFMutableBytes * {
    KFMutableBytes *bytes = (KFBytes *)malloc(sizeof(KFMutableBytes));
    if(bytes == NULL) {
        jclass clazz = (*jenv)->FindClass(jenv, "java/lang/OutOfMemoryError");
        (*jenv)->ThrowNew(jenv, clazz, "Not enough memory");
        return $null;
    }
    KFMutableBytes b = KFMutableBytesCreate((uint8_t *) JCALL2(GetByteArrayElements, jenv, $input, 0), 0, (size_t) JCALL1(GetArrayLength, jenv, $input));
    memcpy(bytes, &b, sizeof(b));
    $1 = bytes;
}
%typemap(javain) KFMutableBytes * "$javainput"
/* Prevent default freearg typemap from being used */
%typemap(freearg) KFMutableBytes * {
        JCALL3(ReleaseByteArrayElements, jenv, $input, (jbyte *) $1->bytes, 0);
        free($1);
}

这意味着在 Java 中,someFuncsomeFunc(byte[] bytes)。问题是你无法获取写入的长度,并且无法在 C 中修改字节数组长度。所以实际上我只需要将 byte[] 映射到 bytes capacity 成员,并将 length 成员映射到 long。但我不确定如何将字节数组映射到结构成员?

最佳答案

我已经解决了这个问题,但是解决方案很复杂。本质上,我忽略了结构体的属性,将我自己的构造函数和属性添加到 Java 中的类中,将结构体的 Java 表示直接传递给 C,然后我提取 Java 属性来创建 C 结构体,然后在函数执行之后调用,将长度写回java对象并释放数组。

// ignore the properties of the struct to prevent them being added to the Java class
%ignore KFMutableBytes::bytes;
%ignore KFMutableBytes::capacity;
%ignore KFMutableBytes::length;
// Add our own constructors, properties and methods to the Java class
%extend KFMutableBytes {
#if defined(SWIG)
%proxycode %{
    private byte[] bytes;
    private int length;
    public byte[] getBytes() {
        if(length == 0) { return new byte[0]; }
        return Arrays.copyOfRange(bytes, 0, length);
    }
    public KFMutableBytes(byte[] bytes) {
        this.bytes = bytes;
        length = 0;
    }

    public KFMutableBytes(int capacity) {
        this.bytes = new byte[capacity];
        length = 0;
    }
%}
#endif
}
// Tell SWIG to pass the java object straight to C
%typemap(jni) KFMutableBytes * "jobject"
%typemap(jtype) KFMutableBytes * "KFMutableBytes"
%typemap(jstype) KFMutableBytes * "KFMutableBytes"
// In C, get the added properties from the Java object and set those to our struct
%typemap(in) KFMutableBytes * {
    jclass clazz = JCALL1(FindClass, jenv, "com/example/myapplication/cppinterface/KFMutableBytes");
    jfieldID fid = JCALL3(GetFieldID, jenv, clazz, "bytes", "[B");
    jobject byteArrayObj = JCALL2(GetObjectField, jenv, $input, fid);
    jbyteArray byteArray = *(jbyteArray *)&byteArrayObj;
    jfieldID fid2 = JCALL3(GetFieldID, jenv, clazz, "length", "I");
    int length = JCALL2(GetIntField, jenv, $input, fid2);

    KFMutableBytes *bytes = (KFBytes *)malloc(sizeof(KFMutableBytes));
    if(bytes == NULL) {
        jclass clazz = (*jenv)->FindClass(jenv, "java/lang/OutOfMemoryError");
        (*jenv)->ThrowNew(jenv, clazz, "Not enough memory");
        return $null;
    }
    // Because ->capacity is const, we can't set it directly, we have to create another struct and copy to our allocated struct
    KFMutableBytes b = KFMutableBytesCreate((uint8_t *) JCALL2(GetByteArrayElements, jenv, byteArray, 0), length, (size_t) JCALL1(GetArrayLength, jenv, byteArray));
    memcpy(bytes, &b, sizeof(b));
    $1 = bytes;
}
%typemap(javain) KFMutableBytes * "$javainput"
// When finished with the struct, set the length back to the object and release the byte array before freeing
%typemap(freearg) KFMutableBytes * {
    jclass clazz = JCALL1(FindClass, jenv, "com/example/myapplication/cppinterface/KFMutableBytes");
    jfieldID fid = JCALL3(GetFieldID, jenv, clazz, "bytes", "[B");
    jobject byteArrayObj = JCALL2(GetObjectField, jenv, $input, fid);
    jbyteArray byteArray = *(jbyteArray *)&byteArrayObj;
    jfieldID fid2 = JCALL3(GetFieldID, jenv, clazz, "length", "I");
    JCALL3(SetIntField, jenv, $input, fid2, (int)$1->length);
    JCALL3(ReleaseByteArrayElements, jenv, byteArray, (jbyte *) $1->bytes, 0);

    free($1);
}

关于java - 使用 SWIG 将字节数组转换为 C 结构体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62778840/

相关文章:

java - 什么是NullPointerException,我该如何解决?

java - Flink中读取xml文件

Java/Tesseract-OCR : Unsatisfied LinkError libtesseract302

c# - 如何使用 swig 从 `uint []` 返回 `unsigned int *`

python - 在 Python 中从 C++ 继承 Base,使用 SWIG 调用抽象方法

php - 无法使用 php 安装 webpay 扩展?

c++ - 使用 SWIG 为 perl 创建 C++ 接口(interface)?

java - 应用程序在 android 中意外停止

java - Jenkins Pipeline Java 应用程序中的错误 : script. sh : docker not found,

c - 通过 SWIG 输入 Python 3 个字节到 C char*