How to extract UnsafePointer<CGFloat> from UnsafePointer<CGPoint> - Swift - 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)")
}

Related

subdata method in Swift doesn't seem to be doing as I want

I have the following code in a playground (Swift 5)
import Foundation
let array : [UInt8] = [0,1,2,3,4,5,6,7,8,9,10,11,12]
public extension Data {
func uint32( offset:Int)-> UInt32{
let range = offset..<(offset+4)
let copy = self.subdata(in: range)
print(copy as NSData) // Prints <02030405>
return copy.withUnsafeBytes{
$0.load(fromByteOffset: 0, as: UInt32.self).bigEndian
}
}
}
let data = Data(array)
let datadropped = data.dropFirst(2)
print(data as NSData) // Prints <00010203 04050607 08090a0b 0c>
print(datadropped as NSData) // Prints <02030405 06070809 0a0b0c>
let sub = data.subdata(in: 4..<8 ) // gives 4,5,6,7
let sub2 = datadropped.subdata(in: 4..<8) // also gives 4,5,6,7
data.uint32(offset: 2)
Now if I set the offset in the final line as 0 or 1 it crashes. An offset of 2 works but returns a uint constructed using the bytes 02,03,04,05 which is not what I would expect. The documentation states the dropFirst() and subdata() return copies of the data.
I did get my uint32 function working with the following code. But I would like to know why the ranges of bytes in the initial function are not working. How do I force a genuine new copy of the Data? If someone could explain it to me I'd be grateful.
extension Data
func uint32( offset:Int)-> UInt32{
let array = Array(0...3).map {
uint8(offset: $0+ offset)
}
return array.withUnsafeBytes{
$0.load(fromByteOffset: 0, as: UInt32.self).bigEndian
}
}
func uint8( offset:Int)-> UInt8 {
return self.withUnsafeBytes{
$0.load(fromByteOffset: offset, as: UInt8.self).bigEndian
}
}
}
datadropped is a Slice
It contains the subset of the data but it shares the same indices with the original collection. It crashes because the first index of datadropped is 2, not 0.
To get a new Data object you have to write
let datadropped = Data(data.dropFirst(2))
For more information about slices please watch WWDC 2018: Using Collections Effectively (from 11:00)
Note: You can drop the fromByteOffset parameter
return copy.withUnsafeBytes{
$0.load(as: UInt32.self).bigEndian
}

Swift 4 Int32 to [UInt8]

