Why does the first character of my string get cut off when I pass from swift to C? - c

Here is the function in swift to convert from a swift string to a C string
func swiftStringToCString(swiftString: String) -> UnsafeMutablePointer<CString>?{
let convertedCString: [CChar]? = swiftString.cString(using: .utf8)
if let safeConvertedCString = convertedCString {
var cString = UnsafeMutablePointer<CString>.allocate(capacity: 1)
//defer {
// cString.deallocate()
//}
cString.pointee.count = UInt32(safeConvertedCString.count) - 1
cString.pointee.data = UnsafePointer<Int8>(safeConvertedCString)
return cString
}
else
{
return nil
}
}
The CString struct is defined in a C header file:
typedef struct {
const char *data;
uint32_t count;
} CString;
I also have an addition test function which simply prints out the string passed in:
extern void __cdecl testCString(CString *pCString);
When I call
testCString(swiftStringToCString(swiftString: "swiftString"))
This gets printed out:
wiftString
I also noticed that I get the warning
Initialization of 'UnsafePointer<Int8>' results in a dangling pointer
when I do
cString.pointee.data = UnsafePointer<Int8>(safeConvertedCString)

This approach is incorrect. You can't just hold onto pointers into a String's internal storage and expect it to stick around past the current line of code. That's what the warning is telling you.
In order to ensure that a pointer is valid, you need to use .withUnsafeBufferPointer. I would expect something along these lines:
"swiftString".utf8CString.withUnsafeBufferPointer { buffer in
var cString = CString(data: buffer.baseAddress!, count: UInt32(buffer.count))
testCString(&cString);
}
This ensures that the utf8 buffer exists until the end of the block. If you want to hold onto it beyond that, you're going to need to add code to copy those bytes into memory you allocate and release yourself.

Related

Swift: Turning a String into UnsafeMutablePointer<Int8>

I have a C function mapped to Swift defined as
predictX010(inputString: UnsafePointer<emxArray_char_T>!, ypred: UnsafeMutablePointer<emxArray_real_T>!)
I want to input a string in the inputString, but in order to do that I have to play around with emxArray_char_T which is
emxArray_char_T.init(data: UnsafeMutablePointer<Int8>!, size: UnsafeMutablePointer<Int32>!, allocatedSize: Int32, numDimensions: Int32, canFreeData: boolean_T)
My string will consist of let x = ":1580222503,GCP001,007,Male,30,Left,1,IL8 and IL10,0000; 0,281411,-78,521074,-3,344657,132,347776,-93,25,44" and I just cannot figure out how to input it in data of the emxArray_char_T
First you should write a wrapper function in your bridging header that accepts char pointers then cast/create the emxArray_char_T in c IE:
// casting the func as void because you didn't specify
void predictWrapper(const char *aChar, char *bChar) {
// do your casting and call original func predictX010(...)
}
Then in swift (This isn't going to be pretty)
var arg: String = "some arg"
var arg2: String = "another arg"
// use closure to prevent dangling pointer
arg.withCString{ body in // body is UnsafePointer<Int8>
arg2.withCString{ body2 in // we'll cast this to UnsafeMutablePointer
var arg2Mutable = UnsafeMutablePointer<Int8>(mutating: body2)
//call your wrapper
predictWrapper(body, arg2Mutable)
}
}
You may be able to use the original types and function, but i've always found it easier (less banging my head on the desk) to use the most standard c types you can in swift and casting to custom/complex types in c

Why does this tuple -> String conversion trigger sporadic memory overlap exception

I am working with some mixed C-Swift code that has a construction for converting a C-string in a struct to a Swift string like so:
struct MyCStruct { // from the c header file
char string[75];
}
extension MyCStruct {
var swiftString: String {
get { return String(cString: UnsafeRawPointer([self.string]).assumingMemoryBound(to: CChar.self)) }
}
}
As of Swift 5.1, I started getting a sporadic memory overlap exception, possibly triggered by memory allocation elsewhere or a thread context switch. I was able to fix it by changing the get to:
get { return withUnsafePointer(to: self.string) { ptr -> String in
return String(cString: UnsafeRawPointer(ptr).assumingMemoryBound(to: CChar.self))
} }
But, I am wondering is this just masking some deeper problem unrelated to the original construction? Or, is the original construction faulty, and just lucky it never crashed before?
Your method relies on undefined behavior. Here
UnsafeRawPointer([self.string])
a temporary array is created, and the address of its element storage is passed to the UnsafeRawPointer() initializer. That memory address is only valid for the duration of of the init call, but must not be used when the initializer returns.
What you can do is to get a pointer to the string variable itself (which is mapped as a tuple to Swift) and rebind that (compare Converting a C char array to a String):
extension MyCStruct {
var swiftString: String {
withUnsafePointer(to: self.string) {
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: self.string)) {
String(cString: $0)
}
}
}
}
(It is assumed – as in your original code – that the C array of character is null-terminated.)

