How to copy a pointer to an array in Swift? - c

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 "こんいちは"

Related

Is there a way to pass fixed array as parameter to utility function is Solidity?

I have two arrays with fixed lengths which I want to send to a utility function, but it gives the following error since arrays length are different.
TypeError: Invalid type for argument in function call. Invalid implicit conversion from string storage ref[2] storage ref to string storage ref[] storage pointer requested.
Is there a way to make a utility function that will take fixed arrays with arbitrary lengths?
Full code snippet
contract test {
string[2] internal list1 = ["str1", "str2"];
string[3] internal list2 = ["str3", "str4", "str5"];
function entryPoint() public view returns (uint256) {
return utilityFunction(list1, list2);
}
function utilityFunction(string[] storage _list1, string[] storage _list2) internal pure returns (uint256 result) {
// some logic here
}
}
You can have the function accept the predefined fixed length
// changed `string[]` to `string[2]` and the other `string[]` to `string[3]`
function utilityFunction(string[2] storage _list1, string[3] storage _list2) internal pure returns (uint256 result) {
Or you can convert the arrays to dynamic-size at first, and them pass them to the dynamic-size accepting function
function entryPoint() public view returns (uint256) {
// declare a dynamic-size array in memory with 2 empty items
string[] memory _list1 = new string[](2);
// assign values to the dynamic-size array
_list1[0] = list1[0];
_list1[1] = list1[1];
string[] memory _list2 = new string[](3);
_list2[0] = list2[0];
_list2[1] = list2[1];
_list2[2] = list2[2];
// pass the dynamic-size in-memory arrays
return utilityFunction(_list1, _list2);
}
// changed location to memory, now you can accept dynamic-size arrays with arbitrary length
function utilityFunction(string[] memory _list1, string[] memory _list2) internal pure returns (uint256 result) {
}
But there's currently (v0.8) no way to accept fixed-size arrays with arbitrary length. Just with the predefined length.
Note: There's some space for gas optimization in my entryPoint() example (by making in-memory copies of the list1 and list2 at first, and then reading from the in-memory copies when assigning the _list1 and _list2 values). But my goal was to make a clearer example of assigning to the dynamic-size array instead of worsening code readability by optimizations.

Why does the first character of my string get cut off when I pass from swift to 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.

How to create a generic array with nullable values in kotlin

Okay, so to try to learn the kotlin language, I am trying to implement a heap.
class Heap <T : Comparable<T>>(private var heap: Array<T>, private var size: Int){
I am having some difficulties with how the nullable types behave. In the beginning I gave a array of elements, but I started having difficulties when I wanted to allocate more elements
private fun increaseSize(){
if(size == heap.size){
//okay this is garbage
var enlargedHeap = arrayOfNulls<Comparable<*>>(heap.size * 2) as Array<T>
System.arraycopy(heap, 0, enlargedHeap, 0, size)
heap = enlargedHeap
}
}
So here I am guessing I am changing the heap from Array<T> to Array<T?> which makes sense. So I changed the constructor as well, to take Array<T?> and it suggests some pretty interesting things wherever I try to access heap[x]
else heap[pos1].compareTo(heap[pos2]) < 0
to
else heap[pos2]?.let { heap[pos1]?.compareTo(it) }!! < 0
then I looked at a tooltip stating that arrayOfNulls<Comparable<*>>(heap.size * 2) as Array<T> returned Array<T>
But when doing the arrayOfNulls it indeed returns an array with null values. However I get an error if I try to heap[x] = null
stating
Null cannot be a value of non-null type T
If I change the constructor to take a Array<T?>, then input arrays have to explicitly nullable too.
Which I fixed with
class Heap <T : Comparable<T>>{
private var heap: Array<T?>
private var size = 0
constructor(heap: Array<T>, size){
this.heap = heap as Array<T?>
this.size = size
}
But now it does not accept arrays that has null values when I try to instantiate it with an array with nullable values
var arr = arrayOfNulls<String>(9)
var heap = Heap(arr, arr.size-1)
So now I need two constructors? What is going on
Type mismatch. Required: Comparable Found:String?
Even with Àrray<T?> there is errors with the compareTo's not accepting nullable values.
Even with checks it still gives an error
return if(pos2 > size || (heap[pos1] == null || heap[pos2] == null)) false
else heap[pos1].compareTo(heap[pos2]) < 0
Type mismatch. Required:T Found: T?
How do I allow the array to contain null values?
Hopefully without making Array<T> nullable since it breaks the compareTo's and multiple constructors seems like bad design.
Arrays are tricky to work with in a generic class because they have reified types.
I think you need to move the backing array property out of the constructor so the constructor won't be ambiguous about nullability. Otherwise type inference won't work with the constructor, and you would also be forced to use a nullable-typed array as the constructor parameter even if you want the heap to have a non-nullable type. Anyway, it's good that you copy the array from the constructor rather than using it directly as a backing array, because otherwise there is the possibility that some outside object could still be modifying that backing array.
Since Arrays always have reified type, I think you also need to use a backing array of type Any?. This will mean you have to cast objects that are passed out of the heap in the public functions. You can make a single get function for that, and use it internally with this[] to avoid having to put casts all over your class. The unchecked casting is safe as long as you don't put anything besides a T in the part of the array below size, and you don't try to get values above size, since they're always null and T might not be nullable.
class Heap <T : Comparable<T>>(initialValues: Array<T>, private var size: Int) {
init {
if (size > initialValues.size)
error("Initial array is too small for initial size.")
}
private var heap = Array<Any?>(size){ initialValues[it] }
#Suppress("UNCHECKED_CAST")
private operator fun get(index: Int) = heap[index] as T
private fun increaseSize(){
var enlargedHeap = arrayOfNulls<Any>(heap.size * 2)
System.arraycopy(heap, 0, enlargedHeap, 0, size)
heap = enlargedHeap
}
fun pop(): T {
if (size == 0)
error("Cannot pop when size is 0")
size--
val out = this[size] // Use this class's getter to avoid having to cast
heap[size] = null
return out
}
fun push(value: T) {
if (size == heap.size)
increaseSize()
heap[size] = value
size++
}
}
You might also consider moving the size parameter out of the constructor to make usage simpler and avoid the ambiguous case of what happens if the input size is smaller than the array you pass in.
class Heap <T : Comparable<T>>(initialValues: Array<T>) {
private var size = initialValues.size
private var heap = Array<Any?>(size){ initialValues[it] }
//...
Please use Array<T?>, instead of Array<T>.
E.g. in your class signature, properties should be:
class Heap <T : Comparable<T>>(private var heap: Array<T?>, private var size: Int) {
If you need input with not null values, please use something like this:
private var heap = initialHeap.toArray(); // copy input parameter to internal array

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.)

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