How to copy [UInt8] array to C pointer in Swift 3? - ios

I'm trying to copy Swift [UInt8] buffer to a C pointer. I can't find the right solution, here is my code:
uploadBodyBytes = [UInt8]()
...
...
var data = crl.uploadBodyBytes[crl.bodyBytesUploaded..<crl.bodyBytesUploaded+actualLen]
_ = data.withUnsafeBytes({ (rawData /*provides UnsafeRawBufferPointer*/) -> UnsafeMutableRawPointer in
return memcpy(a, rawData /*expected UnsafeRawPointer*/, actualLen)
})
data.withUnsafeBytes gives me UnsafeRawBufferPointer but that seems to be incompatible with memcpy which expects UnsafeRawPointer.
Help appreciated.

You can access it with the baseAddress property, but you don't need to.

Related

Swift convert Data to UnsafeMutablePointer<Int8>

I'm calling a function in an objective c class from swift.
-(char *)decrypt:(char *)crypt el:(int)el{}
when calling this function from swift, it asks for an UnsafeMutablePointer<Int8> as the value for the parameter 'crypt'
the value for the 'crypt' is comming from a server and it is a base64encoded string. So I decode that string and got a Data object.
let resultData = Data(base64Encoded: base64String)
Now I need to pass this data to the above mentioned function. I have tried to convert this Data object to a UnsafeMutablePointer<Int8>
resultData?.withUnsafeBytes { (u8Ptr: UnsafeMutablePointer<Int8>) in
let decBytes = tea?.decrypt(u8Ptr , el: el)}
But it is not compiling. Gives below error
'UnsafeMutablePointer' is not convertible to 'UnsafePointer<_>'
I don't know much about objective c. So could anyone help me to pass this parameter to objective c function.
you have to change UnsafeMutablePointer to UnsafePointer
UnsafePointer
resultData?.withUnsafeBytes {(bytes: UnsafePointer<CChar>)->Void in
//Use `bytes` inside this closure
}
UnsafeMutablePointer
var data2 = Data(capacity: 1024)
data2.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<UInt8>) -> Void in
//Use `bytes` inside this closure
})
Edit, updated my answer for two things:
Not returning the pointer from withUnsafeBytes
Accounting for Swift 5' deprecation warning: 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R instead
// buffer pointer captured is converted to byte pointer which is used in the block to decode the base64 encoded Data
encodedStringData.withUnsafeMutableBytes { (rawBufferPtr: UnsafeMutableRawBufferPointer) in
if let rawPtr = rawBufferPtr.baseAddress {
let decodedString = String(bytesNoCopy: rawPtr, length: rawBufferPtr.count, encoding: .utf8, freeWhenDone: false)
print(decodedString!)
}
}
someData.withUnsafeBytes { (bufferRawPtr: UnsafeRawBufferPointer) in
// For converting an UnsafeRawBufferPointer to its typed variant, in this case UnsafeBufferPointer<UInt8>
let bufferTypedPtr = bufferRawPtr.bindMemory(to: UInt8.self)
// Then, getting the typed UnsafePointer, in this case UnsafePointer<UInt8>
let unsafePointer = bufferTypedPtr.baseAddress!
}
Note: Swift 5 doesn't allow you to access encodedStringData from within the withUnsafeMutableBytes block! Read Swift 5 Exclusivity Enforcement for why.
Capturing the pointer outside of the block is apparently not recommended, it works but the behavior can get to be undefined in the future
Old answer:
This will help someone looking for getting to the underlying raw bytes (in a UnsafeMutablePointer<UInt8> representation) of a Data object as a variable for further use (instead of having to write all of the logic in the withUnsafeMutableBytes block).
var encodedStringData = Data(base64Encoded: "Vmlub2QgaXMgZ3JlYXQh")!
// byte pointer variable used later to decode the base64 encoded Data
let rawPtr: UnsafeMutablePointer<UInt8> = encodedStringData.withUnsafeMutableBytes { (bytePtr: UnsafeMutablePointer<UInt8>) in bytePtr }
let decodedString = String(bytesNoCopy: rawPtr, length: encodedStringData.count, encoding: .utf8, freeWhenDone: false)
print(decodedString, encodedStringData)
Solution using NSData
let data = NSData(bytes: arrayOfUInt8, length: arrayOfUInt8.count)
let pointer: UnsafeMutablePointer<Int8> = data.bytes.assumingMemoryBound(to: UInt8.self)

