Reputation: 8970
I'm looking for the simplest ways to achieve reasonable C interoperability in Swift, and my current block is converting an UnsafePointer<Int8>
(which was a const char *
), into an [Int8]
array.
Currently, I have a naïve algorithm that can take an UnsafePointer
and a number of bytes and converts it to an array, element by element:
func convert(length: Int, data: UnsafePointer<Int8>) {
let buffer = UnsafeBufferPointer(start: data, count: length);
var arr: [Int8] = [Int8]()
for (var i = 0; i < length; i++) {
arr.append(buffer[i])
}
}
The loop itself can be sped up by using arr.reserveCapacity(length)
, however that does not remove the issue of the loop itself.
I'm aware of this SO question which covers how to convert UnsafePointer<Int8>
to String
, however String
is a different beast entirely to [T]
. Is there a convenient Swift way of copying length bytes from an UnsafePointer<T>
into a [T]
? I'd prefer pure Swift methods, without passing through NSData
or similar. If the above algorithm is really the only way to do it, I'm happy to stick with that.
Upvotes: 33
Views: 21954
Reputation: 2737
You can also use the Array init function:
init(unsafeUninitializedCapacity: Int, initializingWith initializer: (inout UnsafeMutableBufferPointer<Element>, inout Int) throws -> Void) rethrows
First convert the unsafeRawPointer to unsafePointer if needed Then convert the pointer to a buffer pointer, and finally the buffer pointer to an array.
Example, assuming you have an unsafeRawPointer (dataPtr
) and its size (dataSize
)
let numberOfItems = dataSize / MemoryLayout<MyClass>.stride
let myArray = dataPtr.withMemoryRebound(to: MyClass.self,
capacity: numberOfItems) { typedPtr in
// Convert pointer to buffer pointer to access buffer via indices
let bufferPointer = UnsafeBufferPointer(start: typedPtr, count: numberOfItems)
// Construct array
return [MyClass](unsafeUninitializedCapacity: numberOfItems) { arrayBuffer, count in
count = numberOfItems
for i in 0..<count {
arrayBuffer[i] = bufferPointer[i]
}
}
}
Upvotes: 0
Reputation: 539765
You can simply initialize a Swift Array
from an UnsafeBufferPointer
:
func convert(length: Int, data: UnsafePointer<Int8>) -> [Int8] {
let buffer = UnsafeBufferPointer(start: data, count: length);
return Array(buffer)
}
This creates an array of the needed size and copies the data.
Or as a generic function:
func convert<T>(count: Int, data: UnsafePointer<T>) -> [T] {
let buffer = UnsafeBufferPointer(start: data, count: count);
return Array(buffer)
}
where length
is the number of items that the pointer points to.
If you have a UInt8
pointer but want to create an [T]
array from
the pointed-to data, then this is a possible solution:
// Swift 2:
func convert<T>(length: Int, data: UnsafePointer<UInt8>, _: T.Type) -> [T] {
let buffer = UnsafeBufferPointer<T>(start: UnsafePointer(data), count: length/strideof(T));
return Array(buffer)
}
// Swift 3:
func convert<T>(length: Int, data: UnsafePointer<UInt8>, _: T.Type) -> [T] {
let numItems = length/MemoryLayout<T>.stride
let buffer = data.withMemoryRebound(to: T.self, capacity: numItems) {
UnsafeBufferPointer(start: $0, count: numItems)
}
return Array(buffer)
}
where length
now is the number of bytes. Example:
let arr = convert(12, data: ptr, Float.self)
would create an array of 3 Float
s from the 12 bytes pointed to by ptr
.
Upvotes: 52
Reputation: 827
extension NSData {
public func convertToBytes() -> [UInt8] {
let count = self.length / sizeof(UInt8)
var bytesArray = [UInt8](count: count, repeatedValue: 0)
self.getBytes(&bytesArray, length:count * sizeof(UInt8))
return bytesArray
}
}
You can convert row data to byts (Uint8)
Copy Extension and use it..
Upvotes: 2