Skip to content

Commit 4eab835

Browse files
committed
[stdlib] String: prefer passing ranges to start+end argument pairs
1 parent 06090ce commit 4eab835

File tree

3 files changed

+87
-50
lines changed

3 files changed

+87
-50
lines changed

stdlib/public/core/StringGraphemeBreaking.swift

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,22 +109,25 @@ extension _StringGuts {
109109

110110
internal func roundDownToNearestCharacter(
111111
_ i: String.Index,
112-
from start: String.Index,
113-
to end: String.Index
112+
in bounds: Range<String.Index>
114113
) -> String.Index {
115-
_internalInvariant(start._isScalarAligned && end._isScalarAligned)
116-
_internalInvariant(hasMatchingEncoding(start) && hasMatchingEncoding(end))
117-
_internalInvariant(start <= end && end <= endIndex)
114+
_internalInvariant(
115+
bounds.lowerBound._isScalarAligned && bounds.upperBound._isScalarAligned)
116+
_internalInvariant(
117+
hasMatchingEncoding(bounds.lowerBound) && hasMatchingEncoding(bounds.upperBound))
118+
_internalInvariant(bounds.upperBound <= endIndex)
118119

119120
_internalInvariant(i._isScalarAligned)
120121
_internalInvariant(hasMatchingEncoding(i))
121-
_internalInvariant(i >= start && i <= end)
122+
_internalInvariant(i >= bounds.lowerBound && i <= bounds.upperBound)
122123

123124
// We can only use the `_isCharacterAligned` bit if the start index is also
124125
// character-aligned.
125-
if start._isCharacterAligned && i._isCharacterAligned { return i }
126+
if bounds.lowerBound._isCharacterAligned && i._isCharacterAligned {
127+
return i
128+
}
126129

127-
if i == start || i == end { return i }
130+
if i == bounds.lowerBound || i == bounds.upperBound { return i }
128131

129132
let offset = i._encodedOffset
130133
let prior = offset - _opaqueCharacterStride(endingAt: offset)
@@ -136,7 +139,7 @@ extension _StringGuts {
136139
return i
137140
}
138141
var r = String.Index(encodedOffset: prior, characterStride: stride)
139-
if start._isCharacterAligned {
142+
if bounds.lowerBound._isCharacterAligned {
140143
r = r._characterAligned
141144
} else {
142145
r = r._scalarAligned

stdlib/public/core/StringGuts.swift

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -449,13 +449,13 @@ extension _StringGuts {
449449
@_alwaysEmitIntoClient
450450
internal func validateScalarIndex(
451451
_ i: String.Index,
452-
from start: String.Index,
453-
to end: String.Index
452+
in bounds: Range<String.Index>
454453
) -> String.Index {
455-
_internalInvariant(start <= end && end <= endIndex)
454+
_internalInvariant(bounds.upperBound <= endIndex)
456455

457456
let i = ensureMatchingEncoding(i)
458-
_precondition(i >= start && i < end, "Substring index is out of bounds")
457+
_precondition(i >= bounds.lowerBound && i < bounds.upperBound,
458+
"Substring index is out of bounds")
459459
return scalarAlign(i)
460460
}
461461
}
@@ -486,13 +486,13 @@ extension _StringGuts {
486486
/// - is aligned on a scalar boundary.
487487
internal func validateInclusiveScalarIndex(
488488
_ i: String.Index,
489-
from start: String.Index,
490-
to end: String.Index
489+
in bounds: Range<String.Index>
491490
) -> String.Index {
492-
_internalInvariant(start <= end && end <= endIndex)
491+
_internalInvariant(bounds.upperBound <= endIndex)
493492

494493
let i = ensureMatchingEncoding(i)
495-
_precondition(i >= start && i <= end, "Substring index is out of bounds")
494+
_precondition(i >= bounds.lowerBound && i <= bounds.upperBound,
495+
"Substring index is out of bounds")
496496
return scalarAlign(i)
497497
}
498498
}
@@ -517,18 +517,20 @@ extension _StringGuts {
517517
@_alwaysEmitIntoClient
518518
internal func validateSubscalarRange(
519519
_ range: Range<String.Index>,
520-
from start: String.Index,
521-
to end: String.Index
520+
in bounds: Range<String.Index>
522521
) -> Range<String.Index> {
523-
_internalInvariant(start <= end && end <= endIndex)
522+
_internalInvariant(bounds.upperBound <= endIndex)
524523

525524
let upper = ensureMatchingEncoding(range.upperBound)
526525
let lower = ensureMatchingEncoding(range.lowerBound)
527526

528527
// Note: if only `lower` was miscoded, then the range invariant `lower <=
529528
// upper` may no longer hold after the above conversions, so we need to
530529
// re-check it here.
531-
_precondition(upper <= end && lower >= start && lower <= upper,
530+
_precondition(
531+
upper <= bounds.upperBound
532+
&& lower >= bounds.lowerBound
533+
&& lower <= upper,
532534
"Substring index range is out of bounds")
533535

534536
return Range(_uncheckedBounds: (lower, upper))
@@ -578,18 +580,20 @@ extension _StringGuts {
578580
/// - are aligned on a scalar boundary.
579581
internal func validateScalarRange(
580582
_ range: Range<String.Index>,
581-
from start: String.Index,
582-
to end: String.Index
583+
in bounds: Range<String.Index>
583584
) -> Range<String.Index> {
584-
_internalInvariant(start <= end && end <= endIndex)
585+
_internalInvariant(bounds.upperBound <= endIndex)
585586

586587
var upper = ensureMatchingEncoding(range.upperBound)
587588
var lower = ensureMatchingEncoding(range.lowerBound)
588589

589590
// Note: if only `lower` was miscoded, then the range invariant `lower <=
590591
// upper` may no longer hold after the above conversions, so we need to
591592
// re-check it here.
592-
_precondition(upper <= end && lower >= start && lower <= upper,
593+
_precondition(
594+
upper <= bounds.upperBound
595+
&& lower >= bounds.lowerBound
596+
&& lower <= upper,
593597
"Substring index range is out of bounds")
594598

595599
upper = scalarAlign(upper)

stdlib/public/core/Substring.swift

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,23 @@ extension Substring {
145145
return Range(_uncheckedBounds: (lower, upper))
146146
}
147147

148+
@inlinable @inline(__always)
149+
internal var _bounds: Range<Index> {
150+
Range(_uncheckedBounds: (startIndex, endIndex))
151+
}
152+
}
153+
154+
extension Substring {
155+
internal var _startIsCharacterAligned: Bool {
156+
startIndex._isCharacterAligned
157+
}
158+
159+
internal var _endIsCharacterAligned: Bool {
160+
endIndex._isCharacterAligned
161+
}
162+
}
163+
164+
extension Substring {
148165
#if !INTERNAL_CHECKS_ENABLED
149166
@inlinable @inline(__always) internal func _invariantCheck() {}
150167
#else
@@ -167,28 +184,28 @@ extension Substring {
167184
extension Substring {
168185
@inline(__always)
169186
internal func _validateScalarIndex(_ i: String.Index) -> String.Index {
170-
_wholeGuts.validateScalarIndex(i, from: startIndex, to: endIndex)
187+
_wholeGuts.validateScalarIndex(i, in: _bounds)
171188
}
172189

173190
@inline(__always)
174191
internal func _validateInclusiveScalarIndex(
175192
_ i: String.Index
176193
) -> String.Index {
177-
_wholeGuts.validateInclusiveScalarIndex(i, from: startIndex, to: endIndex)
194+
_wholeGuts.validateInclusiveScalarIndex(i, in: _bounds)
178195
}
179196

180197
@inline(__always)
181198
internal func _validateScalarRange(
182199
_ range: Range<String.Index>
183200
) -> Range<String.Index> {
184-
_wholeGuts.validateScalarRange(range, from: startIndex, to: endIndex)
201+
_wholeGuts.validateScalarRange(range, in: _bounds)
185202
}
186203

187204
@inline(__always)
188205
internal func _roundDownToNearestCharacter(
189206
_ i: String.Index
190207
) -> String.Index {
191-
_wholeGuts.roundDownToNearestCharacter(i, from: startIndex, to: endIndex)
208+
_wholeGuts.roundDownToNearestCharacter(i, in: _bounds)
192209
}
193210

194211
/// Return true if and only if `i` is a valid index in this substring,
@@ -647,16 +664,6 @@ extension Substring: StringProtocol {
647664
}
648665
}
649666

650-
extension Substring {
651-
internal var _startIsCharacterAligned: Bool {
652-
startIndex._isCharacterAligned
653-
}
654-
655-
internal var _endIsCharacterAligned: Bool {
656-
endIndex._isCharacterAligned
657-
}
658-
}
659-
660667
extension Substring {
661668
internal func _characterStride(startingAt i: Index) -> Int {
662669
_internalInvariant(i._isScalarAligned)
@@ -737,6 +744,11 @@ extension Substring {
737744

738745
@_alwaysEmitIntoClient @inline(__always)
739746
internal var _base: String.UTF8View { _slice._base }
747+
748+
@_alwaysEmitIntoClient @inline(__always)
749+
internal var _bounds: Range<Index> {
750+
Range(_uncheckedBounds: (_slice._startIndex, _slice._endIndex))
751+
}
740752
}
741753
}
742754

@@ -833,7 +845,7 @@ extension Substring.UTF8View: BidirectionalCollection {
833845
@inlinable
834846
public subscript(r: Range<Index>) -> Substring.UTF8View {
835847
// FIXME(strings): tests.
836-
let r = _wholeGuts.validateSubscalarRange(r, from: startIndex, to: endIndex)
848+
let r = _wholeGuts.validateSubscalarRange(r, in: _bounds)
837849
return Substring.UTF8View(_slice.base, _bounds: r)
838850
}
839851
}
@@ -894,6 +906,11 @@ extension Substring {
894906

895907
@_alwaysEmitIntoClient @inline(__always)
896908
internal var _base: String.UTF16View { _slice._base }
909+
910+
@_alwaysEmitIntoClient @inline(__always)
911+
internal var _bounds: Range<Index> {
912+
Range(_uncheckedBounds: (_slice._startIndex, _slice._endIndex))
913+
}
897914
}
898915
}
899916

@@ -981,7 +998,7 @@ extension Substring.UTF16View: BidirectionalCollection {
981998

982999
@inlinable
9831000
public subscript(r: Range<Index>) -> Substring.UTF16View {
984-
let r = _wholeGuts.validateSubscalarRange(r, from: startIndex, to: endIndex)
1001+
let r = _wholeGuts.validateSubscalarRange(r, in: _bounds)
9851002
return Substring.UTF16View(_slice.base, _bounds: r)
9861003
}
9871004
}
@@ -1039,16 +1056,31 @@ extension Substring {
10391056

10401057
/// Creates an instance that slices `base` at `_bounds`.
10411058
@usableFromInline // This used to be inlinable before 5.7
1042-
@available(*, deprecated) // Use `init(_unchecked:)` in new code.
1059+
@available(*, deprecated, message: "Use `init(_unchecked:bounds)` in new code")
10431060
internal init(_ base: String.UnicodeScalarView, _bounds: Range<Index>) {
10441061
let start = base._guts.scalarAlign(_bounds.lowerBound)
10451062
let end = base._guts.scalarAlign(_bounds.upperBound)
10461063
_slice = Slice(base: base, bounds: Range(_uncheckedBounds: (start, end)))
10471064
}
1065+
}
1066+
}
10481067

1049-
@_alwaysEmitIntoClient
1050-
@inline(__always)
1051-
internal var _wholeGuts: _StringGuts { _slice._base._guts }
1068+
extension Substring.UnicodeScalarView {
1069+
@_alwaysEmitIntoClient
1070+
@inline(__always)
1071+
internal var _wholeGuts: _StringGuts { _slice._base._guts }
1072+
1073+
@inline(__always)
1074+
internal var _offsetRange: Range<Int> {
1075+
let lower = _slice._startIndex._encodedOffset
1076+
let upper = _slice._endIndex._encodedOffset
1077+
return Range(_uncheckedBounds: (lower, upper))
1078+
}
1079+
1080+
@_alwaysEmitIntoClient
1081+
@inline(__always)
1082+
internal var _bounds: Range<Index> {
1083+
Range(_uncheckedBounds: (startIndex, endIndex))
10521084
}
10531085
}
10541086

@@ -1069,8 +1101,7 @@ extension Substring.UnicodeScalarView: BidirectionalCollection {
10691101

10701102
@inlinable
10711103
public subscript(index: Index) -> Element {
1072-
let index = _wholeGuts.validateScalarIndex(
1073-
index, from: startIndex, to: endIndex)
1104+
let index = _wholeGuts.validateScalarIndex(index, in: _bounds)
10741105
return _wholeGuts.errorCorrectedScalar(startingAt: index._encodedOffset).0
10751106
}
10761107

@@ -1130,7 +1161,7 @@ extension Substring.UnicodeScalarView: BidirectionalCollection {
11301161

11311162
public subscript(r: Range<Index>) -> Substring.UnicodeScalarView {
11321163
// Note: This used to be inlinable until Swift 5.7
1133-
let r = _wholeGuts.validateScalarRange(r, from: startIndex, to: endIndex)
1164+
let r = _wholeGuts.validateScalarRange(r, in: _bounds)
11341165
return Substring.UnicodeScalarView(_unchecked: _slice._base, bounds: r)
11351166
}
11361167
}
@@ -1175,8 +1206,7 @@ extension Substring.UnicodeScalarView: RangeReplaceableCollection {
11751206
_ subrange: Range<Index>, with replacement: C
11761207
) where C.Element == Element {
11771208
// TODO(lorentey): Review index validation
1178-
let subrange = _slice._base._guts.validateScalarRange(
1179-
subrange, from: startIndex, to: endIndex)
1209+
let subrange = _wholeGuts.validateScalarRange(subrange, in: _bounds)
11801210
_slice.replaceSubrange(subrange, with: replacement)
11811211
}
11821212
}

0 commit comments

Comments
 (0)