Converting C array ptr to native array in Swift 3 - swift

I have a ptr from a C library that points to an array of Floats. Its type is UnsafeMutablePointer. How do I create a native [Float] array from this in Swift 3?
Here's what I'm trying:
var reconstructedFloats = [Float](repeatElement(0, count: size))
reconstructedFloats.withUnsafeMutableBufferPointer {
let reconstructedFloatsPtr = $0
print(type(of:$0)) // "UnsafeMutableBufferPointer<Float>"
cFloatArrayPtr?.withMemoryRebound(to: [Float].self, capacity: size) {
UnsafeMutableRawPointer(reconstructedFloatsPtr.baseAddress!).storeBytes(of: $0.pointee, as: Float.self)
}
UnsafeMutableRawPointer(reconstructedFloatsPtr.baseAddress!).storeBytes(of: (cFloatArrayPtr?.pointee)!, as: Float.self)
}
That seems insanely overcomplicated so hopefully there's an easy way, but even this code produces a compile error: Type of expression is ambiguous without more context.
If you want to plug it into a playground, here's a complete sample that contrives the cFloatArrayPtr:
// Let's contrive a C array ptr:
var size = 3
var someFloats: [Float] = [0.1, 0.2, 0.3]
var cFloatArrayPtr: UnsafeMutablePointer<Float>?
someFloats.withUnsafeMutableBufferPointer {
cFloatArrayPtr = $0.baseAddress
}
print(type(of:cFloatArrayPtr!)) // "UnsafeMutablePointer<Float>"
var reconstructedFloats = [Float](repeatElement(0, count: size))
reconstructedFloats.withUnsafeMutableBufferPointer {
let reconstructedFloatsPtr = $0
print(type(of:$0))
cFloatArrayPtr?.withMemoryRebound(to: [Float].self, capacity: size) {
UnsafeMutableRawPointer(reconstructedFloatsPtr.baseAddress!).storeBytes(of: $0.pointee, as: Float.self)
}
UnsafeMutableRawPointer(reconstructedFloatsPtr.baseAddress!).storeBytes(of: (cFloatArrayPtr?.pointee)!, as: Float.self)
}
print(reconstructedFloats)

You can make an UnsafeBufferPointer from your pointer. UnsafeBufferPointer is a Sequence, so you can directly make an array from it:
let buffer = UnsafeBufferPointer(start: cFloatArrayPtr, count: size)
var reconstructedFloats = Array(buffer)
Of course, this creates a copy.

Related

How to extract UnsafePointer<CGFloat> from UnsafePointer<CGPoint> - Swift

I’m playing around with learning about pointers in Swift.
For instance, this code starts with a CGPoint array, creates an UnsafePointer and then extracts all the x values into a CGFloat array:
import Foundation
let points = [CGPoint(x:1.2, y:3.33), CGPoint(x:1.5, y:1.21), CGPoint(x:1.48, y:3.97)]
print(points)
let ptr = UnsafePointer(points)
print(ptr)
func xValues(buffer: UnsafePointer<CGPoint>, count: Int) -> [CGFloat]? {
return UnsafeBufferPointer(start: buffer, count: count).map { $0.x }
}
let x = xValues(buffer: ptr, count: points.count)
print(x)
And the expected output is:
[Foundation.CGPoint(x: 1.2, y: 3.33), Foundation.CGPoint(x: 1.5, y: 1.21), Foundation.CGPoint(x: 1.48, y: 3.97)]
0x0000556d6b818aa0
Optional([1.2, 1.5, 1.48])
Now I’d like to have the xValues function return directly UnsafePointer<CGFloat>, instead of going through [CGFloat].
How do I do that, is that possible?
It is unsafe to output pointers like that. As mentioned in comments you should use withUnsafeBufferPointer method to access the underlying buffer:
let points = [
CGPoint(x:1.2, y:3.33),
CGPoint(x:1.5, y:1.21),
CGPoint(x:1.48, y:3.97)
]
let xValues = points.withUnsafeBufferPointer { buffer in
return buffer.map { $0.x }
}
If you need a pointer to the array of CGFloat just use the same method as above:
xValues.withUnsafeBufferPointer { buffer in
// Do things with UnsafeBufferPointer<CGFloat>
}
A good Swift Pointer tutorial here.
Edit
Here is a working example:
let points = [
CGPoint(x:1.2, y:3.33),
CGPoint(x:1.5, y:1.21),
CGPoint(x:1.48, y:3.97)
]
// Create, init and defer dealoc
let ptr = UnsafeMutablePointer<CGFloat>.allocate(capacity: points.count)
ptr.initialize(repeating: 0.0, count: points.count)
defer {
ptr.deallocate()
}
// Populate pointer
points.withUnsafeBufferPointer { buffer in
for i in 0..<buffer.count {
ptr.advanced(by: i).pointee = buffer[i].x
}
}
// Do things with UnsafeMutablePointer<CGFloat>, for instance:
let buffer = UnsafeBufferPointer(start: ptr, count: points.count)
for (index, value) in buffer.enumerated() {
print("index: \(index), value: \(value)")
}

