@@ -137,18 +137,23 @@ extension String.UTF16View: BidirectionalCollection {
137
137
/// In an empty UTF-16 view, `endIndex` is equal to `startIndex`.
138
138
@inlinable @inline ( __always)
139
139
public var endIndex : Index { return _guts. endIndex }
140
-
140
+
141
141
@inlinable @inline ( __always)
142
142
public func index( after idx: Index ) -> Index {
143
+ var idx = _guts. ensureMatchingEncoding ( idx)
144
+ _precondition ( idx. _encodedOffset < _guts. count,
145
+ " String index is out of bounds " )
143
146
if _slowPath ( _guts. isForeign) { return _foreignIndex ( after: idx) }
144
- if _guts. isASCII { return idx. nextEncoded. _knownUTF8. _knownUTF16 }
147
+ if _guts. isASCII {
148
+ return idx. nextEncoded. _scalarAligned. _knownUTF8. _knownUTF16
149
+ }
145
150
146
151
// For a BMP scalar (1-3 UTF-8 code units), advance past it. For a non-BMP
147
152
// scalar, use a transcoded offset first.
148
153
149
154
// TODO: If transcoded is 1, can we just skip ahead 4?
150
155
151
- let idx = _utf16AlignNativeIndex ( idx)
156
+ idx = _utf16AlignNativeIndex ( idx)
152
157
153
158
let len = _guts. fastUTF8ScalarLength ( startingAt: idx. _encodedOffset)
154
159
if len == 4 && idx. transcodedOffset == 0 {
@@ -163,16 +168,20 @@ extension String.UTF16View: BidirectionalCollection {
163
168
164
169
@inlinable @inline ( __always)
165
170
public func index( before idx: Index ) -> Index {
166
- _precondition ( !idx. isZeroPosition)
171
+ var idx = _guts. ensureMatchingEncoding ( idx)
172
+ _precondition ( !idx. isZeroPosition && idx <= endIndex,
173
+ " String index is out of bounds " )
167
174
if _slowPath ( _guts. isForeign) { return _foreignIndex ( before: idx) }
168
- if _guts. isASCII { return idx. priorEncoded. _knownUTF8. _knownUTF16 }
175
+ if _guts. isASCII {
176
+ return idx. priorEncoded. _scalarAligned. _knownUTF8. _knownUTF16
177
+ }
169
178
170
179
if idx. transcodedOffset != 0 {
171
180
_internalInvariant ( idx. transcodedOffset == 1 )
172
- return idx. strippingTranscoding. _knownUTF8
181
+ return idx. strippingTranscoding. _scalarAligned . _knownUTF8
173
182
}
174
183
175
- let idx = _utf16AlignNativeIndex ( idx)
184
+ idx = _utf16AlignNativeIndex ( idx)
176
185
let len = _guts. fastUTF8ScalarLength ( endingAt: idx. _encodedOffset)
177
186
if len == 4 {
178
187
// 2 UTF-16 code units comprise this scalar; advance to the beginning and
@@ -186,6 +195,8 @@ extension String.UTF16View: BidirectionalCollection {
186
195
}
187
196
188
197
public func index( _ i: Index , offsetBy n: Int ) -> Index {
198
+ let i = _guts. ensureMatchingEncoding ( i)
199
+ _precondition ( i <= endIndex, " String index is out of bounds " )
189
200
if _slowPath ( _guts. isForeign) {
190
201
return _foreignIndex ( i, offsetBy: n)
191
202
}
@@ -198,6 +209,12 @@ extension String.UTF16View: BidirectionalCollection {
198
209
public func index(
199
210
_ i: Index , offsetBy n: Int , limitedBy limit: Index
200
211
) -> Index ? {
212
+ let limit = _guts. ensureMatchingEncoding ( limit)
213
+ guard _fastPath ( limit <= endIndex) else { return index ( i, offsetBy: n) }
214
+
215
+ let i = _guts. ensureMatchingEncoding ( i)
216
+ _precondition ( i <= endIndex, " String index is out of bounds " )
217
+
201
218
if _slowPath ( _guts. isForeign) {
202
219
return _foreignIndex ( i, offsetBy: n, limitedBy: limit)
203
220
}
@@ -219,6 +236,14 @@ extension String.UTF16View: BidirectionalCollection {
219
236
}
220
237
221
238
public func distance( from start: Index , to end: Index ) -> Int {
239
+ let start = _guts. ensureMatchingEncoding ( start)
240
+ let end = _guts. ensureMatchingEncoding ( end)
241
+
242
+ _precondition ( start. _encodedOffset <= _guts. count,
243
+ " String index is out of bounds " )
244
+ _precondition ( end. _encodedOffset <= _guts. count,
245
+ " String index is out of bounds " )
246
+
222
247
if _slowPath ( _guts. isForeign) {
223
248
return _foreignDistance ( from: start, to: end)
224
249
}
@@ -250,8 +275,14 @@ extension String.UTF16View: BidirectionalCollection {
250
275
/// less than the view's end index.
251
276
@inlinable @inline ( __always)
252
277
public subscript( idx: Index ) -> UTF16 . CodeUnit {
253
- String ( _guts) . _boundsCheck ( idx)
278
+ let idx = _guts. ensureMatchingEncoding ( idx)
279
+ _precondition ( idx. _encodedOffset < _guts. count,
280
+ " String index is out of bounds " )
281
+ return self [ _unchecked: idx]
282
+ }
254
283
284
+ @_alwaysEmitIntoClient @inline ( __always)
285
+ internal subscript( _unchecked idx: Index ) -> UTF16 . CodeUnit {
255
286
if _fastPath ( _guts. isFastUTF8) {
256
287
let scalar = _guts. fastUTF8Scalar (
257
288
startingAt: _guts. scalarAlign ( idx) . _encodedOffset)
@@ -427,6 +458,7 @@ extension String.UTF16View {
427
458
public typealias SubSequence = Substring . UTF16View
428
459
429
460
public subscript( r: Range < Index > ) -> Substring . UTF16View {
461
+ let r = _guts. validateSubscalarRange ( r)
430
462
return Substring . UTF16View ( self , _bounds: r)
431
463
}
432
464
}
@@ -474,14 +506,20 @@ extension String.UTF16View {
474
506
if n > 0 ? l >= 0 && l < n : l <= 0 && n < l {
475
507
return nil
476
508
}
477
- return i. strippingTranscoding. encoded ( offsetBy: n)
509
+ let offset = i. _encodedOffset &+ n
510
+ _precondition ( offset >= 0 && offset <= _guts. count,
511
+ " String index is out of bounds " )
512
+ return Index ( _encodedOffset: offset) . _knownUTF16
478
513
}
479
514
480
515
@usableFromInline @inline ( never)
481
516
@_effects ( releasenone)
482
517
internal func _foreignIndex( _ i: Index , offsetBy n: Int ) -> Index {
483
518
_internalInvariant ( _guts. isForeign)
484
- return i. strippingTranscoding. encoded ( offsetBy: n)
519
+ let offset = i. _encodedOffset &+ n
520
+ _precondition ( offset >= 0 && offset <= _guts. count,
521
+ " String index is out of bounds " )
522
+ return Index ( _encodedOffset: offset) . _knownUTF16
485
523
}
486
524
487
525
@usableFromInline @inline ( never)
@@ -631,10 +669,11 @@ extension String.UTF16View {
631
669
return utf16Count
632
670
}
633
671
}
634
-
672
+
635
673
@usableFromInline
636
674
@_effects ( releasenone)
637
675
internal func _nativeGetOffset( for idx: Index ) -> Int {
676
+ _internalInvariant ( idx. _encodedOffset <= _guts. count)
638
677
// Trivial and common: start
639
678
if idx == startIndex { return 0 }
640
679
@@ -656,13 +695,14 @@ extension String.UTF16View {
656
695
// Otherwise, find the nearest lower-bound breadcrumb and count from there
657
696
let ( crumb, crumbOffset) = breadcrumbsPtr. pointee. getBreadcrumb (
658
697
forIndex: idx)
659
-
660
698
return crumbOffset + _utf16Distance( from: crumb, to: idx)
661
699
}
662
700
663
701
@usableFromInline
664
702
@_effects ( releasenone)
665
703
internal func _nativeGetIndex( for offset: Int ) -> Index {
704
+ _precondition ( offset >= 0 , " String index is out of bounds " )
705
+
666
706
// Trivial and common: start
667
707
if offset == 0 { return startIndex }
668
708
@@ -701,6 +741,7 @@ extension String.UTF16View {
701
741
}
702
742
703
743
while true {
744
+ _precondition ( readIdx < readEnd, " String index is out of bounds " )
704
745
let len = _utf8ScalarLength ( utf8 [ _unchecked: readIdx] )
705
746
let utf16Len = len == 4 ? 2 : 1
706
747
utf16I &+= utf16Len
0 commit comments