@@ -278,8 +278,14 @@ extension AttributedString.Runs: BidirectionalCollection {
278278 precondition ( i < _bounds. upperBound, " Can't advance AttributedString.Runs index beyond end " )
279279 let ( resolvedIdx, runStartIdx) = _resolve ( i)
280280 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+ }
283289 if currentRange. upperBound. utf8Offset <= next. utf8Offset {
284290 let nextRangeIdx = currentRangeIdx + 1
285291 if nextRangeIdx == _strBounds. ranges. count {
@@ -300,7 +306,7 @@ extension AttributedString.Runs: BidirectionalCollection {
300306 public func index( before i: Index ) -> Index {
301307 precondition ( i > _bounds. lowerBound, " Can't step AttributedString.Runs index below start " )
302308 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
304310 if i == endIndex || runStartIdx. utf8Offset <= _strBounds. ranges [ currentRangeIdx] . lowerBound. utf8Offset {
305311 // The current run starts on or before our current range, look up the next range
306312 let previousRange = _strBounds. ranges [ currentRangeIdx - 1 ]
@@ -394,27 +400,34 @@ extension AttributedString.Runs: BidirectionalCollection {
394400
395401 public subscript( position: Index ) -> Run {
396402 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- }
400403 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]
402418 }
403419
404420 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
408422 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 ]
410424 }
411425
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 {
413427 let run = _guts. runs [ i]
414428 // 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)
418431 let upperBound = _guts. string. utf8. index ( stringIdx, offsetBy: upperUTF8 - stringIdx. utf8Offset)
419432 return Run ( _attributes: run. attributes, Range ( uncheckedBounds: ( lowerBound, upperBound) ) , _guts)
420433 }
@@ -428,8 +441,7 @@ extension AttributedString.Runs {
428441 _strBounds. contains ( position. _value) ,
429442 " AttributedString index is out of bounds " )
430443 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)
433445 let strIdx = Swift . max ( range. lowerBound, r. start)
434446 return Index ( _runIndex: r. runIndex, startStringIndex: r. start, stringIndex: strIdx, rangeOffset: rangeIdx, withinDiscontiguous: _isDiscontiguous)
435447 }
@@ -484,13 +496,10 @@ extension AttributedString.Runs {
484496 constraints: Set < AttributeRunBoundaries ? > ,
485497 endOfCurrent: Bool
486498 ) -> 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
490500 precondition ( !attributeNames. isEmpty)
491501 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)
494503
495504 guard constraints. count != 1 || constraints. contains ( nil ) else {
496505 // We have a single constraint and attributes are guaranteed to be consistent between constraint boundaries
@@ -540,18 +549,15 @@ extension AttributedString.Runs {
540549 constraints: Set < AttributeRunBoundaries ? > ,
541550 endOfPrevious: Bool
542551 ) -> 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
546553 precondition ( !attributeNames. isEmpty)
547554 var currentRangeIdx : Int
548555 var currentRange : Range < BigString . Index >
549556 if i. _value == endIndex. _stringIndex {
550557 currentRangeIdx = _strBounds. ranges. count
551558 currentRange = Range ( uncheckedBounds: ( endIndex. _stringIndex!, endIndex. _stringIndex!) )
552559 } else {
553- currentRangeIdx = _strBounds. rangeIdx ( containing: i. _value)
554- currentRange = _strBounds. ranges [ currentRangeIdx]
560+ ( currentRange, currentRangeIdx) = _strBounds. range ( containing: i. _value)
555561 }
556562 var currentStringIdx = i. _value
557563 if currentRange. lowerBound == i. _value {
@@ -585,15 +591,13 @@ extension AttributedString.Runs {
585591 attributeNames: [ String ] ,
586592 constraints: Set < AttributeRunBoundaries ? >
587593 ) -> ( 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
591595 precondition ( !attributeNames. isEmpty)
592596 let r = _guts. findRun ( at: i. _value)
593597 if r. runIndex. offset == endIndex. _runOffset {
594598 return ( i, r. runIndex)
595599 }
596- let currentRange = _strBounds. ranges [ _strBounds . rangeIdx ( containing: i. _value) ]
600+ let currentRange = _strBounds. range ( containing: i. _value) . range
597601
598602 guard constraints. count != 1 || constraints. contains ( nil ) else {
599603 let nextIndex = _guts. string. unicodeScalars. index ( after: i. _value)
@@ -719,20 +723,20 @@ extension BigSubstring.UnicodeScalarView {
719723}
720724
721725extension RangeSet {
722- fileprivate func rangeIdx ( containing index: Bound ) -> Int {
726+ fileprivate func range ( containing index: Bound ) -> ( range : Range < Bound > , offset : Int ) {
723727 var start = 0
724728 var end = self . ranges. count
725729 while start < end {
726730 let middle = ( start + end) / 2
727731 let value = self . ranges [ middle]
728732 if value. contains ( index) {
729- return middle
733+ return ( value , middle)
730734 } else if index < value. lowerBound {
731735 end = middle
732736 } else {
733737 start = middle + 1
734738 }
735739 }
736- preconditionFailure ( " Internal Inconsistency: Provided index \( index ) is out of bounds " )
740+ preconditionFailure ( " AttributedString.Runs index is out of bounds" )
737741 }
738742}
0 commit comments