Copying swift string to fixed size char[][]

I have a C struct like this.
struct someStruct {
char path[10][MAXPATHLEN];
};
I'd like to copy a list of Swift strings into the char[10][] array.
For me it's very challenging to handle c two-dimensional char array in Swift. Could anyone share some code which can work with Swift 5? Thanks!
C Arrays are imported to Swift as tuples. Here we have a two-dimensional C array, which becomes a nested tuple in Swift:
public struct someStruct {
public var path: (
(Int8, ..., Int8),
(Int8, ..., Int8),
...
(Int8, ..., Int8)
)
}
There is no really “nice” solution that I am aware of, but using the fact that Swift preserves the memory layout of imported C structures (source), one can achive the goal with some pointer magic:
var s = someStruct()
let totalSize = MemoryLayout.size(ofValue: s.path)
let itemSize = MemoryLayout.size(ofValue: s.path.0)
let numItems = totalSize / itemSize
withUnsafeMutablePointer(to: &s.path) {
$0.withMemoryRebound(to: Int8.self, capacity: totalSize) { ptr in
for i in 0..<numItems {
let itemPtr = ptr + i * itemSize
strlcpy(itemPtr, "String \(i)", itemSize)
}
print(ptr)
}
}
ptr is a pointer to s.path, and itemPtr is pointer to s.path[i]. strlcpy copies the string, here we use the fact that one can pass a Swift string directly to a C function taking a const char* argument (and a temporary null-terminated UTF-8 representation is created automatically).
I strongly encourage you to use some kind of helper methods.
Example:
/* writes str to someStruct instance at index */
void writePathToStruct(struct someStruct* s, size_t index, const char* str) {
assert(index < 10 && "Specified index is out of bounds");
strcpy(s->path[index], str);
}
Now, when calling this function, filling the array looks much cleaner:
var someStructInstance = someStruct()
let pathIndex: Int = 3
let path = "/dev/sda1"
let encoding = String.Encoding.ascii
withUnsafeMutablePointer(to: &someStructInstance) { pointer -> Void in
writePathToStruct(pointer, pathIndex, path.cString(using: encoding)!)
}
By design, tuples can not be accessed by variable index. Reading statically can thus be done without a helper function.
let pathRead = withUnsafeBytes(of: &someStructInstance.path.3) { pointer -> String? in
return String(cString: pointer.baseAddress!.assumingMemoryBound(to: CChar.self), encoding: encoding)
}
print(pathRead ?? "<Empty path>")
However, I assume you will definitely have to read the array with a dynamic index.
In that case, I encourage you to use a helper method as well:
const char* readPathFromStruct(const struct someStruct* s, size_t index) {
assert(index < 10 && "Specified index is out of bounds");
return s->path[index];
}
which will result in a much cleaner Swift code:
pathRead = withUnsafePointer(to: &someStructInstance) { pointer -> String? in
return String(cString: readPathFromStruct(pointer, 3), encoding: encoding)
}

How to convert Data of Int16 audio samples to array of float audio samples

