ios - 如何将 Swift [Data] 转换为 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 库中,我将视频流数据存储在一个数组中,所以问题是将数组转换为 C类型[Data]
的正确方法是什么。[Data]
char **
我已经尝试了一些东西,但没有一个奏效。目前我有以下转换逻辑,它给了我一个UnsafeMutablePointer<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 = 指向数据的 k 指针数组,大小为字节
编辑:
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);
}
那么jerasure_matrix_decode
从 swift 调用该方法的最佳选择是什么?我应该使用不同于 [Data] 的东西吗?
可能的类似问题:
解决方案
一种可能的解决方案是分配适当的内存并用数据填充它。
结盟
C 代码的等价物char **
将UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>
在 Swift 端。
在您在问题中显示的定义中data_ptrs
,我们看到每个数据块都将分配有 malloc。
C malloc 的一个特性是它不知道最终将转换为哪种指针类型。因此,它保证了最严格的内存对齐:
如果分配成功,则返回的指针经过适当对齐,以便可以将其分配给具有基本对齐要求的任何类型对象的指针,然后用于访问分配的空间中的此类对象或此类对象的数组(直到空间被显式释放)。
见https://port70.net/~nsz/c/c11/n1570.html#7.22.3
特别是性能关键的 C 例程通常不会逐字节操作,而是转换为更大的数字类型或使用SIMD。
因此,根据您的内部 C 库实现,分配 withUnsafeMutablePointer<CChar>.allocate(capacity: columns)
可能会有问题,因为
UnsafeMutablePointer 不提供自动内存管理或对齐保证。见https://developer.apple.com/documentation/swift/unsafemutablepointer
另一种方法是使用带有对齐参数的 UnsafeMutableRawPointer。您可以使用MemoryLayout<max_align_t>.alignment
来找出最大对齐约束。
填充数据
AnUnsafeMutablePointer<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])
}
完整的独立测试程序
您的库可能对数据有某些要求(例如支持的矩阵维度),我不知道。当然,这些都必须考虑在内。但是对于基本的技术测试,我们可以创建一个独立的测试程序。
#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 端。
相应的标头必须包含在桥标头中,如下所示:
#ifndef matrix_h
#define matrix_h
void some_matrix_operation(int rows, int cols, char **data_ptrs);
#endif /* matrix_h */
在 Swift 方面,我们可以将所有内容放在一个名为 Matrix 的类中:
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($0) }
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 - 如何在 Simulink 中使用 S 函数运行 Java 程序?
- batch-file - 如果找到的文件夹没有另一个同名的子文件夹,请执行操作
- node.js - 如何从谷歌助手的用户那里获取文本,并从 sqlite db 响应某些内容
- javascript - 为什么 Await 不阻塞下一行代码?
- swift - 向我的 Swift 应用程序添加了一个命令行目标,但它看不到项目的其余部分
- android - 如何在房间数据库类中获取或观察实时数据
- python - 如何使用 Python 正则表达式查找所有有字母的单词在其中出现两次?
- javascript - 我无法重新分配提示结果
- python - 使用 Series.plot() 绘制每小时数据
- css - 当 div 使用显示网格或 flex 时,粘性 SVG 不浮动在 div 上