Errors when update code to avoid deprecation warnings withUnsafeMutableBytes in swift 5 - swift

I've updated to swift 5 and one of the dependencies I use won't compile in swift 5. I've fixed it, but now I'm getting 350+ deprecation warnings all over the file. They're all similar to this:
withUnsafeMutableBytes is deprecated: use withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R instead
And this is a snipit of the code (it's basically just calling a c library's functions):
var k = Data(count: crypto_generichash_keybytes())
k.withUnsafeMutableBytes { kPtr in
flutter_sodium.crypto_generichash_keygen(kPtr)
}
For reference, in the above crypto_generichash_keybytes() just returns a size_t and crypto_generichash_keygen's signature is void crypto_generichash_keygen(unsigned char k[crypto_generichash_KEYBYTES]);.
I figured out (as this answer states) that the way to get around this should be to call kPtr.baseAddress:
var k = Data(count: crypto_generichash_keybytes())
k.withUnsafeMutableBytes { kPtr in
flutter_sodium.crypto_generichash_keygen(kPtr.baseAddress)
}
as that should use the withUnsafeMutableBytes<ResultType> variant rather than the deprecated withUnsafeMutableBytes<ResultType, ContentType>. However, this instead results in the error
value of type 'UnsafeMutablePointer<_>' has no member 'baseAddress'.
If I explicitly specify the resultType and kPtr:
var k = Data(count: crypto_generichash_keybytes())
k.withUnsafeMutableBytes { (kPtr: UnsafeMutableRawBufferPointer) -> Void in
flutter_sodium.crypto_generichash_keygen(kPtr.baseAddress)
}
I instead get
UnsafeMutableRawBufferPointer' is not convertible to 'UnsafeMutablePointer<_>'.
Are there any swift experts out there that can help me figure out the right way to do this? I know the warnings are just warnings, but I prefer to have code that compiles with no warnings.
I took a look at Swift 5.0: 'withUnsafeBytes' is deprecated: use `withUnsafeBytes<R>(...) before posting this question and it doesn't help my situation as I'm not loading the pointer but rather using the data. Also, I've done exactly what the documentation tells me to but that still isn't helping.
EDIT: To be a bit more clear, some of the 350+ warnings were related to code where the Data is allocated in the code, however some of them are where I receive Data from an external source. That looks something like this:
let args = call.arguments as! NSDictionary
let server_pk = (args["server_pk"] as! FlutterStandardTypedData).data
let server_sk = (args["server_sk"] as! FlutterStandardTypedData).data
let client_pk = (args["client_pk"] as! FlutterStandardTypedData).data
var rx = Data(count: flutter_sodium.crypto_kx_sessionkeybytes())
var tx = Data(count: flutter_sodium.crypto_kx_sessionkeybytes())
let ret = rx.withUnsafeMutableBytes { rxPtr in
tx.withUnsafeMutableBytes { txPtr in
server_pk.withUnsafeBytes { server_pkPtr in
server_sk.withUnsafeBytes { server_skPtr in
client_pk.withUnsafeBytes { client_pkPtr in
flutter_sodium.crypto_kx_server_session_keys(rxPtr, txPtr, server_pkPtr, server_skPtr, client_pkPtr)
}
}
}
}
}
with the corresponding method call
SODIUM_EXPORT
int crypto_kx_client_session_keys(unsigned char rx[crypto_kx_SESSIONKEYBYTES],
unsigned char tx[crypto_kx_SESSIONKEYBYTES],
const unsigned char client_pk[crypto_kx_PUBLICKEYBYTES],
const unsigned char client_sk[crypto_kx_SECRETKEYBYTES],
const unsigned char server_pk[crypto_kx_PUBLICKEYBYTES])
__attribute__ ((warn_unused_result));
(and I know that the code is not really optimal swift, but when dealing with interoperability between dart and swift this is what the flutter team came up with for how to do it).
When I asked the question I was trying to distill it down to the simplest case but that case had a specific answer which differs to the overall problem I'm having.

I wouldn't use Data here ā€“ Data represents an untyped collection of "raw" bytes, however crypto_generichash_keygen wants a mutable pointer to typed memory. The reason why the UnsafeMutablePointer<T> variant of withUnsafeMutableBytes was deprecated is that it's fundamentally the wrong abstraction to be providing on untyped memory.
The simplest way to get a buffer of typed memory in Swift is with an Array:
var k = [UInt8](repeating: 0, count: crypto_generichash_keybytes())
flutter_sodium.crypto_generichash_keygen(&k)
You can always turn the resulting buffer into a Data value afterwards by saying Data(k).
Another option is to use an UnsafeMutableBufferPointer:
let k = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: crypto_generichash_keybytes())
defer {
k.deallocate()
}
flutter_sodium.crypto_generichash_keygen(k.baseAddress!)
// Now use the buffer `k` ā€“ just make sure you finish using it before the end of
// the scope when `deallocate()` gets called!
Unlike Array, this avoids having to pre-fill the resulting buffer with zeros before being passed off to the C API, however this likely isn't of concern. But just like Array, you can turn such a buffer into a Data by just saying Data(k).
For cases where you get handed a Data value from some external source and need to pass it off to an API as a typed pointer, the simplest and safest option is to just turn it into an array before passing it by saying Array(someData).
For example:
let args = call.arguments as! NSDictionary
let server_pk = (args["server_pk"] as! FlutterStandardTypedData).data
let server_sk = (args["server_sk"] as! FlutterStandardTypedData).data
let client_pk = (args["client_pk"] as! FlutterStandardTypedData).data
var rx = [UInt8](repeating: 0, count: flutter_sodium.crypto_kx_sessionkeybytes())
var tx = [UInt8](repeating: 0, count: flutter_sodium.crypto_kx_sessionkeybytes())
flutter_sodium.crypto_kx_server_session_keys(
&rx, &tx, Array(server_pk), Array(server_sk), Array(client_pk)
)
You probably could use withUnsafeBytes and call bindMemory on the underlying pointer, but I would discourage it, as it changes the type of the underlying memory which could subtly impact the soundness of any other Swift code sharing that memory due to the fact that you're switching out the type from under it.

Related

List all window names in Swift

Iā€™m learning Swift. How do I fix the following code to list the window names?
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
if let window = CFArrayGetValueAtIndex(windows, i) {
print(CFDictionaryGetValue(window, kCGWindowName))
}
}
The error:
main.swift:6:32: error: cannot convert value of type 'UnsafeRawPointer' to expected argument type 'CFDictionary?'
print(CFDictionaryGetValue(window, kCGWindowName))
^~~~~~
as! CFDictionary
It becomes easier if you avoid using the Core Foundation types and methods, and bridge the values to native Swift types as early as possible.
Here, CGWindowListCopyWindowInfo() returns an optional CFArray of CFDictionaries, and that can be bridged to the corresponding Swift type [[String : Any]]. Then you can access its values with the usual Swift methods (array enumeration and dictionary subscripting):
if let windowInfo = CGWindowListCopyWindowInfo(.optionAll, kCGNullWindowID) as? [[ String : Any]] {
for windowDict in windowInfo {
if let windowName = windowDict[kCGWindowName as String] as? String {
print(windowName)
}
}
}
You can use unsafeBitCast(_:to:) to convert the opaque raw pointer to a CFDictionary. Note that you'll also need to convert the second parameter, to a raw pointer:
CFDictionaryGetValue(unsafeBitCast(window, to: CFDictionary.self), unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self))
unsafeBitCast(_:to:) tells the compiler to treat that variable as another type, however it's not very safe (thus the unsafe prefix), recommending to read the documentation for more details, especially the following note:
Warning
Calling this function breaks the guarantees of the Swift type system; use with extreme care.
In your particular case there should not be any problems using the function, since you're working with the appropriate types, as declared in the documentation of the Foundation functions you're calling.
Complete, workable code could look something like this:
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
let windowDict = unsafeBitCast(CFArrayGetValueAtIndex(windows, i), to: CFDictionary.self)
let rawWindowNameKey = unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self)
let rawWindowName = CFDictionaryGetValue(windowDict, rawWindowNameKey)
let windowName = unsafeBitCast(rawWindowName, to: CFString?.self) as String?
print(windowName ?? "")
}
Update
You can bring the CoreFoundation array sooner to the Swift world by casting right from the start:
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID) as? [[AnyHashable: Any]]
windows?.forEach { window in
print(window[kCGWindowName])
}
The code is much readable, however it might pose performance problems, as the cast to [[AnyHashable: Any]]` can be expensive for large array consisting of large dictionaries.