I'm currently working with audio samples.
I get them from AVAssetReader and have a CMSampleBuffer with something like this:
guard let sampleBuffer = readerOutput.copyNextSampleBuffer() else {
guard reader.status == .completed else { return nil }
// Completed
// samples is an array of Int16
let samples = sampleData.withUnsafeBytes {
Array(UnsafeBufferPointer<Int16>(
start: $0, count: sampleData.count / MemoryLayout<Int16>.size))
}
// The only way I found to convert [Int16] -> [Float]...
return samples.map { Float($0) / Float(Int16.max)}
}
guard let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else {
return nil
}
let length = CMBlockBufferGetDataLength(blockBuffer)
let sampleBytes = UnsafeMutablePointer<UInt8>.allocate(capacity: length)
CMBlockBufferCopyDataBytes(blockBuffer, 0, length, sampleBytes)
sampleData.append(sampleBytes, count: length)
}
As you can see the only I found to convert [Int16] -> [Float] issamples.map { Float($0) / Float(Int16.max) but by doing this my processing time is increasing. Does it exist an other way to cast a pointer of Int16 to a pointer of Float?
"Casting" or "rebinding" a pointer only changes the way how memory is
interpreted. You want to compute floating point values from integers,
the new values have a different memory representation (and also a different
size).
Therefore you somehow have to iterate over all input values
and compute the new values. What you can do is to omit the Array
creation:
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Int16>(start: $0, count: sampleData.count / MemoryLayout<Int16>.size)
}
return samples.map { Float($0) / Float(Int16.max) }
Another option would be to use the vDSP functions from the
Accelerate framework:
import Accelerate
// ...
let numSamples = sampleData.count / MemoryLayout<Int16>.size
var factor = Float(Int16.max)
var floats: [Float] = Array(repeating: 0.0, count: numSamples)
// Int16 array to Float array:
sampleData.withUnsafeBytes {
vDSP_vflt16($0, 1, &floats, 1, vDSP_Length(numSamples))
}
// Scaling:
vDSP_vsdiv(&floats, 1, &factor, &floats, 1, vDSP_Length(numSamples))
I don't know if that is faster, you'll have to check.
(Update: It is faster, as ColGraff demonstrated in his answer.)
An explicit loop is also much faster than using map:
let factor = Float(Int16.max)
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Int16>(start: $0, count: sampleData.count / MemoryLayout<Int16>.size)
}
var floats: [Float] = Array(repeating: 0.0, count: samples.count)
for i in 0..<samples.count {
floats[i] = Float(samples[i]) / factor
}
return floats
An additional option in your case might be to use CMBlockBufferGetDataPointer() instead of CMBlockBufferCopyDataBytes()
into allocated memory.
You can do considerably better if you use the Accelerate Framework for the conversion:
import Accelerate
// Set up random [Int]
var randomInt = [Int16]()
randomInt.reserveCapacity(10000)
for _ in 0..<randomInt.capacity {
let value = Int16(Int32(arc4random_uniform(UInt32(UInt16.max))) - Int32(UInt16.max / 2))
randomInt.append(value)
}
// Time elapsed helper: https://stackoverflow.com/a/25022722/887210
func printTimeElapsedWhenRunningCode(title:String, operation:()->()) {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for \(title): \(timeElapsed) s.")
}
// Testing
printTimeElapsedWhenRunningCode(title: "vDSP") {
var randomFloat = [Float](repeating: 0, count: randomInt.capacity)
vDSP_vflt16(randomInt, 1, &randomFloat, 1, vDSP_Length(randomInt.capacity))
}
printTimeElapsedWhenRunningCode(title: "map") {
randomInt.map { Float($0) }
}
// Results
//
// Time elapsed for vDSP : 0.000429034233093262 s.
// Time elapsed for flatMap: 0.00233501195907593 s.
It's an improvement of about 5 times faster.
(Edit: Added some changes suggested by Martin R)
#MartinR and #ColGraff gave really good answers, and thank you for everybody and the fast replies.
however I found an easier way to do that without any computation. AVAssetReaderAudioMixOutput requires an audio settings dictionary. Inside we can set the key AVLinearPCMIsFloatKey: true. This way I will read my data like this
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Float>(start: $0,
count: sampleData.count / MemoryLayout<Float>.size)
}
for: Xcode 8.3.3 • Swift 3.1
extension Collection where Iterator.Element == Int16 {
var floatArray: [Float] {
return flatMap{ Float($0) }
}
}
usage:
let int16Array: [Int16] = [1, 2, 3 ,4]
let floatArray = int16Array.floatArray

How to convert UInt16 to UInt8 in Swift 3?

