Skip to content

Commit d52c692

Browse files
milsemanairspeedswift
authored andcommitted
[String] Custom iterator for UTF16View (#20930)
Defining a custom iterator for the UTF16View avoid some redundant computation over the indexing model. This speeds up iteration by around 40% on non-ASCII strings.
1 parent af392c4 commit d52c692

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed

stdlib/public/core/StringCharacterView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ extension String {
234234

235235
@inlinable
236236
internal init(_ guts: _StringGuts) {
237-
self._guts = guts
238237
self._end = guts.count
238+
self._guts = guts
239239
}
240240

241241
@inlinable

stdlib/public/core/StringUTF16View.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,59 @@ extension String.UTF16View: BidirectionalCollection {
265265
}
266266
}
267267
}
268+
269+
extension String.UTF16View {
270+
@_fixed_layout
271+
public struct Iterator: IteratorProtocol {
272+
@usableFromInline
273+
internal var _guts: _StringGuts
274+
275+
@usableFromInline
276+
internal var _position: Int = 0
277+
278+
@usableFromInline
279+
internal var _end: Int
280+
281+
// If non-nil, return this value for `next()` (and set it to nil).
282+
//
283+
// This is set when visiting a non-BMP scalar: the leading surrogate is
284+
// returned, this field is set with the value of the trailing surrogate, and
285+
// `_position` is advanced to the start of the next scalar.
286+
@usableFromInline
287+
internal var _nextIsTrailingSurrogate: UInt16? = nil
288+
289+
@inlinable
290+
internal init(_ guts: _StringGuts) {
291+
self._end = guts.count
292+
self._guts = guts
293+
}
294+
295+
@inlinable
296+
public mutating func next() -> UInt16? {
297+
if _slowPath(_nextIsTrailingSurrogate != nil) {
298+
let trailing = self._nextIsTrailingSurrogate._unsafelyUnwrappedUnchecked
299+
self._nextIsTrailingSurrogate = nil
300+
return trailing
301+
}
302+
guard _fastPath(_position < _end) else { return nil }
303+
304+
let (scalar, len) = _guts.errorCorrectedScalar(startingAt: _position)
305+
_position &+= len
306+
307+
if _slowPath(scalar.value > UInt16.max) {
308+
self._nextIsTrailingSurrogate = scalar.utf16[1]
309+
return scalar.utf16[0]
310+
}
311+
return UInt16(truncatingIfNeeded: scalar.value)
312+
}
313+
}
314+
@inlinable
315+
public __consuming func makeIterator() -> Iterator {
316+
return Iterator(_guts)
317+
}
318+
}
319+
320+
268321
extension String.UTF16View: CustomStringConvertible {
269322
@inlinable
270323
public var description: String {

stdlib/public/core/StringUnicodeScalarView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ extension String.UnicodeScalarView {
180180

181181
@inlinable
182182
internal init(_ guts: _StringGuts) {
183-
self._guts = guts
184183
self._end = guts.count
184+
self._guts = guts
185185
}
186186

187187
@inlinable

test/api-digester/Outputs/stability-stdlib-abi.swift.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,9 @@ Var _StringGutsSlice.isNFCFastUTF8 has been removed
500500
Var _StringGutsSlice.range has been removed
501501
Var _StringGutsSlice.start has been removed
502502

503+
Struct String.UTF16View has type witness type for Collection.Iterator changing from IndexingIterator<String.UTF16View> to String.UTF16View.Iterator
504+
Struct String.UTF16View has type witness type for Sequence.Iterator changing from IndexingIterator<String.UTF16View> to String.UTF16View.Iterator
505+
503506
Func ManagedBufferPointer._sanityCheckValidBufferClass(_:creating:) has been removed
504507
Func _sanityCheck(_:_:file:line:) has been removed
505508
Func _sanityCheckFailure(_:file:line:) has been removed

0 commit comments

Comments
 (0)