What does pointee property do in swift?

let data = Data()
data.withUnsafeBytes { (uPtr: UnsafePointer<UInt8>) in
var ptr = uPtr
var j = ptr.pointee
var new = ptr.advanced(by: 1)
}
What does j represent or store here, is it pointing to the next byte
of the data? Will new variable store the next byte? Any Link To Learn Pointer in A Simpler Way In Swift And What Are the Advantages of using pointer in Swift?
pointee is the value being stored and pointed to by the pointer. It used to be called memory in earlier versions of Swift. Don't use pointers in Swift unless you absolutely must, because it involves you in retaining and releasing memory manually:
UnsafePointer provides no automated memory management or alignment
guarantees. You are responsible for handling the life cycle of any
memory you work with through unsafe pointers to avoid leaks or
undefined behavior.
In your example you have no data but if you were to have data then the first pointer would first of all point to the memory address of the first value in the data array, e.g.
var str = "Hello, playground"
guard let data = str.data(using: .utf8) else {fatalError()}
data.withUnsafeBytes { (uPtr: UnsafePointer<UInt8>) in
let ptr = uPtr
ptr.pointee // 72
let ptr2 = ptr.advanced(by: 1)
ptr2.pointee // 101
}
So here we'd expect ptr.pointee to be the utf8 value of "H" which is 72, and when we advance by 1 we then have a pointer to the value of "e" which is 101, i.e. we are working our way through the Array. But you wouldn't want to actually do this because it assumes that the memory addresses for each item in the array are contiguous, which they might not be.
A more sober approach to retrieving the bytes would be:
data.withUnsafeBytes { [UInt8](UnsafeBufferPointer(start:$0, count:data.count))
}
or better still:
[UInt8](data)
As outlined in this post.

