Convert String to UnsafeMutablePointer<char_t> in Swift - 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
}

Related

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

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.

AudioToolbox, C-function pointers, and Swift

I'm working, tentatively, with the AudioToolbox API using Swift 2.0 and Xcode 7b6. The API uses a lot of c-language constructs, including function pointers. This is my first time working with commands like withUnsafeMutablePointer and unsafeBitCast. I am looking for a reality check to make sure that I am not way off base in what I am doing.
For example, to open a file stream, you use the following function:
func AudioFileStreamOpen(
_ inClientData: UnsafeMutablePointer<Void>
, _ inPropertyListenerProc: AudioFileStream_PropertyListenerProc
, _ inPacketsProc: AudioFileStream_PacketsProc
, _ inFileTypeHint: AudioFileTypeID
, _ outAudioFileStream: UnsafeMutablePointer<AudioFileStreamID>) -> OSStatus
Just the type signature of the function makes me start to sweat.
At any rate, the inClientData parameter needs to be an UnsafeMutablePointer<Void>, and the pointer will point to an instance of the same class I am working in. In other words, it needs to be a pointer to self. My approach is to call the function using withUnsafeMutablePointer like this:
var proxy = self
let status = withUnsafeMutablePointer(&proxy) {
AudioFileStreamOpen($0, AudioFileStreamPropertyListener
, AudioFileStreamPacketsListener, 0, &audioFileStreamID)
}
My first question is whether or not I'm using withUnsafeMutablePointer correctly here. I wasn't sure how to get a pointer to self - just writing &self doesn't work, because self is immutable. So I declared proxy as a variable and passed a reference to that, instead. I don't know if this will work or not, but it was the best idea I came up with.
Next, AudioFileStreamPropertyListener and AudioFileStreamPacketsListener are C callback functions. They each get passed the pointer to self that I created using withUnsafeMutablePointer in AudioFileStreamOpen. The pointer is passed in as an UnsafeMutablePointer<Void>, and I need to cast it back to the type of my class (AudioFileStream). To do that, I believe I need to use unsafeBitCast. For example, here is AudioFileStreamPropertyListener:
let AudioFileStreamPropertyListener: AudioFileStream_PropertyListenerProc
= { inClientData, inAudioFileStreamID, inPropertyID, ioFlags in
let audioFileStream = unsafeBitCast(inClientData, AudioFileStream.self)
audioFileStream.didChangeProperty(inPropertyID, flags: ioFlags)
}
That compiles fine, but again I'm not sure if I'm using unsafeBitCast correctly, or if that is even the correct function to be using in this kind of situation. So, is unsafeBitCast the correct way to take an UnsafeMutablePointer<Void> and cast it to a type that you can actually use inside of a C function pointer?
It's interesting that the inClientData "context" param is bridged as UnsafeMutablePointer, since I doubt the AudioToolbox APIs will modify your data. It seems it would be more appropriate if they'd used COpaquePointer. Might want to file a bug.
I think your use of withUnsafeMutablePointer is wrong. The pointer ($0) will be the address of the variable proxy, not the address of your instance. (You could say $0.memory = [a new instance] to change it out for a different instance, for example. This is a bit confusing because its type is UnsafeMutablePointer<MyClass> — and in Swift, the class type is itself a pointer/reference type.)
I was going to recommend you use Unmanaged / COpaquePointer, but I tested it, and realized this does exactly the same thing as unsafeAddressOf(self)!
These are equivalent:
let data = UnsafeMutablePointer<Void>(Unmanaged.passUnretained(self).toOpaque())
let data = unsafeAddressOf(self)
And these are equivalent:
let obj = Unmanaged<MyClass>.fromOpaque(COpaquePointer(data)).takeUnretainedValue()
let obj = unsafeBitCast(data, MyClass.self)
While the Unmanaged approach makes logical sense, I think you can see why it might be prefereable to use unsafeAddressOf/unsafeBitCast :-)
Or, you might consider an extension on Unmanaged for your own convenience:
extension Unmanaged
{
func toVoidPointer() -> UnsafeMutablePointer<Void> {
return UnsafeMutablePointer<Void>(toOpaque())
}
static func fromVoidPointer(value: UnsafeMutablePointer<Void>) -> Unmanaged<Instance> {
return fromOpaque(COpaquePointer(value))
}
}
Then you can use:
let data = Unmanaged.passUnretained(self).toVoidPointer()
let obj = Unmanaged<MyClass>.fromVoidPointer(data).takeUnretainedValue()
Of course, you will need to ensure that your object is being retained for the duration that you expect it to be valid in callbacks. You could use passRetained, but I would recommend having your top-level controller hold onto it.
See some related discussion at https://forums.developer.apple.com/thread/5134#15725.