I am trying to get the bytes from an integer into an [UInt8] to send them over a wire protocol. While I found answers that work for Swift 2/3, none of the solutions work for Swift 4.
The following snippet works to encode a message for small message sizes (just the raw string data prepended with a network byte order Int32 size):
func send(message: String) {
let messageSize = message.utf8.count
let encodedMessageSize = Int32(messageSize).bigEndian
let frameSize = messageSize + 4
var buffer: [UInt8] = Array()
buffer.append(0)
buffer.append(0)
buffer.append(0)
buffer.append(UInt8(messageSize))
buffer.append(contentsOf: message.utf8)
outputStream.write(buffer, maxLength: frameSize)
}
I have also tried using raw pointers directly, but cannot get anything to work for Swift 4 along that avenue either.
The overall tasks is to encode and frame messages that consist of integers and strings. The encoding converts everything to strings and adds a null at the end of each string. The framing simply prepends the message with a network byte order Int32 size. I cannot change the protocol, but am willing to consider other approaches to achieving this end.
cheers,
[EDIT] Updated code using #MartinR's code (with #Hamish's suggestion). Also made some progress of the overall task in the mean time.
func encodeMessagePart(_ message: String) -> [UInt8] {
var buffer: [UInt8] = Array(message.utf8)
buffer.append(0)
return buffer
}
func encodeMessagePart(_ message: Int) -> [UInt8] {
return encodeMessagePart("\(message)")
}
func frameMessage(_ buffer: [UInt8]) -> [UInt8] {
let bufferSize = buffer.count
var encodedBufferSize = Int32(bufferSize).bigEndian
let encodedBufferSizeData = withUnsafeBytes(of: &encodedBufferSize) { Data($0) }
var frame: [UInt8] = Array()
frame.append(contentsOf: encodedBufferSizeData)
frame.append(contentsOf: buffer)
return frame
}
func sendMessage(_ buffer: [UInt8]) {
let frame = frameMessage(buffer)
outputStream.write(frame, maxLength: frame.count)
}
func sendMessage(_ message: String) {
let encodedPart = encodeMessagePart(message)
sendMessage(encodedPart)
}
// func sendMessage(_ messages: Encodable...) {
// var buffer: [UInt8] = Array()
// for message in messages {
// let b = encodeMessagePart(message)
// buffer.append(contentsOf: b)
// }
// sendMessage(buffer)
// }
You can create a Data value from the integer with
let encodedMessageSize = Int32(messageSize).bigEndian
let data = withUnsafeBytes(of: encodedMessageSize) { Data($0) }
(In Swift versions before 4.2 you'll have to write
var encodedMessageSize = Int32(messageSize).bigEndian
let data = withUnsafeBytes(of: &encodedMessageSize) { Data($0) }
instead.)
The data can then be appended to the array with
buffer.append(contentsOf: data)
Alternatively you can use a data buffer instead of an array:
func send(message: String) {
let messageSize = message.utf8.count
let encodedMessageSize = Int32(messageSize).bigEndian
var data = withUnsafeBytes(of: encodedMessageSize) { Data($0) }
data.append(Data(message.utf8))
let amountWritten = data.withUnsafeBytes { [count = data.count] in
outputStream.write($0, maxLength: count)
}
}
Finally note that that the write() method might write less bytes than
provided (e.g. on network connections), so you should always check
the return value.

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 create a matrix of CAShapeLayers?

I have this code:
var triangles: [[[CAShapeLayer]]] = Array(repeating: Array(repeating: Array(repeating: 0, count: 2), count: 15), count: 15);
But it generates an "Cannot convert value of type..." compilation error.
How can I solve that? I want to access my CAShapeLayers like this:
triangles[1][2][1].fillColor = UIColor(red: 40/255, green: 73/255, blue: 80/255, alpha: 1).cgColor;
Use optionals.
var triangles: [[[CAShapeLayer?]]] = Array(repeating: Array(repeating: Array(repeating: nil, count: 2), count: 15), count: 15)
Now there's a nil instead of a 0, which is what I think you were hinting at. But every triangles[x][y][z] is now an optional type you'll have to safely unwrap.
So now you have to do something like triangles[x][y][z] = CAShapeLayer() before you do anything to that object.
Edit for correction. Thanks #OOPer
I thought about it some more, and realized I didn't really answer your question.
So you may use for loops to initialize everything (which would be a pain), or you could do something like this every time you access an index:
if triangles[x][y][z] == nil
{
triangles[x][y][z] = CAShapeLayer()
}
let bloop = triangles[x][y][z]!
bloop.fillColor = UIColor(...
Then you could pull it out into an outside method so it becomes a 1 liner. Like:
func tri(at x: Int, _ y: Int, _ z: Int) -> CAShapeLayer
{
if triangles[x][y][z] == nil
{
triangles[x][y][z] = CAShapeLayer()
}
return triangles[x][y][z]!
}
Then when using it:
tri(at: 1, 2, 1).fillColor = ...
Of course, you should pull triangles out and make it a property of the class you're in, or you can include it in the parameter list of that 1 liner method.
All that nesting makes your code hard to understand, and Array(repeating:count:) can't do what you want anyway.
func newGrid() -> [[[CAShapeLayer]]] {
func newStack() -> [CAShapeLayer] {
return (0 ..< 2).map({ _ in CAShapeLayer() })
}
func newRow() -> [[CAShapeLayer]] {
return (0 ..< 15).map({ _ in newStack() })
}
return (0 ..< 15).map({ _ in newRow() })
}
var triangles = newGrid()
You cannot use "0" as the repeating value, it will be inferred to be type [[[Int]]]. Just replace "0" with "CAShapeLayer()"

Converting C array ptr to native array in Swift 3

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.