UnsafePointer<UInt8> initializer in Swift 3

I have a receipt validation class that is deprecated since Swift 3 has released. I fixed some issues, but I still have many ...
Here is the GitHub source code I used : https://gist.github.com/baileysh9/4386ea92b047d97c7285#file-parsing_productids-swift and https://gist.github.com/baileysh9/eddcba49d544635b3cf5
First Error :
var p = UnsafePointer<UInt8>(data.bytes)
Compiler throws : Cannot invoke initializer for type UnsafePointer(UInt8) with an argument list of type UnsafeRawPointer
Second error
while (ptr < end)
Binary operators < cannot be applied to two UnsafePointer(UInt8) operands
Thank you very much in advance :)
EDIT
Thanks to LinShiwei answer I found a solution to UnsafePointer declaration. It compiles but not tested yet (because other errors avoid me to test) :
func getProductIdFromReceipt(_ data:Data) -> String?
{
let tempData: NSMutableData = NSMutableData(length: 26)!
data.withUnsafeBytes {
tempData.replaceBytes(in: NSMakeRange(0, data.count), withBytes: $0)
}
var p: UnsafePointer? = tempData.bytes.assumingMemoryBound(to: UInt8.self)
In Swift 3, you cannot init an UnsafePointer using an UnsafeRawPointer.
You can use assumingMemoryBound(to:) to convert an UnsafeRawPointer into an UnsafePointer<T>. Like this:
var ptr = data.bytes.assumingMemoryBound(to: UInt8.self)
Use debugDescription or distance(to:) to compare two pointer.
while(ptr.debugDescription < endPtr.debugDescription)
or
while(ptr.distance(to:endPtr) > 0)
It is possible to put a regular pointer sign i C & in front of a Int8 array or Uint8 array to make a pointer to supply a C-function input. Like the &aBuffer array below (but the data array must copied locally first, to keep control of the storage of the data storage until finished with the operation, you get an error else). Here in a routine handling dropInteraction data (delivering a byte array):
func interpretInstanceData(filename: String, Buffer: [UInt8]) -> String {
var aBuffer = Buffer
let sInstanceData = String(cString: Ios_C_InterpretInstanceData(filename, &aBuffer, Int32(aBuffer.count)))
The question is slightly old. But googling for a solution on the topic of converting a swift byte array to a C-pointer (what else is UnsafePointer< UInt8 >). This question made a hit. But I think this answer is helpful for later editions of Swift (that I use). Would have worked even then. Worked in any kind of use needing a pointer from swift (just make the array of right type first).
May have recently changed to just this, without the ".bytes." part:
var p: UnsafePointer = data.assumingMemoryBound(to: UInt8.self)
from the original:
var p = UnsafePointer<UInt8>(data.bytes)

Casting UnsafeMutablePointers to UnsafeMutableRawPointers

I have a couple of issues updating to swift 3.0. I have the following code:
// Retrieve the Device GUID
let device = UIDevice.current
let uuid = device.identifierForVendor
let mutableData = NSMutableData(length: 16)
(uuid! as NSUUID).getBytes(UnsafeMutablePointer(mutableData!.mutableBytes))
// Verify the hash
var hash = Array<UInt8>(repeating: 0, count: 20)
var ctx = SHA_CTX()
SHA1_Init(&ctx)
SHA1_Update(&ctx, mutableData!.bytes, mutableData!.length)
SHA1_Update(&ctx, (opaqueData1! as NSData).bytes, opaqueData1!.count)
SHA1_Update(&ctx, (bundleIdData1! as NSData).bytes, bundleIdData1!.count)
SHA1_Final(&hash, &ctx)
let computedHashData1 = Data(bytes: UnsafePointer(&hash), count: 20)
My first issue is with the line of code:
(uuid! as NSUUID).getBytes(UnsafeMutablePointer(mutableData!.mutableBytes))
mutableData!.mutableBytes now returns an UnsafeMutableRawPointer and the compiler complains that "cannot invoke initializer for type 'UnsafeMutablePointer<_> with an argument of type '(UnsafeMutableRawPointer)'" Now I have been trying to cast them to the same types but have had no success.
My second issue is with the line:
let computedHashData1 = Data(bytes: UnsafePointer(&hash), count: 20)
This line causes a compiler error "Ambiguous use of 'init'"
Your first issue, you can write something like this:
(uuid! as NSUUID).getBytes(mutableData!.mutableBytes.assumingMemoryBound(to: UInt8.self))
But if you can accept Data having the same raw UUID bytes, you can write it as:
var uuidBytes = uuid!.uuid
let data = Data(bytes: &uuidBytes, count: MemoryLayout.size(ofValue: uuidBytes))
Your second issue, in Data.init(bytes:count:), the type of the first parameter is UnsafeRawPointer, to which you can pass arbitrary type of Unsafe(Mutable)Pointers.
Using Swift with Cocoa and Objective-C (Swift 3)
Check the Constant Pointers part of Pointers.
When a function is declared as taking an UnsafeRawPointer argument,
it can accept the same operands as UnsafePointer<Type> for any type
Type.
You have no need to cast pointer types.
let computedHashData1 = Data(bytes: &hash, count: 20)

Converting C pointers to Swift 3

I have the code:
let data = Data(bytes: UnsafePointer<UInt8>(audioBuffer.mData), count: Int(bufferSize))
and
let u16 = UnsafePointer<Int32>(audioBuffer.mData).pointee
Both of which work in Swift 2.3 but not in Swift 3. How do I convert them so they act equivalently? (and why?)
To read 16-bit audio samples from Audio Unit callback buffers in Swift 3, I use:
let bufferPointer = UnsafeMutableRawPointer(mBuffers.mData)
if var bptr = bufferPointer {
for i in 0..<(Int(frameCount)) {
let oneSampleI16 = bptr.assumingMemoryBound(to: Int16.self).pointee
// do something with the audio sample
bptr += 1
}
}
The rest of the Audio Session and Audio Unit code is in this gist: https://gist.github.com/hotpaw2/630a466cc830e3d129b9
I can't say I understand this well, nor have I read the document, but it looks like swift3 pointer casts are scoped to avoid or limit aliasing, so you can't (easily) have two different views of the same piece of memory, or at least not for very long. This means you must either copy the cast data out or do whatever you need to do within a cast callback.
Why eliminate aliasing? I guess it makes for happier compilers.
For Data:
// [NS]Data. probably copying the data
Data(bytes: audioBuffer.mData!, count: Int(audioBuffer.mDataByteSize))
For numeric arrays:
// cast the data to Int32s & (optionally) copy the data out
let umpInt32 = audioBuffer.mData!.assumingMemoryBound(to: Int32.self)
let frameCount = Int(audioBuffer.mDataByteSize/4)
var u32 = [Int32](repeating: 0, count: frameCount)
// copy data from buffer
u32.withUnsafeMutableBufferPointer {
$0.baseAddress!.initialize(from: umpInt32, count: frameCount)
}
p.s. there's some confusion in your code. is u16 supposed to be an array of Int32s? Or UInt16s? Or something else?
Check the latest reference of Data.init(bytes:count:).
The type of the parameter bytes is UnsafeRawPointer, which accepts UnsafeMutableRawPointer. And the type of AudioBuffer.mData is UnsafeMutableRawPointer?. You have no need to convert using initializer.
let data = Data(bytes: audioBuffer.mData!, count: Int(bufferSize))
(You just need to explicitly unwrap mData, as it is imported as nullable type, UnsafeMutableRawPointer?, but you need to pass non-nil UnsafeRawPointer (or UnsafeMutableRawPointer).
The second example, you'd better check what sort of methods are available for UnsafeMutableRawPointer. You can find load(fromByteOffset:as:) method, and can use it like this.
let i32 = audioBuffer.mData!.load(as: Int32.self)
`load(

Swift String to unsigned char [](CUnsignedChar)

Hello i'm trying to use an old legacy C library that uses buffers (unsigned char []) to transform the data. The main issue here is that I couldn't find a way to transform a String to a CUnsignedChar and then be able to alloc that buffer to a UnsafeMutablePointer
If you want to convert a Swift string to an immutable C string to pass to a C function, try this:
let s: String = "hello, world!"
s.nulTerminatedUTF8.withUnsafeBufferPointer { p -> Void in
puts(UnsafePointer<Int8>(p.baseAddress))
Void()
}
You may need to use UnsafePointer<UInt8> if your function takes an unsigned char *.
I turn String to NSData first ,then turn NSData to CUnsignedChar array
if let data = str.dataUsingEncoding(NSUTF8StringEncoding){
var rawData = [CUnsignedChar](count: data.length,repeatedValue: 0)
data.getBytes(&rawData, length: data.length)
}

Resources