@@ -156,10 +156,9 @@ extension String.CharacterView : BidirectionalCollection {
156
156
/// // Prints "[72, 101, 97, 114, 116, 115]"
157
157
public struct Index : Comparable , CustomPlaygroundQuickLookable {
158
158
public // SPI(Foundation)
159
- init ( _base: String . UnicodeScalarView . Index ) {
159
+ init ( _base: String . UnicodeScalarView . Index , in c : String . CharacterView ) {
160
160
self . _base = _base
161
- self . _countUTF16 =
162
- Index . _measureExtendedGraphemeClusterForward ( from: _base)
161
+ self . _countUTF16 = c. _measureExtendedGraphemeClusterForward ( from: _base)
163
162
}
164
163
165
164
internal init ( _base: UnicodeScalarView . Index , _countUTF16: Int ) {
@@ -181,88 +180,7 @@ extension String.CharacterView : BidirectionalCollection {
181
180
/// The one past end index for this extended grapheme cluster in Unicode
182
181
/// scalars.
183
182
internal var _endBase : UnicodeScalarView . Index {
184
- return UnicodeScalarView . Index (
185
- _utf16Index + _countUTF16, _base. _core)
186
- }
187
-
188
- /// Returns the length of the first extended grapheme cluster in UTF-16
189
- /// code units.
190
- @inline ( never)
191
- internal static func _measureExtendedGraphemeClusterForward(
192
- from start: UnicodeScalarView . Index
193
- ) -> Int {
194
- var start = start
195
- let end = start. _viewEndIndex
196
- if start == end {
197
- return 0
198
- }
199
-
200
- let startIndexUTF16 = start. _position
201
- let unicodeScalars = UnicodeScalarView ( start. _core)
202
- let graphemeClusterBreakProperty =
203
- _UnicodeGraphemeClusterBreakPropertyTrie ( )
204
- let segmenter = _UnicodeExtendedGraphemeClusterSegmenter ( )
205
-
206
- var gcb0 = graphemeClusterBreakProperty. getPropertyRawValue (
207
- unicodeScalars [ start] . value)
208
- unicodeScalars. formIndex ( after: & start)
209
-
210
- while start != end {
211
- // FIXME(performance): consider removing this "fast path". A branch
212
- // that is hard to predict could be worse for performance than a few
213
- // loads from cache to fetch the property 'gcb1'.
214
- if segmenter. isBoundaryAfter ( gcb0) {
215
- break
216
- }
217
- let gcb1 = graphemeClusterBreakProperty. getPropertyRawValue (
218
- unicodeScalars [ start] . value)
219
- if segmenter. isBoundary ( gcb0, gcb1) {
220
- break
221
- }
222
- gcb0 = gcb1
223
- unicodeScalars. formIndex ( after: & start)
224
- }
225
-
226
- return start. _position - startIndexUTF16
227
- }
228
-
229
- /// Returns the length of the previous extended grapheme cluster in UTF-16
230
- /// code units.
231
- @inline ( never)
232
- internal static func _measureExtendedGraphemeClusterBackward(
233
- from end: UnicodeScalarView . Index
234
- ) -> Int {
235
- let start = end. _viewStartIndex
236
- if start == end {
237
- return 0
238
- }
239
-
240
- let endIndexUTF16 = end. _position
241
- let unicodeScalars = UnicodeScalarView ( start. _core)
242
- let graphemeClusterBreakProperty =
243
- _UnicodeGraphemeClusterBreakPropertyTrie ( )
244
- let segmenter = _UnicodeExtendedGraphemeClusterSegmenter ( )
245
-
246
- var graphemeClusterStart = end
247
-
248
- unicodeScalars. formIndex ( before: & graphemeClusterStart)
249
- var gcb0 = graphemeClusterBreakProperty. getPropertyRawValue (
250
- unicodeScalars [ graphemeClusterStart] . value)
251
-
252
- var graphemeClusterStartUTF16 = graphemeClusterStart. _position
253
-
254
- while graphemeClusterStart != start {
255
- unicodeScalars. formIndex ( before: & graphemeClusterStart)
256
- let gcb1 = graphemeClusterBreakProperty. getPropertyRawValue (
257
- unicodeScalars [ graphemeClusterStart] . value)
258
- if segmenter. isBoundary ( gcb1, gcb0) {
259
- break
260
- }
261
- gcb0 = gcb1
262
- graphemeClusterStartUTF16 = graphemeClusterStart. _position
263
- }
264
-
265
- return endIndexUTF16 - graphemeClusterStartUTF16
183
+ return UnicodeScalarView . Index ( _position: _utf16Index + _countUTF16)
266
184
}
267
185
268
186
public var customPlaygroundQuickLook : PlaygroundQuickLook {
@@ -276,39 +194,124 @@ extension String.CharacterView : BidirectionalCollection {
276
194
///
277
195
/// In an empty character view, `startIndex` is equal to `endIndex`.
278
196
public var startIndex : Index {
279
- return Index ( _base: unicodeScalars. startIndex)
197
+ return Index ( _base: unicodeScalars. startIndex, in : self )
280
198
}
281
199
282
200
/// A character view's "past the end" position---that is, the position one
283
201
/// greater than the last valid subscript argument.
284
202
///
285
203
/// In an empty character view, `endIndex` is equal to `startIndex`.
286
204
public var endIndex : Index {
287
- return Index ( _base: unicodeScalars. endIndex)
205
+ return Index ( _base: unicodeScalars. endIndex, in : self )
288
206
}
289
207
290
208
/// Returns the next consecutive position after `i`.
291
209
///
292
210
/// - Precondition: The next position is valid.
293
211
public func index( after i: Index ) -> Index {
294
- _precondition ( i. _base != i. _base. _viewEndIndex, " cannot increment endIndex " )
295
- return Index ( _base: i. _endBase)
212
+ _precondition ( i. _base < unicodeScalars. endIndex,
213
+ " cannot increment beyond endIndex " )
214
+ _precondition ( i. _base >= unicodeScalars. startIndex,
215
+ " cannot increment invalid index " )
216
+ return Index ( _base: i. _endBase, in: self )
296
217
}
297
218
298
219
/// Returns the previous consecutive position before `i`.
299
220
///
300
221
/// - Precondition: The previous position is valid.
301
222
public func index( before i: Index ) -> Index {
302
- // FIXME: swift-3-indexing-model: range check i?
303
- _precondition ( i. _base != i. _base. _viewStartIndex,
304
- " cannot decrement startIndex " )
223
+ _precondition ( i. _base > unicodeScalars. startIndex,
224
+ " cannot decrement before startIndex " )
225
+ _precondition ( i. _base <= unicodeScalars. endIndex,
226
+ " cannot decrement invalid index " )
305
227
let predecessorLengthUTF16 =
306
- Index . _measureExtendedGraphemeClusterBackward ( from: i. _base)
228
+ _measureExtendedGraphemeClusterBackward ( from: i. _base)
307
229
return Index (
308
230
_base: UnicodeScalarView . Index (
309
- i. _utf16Index - predecessorLengthUTF16, i. _base. _core) )
231
+ _position: i. _utf16Index - predecessorLengthUTF16
232
+ ) ,
233
+ in: self
234
+ )
310
235
}
311
236
237
+ /// Returns the length of the first extended grapheme cluster in UTF-16
238
+ /// code units.
239
+ @inline ( never)
240
+ internal func _measureExtendedGraphemeClusterForward(
241
+ from start: UnicodeScalarView . Index
242
+ ) -> Int {
243
+ var start = start
244
+ let end = UnicodeScalarView . Index ( _position: _core. count)
245
+ if start == end {
246
+ return 0
247
+ }
248
+
249
+ let startIndexUTF16 = start. _position
250
+ let graphemeClusterBreakProperty =
251
+ _UnicodeGraphemeClusterBreakPropertyTrie ( )
252
+ let segmenter = _UnicodeExtendedGraphemeClusterSegmenter ( )
253
+
254
+ var gcb0 = graphemeClusterBreakProperty. getPropertyRawValue (
255
+ unicodeScalars [ start] . value)
256
+ unicodeScalars. formIndex ( after: & start)
257
+
258
+ while start != end {
259
+ // FIXME(performance): consider removing this "fast path". A branch
260
+ // that is hard to predict could be worse for performance than a few
261
+ // loads from cache to fetch the property 'gcb1'.
262
+ if segmenter. isBoundaryAfter ( gcb0) {
263
+ break
264
+ }
265
+ let gcb1 = graphemeClusterBreakProperty. getPropertyRawValue (
266
+ unicodeScalars [ start] . value)
267
+ if segmenter. isBoundary ( gcb0, gcb1) {
268
+ break
269
+ }
270
+ gcb0 = gcb1
271
+ unicodeScalars. formIndex ( after: & start)
272
+ }
273
+
274
+ return start. _position - startIndexUTF16
275
+ }
276
+
277
+ /// Returns the length of the previous extended grapheme cluster in UTF-16
278
+ /// code units.
279
+ @inline ( never)
280
+ internal func _measureExtendedGraphemeClusterBackward(
281
+ from end: UnicodeScalarView . Index
282
+ ) -> Int {
283
+ let start = UnicodeScalarView . Index ( _position: 0 )
284
+ if start == end {
285
+ return 0
286
+ }
287
+
288
+ let endIndexUTF16 = end. _position
289
+ let graphemeClusterBreakProperty =
290
+ _UnicodeGraphemeClusterBreakPropertyTrie ( )
291
+ let segmenter = _UnicodeExtendedGraphemeClusterSegmenter ( )
292
+
293
+ var graphemeClusterStart = end
294
+
295
+ unicodeScalars. formIndex ( before: & graphemeClusterStart)
296
+ var gcb0 = graphemeClusterBreakProperty. getPropertyRawValue (
297
+ unicodeScalars [ graphemeClusterStart] . value)
298
+
299
+ var graphemeClusterStartUTF16 = graphemeClusterStart. _position
300
+
301
+ while graphemeClusterStart != start {
302
+ unicodeScalars. formIndex ( before: & graphemeClusterStart)
303
+ let gcb1 = graphemeClusterBreakProperty. getPropertyRawValue (
304
+ unicodeScalars [ graphemeClusterStart] . value)
305
+ if segmenter. isBoundary ( gcb1, gcb0) {
306
+ break
307
+ }
308
+ gcb0 = gcb1
309
+ graphemeClusterStartUTF16 = graphemeClusterStart. _position
310
+ }
311
+
312
+ return endIndexUTF16 - graphemeClusterStartUTF16
313
+ }
314
+
312
315
/// Accesses the character at the given position.
313
316
///
314
317
/// The following example searches a string's character view for a capital
0 commit comments