How to convert UnsafePointer<[Float]> or fit Float Array data to Unsafe[Mutable]Pointer<Float> in Swift 3?

I have an array of float value (floatArray) coming from HDF5 file, and I'd like to use this data as an input for the function that accepts Unsafe[Mutable]Pointer (inputForFunction). I'm new to UnsafePointer, so I need someone's help.
One way I have in mind is, to store the array to a file, then to open it and to map the file descriptor to the memory of the variable for the input of the function:
// read the data from HDF5 file
var floatArray: [Float] = try originalHDF5DataSet.read()
// store the data
let dataToStore = NSData(bytes: &floatArray, length: sizeOfArray * MemoryLayout<Float>.size)
let tmpPath = URL(fileURLWithPath: NSTemporaryDirectory())
let filePath = tmpPath.appendingPathComponent("floatArrayData").appendingPathExtension("dat")
do{
try data.write(to: filePath, options: .atomic)
let fileManager : FileManager = FileManager.default
if fileManager.fileExists(atPath:filePath.path){
print("Success")
}else{
print("Fail")
}
// open the data
let dataPath = filePath.path
let fd = open(dataPath, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
assert(fd != -1, "Error: failed to open output file at \""+dataPath+"\" errno = \(errno)\n")
// memory map the parameters
let hdr = mmap(nil, Int(sizeOfArray), PROT_READ, MAP_FILE | MAP_SHARED, fd, 0)
// cast Void pointers to Float
let inputForFunction = UnsafePointer<Float>(hdr!.assumingMemoryBound(to: Float.self))
} catch{
print("failed")
}
However, it may not be the best implementation because the process to store the data seems time-consuming.
That's why I'm considering to use UnsafePointer to pass the pointer from floatArray to inputForFunction, but I'm not sure how to implement it. Just for your information, one way I'm thinking of is to use withUnsafePointermethod directly:
var floatArray: [Float] = try originalHDF5DataSet.read()
var inputForFunction = UnsafeMutablePointer<Float>.allocate(capacity: Int(sizeOfArray))
withUnsafePointer(to: &floatArray[0]) { (ptr: UnsafePointer<Float>) in
inputForFunction = UnsafeMutablePointer(mutating: ptr)
}
or the other way is to use withMemoryRebound (but the following didn't work):
var floatArray: [Float] = try originalHDF5DataSet.read()
var inputForFunction = UnsafeMutablePointer<Float>.allocate(capacity: Int(sizeOfArray))
withUnsafePointer(to: &floatArray) { (ptrDataArray: UnsafePointer<[Float]>) in
inputForFunction = ptrDataArray.withMemoryRebound(to: inputForFunction.self, capacity: Int(sizeOfArray), { (ptr: UnsafePointer<[Float]>) -> UnsafeMutablePointer<Float> in
return ptr[0]
})
}
My question is
How to pass the address of floatArray's data to inputForFunction?
If I need to convert from UnsafePointer<[Float]> to Unsafe[Mutable]Pointer, how to do it?
If needed, how to use withMemoryRebound for this purpose? Can I assign
the address/value to inputForFunction inside the closure?
When I checked the addresses of floatArray and floatArray[0], they were different (This seems different from C/C++). Which address should be passed to inputForFunction?
You can find many articles explaining how you can get Unsafe(Mutable)Pointer<Float> from [Float]. Some of them answer some of your questions. Try find them later.
How to pass the address of floatArray's data to inputForFunction?
You usually use the Array's methods withUnsafeBufferPointer or withUnsafeMutableBufferPointer. You can get an Unsafe(Mutable)BufferPointer<Element> inside the closure parameter, which contains an Unsafe(Mutable)Pointer<Element> as its baseAddress property.
var floatArray: [Float] = try originalHDF5DataSet.read()
floatArray.withUnsafeBufferPointer {unsafeBufferPointer in
let inputForFunction = unsafeBufferPointer.baseAddress
//`unsafeBufferPointer` (including its `baseAddress`) is valid only in this closure.
//So, do not pass `inputForFunction` outside of the closure, use it inside.
//...
}
If I need to convert from UnsafePointer<[Float]> to Unsafe[Mutable]Pointer, how to do it?
When you find something like UnsafePointer<[Float]>, then you are going the wrong way. In Swift, Array is a hidden struct which may contain (multi-level) indirect reference to its elements. You usually have no need to use UnsafePointer<Array<Float>>.
If needed, how to use withMemoryRebound for this purpose? Can I assign the address/value to inputForFunction inside the closure?
When you have an Array of AType and want to pass it as an Unsafe(Mutable)Pointer<AnotherType>, you may utilize withMemoryRebound. But in your case, you only need to work with Float, so withMemoryRebound may not be needed for your purpose.
When I checked the addresses of floatArray and floatArray[0], they were different (This seems different from C/C++). Which address should be passed to inputForFunction?
None. As I wrote above, address of floatArray is just pointing somewhere which contains a hidden struct, so it's completely useless for your purpose. And address of floatArray[0] is neither guaranteed to be the address of the first element of the whole content. Swift may create a temporal region which contains only one element, and passing the address of the temporal region.
By the way, do you really need to convert your [Float] to Unsafe(Mutable)Pointer<Float>?
If your functions' signature is something like this:
func takingUnsafePointer(_ pointer: UnsafePointer<Float>)
You can call it like:
takingUnsafePointer(floatArray)
//In this case `floatArray` can be a `let` constant.
Or else, if the function signature is:
func takingUnsafeMutablePointer(_ pointer: UnsafeMutablePointer<Float>)
Call it as:
takingUnsafeMutablePointer(&floatArray)
//In this case `floatArray` needs to be a `var`, not `let`.
Remember, in both cases, the passed pointer is valid only while the function call. You should not export the pointer somewhere else.
Array type have withUnsafeBufferPointer method.
This method accept closure with UnsafeBufferPointer which is like UnsafePointer<[Float]> you're asking for.
In you need UnsafePointer to the start of Array you can use baseAddress property of UnsafeBufferPointer.
Example code:
let bufferPointer: UnsafeBufferPointer<Float> = floatArray.withUnsafeBufferPointer { bufferPointer in
return bufferPointer
}
or
let baseAddress: UnsafePointer<Float> = floatArray.withUnsafeBufferPointer { bufferPointer in
return bufferPointer.baseAddress
}
EDIT: Or if it's function just passing &floatArray may work.

Calling getsectiondata from Swift

This question and answer describe how to read data from a Mach-O section with Objective-C on modern OS X/macOS versions: Crash reading bytes from getsectbyname
The described answer works. I'm trying to implement the same thing with Swift. I can't make it work.
I have the following in "Other linker flags": -Wl,-sectcreate,__LOCALIZATIONS,__base,en.lproj/Localizable.strings,-segprot,__LOCALIZATIONS,r,r.
This Swift code gets me the a pointer to the embedded data, until I try to run the code outside Xcode and ASLR breaks it:
var size: UInt = 0
let _localizationSection = getsectdata(
"__LOCALIZATIONS",
"__base",
&size)
To get around the ASLR problem, according to the above question and answer, and based on my own testing, I should be using getsectiondata instead. It works great in Objective-C, but I'm having no luck in Swift. The following is the only thing I've managed to get past the compiler, but it returns nil:
var size: UInt = 0
var header = _mh_execute_header
let localizationSection = getsectiondata(
&header,
"__LOCALIZATIONS",
"__base",
&size)
Is taking a copy of _mh_execute_header the problem and is there any way to avoid it? I need an UnsafePointer<mach_header_64>, but using &_mh_execute_header as the first parameter to getsectiondata causes a compilation error.
I'm using Swift 3.0, and running my code on macOS 10.12.
The difference between the linked-to Objective-C code
void *ptr = getsectiondata(&_mh_execute_header, ...);
and your Swift translation
var header = _mh_execute_header
let localizationSection = getsectiondata(&header, ...)
is that the latter passes the address of a copy of the global
_mh_execute_header variable to the function, and apparently that
is not accepted. If you modify the Objective-C code to
struct mach_header_64 header = _mh_execute_header;
void *ptr = getsectiondata(&header, ...);
then it fails as well (and actually crashed in my test).
Now the problem is that _mh_execute_header is exposed to Swift
as a constant:
public let _mh_execute_header: mach_header_64
and one cannot take the address of a constant in Swift. One possible
workaround is to define
#import <mach-o/ldsyms.h>
static const struct mach_header_64 *mhExecHeaderPtr = &_mh_execute_header;
in the bridging header file, and then use it as
let localizationSection = getsectiondata(mhExecHeaderPtr, ...)
in Swift.
Another option is to lookup the symbol via dlopen/dlsym
import MachO
if let handle = dlopen(nil, RTLD_LAZY) {
defer { dlclose(handle) }
if let ptr = dlsym(handle, MH_EXECUTE_SYM) {
let mhExecHeaderPtr = ptr.assumingMemoryBound(to: mach_header_64.self)
var size: UInt = 0
let localizationSection = getsectiondata(
mhExecHeaderPtr,
"__LOCALIZATIONS",
"__base",
&size)
// ...
}
}

Convert String to UnsafeMutablePointer<char_t> in Swift

I'm working with a third party c API I'm trying to call one of the functions with a simple string. Something like this:
some_c_func("aString");
I get a build error:
Type 'UnsafeMutablePointer<char_t>' does not conform to protocol 'StringLiteralConvertible'
I've seen some suggestions to use utf8 on String or similar conversions, which gets nearly there, but with the following error:
some_c_func("aString".cStringUsingEncoding(NSUTF8StringEncoding));
'UnsafePointer<Int8>' is not convertible to 'UnsafeMutablePointer<char_t>'
How can I create an UnsafeMutablePointer?
It all depends on what char_t is.
If char_t converts to Int8 then the following will work.
if let cString = str.cStringUsingEncoding(NSUTF8StringEncoding) {
some_c_func(strdup(cString))
}
This can be collapsed to
some_c_func(strdup(str.cStringUsingEncoding(NSUTF8StringEncoding)!))
WARNING! This second method will cause a crash if func cStringUsingEncoding(_:) returns nil.
Updating for Swift 3, and to fix memory leak
If the C string is only needed in a local scope, then no strdup() is needed.
guard let cString = str.cString(using: .utf8) else {
return
}
some_c_func(cString)
cString will have the same memory lifecycle as str (well similar at least).
If the C string needs to live outside the local scope, then you will need a copy. That copy will need to be freed.
guard let interimString = str.cString(using: .utf8), let cString = strdup(interimString) else {
return
}
some_c_func(cString)
//ā€¦
free(cString)
it may be simpler than that - many C APIs pass strings around as char * types, and swift treats these as unsafe.
try updating the C API (good) or hack it's header files (bad) to declare these as const char * instead.
in my experience this allows you to pass standard swift String types directly to the C API.
apparently a constant is required, in order to conform to the protocol.
I haven't tried passing strings like that, but I have a C function that I call from Swift, that takes a lot more parameters than shown here, among which is a reference to a Swift C typecast buffer to hold an error string. The compiler doesn't complain and the function call works. Hopefully this will steer you closer to the answer and you can provide an update with the final answer or someone else can.
var err = [CChar](count: 256, repeatedValue: 0)
var rv = somefunc((UnsafeMutablePointer<Int8>)(err))
if (rv < 0) {
println("Error \(err)")
return
}