How to Use Unsafemutablerawpointer to Fill an Array

How to use UnsafeMutableRawPointer to fill an array?

First, let's assume you have a UnsafeRawPointer and a length:

let ptr: UnsafeRawPointer = ...
let length: Int = ...

Now you want to convert that to an [float4]. First, you can convert your UnsafeRawPointer to a typed pointer by binding it to a type:

let float4Ptr = ptr.bindMemory(to: float4.self, capacity: length)

Now you can convert that to a typed buffer pointer:

let float4Buffer = UnsafeBufferPointer(start: float4Ptr, count: length)

And since a buffer is a collection, you can initialize an array with it:

let output = Array(float4Buffer)

For much more on working with UnsafeRawPointer, see SE-0138, SE-0107, and the UnsafeRawPointer Migration Guide.

Dereference UnsafeMutablePointer UnsafeMutableRawPointer

Based on this answer, I've written the particle system handler function in swift as:

    ps.handle(SCNParticleEvent.birth, forProperties [SCNParticleSystem.ParticleProperty.color]) {
(data:UnsafeMutablePointer<UnsafeMutableRawPointer>, dataStride:UnsafeMutablePointer<Int>, indicies:UnsafeMutablePointer<UInt32>?, count:Int) in

for i in 0..<count {

// get an UnsafeMutableRawPointer to the i-th rgba element in the data
let colorsPointer:UnsafeMutableRawPointer = data[0] + dataStride[0] * i

// convert the UnsafeMutableRawPointer to a typed pointer by binding it to a type:
let floatPtr = colorsPointer.bindMemory(to: Float.self, capacity: dataStride[0])
// convert that to a an UnsafeMutableBufferPointer
var rgbaBuffer = UnsafeMutableBufferPointer(start: floatPtr, count: dataStride[0])
// At this point, I could convert the buffer to an Array, but doing so copies the data into the array and any changes made in the array are not reflected in the original data. UnsafeMutableBufferPointer are subscriptable, nice.
//var rgbaArray = Array(rgbaBuffer)

// about half the time, mess with the red and green components
if(arc4random_uniform(2) == 1) {
rgbaBuffer[0] = rgbaBuffer[1]
rgbaBuffer[1] = 0
}
}
}

I'm really not certain if this is the most direct way to go about this and seems rather cumbersome compared to the objective-C code (see above question). I'm certainly open to other solutions and/or comments on this solution.

How to get bytes out of an UnsafeMutableRawPointer?

load<T> reads raw bytes from memory and constructs a value of type T:

let ptr = ... // Unsafe[Mutable]RawPointer
let i16 = ptr.load(as: UInt16.self)

optionally at a byte offset:

let i16 = ptr.load(fromByteOffset: 4, as: UInt16.self)

There is also assumingMemoryBound() which converts from a Unsafe[Mutable]RawPointer to a Unsafe[Mutable]Pointer<T>, assuming that the pointed-to memory contains a value of type T:

let i16 = ptr.assumingMemoryBound(to: UInt16.self).pointee

For an array of values you can create a "buffer pointer":

let i16bufptr = UnsafeBufferPointer(start: ptr.assumingMemoryBound(to: UInt16.self), count: count)

A buffer pointer might already be sufficient for your purpose, it
is subscriptable and can be enumerated similarly to an array.
If necessary, create an array from the buffer pointer:

let i16array = Array(i16bufptr)

As @Hamish said, more information and details can be found at

  • SE-0107 UnsafeRawPointer API

Value of type 'UnsafeMutableRawPointer' has no subscripts Swift 5

You need to assign a type to your mutable pointer. In the current state it is a mutable pointer to anything but you want it to be a mutable pointer to a buffer of UInt8 values.

To do so simply call assumingMemoryBound on your data:

let data = calloc(bytesPerRow, height)!.assumingMemoryBound(to: UInt8.self)

How to pass out parameter to function from Swift FFI?


var length = UnsafeMutablePointer<Int32>(bitPattern: 0)

You need to pass storage for your out-parameters. This is defining a null pointer. When Rust tries to write the result to address 0, it crashes, since you don't have access to write there.

Instead of creating two layers of pointers, create a value of the type you want, and then pass the address (&) of that value; this will add the extra layer of pointer automatically.

// Create storage
var content: UnsafeMutablePointer<CChar>? // Could be NULL, so Optional
var length: Int32 = 0
var capacity: Int32 = 0

// Pass as references
do_something(&content, &length, &capacity)

