I have the following C header file which describes a non-null terminated string in C
typedef struct RMFFI_NntString {
const char *data;
uint64_t len;
} RMFFI_NntString;
I was hoping to create an unowned wrapper to this. Originally, I was using String.init(bytesNoCopy:
, but after it's deprecation in macOS 13, have learnt that it might actually be copying bytes. I've had some discussion on this here. But, am starting this new thread, so as not to take that one off topic.
Ideally, I'd like my API to this wrapper to be an extension to Swift String with a new init - String.init?(rmffiNntString: RMFFI_NntString) {}
. My first attempt at this was
extension String {
public init?(rmFfiNntString: RMFFI_NntString) {
self.init(decoding: buffer, as: UTF8.self)
let data = Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: stringData), count: Int(rmFfiNntString.len), deallocator: .none)
self.init(data: data, encoding: .utf8)
}
}
This works. But, couple of replies on the other thread suggest I should be using String.init(decoding:as:)
. However, I don't see a direct way to do this, and the best I've been able to come up with is
public init?(rmFfiNntString: RMFFI_NntString) {
guard let stringData = rmFfiNntString.data else {
return nil
}
let string = UnsafeBufferPointer(start: stringData, count: Int(rmFfiNntString.len))
.withMemoryRebound(to: UInt8.self) { buffer in
// Need to rebind as cchar is Int8, while the decoding protocol for UTf8 expects UInt8
return String.init(decoding: buffer, as: UTF8.self)
}
// Have to do two init's - although the second one might be just a simple copy?
self.init(string)
}
From David Smith's reply on the other thread, it looks like using String(data:...
) might produce a bridged String, while String(decoding:...
will always produce a native Swift String.
Would there be a better way to create a native Swift String than the one outlined above? Thanks