Skip to content

Commit e46f8f8

Browse files
committed
[stdlib] String.UTF16View: Align indices before calling default algorithms
[Bidirectional]Collection’s default index manipulation methods (as well as _utf16Distance) do not expect to be given unreachable indices, and they tend to fail when operating on them. Round indices down to the nearest scalar boundary before calling these.
1 parent cd55016 commit e46f8f8

File tree

1 file changed

+27
-15
lines changed

1 file changed

+27
-15
lines changed

stdlib/public/core/StringUTF16View.swift

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -197,47 +197,57 @@ extension String.UTF16View: BidirectionalCollection {
197197
return idx.encoded(offsetBy: -len)._scalarAligned._knownUTF8
198198
}
199199

200+
@_effects(releasenone)
200201
public func index(_ i: Index, offsetBy n: Int) -> Index {
201-
let i = _guts.ensureMatchingEncoding(i)
202+
var i = _guts.ensureMatchingEncoding(i)
202203
_precondition(i <= endIndex, "String index is out of bounds")
204+
203205
if _slowPath(_guts.isForeign) {
204206
return _foreignIndex(i, offsetBy: n)
205207
}
206208

207-
if !_guts.isASCII { // We have ASCII fast paths below
208-
let threshold = (
209-
i == startIndex ? _breadcrumbStride / 2 : _breadcrumbStride)
210-
if n.magnitude < threshold {
211-
// Do not use breadcrumbs if directly computing the result is expected to
212-
// be cheaper.
213-
return _index(i, offsetBy: n)._knownUTF8
214-
}
209+
if _guts.isASCII {
210+
return Index(
211+
_encodedOffset: i._encodedOffset + n
212+
)._scalarAligned._encodingIndependent
213+
}
214+
215+
i = _utf16AlignNativeIndex(i)
216+
let threshold = (
217+
i == startIndex ? _breadcrumbStride / 2 : _breadcrumbStride)
218+
if n.magnitude < threshold {
219+
// Do not use breadcrumbs if directly computing the result is expected
220+
// to be cheaper.
221+
return _index(i, offsetBy: n)._knownUTF8
215222
}
216223

217224
let lowerOffset = _nativeGetOffset(for: i)
218225
let result = _nativeGetIndex(for: lowerOffset + n)
219226
return result
220227
}
221228

229+
@_effects(releasenone)
222230
public func index(
223231
_ i: Index, offsetBy n: Int, limitedBy limit: Index
224232
) -> Index? {
225-
let limit = _guts.ensureMatchingEncoding(limit)
233+
var limit = _guts.ensureMatchingEncoding(limit)
226234
guard _fastPath(limit <= endIndex) else { return index(i, offsetBy: n) }
227235

228-
let i = _guts.ensureMatchingEncoding(i)
236+
var i = _guts.ensureMatchingEncoding(i)
229237
_precondition(i <= endIndex, "String index is out of bounds")
230238

231239
if _slowPath(_guts.isForeign) {
232240
return _foreignIndex(i, offsetBy: n, limitedBy: limit)
233241
}
234242

235243
if !_guts.isASCII { // We have ASCII fast paths below
244+
limit = _utf16AlignNativeIndex(limit)
245+
i = _utf16AlignNativeIndex(i)
236246
let threshold = (
237247
_breadcrumbStride + (i == startIndex ? 0 : _breadcrumbStride / 2))
238248
if n.magnitude < threshold {
239-
// Do not use breadcrumbs if directly computing the result is expected to
240-
// be cheaper.
249+
// Do not use breadcrumbs if directly computing the result is expected
250+
// to be cheaper.
241251
return _index(i, offsetBy: n, limitedBy: limit)?._knownUTF8
242252
}
243253
}
@@ -351,8 +361,8 @@ extension String.UTF16View: BidirectionalCollection {
351361
for range: Range<Index>,
352362
from start: Index
353363
) -> Range<Int> {
354-
let lower = _guts.ensureMatchingEncoding(range.lowerBound)
355-
let upper = _guts.ensureMatchingEncoding(range.upperBound)
364+
var lower = _guts.ensureMatchingEncoding(range.lowerBound)
365+
var upper = _guts.ensureMatchingEncoding(range.upperBound)
356366
_internalInvariant(_guts.hasMatchingEncoding(start))
357367

358368
_precondition(
@@ -378,6 +388,8 @@ extension String.UTF16View: BidirectionalCollection {
378388
}
379389

380390
if utf8Distance.magnitude <= _breadcrumbStride / 2 {
391+
lower = _utf16AlignNativeIndex(lower)
392+
upper = _utf16AlignNativeIndex(upper)
381393
let lowerOffset = distance(from: start, to: lower)
382394
let distance = _utf16Distance(from: lower, to: upper)
383395
return Range(uncheckedBounds: (lowerOffset, lowerOffset + distance))

0 commit comments

Comments
 (0)