How to copy a pointer to an array in Swift?

I'm basically trying to wrap a C structure within a Swift class.
The Swift class has an instance property of type of the C structure.
The C structure contains several properties which are of type const char *.
In order to assign values to the structure, I wrote getters and setters for each property:
public class MyClass: NSObject {
private var configuration = c_structure_config()
}
// Function of MyClass
// f.ex. on "registrationUri:"
private(set) public var registrationUri: String {
get {
return String(cString: configuration.reg_uri)
}
set {
if (configuration.reg_uri != nil) {
configuration.reg_uri.deallocate()
}
let maxLength = newValue.count + 1 // +1 for NULL
var buffer: [CChar] = [CChar](UnsafeMutableBufferPointer<Int8>.allocate(capacity: maxLength))
guard newValue.getCString(&buffer, maxLength: maxLength, encoding: .utf8) == true else {
fatalError("Could not allocate memory for Account Config reg uri")
}
// "configuration" is the instance property (see above)
// reg_uri is of type char const *
configuration.reg_uri = UnsafePointer<Int8>(buffer)
}
}
However, this approach leads to weird crashes and error reports complaining about pointers overlapping range (Fatal error: UnsafeMutablePointer.initialize overlapping range).
I know that I'm deallocating and allocating memory whenever the string is set and that that's probably not very efficient. I haven't found a better solution so far though.
What's wrong here (or is this right, I made a wrong assumption and I gotta search somewhere else)?
There are a couple of problems with your solution.
First, it is ill-advised to use String.count to count to get the size of the underlying C buffer. The reason is that String.count returns the number of actual characters of your string, and not the number of bytes used to represent it. Not all characters fit the 256 bits of Int8 (a.k.a. CChar). Hence, your code will probably crash if you attempt setting your property with non-ascii characters. It is better to use the String.utf8CString property, which returns a contiguous array of CChar.
Second, since your buffer is allocated within the setter, it will be deallocated when your setter returns (Swift arrays are instances of value types, who's lifetime is bound by the stack). That means the pointer corresponding to buffer's base is actually invalidated when your setter returns. I suspect this is the reason why the error you reported occurs at runtime.
Finally, please do not test for true in guards and if statements.
Here is a corrected version:
var registrationUri: String {
get {
return String(cString: reg_uri)
}
set {
if (reg_uri != nil) {
reg_uri.deallocate()
}
newValue.withCString { cString in
// No need to add 1, newValue.utf8CString already has the correct buffer capacity.
let capacity = newValue.utf8CString.count
let buffer: UnsafeMutablePointer<Int8> = .allocate(capacity: capacity)
buffer.assign(from: cString, count: capacity)
reg_uri = UnsafePointer(buffer)
}
}
}
// ...
myInstance.registrationUri = "こんいちは"
print(myInstance.registrationUri)
// Prints "こんいちは"

Modifying value char** from C callback in Swift and Avoiding EXC_BAD_ACCESS

I am working on a project that implements a C library using Swift. So far I have been able to manage how to get String from C strings and some others.
Now I am facing an issue when dealing with a C callback that returns OUT variables type char**. The swift code needs to reallocate memory and change the value. These variables are used for String type.
The Header for the C function is:
DllExport void STDCALL DvProviderGetProtocolInfo(THandle aProvider, CallbackGetProtocolInfo aCallback, void* aPtr);
And the header for the C Callback is:
typedef int32_t (STDCALL *CallbackGetProtocolInfo)(void* aPtr, IDvInvocationC* aInvocation, void* aInvocationPtr, char** aSource, char** aSink);
In swift I call the function like that:
DvProviderGetProtocolInfo(prvHandleId, { (pointer, aInvocation, aInvocationPtr, aSource, aSink) -> Int32 in
let senderClass:SenderClass = bridgeToTypeUnretained(ptr: pointer!)
senderClass.writeCStringValue(from: aSource, withValue: senderClass.sourceProtocolInfoArray)
senderClass.writeCStringValue(from: aSink, withValue: senderClass.sinkProtocolInfoArray)
return 0
}, bridgeToPointerRetained(obj: self))
The functions used are:
public func writeCStringValue(from pointer:UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?, withValue value:String){
pointer!.pointee = UnsafeMutablePointer<Int8>.allocate(capacity:value.utf8.count)
strcpy(pointer!.pointee, value)
}
And declared in another Swift File:
/*** Convert const void* To Any T ***/
func bridgeToTypeRetained<T : AnyObject>(ptr : UnsafeMutableRawPointer) -> T {
return Unmanaged<T>.fromOpaque(ptr).takeRetainedValue()
}
func bridgeToTypeUnretained<T : AnyObject>(ptr : UnsafeRawPointer) -> T {
return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
}
/*** Convert const void* To Any T ***/
func bridgeToPointerRetained<T : AnyObject>(obj : T) -> UnsafeMutableRawPointer {
return UnsafeMutableRawPointer(Unmanaged.passRetained(obj).toOpaque())
}
func bridgeToPointerUnretained<T : AnyObject>(obj : T) -> UnsafeMutableRawPointer {
return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
}
So far, for small values, writeCStringValue function works fine but when I try to send a long String like:
let aTest = "http-get:*:audio/m4a:*,http-get:*:audio/x-m4a:*,http-get:*:audio/aiff:*,http-get:*:audio/x-aiff:*,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3,http-get:*:audio/mp4:*,http-get:*:audio/wav:*,http-get:*:audio/wave:*,http-get:*:audio/x-wav:*,http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG,http-get:*:image/png:DLNA.ORG_PN=PNG_TN,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG"
I get a EXC_BAD_ACCESS at the end of writeCStringValue function.
If I include the code in writeCStringValue function inside the callback, then there is no crash.
Ideally i would like to use writeCStringValue function.
Am i changing the values of char** correctly?
Thank you
strcpy(pointer!.pointee, value)
creates a temporary C string representation of the Swift string value,
and copies that to the address given by pointer!.pointee.
C strings are delimited by a trailing null character and that is not
taken into account in the allocation
pointer!.pointee = UnsafeMutablePointer<Int8>.allocate(capacity:value.utf8.count)
Therefore strcpy() copies one char more than is allocated. That may or may
not cause a crash, but is undefined behavior in any case.
strdup() does both the allocation and the copy, so a simpler solution is
pointer?.pointee = strdup(value)
That may be better anyway, if the C function (eventually) releases the string with free().

Swift 3: Compilation error converting String Array to C Char Array

I have the following C struct:
typedef struct {
char** categories;
int category_size;
} category_fmc_s_type;
My Swift array has the following values:
let categories = ["Weekday", "Weekend"]
I want to populate the C Struct field 'categories' with 'Weekday' & 'Weekend'. To do this I call my toPointer():
fileprivate static func toPointer(_ args: [String]) -> UnsafeMutablePointer<UnsafeMutablePointer<Int8>> {
let buffer = UnsafeMutablePointer<UnsafeMutablePointer<Int8>>.allocate(capacity: args.count)
for (index, value) in args.enumerated() {
buffer[index] = UnsafeMutablePointer<Int8>(mutating: (value as NSString).utf8String!)
}
return buffer
}
I keep getting the following XCode 8 error:
Cannot convert value of type 'UnsafeMutablePointer<UnsafeMutablePointer<Int8>>' to expected argument type 'UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!'
Any suggestions? I don't understand why there is the optional and '!' in the C-Struct definition implicitly.
As the compiler emits as an error, you need to unwrap after Int8 w/ "?" as follows.
fileprivate func toPointer(_ args: [String]) -> UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> {
let buffer = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: args.count)
for (index, value) in args.enumerated() {
buffer[index] = UnsafeMutablePointer<Int8>(mutating: (value as NSString).utf8String!)
}
return buffer
}
then,
func testMyCat() {
let categories = ["Weekday", "Weekend"]
let buffer = toPointer(categories)
var mycat = category_fmc_s_type()
mycat.categories = buffer // you would see compile error w/o "?"
}
the code above works w/o error. Martin's solution gives a compile error at
mycat.categories = &cargs (see the link)
I don't know why.
Check the reference of utf8String property of NSString:
Discussion
This C string is a pointer to a structure inside the string object,
which may have a lifetime shorter than the string object and will
certainly not have a longer lifetime. Therefore, you should copy the
C string if it needs to be stored outside of the memory context in
which you use this property.
The term memory context is not well-defined, but one thing sure is that you cannot expect the allocated region for the C string would live forever. When the member categories in the category_fmc_s_type is accessed, the pointers may be pointing to the already freed regions.
Applying the suggestion from Martin R to your code, your code would be like this:
fileprivate static func toPointer(_ args: [String]) -> UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> {
let buffer = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: args.count)
buffer.initialize(from: args.lazy.map{strdup($0)})
return buffer
}
And remember, after you finish using the category_fmc_s_type, you need to deallocate the regions allocated by strdup(_:) and UnsafeMutablePointer.allocate(capacity:):
fileprivate static func freePointer(_ pointers: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>, count: Int) {
for i in 0..<count {
free(pointers[i])
}
pointers.deinitialize(count: count)
pointers.deallocate(capacity: count)
}

Resources