// Copy the data
let data = Array(UnsafeRawBufferPointer(start: content, count: Int(length)))

content is still a pointer here because the thing being updated is a pointer. You're not providing storage for content, Rust is. But you do need to provide storage for the pointer (and that's what this does).

I can't compile your code because it's missing a lot (an MCVE would be much better here), so I can't test that this is doing exactly what you mean, but it should be close.

In your example, you're leaking the memory, but since your C# calls free_do_something_result (which I assume cleans it up), I assume you're actually doing the same in the Swift.

Creating an UnsafeMutablePointer UnsafeMutablePointer Float parameter in Swift 3

When you need to pass multiple values for UnsafeMutablePointer<T> parameters, you need to declare a variable of type [T] (aka Array<T>) and pass it as an inout argument. In your case T is UnsafeMutablePointer<Float>.

So, if you want to start with [Float], you may need to write something like this:

let input: [Float] = [/*...*/]
var output: [Float] = Array(repeating: 0, count: outputTotalSize)
input.withUnsafeBufferPointer {inBuf in
let inputPtr = inBuf.baseAddress!
output.withUnsafeMutableBufferPointer {outBuf in
let outputPtr = outBuf.baseAddress!
var pInputs: [UnsafePointer<Float>] = [inputPtr,/*...*/]
var pOutputs: [UnsafeMutablePointer<Float>] = [outputPtr/*...*/]
vDSP_biquadm(setup, &pInputs, inStride, &pOutputs, outStride, length)
}
}

How to convert Swift [Data] to char **

A possible solution could be to allocate appropriate memory and fill it with the data.

Alignment

The equivalent to char ** of the C code would be UnsafeMutablePointer<UnsafeMutablePointer<CChar>?> on Swift side.

In the definition of data_ptrs that you show in your question, we see that each data block is to be allocated with malloc.

A property of C malloc is that it does not know which pointer type it will eventually be cast into. Therefore, it guarantees strictest memory alignment:

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).

see https://port70.net/~nsz/c/c11/n1570.html#7.22.3

Particularly performance-critical C routines often do not operate byte by byte, but cast to larger numeric types or use SIMD.

So, depending on your internal C library implementation, allocating with UnsafeMutablePointer<CChar>.allocate(capacity: columns) could be problematic, because

UnsafeMutablePointer provides no automated memory management or alignment guarantees.
see https://developer.apple.com/documentation/swift/unsafemutablepointer

The alternative could be to use UnsafeMutableRawPointer with an alignment parameter. You can use MemoryLayout<max_align_t>.alignment to find out the maximum alignment constraint.

Populating Data

An UnsafeMutablePointer<CChar> would have the advantage that we could use pointer arithmetic. This can be achieved by converting the UnsafeMutableRawPointer to an OpaquePointer and then to an UnsafeMutablePointer. In the code it would then look like this:

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])
}

Complete Self-contained Test Program

Your library will probably have certain requirements for the data (e.g. supported matrix dimensions), which I don't know. These must be taken into account, of course. But for a basic technical test we can create an independent test program.

#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");
}

It simply prints the bytes and adds 100 to each byte to be able to verify that the changes arrive on the Swift side.

The corresponding header must be included in the bridge header and looks like this:

#ifndef matrix_h
#define matrix_h

void some_matrix_operation(int rows, int cols, char **data_ptrs);

#endif /* matrix_h */

On the Swift side, we can put everything in a class called 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
}

}

You can call it as shown here:

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)

Test Result

The test result is as follows and seems to show the expected result.

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

In Swift 3, how do I get UnsafeRawPointer from Data?

withUnsafeBytes() gives you a (typed) pointer to the bytes,
this can be converted to a raw pointer:

let data = <Data from somewhere>
data.withUnsafeBytes { (u8Ptr: UnsafePointer<UInt8>) in
let rawPtr = UnsafeRawPointer(u8Ptr)
// ... use `rawPtr` ...
}

The pointer is only valid during the lifetime of the call to the
closure.

Alternatively, you can bridge to NSData and access the raw bytes:

let nsData = data as NSData
let rawPtr = nsData.bytes

Now the pointer is valid in the same scope where nsData is valid.

As of Swift 5 it is

let data = <Data from somewhere>
data.withUnsafeBytes { rawBufferPointer in
let rawPtr = rawBufferPointer.baseAddress!
// ... use `rawPtr` ...
}

because the closure argument is now a UnsafeRawBufferPointer.



Related Topics



Leave a reply



Submit