I want to convert UInt16 to UInt8 array, but am getting the following error message:
'init' is unavailable: use 'withMemoryRebound(to:capacity:_)' to
temporarily view memory as another layout-compatible type.
The code:
let statusByte: UInt8 = UInt8(status)
let lenghtByte: UInt16 = UInt16(passwordBytes.count)
var bigEndian = lenghtByte.bigEndian
let bytePtr = withUnsafePointer(to: &bigEndian) {
UnsafeBufferPointer<UInt8>(start: UnsafePointer($0), count: MemoryLayout.size(ofValue: bigEndian))
}
As the error message indicates, you have to use withMemoryRebound()
to reinterpret the pointer to UInt16 as a pointer to UInt8:
let bytes = withUnsafePointer(to: &bigEndian) {
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: bigEndian)) {
Array(UnsafeBufferPointer(start: $0, count: MemoryLayout.size(ofValue: bigEndian)))
}
}
The closures are invoked with pointers ($0) which are only valid
for the lifetime of the closure and must not be passed to the outside
for later use. That's why an Array is created and used as return value.
There is a simpler solution however:
let bytes = withUnsafeBytes(of: &bigEndian) { Array($0) }
Explanation: withUnsafeBytes invokes the closure with a UnsafeRawBufferPointer to the storage of the bigEndian variable.
Since UnsafeRawBufferPointer is a Sequence of UInt8, an array
can be created from that with Array($0).
You can extend Numeric protocol and create a data property as follow:
Swift 4 or later
extension Numeric {
var data: Data {
var source = self
return Data(bytes: &source, count: MemoryLayout<Self>.size)
}
}
Since Swift 3 Data conforms to RandomAccessCollection so you can just create an array of bytes from your UInt16 bigEndian data:
extension Data {
var array: [UInt8] { return Array(self) }
}
let lenghtByte = UInt16(8)
let bytePtr = lenghtByte.bigEndian.data.array // [0, 8]

Issues using withMemoryRebound in Swift 3

I have an app that was previously using UnsafeMutablePointer to call C-functions like so:
var size = HOST_BASIC_INFO_COUNT
let hostInfo = host_basic_info_t.allocate(capacity: 1)
let result = host_info(machHost, HOST_BASIC_INFO,
UnsafeMutablePointer(hostInfo), &size)
Since moving to Swift 3, Xcode Beta 6 you are prompted to use withMemoryRebound. Problem is I don't understand how to use it in this situation, and there is no documentation or sample code for it yet.
My approach:
var size = HOST_BASIC_INFO_COUNT
let hostInfo = host_basic_info_t.allocate(capacity: 1)
let temp = hostInfo.withMemoryRebound(to: host_info_t!.self, capacity: Int(size)) {
UnsafeBufferPointer(start: $0, count: Int(size))
}
let result = host_statistics(machHost,
HOST_BASIC_INFO,
temp.baseAddress?.pointee,
&size)
Simply crashes with Bad Access. What is the correct way to use withMemoryRebound?
hostInfo is a UnsafeMutablePointer<host_basic_info>, and that pointer
must be "rebound" to a pointer to HOST_BASIC_INFO_COUNT items
of integer_t, as expected by the hostInfo() function:
let HOST_BASIC_INFO_COUNT = MemoryLayout<host_basic_info>.stride/MemoryLayout<integer_t>.stride
var size = mach_msg_type_number_t(HOST_BASIC_INFO_COUNT)
let hostInfo = host_basic_info_t.allocate(capacity: 1)
let result = hostInfo.withMemoryRebound(to: integer_t.self, capacity: HOST_BASIC_INFO_COUNT) {
host_info(mach_host_self(), HOST_BASIC_INFO, $0, &size)
}
print(result, hostInfo.pointee)
hostInfo.deallocate(capacity: 1)
Instead of allocate/deallocate you can also work with a
local variable and pass its address:
let HOST_BASIC_INFO_COUNT = MemoryLayout<host_basic_info>.stride/MemoryLayout<integer_t>.stride
var size = mach_msg_type_number_t(HOST_BASIC_INFO_COUNT)
var hostInfo = host_basic_info()
let result = withUnsafeMutablePointer(to: &hostInfo) {
$0.withMemoryRebound(to: integer_t.self, capacity: Int(size)) {
host_info(mach_host_self(), Int32(HOST_BASIC_INFO), $0, &size)
}
}
print(result, hostInfo)
this one should work better with swift 3
result = withUnsafePointer(to: &size) {
$0.withMemoryRebound(to: integer_t.self, capacity: HOST_BASIC_INFO_COUNT) {
host_info(machHost, HOST_BASIC_INFO,
$0,
&size)
}
withMemoryRebound is very similar to withUnsafePointer function.
capacity need to be size of host_info_t.
You need to do like below :
let temp = hostInfo.withMemoryRebound(to: host_info_t.self or type(of: host_info), capacity: MemoryLayout<host_info_t>.stride * Int(size))
additionally, you don't need UnsafeBufferPointer.