Swift convert string to UnsafeMutablePointer<Int8>

I have a C function mapped to Swift defined as:
func swe_set_eph_path(path: UnsafeMutablePointer<Int8>) -> Void
I am trying to pass a path to the function and have tried:
var path = [Int8](count: 1024, repeatedValue: 0);
for i in 0...NSBundle.mainBundle().bundlePath.lengthOfBytesUsingEncoding(NSUTF16StringEncoding)-1
{
var range = i..<i+1
path[i] = String.toInt(NSBundle.mainBundle().bundlePath[range])
}
println("\(path)")
swe_set_ephe_path(&path)
but on the path[i] line I get the error:
'subscript' is unavailable: cannot subscript String with a range of
Int
swe_set_ephe_path(NSBundle.mainBundle().bundlePath)
nor
swe_set_ephe_path(&NSBundle.mainBundle().bundlePath)
don't work either
Besides not working, I feel there has got to be a better, less convoluted way of doing this. Previous answers on StackOverflow using CString don't seem to work anymore. Any suggestions?
Previous answers on StackOverflow using CString don't seem to work anymore
Nevertheless, UnsafePointer<Int8> is a C string. If your context absolutely requires an UnsafeMutablePointer, just coerce, like this:
let s = NSBundle.mainBundle().bundlePath
let cs = (s as NSString).UTF8String
var buffer = UnsafeMutablePointer<Int8>(cs)
swe_set_ephe_path(buffer)
Of course I don't have your swe_set_ephe_path, but it works fine in my testing when it is stubbed like this:
func swe_set_ephe_path(path: UnsafeMutablePointer<Int8>) {
println(String.fromCString(path))
}
In current version of Swift language you can do it like this (other answers are outdated):
let path = Bundle.main.bundlePath
let param = UnsafeMutablePointer<Int8>(mutating: (path as NSString).utf8String)
It’s actually extremely irritating of the library you’re using that it requires (in the C declaration) a char * path rather than const char * path. (this is assuming the function doesn’t mutate the input string – if it does, you’re in a whole different situation).
If it didn’t, the function would come over to Swift as:
// note, UnsafePointer not UnsafeMutablePointer
func swe_set_eph_path(path: UnsafePointer<Int8>) -> Void
and you could then rely on Swift’s implicit conversion:
let str = "blah"
swe_set_eph_path(str) // Swift implicitly converts Strings
// to const C strings when calling C funcs
But you can do an unsafe conversion quite easily, in combination with the withCString function:
str.withCString { cstr in
swe_set_eph_path(UnsafeMutablePointer(cstr))
}
I had a static library (someLibrary.a) written in C++ compiled for iOS.
The header file (someLibrary.h) had a function exposed like this:
extern long someFunction(char* aString);
The declaration in Swift looks like this:
Int someFunction(aString: UnsafeMutablePointer<Int8>)
I made an extension to String:
extension String {
var UTF8CString: UnsafeMutablePointer<Int8> {
return UnsafeMutablePointer((self as NSString).UTF8String)
}
}
So then I can call the method like so:
someFunction(mySwiftString.UTF8CString)
Update: Make String extension (swift 5.7)
extension String {
var UTF8CString: UnsafeMutablePointer<Int8> {
return UnsafeMutablePointer(mutating: (self as NSString).utf8String!)
}
}

Swift basic expression

