@@ -481,19 +481,36 @@ extension AttributedString.Runs {
481481 internal func _slicedRunBoundary(
482482 after i: AttributedString . Index ,
483483 attributeNames: [ String ] ,
484- constraints: [ AttributeRunBoundaries ] ,
484+ constraints: Set < AttributeRunBoundaries ? > ,
485485 endOfCurrent: Bool
486486 ) -> AttributedString . Index {
487487 precondition (
488488 self . _strBounds. contains ( i. _value) ,
489489 " AttributedString index is out of bounds " )
490490 precondition ( !attributeNames. isEmpty)
491491 let r = _guts. findRun ( at: i. _value)
492+ let currentRangeIdx = _strBounds. rangeIdx ( containing: i. _value)
493+ let currentRange = _strBounds. ranges [ currentRangeIdx]
494+
495+ guard constraints. count != 1 || constraints. contains ( nil ) else {
496+ // We have a single constraint and attributes are guaranteed to be consistent between constraint boundaries
497+ // This means that we will not break until the next constraint boundary, so we don't need to enumerate the actual run contents
498+ let constraintBreak = _guts. string. _firstConstraintBreak ( in: i. _value ..< currentRange. upperBound, with: constraints)
499+ if !endOfCurrent && constraintBreak == currentRange. upperBound {
500+ // No constraint break, return the next subrange start or the end index
501+ if currentRangeIdx == _strBounds. ranges. count - 1 {
502+ return . init( currentRange. upperBound, version: _guts. version)
503+ } else {
504+ return . init( _strBounds. ranges [ currentRangeIdx + 1 ] . lowerBound, version: _guts. version)
505+ }
506+ } else {
507+ return . init( constraintBreak, version: _guts. version)
508+ }
509+ }
510+
492511 let endRun = _lastOfMatchingRuns ( with: r. runIndex, comparing: attributeNames)
493512 let utf8End = endRun. utf8Offset + _guts. runs [ endRun] . length
494513 let strIndexEnd = _guts. string. utf8. index ( r. start, offsetBy: utf8End - r. start. utf8Offset)
495- let currentRangeIdx = _strBounds. rangeIdx ( containing: i. _value)
496- let currentRange = _strBounds. ranges [ currentRangeIdx]
497514 if strIndexEnd < currentRange. upperBound {
498515 // The coalesced run ends within the current range, so just look for the next break in the coalesced run
499516 return . init( _guts. string. _firstConstraintBreak ( in: i. _value ..< strIndexEnd, with: constraints) , version: _guts. version)
@@ -520,7 +537,7 @@ extension AttributedString.Runs {
520537 internal func _slicedRunBoundary(
521538 before i: AttributedString . Index ,
522539 attributeNames: [ String ] ,
523- constraints: [ AttributeRunBoundaries ] ,
540+ constraints: Set < AttributeRunBoundaries ? > ,
524541 endOfPrevious: Bool
525542 ) -> AttributedString . Index {
526543 precondition (
@@ -545,6 +562,11 @@ extension AttributedString.Runs {
545562 currentStringIdx = currentRange. upperBound
546563 if endOfPrevious { return . init( currentStringIdx, version: _guts. version) }
547564 }
565+
566+ guard constraints. count != 1 || constraints. contains ( nil ) else {
567+ return . init( _guts. string. _lastConstraintBreak ( in: currentRange. lowerBound ..< currentStringIdx, with: constraints) , version: _guts. version)
568+ }
569+
548570 let beforeStringIdx = _guts. string. utf8. index ( before: currentStringIdx)
549571 let r = _guts. runs. index ( atUTF8Offset: beforeStringIdx. utf8Offset)
550572 let startRun = _firstOfMatchingRuns ( with: r. index, comparing: attributeNames)
@@ -561,7 +583,7 @@ extension AttributedString.Runs {
561583 internal func _slicedRunBoundary(
562584 roundingDown i: AttributedString . Index ,
563585 attributeNames: [ String ] ,
564- constraints: [ AttributeRunBoundaries ]
586+ constraints: Set < AttributeRunBoundaries ? >
565587 ) -> ( index: AttributedString . Index , runIndex: AttributedString . _InternalRuns . Index ) {
566588 precondition (
567589 _strBounds. contains ( i. _value) || i. _value == endIndex. _stringIndex,
@@ -571,8 +593,22 @@ extension AttributedString.Runs {
571593 if r. runIndex. offset == endIndex. _runOffset {
572594 return ( i, r. runIndex)
573595 }
574- let startRun = _firstOfMatchingRuns ( with: r. runIndex, comparing: attributeNames)
575596 let currentRange = _strBounds. ranges [ _strBounds. rangeIdx ( containing: i. _value) ]
597+
598+ guard constraints. count != 1 || constraints. contains ( nil ) else {
599+ let nextIndex = _guts. string. unicodeScalars. index ( after: i. _value)
600+ let constraintBreak = _guts. string. _lastConstraintBreak ( in: currentRange. lowerBound ..< nextIndex, with: constraints)
601+ var runIdx = r. runIndex
602+ while runIdx. utf8Offset > constraintBreak. utf8Offset {
603+ _guts. runs. formIndex ( before: & runIdx)
604+ }
605+ return (
606+ . init( constraintBreak, version: _guts. version) ,
607+ runIdx
608+ )
609+ }
610+
611+ let startRun = _firstOfMatchingRuns ( with: r. runIndex, comparing: attributeNames)
576612 let stringStart = Swift . max (
577613 _guts. string. utf8. index ( r. start, offsetBy: startRun. utf8Offset - r. start. utf8Offset) ,
578614 currentRange. lowerBound)
@@ -587,9 +623,9 @@ extension AttributedString.Runs {
587623extension BigString {
588624 internal func _firstConstraintBreak(
589625 in range: Range < Index > ,
590- with constraints: [ AttributedString . AttributeRunBoundaries ]
626+ with constraints: Set < AttributedString . AttributeRunBoundaries ? >
591627 ) -> Index {
592- guard !constraints . isEmpty , ! range. isEmpty else { return range. upperBound }
628+ guard !range. isEmpty else { return range. upperBound }
593629
594630 var r = range
595631 if
@@ -602,7 +638,7 @@ extension BigString {
602638 if constraints. _containsScalarConstraint {
603639 // Note: we need to slice runs on matching scalars even if they don't carry
604640 // the attributes we're looking for.
605- let scalars : [ UnicodeScalar ] = constraints. compactMap { $0. _constrainedScalar }
641+ let scalars : [ UnicodeScalar ] = constraints. compactMap { $0? . _constrainedScalar }
606642 if let firstBreak = self . unicodeScalars [ r] . _findFirstScalarBoundary ( for: scalars) {
607643 r = r. lowerBound ..< firstBreak
608644 }
@@ -613,9 +649,9 @@ extension BigString {
613649
614650 internal func _lastConstraintBreak(
615651 in range: Range < Index > ,
616- with constraints: [ AttributedString . AttributeRunBoundaries ]
652+ with constraints: Set < AttributedString . AttributeRunBoundaries ? >
617653 ) -> Index {
618- guard !constraints . isEmpty , ! range. isEmpty else { return range. lowerBound }
654+ guard !range. isEmpty else { return range. lowerBound }
619655
620656 var r = range
621657 if
@@ -628,7 +664,7 @@ extension BigString {
628664 if constraints. _containsScalarConstraint {
629665 // Note: we need to slice runs on matching scalars even if they don't carry
630666 // the attributes we're looking for.
631- let scalars : [ UnicodeScalar ] = constraints. compactMap { $0. _constrainedScalar }
667+ let scalars : [ UnicodeScalar ] = constraints. compactMap { $0? . _constrainedScalar }
632668 if let lastBreak = self . unicodeScalars [ r] . _findLastScalarBoundary ( for: scalars) {
633669 r = lastBreak ..< r. upperBound
634670 }
0 commit comments