如何将 Swift [Data] 转换为 char **
How to convert Swift [Data] to char **
我目前正在尝试将我的 Java Android 库移植到 Swift。在我的 Android 库中,我使用 Jerasure 的 JNI 包装器来调用以下 C 方法
int jerasure_matrix_decode(int k, int m, int w, int *matrix, int row_k_ones, int *erasures, char **data_ptrs, char **coding_ptrs, int size)
我不得不承认我对 Swift 比较陌生,所以我的一些东西可能是错误的。在我的 Java 代码中 char **data_ptrs
和 char **coding_ptrs
实际上是二维数组(例如 byte[][] dataShard = new byte[3][1400])。这些二维数组包含实际的视频流数据。在我的 Swift 库中,我将视频流数据存储在 [Data]
数组中,因此问题是将 [Data]
数组转换为 C char **
类型的正确方法是什么。
我已经尝试了一些方法,但 none 成功了。目前我有以下转换逻辑,它给了我一个 UnsafeMutablePointer? pointer (data = [Data])
let ptr1 = ptrFromAddress(p: &data)
ptr1.withMemoryRebound(to: UnsafeMutablePointer<Int8>?.self, capacity: data.count) { pp in
// here pp is UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?
}
func ptrFromAddress<T>(p:UnsafeMutablePointer<T>) -> UnsafeMutablePointer<T>
{
return p
}
预期的结果是 jerasure 能够在调用 jerasure_matrix_decode
方法时恢复我的 [Data] 数组丢失的数据碎片,但它完全弄乱了我的 [Data] 数组并访问它导致EXC_BAD_ACCESS。所以我认为这是完全错误的方式。
jerasure.h 头文件中的文档写了以下关于 data_ptrs
data_ptrs = An array of k pointers to data which is size bytes
编辑:
jerasure 库正在这样定义 data_ptrs
#define talloc(type, num) (type *) malloc(sizeof(type)*(num))
char **data;
data = talloc(char *, k);
for (i = 0; i < k; i++) {
data[i] = talloc(char, sizeof(long)*w);
}
那么从 swift 调用 jerasure_matrix_decode
方法的最佳选择是什么?我应该使用不同于 [Data] 的东西吗?
可能的类似问题:
一个可能的解决方案是分配适当的内存并用数据填充它。
对齐方式
C 代码的 char **
等价于 Swift 一侧的 UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>
。
在问题中显示的 data_ptrs
的定义中,我们看到每个数据块都将使用 malloc 分配。
C malloc的一个属性就是不知道最终会被强制转换成哪种指针类型。因此,它保证最严格的内存对齐:
The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).
见https://port70.net/~nsz/c/c11/n1570.html#7.22.3
特别是performance-critical C例程通常不会逐字节操作,而是转换为更大的数值类型或使用SIMD.
因此,根据您的内部 C 库实现,使用 UnsafeMutablePointer<CChar>.allocate(capacity: columns)
进行分配可能会有问题,因为
UnsafeMutablePointer provides no automated memory management or alignment guarantees.
see https://developer.apple.com/documentation/swift/unsafemutablepointer
替代方法是使用带有对齐参数的 UnsafeMutableRawPointer。您可以使用 MemoryLayout<max_align_t>.alignment
找出最大对齐约束。
填充数据
UnsafeMutablePointer<CChar>
的优点是我们可以使用指针运算。这可以通过将 UnsafeMutableRawPointer 转换为 OpaquePointer 然后再转换为 UnsafeMutablePointer 来实现。在代码中它看起来像这样:
let colDataRaw = UnsafeMutableRawPointer.allocate(byteCount: cols, alignment: MemoryLayout<max_align_t>.alignment)
let colData = UnsafeMutablePointer<CChar>(OpaquePointer(colDataRaw))
for x in 0..<cols {
colData[x] = CChar(bitPattern: dataArray[y][x])
}
完成Self-contained测试程序
您的图书馆可能对数据有某些要求(例如支持的矩阵维度),我不知道。当然,这些都必须考虑在内。但对于基本的技术测试,我们可以创建一个独立的测试程序。
#include <stdio.h>
#include "matrix.h"
void some_matrix_operation(int rows, int cols, char **data_ptrs) {
printf("C side:\n");
for(int y = 0; y < rows; y++) {
for(int x = 0; x < cols; x++) {
printf("%02d ", (unsigned char)data_ptrs[y][x]);
data_ptrs[y][x] += 100;
}
printf("\n");
}
printf("\n");
}
它只是打印字节并向每个字节添加 100,以便能够验证更改是否到达 Swift 端。
相应的 header 必须包含在桥 header 中,如下所示:
#ifndef matrix_h
#define matrix_h
void some_matrix_operation(int rows, int cols, char **data_ptrs);
#endif /* matrix_h */
在Swift这边,我们可以把所有东西都放在一个叫做class的矩阵中:
import Foundation
class Matrix: CustomStringConvertible {
let rows: Int
let cols: Int
let dataPtr: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>
init(dataArray: [Data]) {
guard !dataArray.isEmpty && !dataArray[0].isEmpty else { fatalError("empty data not supported") }
self.rows = dataArray.count
self.cols = dataArray[0].count
self.dataPtr = Self.copyToCMatrix(rows: rows, cols: cols, dataArray: dataArray)
}
deinit {
for y in 0..<rows {
dataPtr[y]?.deallocate()
}
dataPtr.deallocate()
}
var description: String {
var desc = ""
for data in dataArray {
for byte in data {
desc += "\(byte) "
}
desc += "\n"
}
return desc
}
var dataArray: [Data] {
var array = [Data]()
for y in 0..<rows {
if let ptr = dataPtr[y] {
array.append(Data(bytes: ptr, count: cols))
}
}
return array
}
private static func copyToCMatrix(rows: Int, cols: Int, dataArray: [Data]) -> UnsafeMutablePointer<UnsafeMutablePointer<CChar>?> {
let dataPtr = UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>.allocate(capacity: rows)
for y in 0..<rows {
let colDataRaw = UnsafeMutableRawPointer.allocate(byteCount: cols, alignment: MemoryLayout<max_align_t>.alignment)
let colData = UnsafeMutablePointer<CChar>(OpaquePointer(colDataRaw))
dataPtr[y] = colData
for x in 0..<cols {
colData[x] = CChar(bitPattern: dataArray[y][x])
}
}
return dataPtr
}
}
你可以这样调用它:
let example: [[UInt8]] = [
[ 126, 127, 128, 129],
[ 130, 131, 132, 133],
[ 134, 135, 136, 137]
]
let dataArray = example.map { Data([=14=]) }
let matrix = Matrix(dataArray: dataArray)
print("before on Swift side:")
print(matrix)
some_matrix_operation(Int32(matrix.rows), Int32(matrix.cols), matrix.dataPtr)
print("afterwards on Swift side:")
print(matrix)
测试结果
测试结果如下,似乎是预期的结果
before on Swift side:
126 127 128 129
130 131 132 133
134 135 136 137
C side:
126 127 128 129
130 131 132 133
134 135 136 137
afterwards on Swift side:
226 227 228 229
230 231 232 233
234 235 236 237
我目前正在尝试将我的 Java Android 库移植到 Swift。在我的 Android 库中,我使用 Jerasure 的 JNI 包装器来调用以下 C 方法
int jerasure_matrix_decode(int k, int m, int w, int *matrix, int row_k_ones, int *erasures, char **data_ptrs, char **coding_ptrs, int size)
我不得不承认我对 Swift 比较陌生,所以我的一些东西可能是错误的。在我的 Java 代码中 char **data_ptrs
和 char **coding_ptrs
实际上是二维数组(例如 byte[][] dataShard = new byte[3][1400])。这些二维数组包含实际的视频流数据。在我的 Swift 库中,我将视频流数据存储在 [Data]
数组中,因此问题是将 [Data]
数组转换为 C char **
类型的正确方法是什么。
我已经尝试了一些方法,但 none 成功了。目前我有以下转换逻辑,它给了我一个 UnsafeMutablePointer
let ptr1 = ptrFromAddress(p: &data)
ptr1.withMemoryRebound(to: UnsafeMutablePointer<Int8>?.self, capacity: data.count) { pp in
// here pp is UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?
}
func ptrFromAddress<T>(p:UnsafeMutablePointer<T>) -> UnsafeMutablePointer<T>
{
return p
}
预期的结果是 jerasure 能够在调用 jerasure_matrix_decode
方法时恢复我的 [Data] 数组丢失的数据碎片,但它完全弄乱了我的 [Data] 数组并访问它导致EXC_BAD_ACCESS。所以我认为这是完全错误的方式。
jerasure.h 头文件中的文档写了以下关于 data_ptrs
data_ptrs = An array of k pointers to data which is size bytes
编辑:
jerasure 库正在这样定义 data_ptrs
#define talloc(type, num) (type *) malloc(sizeof(type)*(num))
char **data;
data = talloc(char *, k);
for (i = 0; i < k; i++) {
data[i] = talloc(char, sizeof(long)*w);
}
那么从 swift 调用 jerasure_matrix_decode
方法的最佳选择是什么?我应该使用不同于 [Data] 的东西吗?
可能的类似问题:
一个可能的解决方案是分配适当的内存并用数据填充它。
对齐方式
C 代码的 char **
等价于 Swift 一侧的 UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>
。
在问题中显示的 data_ptrs
的定义中,我们看到每个数据块都将使用 malloc 分配。
C malloc的一个属性就是不知道最终会被强制转换成哪种指针类型。因此,它保证最严格的内存对齐:
The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).
见https://port70.net/~nsz/c/c11/n1570.html#7.22.3
特别是performance-critical C例程通常不会逐字节操作,而是转换为更大的数值类型或使用SIMD.
因此,根据您的内部 C 库实现,使用 UnsafeMutablePointer<CChar>.allocate(capacity: columns)
进行分配可能会有问题,因为
UnsafeMutablePointer provides no automated memory management or alignment guarantees. see https://developer.apple.com/documentation/swift/unsafemutablepointer
替代方法是使用带有对齐参数的 UnsafeMutableRawPointer。您可以使用 MemoryLayout<max_align_t>.alignment
找出最大对齐约束。
填充数据
UnsafeMutablePointer<CChar>
的优点是我们可以使用指针运算。这可以通过将 UnsafeMutableRawPointer 转换为 OpaquePointer 然后再转换为 UnsafeMutablePointer 来实现。在代码中它看起来像这样:
let colDataRaw = UnsafeMutableRawPointer.allocate(byteCount: cols, alignment: MemoryLayout<max_align_t>.alignment)
let colData = UnsafeMutablePointer<CChar>(OpaquePointer(colDataRaw))
for x in 0..<cols {
colData[x] = CChar(bitPattern: dataArray[y][x])
}
完成Self-contained测试程序
您的图书馆可能对数据有某些要求(例如支持的矩阵维度),我不知道。当然,这些都必须考虑在内。但对于基本的技术测试,我们可以创建一个独立的测试程序。
#include <stdio.h>
#include "matrix.h"
void some_matrix_operation(int rows, int cols, char **data_ptrs) {
printf("C side:\n");
for(int y = 0; y < rows; y++) {
for(int x = 0; x < cols; x++) {
printf("%02d ", (unsigned char)data_ptrs[y][x]);
data_ptrs[y][x] += 100;
}
printf("\n");
}
printf("\n");
}
它只是打印字节并向每个字节添加 100,以便能够验证更改是否到达 Swift 端。
相应的 header 必须包含在桥 header 中,如下所示:
#ifndef matrix_h
#define matrix_h
void some_matrix_operation(int rows, int cols, char **data_ptrs);
#endif /* matrix_h */
在Swift这边,我们可以把所有东西都放在一个叫做class的矩阵中:
import Foundation
class Matrix: CustomStringConvertible {
let rows: Int
let cols: Int
let dataPtr: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>
init(dataArray: [Data]) {
guard !dataArray.isEmpty && !dataArray[0].isEmpty else { fatalError("empty data not supported") }
self.rows = dataArray.count
self.cols = dataArray[0].count
self.dataPtr = Self.copyToCMatrix(rows: rows, cols: cols, dataArray: dataArray)
}
deinit {
for y in 0..<rows {
dataPtr[y]?.deallocate()
}
dataPtr.deallocate()
}
var description: String {
var desc = ""
for data in dataArray {
for byte in data {
desc += "\(byte) "
}
desc += "\n"
}
return desc
}
var dataArray: [Data] {
var array = [Data]()
for y in 0..<rows {
if let ptr = dataPtr[y] {
array.append(Data(bytes: ptr, count: cols))
}
}
return array
}
private static func copyToCMatrix(rows: Int, cols: Int, dataArray: [Data]) -> UnsafeMutablePointer<UnsafeMutablePointer<CChar>?> {
let dataPtr = UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>.allocate(capacity: rows)
for y in 0..<rows {
let colDataRaw = UnsafeMutableRawPointer.allocate(byteCount: cols, alignment: MemoryLayout<max_align_t>.alignment)
let colData = UnsafeMutablePointer<CChar>(OpaquePointer(colDataRaw))
dataPtr[y] = colData
for x in 0..<cols {
colData[x] = CChar(bitPattern: dataArray[y][x])
}
}
return dataPtr
}
}
你可以这样调用它:
let example: [[UInt8]] = [
[ 126, 127, 128, 129],
[ 130, 131, 132, 133],
[ 134, 135, 136, 137]
]
let dataArray = example.map { Data([=14=]) }
let matrix = Matrix(dataArray: dataArray)
print("before on Swift side:")
print(matrix)
some_matrix_operation(Int32(matrix.rows), Int32(matrix.cols), matrix.dataPtr)
print("afterwards on Swift side:")
print(matrix)
测试结果
测试结果如下,似乎是预期的结果
before on Swift side:
126 127 128 129
130 131 132 133
134 135 136 137
C side:
126 127 128 129
130 131 132 133
134 135 136 137
afterwards on Swift side:
226 227 228 229
230 231 232 233
234 235 236 237