@@ -278,8 +278,14 @@ extension AttributedString.Runs: BidirectionalCollection {
278
278
precondition ( i < _bounds. upperBound, " Can't advance AttributedString.Runs index beyond end " )
279
279
let ( resolvedIdx, runStartIdx) = _resolve ( i)
280
280
let next = _guts. runs. index ( after: resolvedIdx)
281
- let currentRangeIdx = i. _rangesOffset ?? _strBounds. rangeIdx ( containing: i. _stringIndex ?? runStartIdx)
282
- let currentRange = _strBounds. ranges [ currentRangeIdx]
281
+ let currentRangeIdx : Int
282
+ let currentRange : Range < BigString . Index >
283
+ if let cachedRangeOffset = i. _rangesOffset {
284
+ currentRangeIdx = cachedRangeOffset
285
+ currentRange = _strBounds. ranges [ currentRangeIdx]
286
+ } else {
287
+ ( currentRange, currentRangeIdx) = _strBounds. range ( containing: i. _stringIndex ?? runStartIdx)
288
+ }
283
289
if currentRange. upperBound. utf8Offset <= next. utf8Offset {
284
290
let nextRangeIdx = currentRangeIdx + 1
285
291
if nextRangeIdx == _strBounds. ranges. count {
@@ -300,7 +306,7 @@ extension AttributedString.Runs: BidirectionalCollection {
300
306
public func index( before i: Index ) -> Index {
301
307
precondition ( i > _bounds. lowerBound, " Can't step AttributedString.Runs index below start " )
302
308
let ( resolvedIdx, runStartIdx) = _resolve ( i)
303
- let currentRangeIdx = i. _rangesOffset ?? _strBounds. rangeIdx ( containing: i. _stringIndex ?? runStartIdx)
309
+ let currentRangeIdx = i. _rangesOffset ?? _strBounds. range ( containing: i. _stringIndex ?? runStartIdx) . offset
304
310
if i == endIndex || runStartIdx. utf8Offset <= _strBounds. ranges [ currentRangeIdx] . lowerBound. utf8Offset {
305
311
// The current run starts on or before our current range, look up the next range
306
312
let previousRange = _strBounds. ranges [ currentRangeIdx - 1 ]
@@ -394,27 +400,34 @@ extension AttributedString.Runs: BidirectionalCollection {
394
400
395
401
public subscript( position: Index ) -> Run {
396
402
precondition ( _bounds. contains ( position) , " AttributedString.Runs index is out of bounds " )
397
- if let strIdx = position. _stringIndex {
398
- precondition ( _strBounds. contains ( strIdx) , " AttributedString.Runs index is out of bounds " )
399
- }
400
403
let resolved = _resolve ( position)
401
- return self [ _unchecked: resolved. runIndex, stringStartIdx: position. _startStringIndex ?? resolved. start, stringIdx: position. _stringIndex ?? resolved. start, rangeOffset: position. _rangesOffset]
404
+ let containingRange : Range < BigString . Index >
405
+ let stringIdx : BigString . Index
406
+ if let cachedRangeOffset = position. _rangesOffset {
407
+ containingRange = _strBounds. ranges [ cachedRangeOffset]
408
+ if let cachedStringIdx = position. _stringIndex {
409
+ precondition ( containingRange. contains ( cachedStringIdx) , " AttributedString.Runs index is out of bounds " )
410
+ stringIdx = cachedStringIdx
411
+ }
412
+ } else {
413
+ stringIdx = position. _stringIndex ?? resolved. start
414
+ // No need to check that _strBounds contains stringIdx here, the below call will assert if it cannot find a range that contains the provided index
415
+ containingRange = _strBounds. range ( containing: stringIdx) . range
416
+ }
417
+ return self [ _unchecked: resolved. runIndex, stringStartIdx: position. _startStringIndex ?? resolved. start, stringIdx: position. _stringIndex ?? resolved. start, containingRange: containingRange]
402
418
}
403
419
404
420
public subscript( position: AttributedString . Index ) -> Run {
405
- precondition (
406
- _strBounds. contains ( position. _value) ,
407
- " AttributedString index is out of bounds " )
421
+ let containingRange = _strBounds. range ( containing: position. _value) . range
408
422
let r = _guts. findRun ( at: position. _value)
409
- return self [ _unchecked: r. runIndex, stringStartIdx: r. start, stringIdx: position. _value]
423
+ return self [ _unchecked: r. runIndex, stringStartIdx: r. start, stringIdx: position. _value, containingRange : containingRange ]
410
424
}
411
425
412
- internal subscript( _unchecked i: _InternalRuns . Index , stringStartIdx stringStartIdx: BigString . Index , stringIdx stringIdx: BigString . Index , rangeOffset rangeOffset : Int ? = nil ) -> Run {
426
+ internal subscript( _unchecked i: _InternalRuns . Index , stringStartIdx stringStartIdx: BigString . Index , stringIdx stringIdx: BigString . Index , containingRange containingRange : Range < BigString . Index > ) -> Run {
413
427
let run = _guts. runs [ i]
414
428
// Clamp the run into the bounds of self, using relative calculations.
415
- let range = _strBounds. ranges [ rangeOffset ?? _strBounds. rangeIdx ( containing: stringIdx) ]
416
- let lowerBound = Swift . max ( stringStartIdx, range. lowerBound)
417
- let upperUTF8 = Swift . min ( stringStartIdx. utf8Offset + run. length, range. upperBound. utf8Offset)
429
+ let lowerBound = Swift . max ( stringStartIdx, containingRange. lowerBound)
430
+ let upperUTF8 = Swift . min ( stringStartIdx. utf8Offset + run. length, containingRange. upperBound. utf8Offset)
418
431
let upperBound = _guts. string. utf8. index ( stringIdx, offsetBy: upperUTF8 - stringIdx. utf8Offset)
419
432
return Run ( _attributes: run. attributes, Range ( uncheckedBounds: ( lowerBound, upperBound) ) , _guts)
420
433
}
@@ -428,8 +441,7 @@ extension AttributedString.Runs {
428
441
_strBounds. contains ( position. _value) ,
429
442
" AttributedString index is out of bounds " )
430
443
let r = _guts. findRun ( at: position. _value)
431
- let rangeIdx = _strBounds. rangeIdx ( containing: position. _value)
432
- let range = _strBounds. ranges [ rangeIdx]
444
+ let ( range, rangeIdx) = _strBounds. range ( containing: position. _value)
433
445
let strIdx = Swift . max ( range. lowerBound, r. start)
434
446
return Index ( _runIndex: r. runIndex, startStringIndex: r. start, stringIndex: strIdx, rangeOffset: rangeIdx, withinDiscontiguous: _isDiscontiguous)
435
447
}
@@ -484,13 +496,10 @@ extension AttributedString.Runs {
484
496
constraints: Set < AttributeRunBoundaries ? > ,
485
497
endOfCurrent: Bool
486
498
) -> AttributedString . Index {
487
- precondition (
488
- self . _strBounds. contains ( i. _value) ,
489
- " AttributedString index is out of bounds " )
499
+ // _strBounds.range(containing:) below validates that i._value is within the bounds of this slice
490
500
precondition ( !attributeNames. isEmpty)
491
501
let r = _guts. findRun ( at: i. _value)
492
- let currentRangeIdx = _strBounds. rangeIdx ( containing: i. _value)
493
- let currentRange = _strBounds. ranges [ currentRangeIdx]
502
+ let ( currentRange, currentRangeIdx) = _strBounds. range ( containing: i. _value)
494
503
495
504
guard constraints. count != 1 || constraints. contains ( nil ) else {
496
505
// We have a single constraint and attributes are guaranteed to be consistent between constraint boundaries
@@ -540,18 +549,15 @@ extension AttributedString.Runs {
540
549
constraints: Set < AttributeRunBoundaries ? > ,
541
550
endOfPrevious: Bool
542
551
) -> AttributedString . Index {
543
- precondition (
544
- _strBounds. contains ( i. _value) || i. _value == endIndex. _stringIndex,
545
- " AttributedString index is out of bounds " )
552
+ // _strBounds.range(containing:) below validates that i._value is within the bounds of this slice
546
553
precondition ( !attributeNames. isEmpty)
547
554
var currentRangeIdx : Int
548
555
var currentRange : Range < BigString . Index >
549
556
if i. _value == endIndex. _stringIndex {
550
557
currentRangeIdx = _strBounds. ranges. count
551
558
currentRange = Range ( uncheckedBounds: ( endIndex. _stringIndex!, endIndex. _stringIndex!) )
552
559
} else {
553
- currentRangeIdx = _strBounds. rangeIdx ( containing: i. _value)
554
- currentRange = _strBounds. ranges [ currentRangeIdx]
560
+ ( currentRange, currentRangeIdx) = _strBounds. range ( containing: i. _value)
555
561
}
556
562
var currentStringIdx = i. _value
557
563
if currentRange. lowerBound == i. _value {
@@ -585,15 +591,13 @@ extension AttributedString.Runs {
585
591
attributeNames: [ String ] ,
586
592
constraints: Set < AttributeRunBoundaries ? >
587
593
) -> ( index: AttributedString . Index , runIndex: AttributedString . _InternalRuns . Index ) {
588
- precondition (
589
- _strBounds. contains ( i. _value) || i. _value == endIndex. _stringIndex,
590
- " AttributedString index is out of bounds " )
594
+ // _strBounds.range(containing:) below validates that i._value is within the bounds of this slice
591
595
precondition ( !attributeNames. isEmpty)
592
596
let r = _guts. findRun ( at: i. _value)
593
597
if r. runIndex. offset == endIndex. _runOffset {
594
598
return ( i, r. runIndex)
595
599
}
596
- let currentRange = _strBounds. ranges [ _strBounds . rangeIdx ( containing: i. _value) ]
600
+ let currentRange = _strBounds. range ( containing: i. _value) . range
597
601
598
602
guard constraints. count != 1 || constraints. contains ( nil ) else {
599
603
let nextIndex = _guts. string. unicodeScalars. index ( after: i. _value)
@@ -719,20 +723,20 @@ extension BigSubstring.UnicodeScalarView {
719
723
}
720
724
721
725
extension RangeSet {
722
- fileprivate func rangeIdx ( containing index: Bound ) -> Int {
726
+ fileprivate func range ( containing index: Bound ) -> ( range : Range < Bound > , offset : Int ) {
723
727
var start = 0
724
728
var end = self . ranges. count
725
729
while start < end {
726
730
let middle = ( start + end) / 2
727
731
let value = self . ranges [ middle]
728
732
if value. contains ( index) {
729
- return middle
733
+ return ( value , middle)
730
734
} else if index < value. lowerBound {
731
735
end = middle
732
736
} else {
733
737
start = middle + 1
734
738
}
735
739
}
736
- preconditionFailure ( " Internal Inconsistency: Provided index \( index ) is out of bounds " )
740
+ preconditionFailure ( " AttributedString.Runs index is out of bounds" )
737
741
}
738
742
}
0 commit comments