Last active
May 30, 2021 13:12
-
-
Save nicklockwood/05fd5e46b386f4ab1c15e3e3554398ec to your computer and use it in GitHub Desktop.
UnicodeScalarView.swift
This file contains 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 a really simple drop-in replacement for String.UnicodeScalarView | |
// As of Swift 3.2, String.UnicodeScalarView no longer supports slice operations, and | |
// String.UnicodeScalarView.Subsequence is ~5x slower | |
// | |
// Only a small subset of methods are implemented, specifically the ones useful for | |
// implementing a parser or lexer that consumes a string by repeatedly calling popFirst() | |
// | |
// I've benchmarked popFirst() as ~7x faster than String.UnicodeScalarView.Subsequence in Swift 3.2 and 4.0 | |
// The performance is close to that of String.UnicodeScalarView in Swift 3.1, but may be slightly worse in some use cases | |
import Foundation | |
#if swift(>=3.2) | |
struct UnicodeScalarView { | |
public typealias Index = String.UnicodeScalarView.Index | |
private let characters: String.UnicodeScalarView | |
public private(set) var startIndex: Index | |
public private(set) var endIndex: Index | |
public init(_ unicodeScalars: String.UnicodeScalarView) { | |
characters = unicodeScalars | |
startIndex = characters.startIndex | |
endIndex = characters.endIndex | |
} | |
public init(_ unicodeScalars: String.UnicodeScalarView.SubSequence) { | |
self.init(String.UnicodeScalarView(unicodeScalars)) | |
} | |
public init(_ string: String) { | |
self.init(string.unicodeScalars) | |
} | |
public var first: UnicodeScalar? { | |
return isEmpty ? nil : characters[startIndex] | |
} | |
@available(*, deprecated, message: "Really hurts performance - use a different approach") | |
public var count: Int { | |
return characters.distance(from: startIndex, to: endIndex) | |
} | |
public var isEmpty: Bool { | |
return startIndex >= endIndex | |
} | |
public subscript(_ index: Index) -> UnicodeScalar { | |
return characters[index] | |
} | |
public func index(after index: Index) -> Index { | |
return characters.index(after: index) | |
} | |
public func prefix(upTo index: Index) -> UnicodeScalarView { | |
var view = UnicodeScalarView(characters) | |
view.startIndex = startIndex | |
view.endIndex = index | |
return view | |
} | |
public func suffix(from index: Index) -> UnicodeScalarView { | |
var view = UnicodeScalarView(characters) | |
view.startIndex = index | |
view.endIndex = endIndex | |
return view | |
} | |
public func dropFirst() -> UnicodeScalarView { | |
var view = UnicodeScalarView(characters) | |
view.startIndex = characters.index(after: startIndex) | |
view.endIndex = endIndex | |
return view | |
} | |
public mutating func popFirst() -> UnicodeScalar? { | |
if isEmpty { | |
return nil | |
} | |
let char = characters[startIndex] | |
startIndex = characters.index(after: startIndex) | |
return char | |
} | |
/// Will crash if n > remaining char count | |
public mutating func removeFirst(_ n: Int) { | |
startIndex = characters.index(startIndex, offsetBy: n) | |
} | |
/// Will crash if collection is empty | |
@discardableResult | |
public mutating func removeFirst() -> UnicodeScalar { | |
let oldIndex = startIndex | |
startIndex = characters.index(after: startIndex) | |
return characters[oldIndex] | |
} | |
/// Returns the remaining characters | |
fileprivate var unicodeScalars: String.UnicodeScalarView.SubSequence { | |
return characters[startIndex ..< endIndex] | |
} | |
} | |
typealias _UnicodeScalarView = UnicodeScalarView | |
extension String { | |
init(_ unicodeScalarView: _UnicodeScalarView) { | |
self.init(unicodeScalarView.unicodeScalars) | |
} | |
} | |
extension String.UnicodeScalarView { | |
init(_ unicodeScalarView: _UnicodeScalarView) { | |
self.init(unicodeScalarView.unicodeScalars) | |
} | |
} | |
extension String.UnicodeScalarView.SubSequence { | |
init(_ unicodeScalarView: _UnicodeScalarView) { | |
self.init(unicodeScalarView.unicodeScalars) | |
} | |
} | |
#else | |
typealias UnicodeScalarView = String.UnicodeScalarView | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment