Skip to content

Instantly share code, notes, and snippets.

@beccadax
Created January 21, 2017 09:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save beccadax/8b71f46b424dc64abaa77f18556e607b to your computer and use it in GitHub Desktop.
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`.
"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 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))
}
}
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 } }
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