Skip to content

Commit 64fa7d1

Browse files
Cleanup algorithms code (#771)
Cleanup algorithms code Delete lots of dead code, get off of collections and onto iterators, simplify implementations. --------- Co-authored-by: Nate Cook <[email protected]>
1 parent 93794cd commit 64fa7d1

21 files changed

+88
-1229
lines changed

Sources/_StringProcessing/Algorithms/Algorithms/Ranges.swift

+25-109
Original file line numberDiff line numberDiff line change
@@ -11,107 +11,33 @@
1111

1212
// MARK: `RangesCollection`
1313

14-
struct RangesCollection<Searcher: CollectionSearcher> {
15-
public typealias Base = Searcher.Searched
16-
17-
let base: Base
18-
let searcher: Searcher
19-
private(set) public var startIndex: Index
20-
21-
init(base: Base, searcher: Searcher) {
22-
self.base = base
23-
self.searcher = searcher
24-
25-
var state = searcher.state(for: base, in: base.startIndex..<base.endIndex)
26-
self.startIndex = Index(range: nil, state: state)
27-
28-
if let range = searcher.search(base, &state) {
29-
self.startIndex = Index(range: range, state: state)
30-
} else {
31-
self.startIndex = endIndex
32-
}
33-
}
34-
}
35-
36-
struct RangesIterator<Searcher: CollectionSearcher>: IteratorProtocol {
37-
public typealias Base = Searcher.Searched
38-
39-
let base: Base
14+
struct RangesSequence<Searcher: CollectionSearcher> {
15+
let input: Searcher.Searched
4016
let searcher: Searcher
41-
var state: Searcher.State
4217

43-
init(base: Base, searcher: Searcher) {
44-
self.base = base
18+
init(input: Searcher.Searched, searcher: Searcher) {
19+
self.input = input
4520
self.searcher = searcher
46-
self.state = searcher.state(for: base, in: base.startIndex..<base.endIndex)
4721
}
4822

49-
public mutating func next() -> Range<Base.Index>? {
50-
searcher.search(base, &state)
51-
}
52-
}
53-
54-
extension RangesCollection: Sequence {
55-
public func makeIterator() -> RangesIterator<Searcher> {
56-
Iterator(base: base, searcher: searcher)
57-
}
58-
}
59-
60-
extension RangesCollection: Collection {
61-
// TODO: Custom `SubSequence` for the sake of more efficient slice iteration
62-
63-
public struct Index {
64-
var range: Range<Searcher.Searched.Index>?
23+
struct Iterator: IteratorProtocol {
24+
let base: RangesSequence
6525
var state: Searcher.State
66-
}
6726

68-
public var endIndex: Index {
69-
// TODO: Avoid calling `state(for:startingAt)` here
70-
Index(
71-
range: nil,
72-
state: searcher.state(for: base, in: base.startIndex..<base.endIndex))
73-
}
74-
75-
public func formIndex(after index: inout Index) {
76-
guard index != endIndex else { fatalError("Cannot advance past endIndex") }
77-
index.range = searcher.search(base, &index.state)
78-
}
79-
80-
public func index(after index: Index) -> Index {
81-
var index = index
82-
formIndex(after: &index)
83-
return index
84-
}
85-
86-
public subscript(index: Index) -> Range<Base.Index> {
87-
guard let range = index.range else {
88-
fatalError("Cannot subscript using endIndex")
27+
init(_ base: RangesSequence) {
28+
self.base = base
29+
self.state = base.searcher.state(for: base.input, in: base.input.startIndex..<base.input.endIndex)
8930
}
90-
return range
91-
}
92-
}
9331

94-
extension RangesCollection.Index: Comparable {
95-
static func == (lhs: Self, rhs: Self) -> Bool {
96-
switch (lhs.range, rhs.range) {
97-
case (nil, nil):
98-
return true
99-
case (nil, _?), (_?, nil):
100-
return false
101-
case (let lhs?, let rhs?):
102-
return lhs.lowerBound == rhs.lowerBound
32+
mutating func next() -> Range<Searcher.Searched.Index>? {
33+
base.searcher.search(base.input, &state)
10334
}
10435
}
36+
}
10537

106-
static func < (lhs: Self, rhs: Self) -> Bool {
107-
switch (lhs.range, rhs.range) {
108-
case (nil, _):
109-
return false
110-
case (_, nil):
111-
return true
112-
case (let lhs?, let rhs?):
113-
return lhs.lowerBound < rhs.lowerBound
114-
}
38+
extension RangesSequence: Sequence {
39+
func makeIterator() -> Iterator {
40+
Iterator(self)
11541
}
11642
}
11743

@@ -122,8 +48,8 @@ extension RangesCollection.Index: Comparable {
12248
extension Collection {
12349
func _ranges<S: CollectionSearcher>(
12450
of searcher: S
125-
) -> RangesCollection<S> where S.Searched == Self {
126-
RangesCollection(base: self, searcher: searcher)
51+
) -> RangesSequence<S> where S.Searched == Self {
52+
RangesSequence(input: self, searcher: searcher)
12753
}
12854
}
12955

@@ -132,7 +58,7 @@ extension Collection {
13258
extension Collection where Element: Equatable {
13359
func _ranges<C: Collection>(
13460
of other: C
135-
) -> RangesCollection<ZSearcher<Self>> where C.Element == Element {
61+
) -> RangesSequence<ZSearcher<Self>> where C.Element == Element {
13662
_ranges(of: ZSearcher(pattern: Array(other), by: ==))
13763
}
13864

@@ -163,8 +89,8 @@ extension Collection where Element: Equatable {
16389
}
16490

16591
@available(SwiftStdlib 5.7, *)
166-
struct RegexRangesCollection<Output> {
167-
let base: RegexMatchesCollection<Output>
92+
struct RegexRangesSequence<Output> {
93+
let base: RegexMatchesSequence<Output>
16894

16995
init(
17096
input: String,
@@ -181,9 +107,9 @@ struct RegexRangesCollection<Output> {
181107
}
182108

183109
@available(SwiftStdlib 5.7, *)
184-
extension RegexRangesCollection: Sequence {
110+
extension RegexRangesSequence: Sequence {
185111
struct Iterator: IteratorProtocol {
186-
var matchesBase: RegexMatchesCollection<Output>.Iterator
112+
var matchesBase: RegexMatchesSequence<Output>.Iterator
187113

188114
mutating func next() -> Range<String.Index>? {
189115
matchesBase.next().map(\.range)
@@ -195,16 +121,6 @@ extension RegexRangesCollection: Sequence {
195121
}
196122
}
197123

198-
@available(SwiftStdlib 5.7, *)
199-
extension RegexRangesCollection: Collection {
200-
typealias Index = RegexMatchesCollection<Output>.Index
201-
202-
var startIndex: Index { base.startIndex }
203-
var endIndex: Index { base.endIndex }
204-
func index(after i: Index) -> Index { base.index(after: i) }
205-
subscript(position: Index) -> Range<String.Index> { base[position].range }
206-
}
207-
208124
// MARK: Regex algorithms
209125

210126
extension Collection where SubSequence == Substring {
@@ -214,8 +130,8 @@ extension Collection where SubSequence == Substring {
214130
of regex: R,
215131
subjectBounds: Range<String.Index>,
216132
searchBounds: Range<String.Index>
217-
) -> RegexRangesCollection<R.RegexOutput> {
218-
RegexRangesCollection(
133+
) -> RegexRangesSequence<R.RegexOutput> {
134+
RegexRangesSequence(
219135
input: self[...].base,
220136
subjectBounds: subjectBounds,
221137
searchBounds: searchBounds,
@@ -226,7 +142,7 @@ extension Collection where SubSequence == Substring {
226142
@_disfavoredOverload
227143
func _ranges<R: RegexComponent>(
228144
of regex: R
229-
) -> RegexRangesCollection<R.RegexOutput> {
145+
) -> RegexRangesSequence<R.RegexOutput> {
230146
_ranges(
231147
of: regex,
232148
subjectBounds: startIndex..<endIndex,

Sources/_StringProcessing/Algorithms/Algorithms/Replace.swift

+7-5
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ extension Substring {
3838
}
3939

4040
extension RangeReplaceableCollection {
41-
func _replacing<Ranges: Collection, Replacement: Collection>(
41+
func _replacing<Ranges: Sequence, Replacement: Collection>(
4242
_ ranges: Ranges,
4343
with replacement: Replacement,
4444
maxReplacements: Int = .max
@@ -49,13 +49,15 @@ extension RangeReplaceableCollection {
4949

5050
var result = Self()
5151
var index = startIndex
52-
53-
// `maxRanges` is a workaround for https://github.com/apple/swift/issues/59522
54-
let maxRanges = ranges.prefix(maxReplacements)
55-
for range in maxRanges {
52+
var replacements = 0
53+
54+
for range in ranges {
55+
if replacements == maxReplacements { break }
56+
5657
result.append(contentsOf: self[index..<range.lowerBound])
5758
result.append(contentsOf: replacement)
5859
index = range.upperBound
60+
replacements += 1
5961
}
6062

6163
result.append(contentsOf: self[index...])

Sources/_StringProcessing/Algorithms/Algorithms/Split.swift

+33-33
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111

1212
// MARK: `SplitCollection`
1313

14-
struct SplitCollection<Searcher: CollectionSearcher> {
15-
public typealias Base = Searcher.Searched
14+
struct SplitSequence<Searcher: CollectionSearcher> {
15+
typealias Input = Searcher.Searched
1616

17-
let ranges: RangesCollection<Searcher>
17+
let ranges: RangesSequence<Searcher>
1818
var maxSplits: Int
1919
var omittingEmptySubsequences: Bool
2020

2121
init(
22-
ranges: RangesCollection<Searcher>,
22+
ranges: RangesSequence<Searcher>,
2323
maxSplits: Int,
2424
omittingEmptySubsequences: Bool)
2525
{
@@ -29,53 +29,53 @@ struct SplitCollection<Searcher: CollectionSearcher> {
2929
}
3030

3131
init(
32-
base: Base,
32+
input: Input,
3333
searcher: Searcher,
3434
maxSplits: Int,
3535
omittingEmptySubsequences: Bool)
3636
{
37-
self.ranges = base._ranges(of: searcher)
37+
self.ranges = input._ranges(of: searcher)
3838
self.maxSplits = maxSplits
3939
self.omittingEmptySubsequences = omittingEmptySubsequences
4040
}
4141
}
4242

43-
extension SplitCollection: Sequence {
44-
public struct Iterator: IteratorProtocol {
45-
let base: Base
46-
var index: Base.Index
47-
var ranges: RangesCollection<Searcher>.Iterator
48-
var maxSplits: Int
49-
var omittingEmptySubsequences: Bool
43+
extension SplitSequence: Sequence {
44+
struct Iterator: IteratorProtocol {
45+
var ranges: RangesSequence<Searcher>.Iterator
46+
var index: Input.Index
5047

48+
var maxSplits: Int
5149
var splitCounter = 0
50+
var omittingEmptySubsequences: Bool
5251
var isDone = false
5352

53+
var input: Input { ranges.base.input }
54+
5455
init(
55-
ranges: RangesCollection<Searcher>,
56+
ranges: RangesSequence<Searcher>,
5657
maxSplits: Int,
5758
omittingEmptySubsequences: Bool
5859
) {
59-
self.base = ranges.base
60-
self.index = base.startIndex
60+
self.index = ranges.input.startIndex
6161
self.ranges = ranges.makeIterator()
6262
self.maxSplits = maxSplits
6363
self.omittingEmptySubsequences = omittingEmptySubsequences
6464
}
6565

66-
public mutating func next() -> Base.SubSequence? {
66+
mutating func next() -> Input.SubSequence? {
6767
guard !isDone else { return nil }
6868

6969
/// Return the rest of base if it's non-empty or we're including
7070
/// empty subsequences.
71-
func finish() -> Base.SubSequence? {
71+
func finish() -> Input.SubSequence? {
7272
isDone = true
73-
return index == base.endIndex && omittingEmptySubsequences
73+
return index == input.endIndex && omittingEmptySubsequences
7474
? nil
75-
: base[index...]
75+
: input[index...]
7676
}
7777

78-
if index == base.endIndex {
78+
if index == input.endIndex {
7979
return finish()
8080
}
8181

@@ -96,26 +96,26 @@ extension SplitCollection: Sequence {
9696
}
9797

9898
splitCounter += 1
99-
return base[index..<range.lowerBound]
99+
return input[index..<range.lowerBound]
100100
}
101101
}
102102
}
103103

104-
public func makeIterator() -> Iterator {
104+
func makeIterator() -> Iterator {
105105
Iterator(ranges: ranges, maxSplits: maxSplits, omittingEmptySubsequences: omittingEmptySubsequences)
106106
}
107107
}
108108

109109
// MARK: `CollectionSearcher` algorithms
110110

111111
extension Collection {
112-
func split<Searcher: CollectionSearcher>(
112+
func _split<Searcher: CollectionSearcher>(
113113
by separator: Searcher,
114114
maxSplits: Int,
115115
omittingEmptySubsequences: Bool
116-
) -> SplitCollection<Searcher> where Searcher.Searched == Self {
117-
SplitCollection(
118-
base: self,
116+
) -> SplitSequence<Searcher> where Searcher.Searched == Self {
117+
SplitSequence(
118+
input: self,
119119
searcher: separator,
120120
maxSplits: maxSplits,
121121
omittingEmptySubsequences: omittingEmptySubsequences)
@@ -126,12 +126,12 @@ extension Collection {
126126

127127
extension Collection where Element: Equatable {
128128
@_disfavoredOverload
129-
func split<C: Collection>(
129+
func _split<C: Collection>(
130130
by separator: C,
131131
maxSplits: Int,
132132
omittingEmptySubsequences: Bool
133-
) -> SplitCollection<ZSearcher<Self>> where C.Element == Element {
134-
split(by: ZSearcher(pattern: Array(separator), by: ==), maxSplits: maxSplits, omittingEmptySubsequences: omittingEmptySubsequences)
133+
) -> SplitSequence<ZSearcher<Self>> where C.Element == Element {
134+
_split(by: ZSearcher(pattern: Array(separator), by: ==), maxSplits: maxSplits, omittingEmptySubsequences: omittingEmptySubsequences)
135135
}
136136

137137
// FIXME: Return `some Collection<SubSequence>` for SE-0346
@@ -159,7 +159,7 @@ extension Collection where Element: Equatable {
159159
return str._split(separator: sep, maxSplits: maxSplits, omittingEmptySubsequences: omittingEmptySubsequences) as! [SubSequence]
160160

161161
default:
162-
return Array(split(
162+
return Array(_split(
163163
by: ZSearcher(pattern: Array(separator), by: ==),
164164
maxSplits: maxSplits,
165165
omittingEmptySubsequences: omittingEmptySubsequences))
@@ -186,7 +186,7 @@ extension StringProtocol where SubSequence == Substring {
186186
maxSplits: Int = .max,
187187
omittingEmptySubsequences: Bool = true
188188
) -> [Substring] {
189-
Array(self[...].split(
189+
Array(self[...]._split(
190190
by: SubstringSearcher(text: "" as Substring, pattern: separator[...]),
191191
maxSplits: maxSplits,
192192
omittingEmptySubsequences: omittingEmptySubsequences))
@@ -199,7 +199,7 @@ extension StringProtocol where SubSequence == Substring {
199199
maxSplits: Int = .max,
200200
omittingEmptySubsequences: Bool = true
201201
) -> [Substring] {
202-
Array(self[...].split(
202+
Array(self[...]._split(
203203
by: SubstringSearcher(text: "" as Substring, pattern: separator[...]),
204204
maxSplits: maxSplits,
205205
omittingEmptySubsequences: omittingEmptySubsequences))

0 commit comments

Comments
 (0)