I'm very new to swift, but proficient in other languages like Java, JavaScript, C, ... I'm lost with Swift syntax when it comes to create expressions. Look at this basic example where I just try to find out if one string is contained into another by calling String.rangeOfString that returns an Optional Range (Range?)
This works as expected:
let LEXEMA:String="http://"
let longUrl:String="http://badgirls.many/picture.png"
let range=longUrl.rangeOfString(LEXEMA);
if (range? != nil) {
// blah
}
Now I'm trying to combine the expression inside the if, something like:
if (longUrl.rangeOfString(LEXEMA)? !=nil) {
// blah
}
But I always get syntax errors, the above yields a "Expected Separator" and can't understand why. Done some more tests:
if (absolutePath.rangeOfString(URL_LEXEMA) !=nil) { }
Expected Separator before "!"
if absolutePath.rangeOfString(URL_LEXEMA) !=nil { }
Braced block of statements is an unused closure
What am I doing wrong?
If you’re coming from other like Java, you might be thinking of optionals like pointers/references, and so used to equating them to nil and if non-nil, using them. But this is probably going to lead to more confusion. Instead, think of them like a container for a possible result, that you need to unwrap to use. if let combines the test and unwrapping operation.
With this in mind, here’s how you could adapt your code:
let LEXEMA: String="http://"
let longUrl: String="http://badgirls.many/picture.png"
if let range = longUrl.rangeOfString(LEXEMA) {
// use range, which will be the unwrapped non-optional range
}
else {
// no such range, perhaps log an error if this shouldn’t happen
}
Note, that ? suffixing behaviour you were using changes in Swift 1.2 so even the code in your question that compiles in 1.1 won’t in 1.2.
It’s possible that sometimes you are whether there was a value returned, but you don’t actually need that value, just to know it wasn’t nil. In that case, you can compare the value to nil without the let:
if longUrl.rangeOfString(LEXEMA) != nil {
// there was a value, but you don't care what that value was
}
That said, the above is probably better expressed as:
if longUrl.hasPrefix(LEXEMA) { }
For starters:
You don't need parenthesis with if statements unless you have nested parenthetical subexpressions that require it.
You don't need to specify the type on the left side of the = of a let or var declaration if Swift can figure it out from the right side of the =. Very often Swift can figure it out, and you can tell that Swift can figure it out, so you can avoid that redundant clutter.
You do need to specify the type if Swift cannot figure out the type from
the right side. Example:
For example, consider the following lines:
let LEXEMA = "http://"
let longUrl = "http://badgirls.many/picture.png"
Swift can figure out that they're strings.
Similarly for this function or class that returns a UIView:
var myView = ViewReturningClassOrFunc()
Consider this:
#IBOutlet var myView : UIView!
In the above line, Swift cannot figure out ahead of time it will be assigned a UIView, so you have to provide the type. By providing a ! at the end you've made it an implicitly unwrapped optional. That means, like ?, you're indicating that it can be nil, but that you are confident it will never be nil at the time you access it, so Swift won't require you to put a ! after it when you reference it. That trick is a time saver and big convenience.
You should NOT add the ? to the line:
if (longUrl.rangeOfString(URL_LEXEMA) !=nil) {
As another answer pointed out, you're missing the let.
if let longUrl.rangeOfString(URL_LEXEMA) {
println("What do I win? :-)")
}
swift is case sensitive language. you need to check about whitespaces as well
if longUrl.rangeOfString(LEXEMA) != nil {
//your condition
}
there should be space between statement != nil
Just add a space between != and nil like:
if longUrl.rangeOfString(LEXEMA) != nil {
// blah
}
I tested your code in playground, an error of Expected ',' separator reported.
And do not forget the rules that 1s and 0s and Airspeed Velocity said.

Working with C strings in Swift, or: How to convert UnsafePointer<CChar> to CString

While playing with Standard C Library functions in Swift, I came across problems
when passing C strings around. As a simple example (just to demonstrate the problem), the Standard C Library function
char * strdup(const char *s1);
is exposed to Swift as
func strdup(_: CString) -> UnsafePointer<CChar>
which means that the return value of strdup() cannot be passed to another strdup() call:
let s1 : CString = "abc"
let s2 = strdup(s1) // OK, s2 is a UnsafePointer<CChar>
let s3 = strdup(s2) // error: could not find an overload for '__conversion' that accepts the supplied arguments
My question is: How to create a Swift CString from a UnsafePointer<CChar>,
so that the C string returned by one standard library function can be passed to another function?
The only way that I could find is (using code from How do you convert a String to a CString in the Swift Language?):
let s2a = String.fromCString(s2).bridgeToObjectiveC().UTF8String
let s3 = strdup(s2a)
But I do not find this satisfying for two reasons:
It is too complicated for a simple task.
(Main reason:) The above conversions works only if the C string is a valid UTF-8
string, otherwise it fails with a runtime exception. But a C string is an arbitrary
sequence of characters, delimited by a NUL character.
Remarks/Background: Of course, high-level functions using high-level data structures like Swift String or Objective-C NSString are preferable. But there are BSD functions in the
Standard C Library which do not have an exact counterpart in the Foundation frameworks.
I came across this problem while trying to answer Accessing temp directory in Swift.
Here, mkdtemp() is a BSD function for which no exact NSFileManager replacement exists
(as far as I know).
mkdtemp() returns a UnsafePointer<CChar> which has to be passed to the
NSFileManager function stringWithFileSystemRepresentation which takes a CString
argument.
Update: As of Xcode 6 beta 6, this problem does not exist anymore because the mapping of C-Strings into Swift has been simplified. You can just write
let s1 = "abc" // String
let s2 = strdup(s1) // UnsafeMutablePointer<Int8>
let s3 = strdup(s2) // UnsafeMutablePointer<Int8>
let s4 = String.fromCString(s3) // String
Swift 1.1 (or perhaps earlier) has even better C string bridging:
let haystack = "This is a simple string"
let needle = "simple"
let result = String.fromCString(strstr(haystack, needle))
The CString type is gone completely.
The String struct in Swift has an init routine you can use like:
let myString = String(cString: myUnsafePointer)
see also init(cString: UnsafePointer)