Created
January 21, 2017 09:43
-
-
Save beccadax/8b71f46b424dc64abaa77f18556e607b to your computer and use it in GitHub Desktop.
`UnsafeNulTerminatedBufferPointer` is a Swift type which turns an `UnsafePointer` to zero-terminated data into a `Collection`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"hello world".withCString { cString in | |
// Horribly, `withCString` gives you a pointer to `Int8`, everything else wants | |
// `UInt8`, and the safe way to do this, using `withMemoryRebound`, wants a length. | |
// `unsafeBitCast` is technically invalid but happens to be work in this demo. | |
let workaroundCString = unsafeBitCast(cString, to: UnsafePointer<UInt8>.self) | |
let buffer = UnsafeNulTerminatedBufferPointer(start: workaroundCString) | |
buffer.count | |
print(String(buffer, encoding: UTF8.self)) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is apparently getting renamed or something. | |
typealias UnicodeEncoding = UnicodeCodec | |
extension String.UnicodeScalarView { | |
init<Seq: Sequence, Encoding: UnicodeEncoding>(_ codeUnits: Seq, encoding: Encoding.Type) where Seq.Iterator.Element == Encoding.CodeUnit { | |
self.init() | |
append(contentsOf: codeUnits, encoding: encoding) | |
} | |
mutating func append<Seq: Sequence, Encoding: UnicodeEncoding>(contentsOf codeUnits: Seq, encoding: Encoding.Type) where Seq.Iterator.Element == Encoding.CodeUnit { | |
var codec = encoding.init() | |
var iterator = codeUnits.makeIterator() | |
var pendingError = false | |
func clearError() { | |
guard pendingError else { return } | |
append("\u{FFFD}") | |
pendingError = false | |
} | |
while true { | |
switch codec.decode(&iterator) { | |
case .error: | |
pendingError = true | |
case .emptyInput: | |
clearError() | |
return | |
case .scalarValue(let scalar): | |
clearError() | |
append(scalar) | |
} | |
} | |
} | |
} | |
extension String { | |
init<Seq: Sequence, Encoding: UnicodeEncoding>(_ codeUnits: Seq, encoding: Encoding.Type) where Seq.Iterator.Element == Encoding.CodeUnit { | |
self.init(UnicodeScalarView(codeUnits, encoding: encoding)) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protocol Nulable { | |
var isNul: Bool { get } | |
} | |
extension UInt8: Nulable { var isNul: Bool { return self == 0 } } | |
extension UInt16: Nulable { var isNul: Bool { return self == 0 } } | |
extension UInt32: Nulable { var isNul: Bool { return self == 0 } } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct UnsafeNulTerminatedBufferPointer<Element: Nulable>: Collection { | |
typealias Index = UnsafeNulTerminatedBufferPointerIndex | |
init(start baseAddress: UnsafePointer<Element>?) { | |
self.baseAddress = baseAddress | |
} | |
var baseAddress: UnsafePointer<Element>? | |
subscript(index: Index) -> Element { | |
get { | |
return baseAddress![index.integerIndex()] | |
} | |
} | |
func index(after index: Index) -> Index { | |
let nextIntegerIndex = index.integerIndex() + 1 | |
if baseAddress![nextIntegerIndex].isNul { | |
return .end | |
} | |
else { | |
return .element(nextIntegerIndex) | |
} | |
} | |
var startIndex: Index { | |
return .element(0) | |
} | |
var endIndex: Index { | |
return .end | |
} | |
} | |
enum UnsafeNulTerminatedBufferPointerIndex: Comparable { | |
case element(Int) | |
case end | |
func integerIndex() -> Int { | |
guard case let .element(realIndex) = self else { | |
preconditionFailure("Tried to access integerIndex() of .end") | |
} | |
return realIndex | |
} | |
static func == (lhs: UnsafeNulTerminatedBufferPointerIndex, rhs: UnsafeNulTerminatedBufferPointerIndex) -> Bool { | |
switch (lhs, rhs) { | |
case let (.element(l), .element(r)): | |
return l == r | |
case (.end, .end): | |
return true | |
case (.element, .end), (.end, .element): | |
return false | |
} | |
} | |
static func < (lhs: UnsafeNulTerminatedBufferPointerIndex, rhs: UnsafeNulTerminatedBufferPointerIndex) -> Bool { | |
switch (lhs, rhs) { | |
case let (.element(l), .element(r)): | |
return l < r | |
case (.element, .end): | |
return true | |
case (.end, .end), (.end, .element): | |
return false | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment