[String] NFC iterator fast-paths

Refactor and rename _StringGutsSlice, apply NFC-aware fast paths to a
new buffered iterator.

Also, fix bug in _typeName which used to assume ASCIIness and better
SIL optimizations on StringObject.
This commit is contained in:
Michael Ilseman
2018-10-28 15:47:49 -07:00
parent 7376009ccc
commit 7aea40680d
27 changed files with 716 additions and 285 deletions

View File

@@ -232,13 +232,13 @@ extension _StringGuts {
@inlinable
internal func copyUTF8(into mbp: UnsafeMutableBufferPointer<UInt8>) -> Int? {
// TODO(UTF8 perf): minor perf win by avoiding slicing if fast...
return _SlicedStringGuts(self).copyUTF8(into: mbp)
return _StringGutsSlice(self).copyUTF8(into: mbp)
}
internal var utf8Count: Int {
@inline(__always) get {
if _fastPath(self.isFastUTF8) { return count }
return _SlicedStringGuts(self).utf8Count
return _StringGutsSlice(self).utf8Count
}
}
@@ -258,106 +258,3 @@ extension _StringGuts {
@inline(__always) get { return Index(encodedOffset: self.count) }
}
}
// A sliced _StringGuts, convenient for unifying String/Substring comparison,
// hashing, and RRC.
@_fixed_layout
@usableFromInline
internal struct _SlicedStringGuts {
@usableFromInline
internal var _guts: _StringGuts
@usableFromInline
internal var _offsetRange: Range<Int>
@inlinable @inline(__always)
internal init(_ guts: _StringGuts) {
self._guts = guts
self._offsetRange = 0..<self._guts.count
}
@inlinable @inline(__always)
internal init(_ guts: _StringGuts, _ offsetRange: Range<Int>) {
self._guts = guts
self._offsetRange = offsetRange
}
@inlinable
internal var count: Int {
@inline(__always) get { return _offsetRange.count }
}
@inlinable
internal var isNFCFastUTF8: Bool {
@inline(__always) get { return _guts.isNFCFastUTF8 }
}
@inlinable
internal var isASCII: Bool {
@inline(__always) get { return _guts.isASCII }
}
@inlinable
internal var isFastUTF8: Bool {
@inline(__always) get { return _guts.isFastUTF8 }
}
internal var utf8Count: Int {
@inline(__always) get {
if _fastPath(self.isFastUTF8) {
return _offsetRange.count
}
return Substring(self).utf8.count
}
}
@inlinable
internal var range: Range<String.Index> {
@inline(__always) get {
return String.Index(encodedOffset: _offsetRange.lowerBound)
..< String.Index(encodedOffset: _offsetRange.upperBound)
}
}
@inlinable @inline(__always)
internal func withFastUTF8<R>(
_ f: (UnsafeBufferPointer<UInt8>) throws -> R
) rethrows -> R {
return try _guts.withFastUTF8(range: _offsetRange, f)
}
// Copy UTF-8 contents. Returns number written or nil if not enough space.
// Contents of the buffer are unspecified if nil is returned.
@inlinable
internal func copyUTF8(into mbp: UnsafeMutableBufferPointer<UInt8>) -> Int? {
let ptr = mbp.baseAddress._unsafelyUnwrappedUnchecked
if _fastPath(self.isFastUTF8) {
return self.withFastUTF8 { utf8 in
guard utf8.count <= mbp.count else { return nil }
let utf8Start = utf8.baseAddress._unsafelyUnwrappedUnchecked
ptr.initialize(from: utf8Start, count: utf8.count)
return utf8.count
}
}
return _foreignCopyUTF8(into: mbp)
}
@_effects(releasenone)
@usableFromInline @inline(never) // slow-path
internal func _foreignCopyUTF8(
into mbp: UnsafeMutableBufferPointer<UInt8>
) -> Int? {
var ptr = mbp.baseAddress._unsafelyUnwrappedUnchecked
var numWritten = 0
for cu in Substring(self).utf8 {
guard numWritten < mbp.count else { return nil }
ptr.initialize(to: cu)
ptr += 1
numWritten += 1
}
return numWritten
}
}