Skip to content

Commit f9c1dd4

Browse files
committed
Adopt the new indexing model for better perf in IndexSet
Concurrently with the development of struct IndexSet, the stdlib team was busy reworking the way that all indexes in collections worked (https://github.com/apple/swift-evolution/blob/master/proposals/0065-collections-move-indices.md). Both efforts landed at basically the same time. We did the minimum possible to adopt the new indexing model when IndexSet landed. This change more correctly adopts the new model. In summary, the new model has the Collection change the value of the Index, instead of the Index changing the value on its own. Previously, the Index had methods like successor(), but now the Collection has methods like index(after:). This means that the index no longer has to store a reference to the collection in many cases, which means that CoW semantics can kick in far more often as the index is a dead simple model object that just stores a bunch of integers. So basically, this change moves all the logic for moving indexes from Index into IndexSet. <rdar://problem/26269319> More fully adopt new indexing model for better performance in IndexSet
1 parent 1302449 commit f9c1dd4

File tree

1 file changed

+94
-130
lines changed

1 file changed

+94
-130
lines changed

stdlib/public/SDK/Foundation/IndexSet.swift

Lines changed: 94 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -164,137 +164,23 @@ public struct IndexSet : ReferenceConvertible, Equatable, BidirectionalCollectio
164164

165165
}
166166

167-
/// The mechanism for getting to the integers stored in an IndexSet.
167+
/// The mechanism for accessing the integers stored in an IndexSet.
168168
public struct Index : CustomStringConvertible, Comparable {
169-
private let indexSet : IndexSet
170169
private var value : IndexSet.Element
171170
private var extent : Range<IndexSet.Element>
172171
private var rangeIndex : Int
173172
private let rangeCount : Int
174173

175-
private init(firstIn indexSet : IndexSet) {
176-
self.indexSet = indexSet
177-
self.rangeCount = indexSet._rangeCount
178-
self.rangeIndex = 0
179-
self.extent = indexSet._range(at: 0)
180-
self.value = extent.lowerBound
181-
}
182-
183-
private init(lastIn indexSet : IndexSet) {
184-
self.indexSet = indexSet
185-
let rangeCount = indexSet._rangeCount
186-
self.rangeIndex = rangeCount - 1
187-
if rangeCount > 0 {
188-
self.extent = indexSet._range(at: rangeCount - 1)
189-
self.value = extent.upperBound // "1 past the end" position is the last range, 1 + the end of that range's extent
190-
} else {
191-
self.extent = 0..<0
192-
self.value = 0
193-
}
194-
self.rangeCount = rangeCount
195-
}
196-
197-
private init(indexSet: IndexSet, index: Int) {
198-
self.indexSet = indexSet
199-
self.rangeCount = self.indexSet._rangeCount
200-
self.value = index
201-
if let rangeIndex = self.indexSet._indexOfRange(containing: index) {
202-
self.extent = self.indexSet._range(at: rangeIndex)
203-
self.rangeIndex = rangeIndex
204-
} else {
205-
self.extent = 0..<0
206-
self.rangeIndex = 0
207-
}
208-
}
209-
210-
// First or last value in a specified range
211-
private init(indexSet: IndexSet, rangeIndex: Int, rangeCount: Int, first : Bool) {
212-
self.indexSet = indexSet
213-
let extent = indexSet._range(at: rangeIndex)
214-
if first {
215-
self.value = extent.lowerBound
216-
} else {
217-
self.value = extent.upperBound-1
218-
}
219-
self.extent = extent
220-
self.rangeCount = rangeCount
221-
self.rangeIndex = rangeIndex
222-
}
223-
224-
private init(indexSet: IndexSet, value: Int, extent: Range<Int>, rangeIndex: Int, rangeCount: Int) {
225-
self.indexSet = indexSet
174+
private init(value: Int, extent: Range<Int>, rangeIndex: Int, rangeCount: Int) {
226175
self.value = value
227176
self.extent = extent
228177
self.rangeCount = rangeCount
229178
self.rangeIndex = rangeIndex
230179
}
231180

232-
private func successor() -> Index {
233-
if value + 1 == extent.upperBound {
234-
// Move to the next range
235-
if rangeIndex + 1 == rangeCount {
236-
// We have no more to go; return a 'past the end' index
237-
return Index(indexSet: indexSet, value: value + 1, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount)
238-
} else {
239-
return Index(indexSet: indexSet, rangeIndex: rangeIndex + 1, rangeCount: rangeCount, first: true)
240-
}
241-
} else {
242-
// Move to the next value in this range
243-
return Index(indexSet: indexSet, value: value + 1, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount)
244-
}
245-
}
246-
247-
private mutating func _successorInPlace() {
248-
if value + 1 == extent.upperBound {
249-
// Move to the next range
250-
if rangeIndex + 1 == rangeCount {
251-
// We have no more to go; return a 'past the end' index
252-
value += 1
253-
} else {
254-
rangeIndex += 1
255-
extent = indexSet._range(at: rangeIndex)
256-
value = extent.lowerBound
257-
}
258-
} else {
259-
// Move to the next value in this range
260-
value += 1
261-
}
262-
}
263-
264-
private func predecessor() -> Index {
265-
if value == extent.lowerBound {
266-
// Move to the next range
267-
if rangeIndex == 0 {
268-
// We have no more to go
269-
return Index(indexSet: indexSet, value: value, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount)
270-
} else {
271-
return Index(indexSet: indexSet, rangeIndex: rangeIndex - 1, rangeCount: rangeCount, first: false)
272-
}
273-
} else {
274-
// Move to the previous value in this range
275-
return Index(indexSet: indexSet, value: value - 1, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount)
276-
}
277-
}
278-
279181
public var description: String {
280182
return "index \(value) in a range of \(extent) [range #\(rangeIndex + 1)/\(rangeCount)]"
281183
}
282-
283-
private mutating func _predecessorInPlace() {
284-
if value == extent.lowerBound {
285-
// Move to the next range
286-
if rangeIndex == 0 {
287-
// We have no more to go
288-
} else {
289-
rangeIndex -= 1
290-
extent = indexSet._range(at: rangeIndex)
291-
value = extent.upperBound - 1
292-
}
293-
} else {
294-
// Move to the previous value in this range
295-
value -= 1
296-
}
297-
}
298184
}
299185

300186
public typealias ReferenceType = NSIndexSet
@@ -392,16 +278,25 @@ public struct IndexSet : ReferenceConvertible, Equatable, BidirectionalCollectio
392278
}
393279

394280
public var startIndex: Index {
395-
// TODO: We should cache this result
396-
397281
// If this winds up being NSNotFound, that's ok because then endIndex is also NSNotFound, and empty collections have startIndex == endIndex
398-
return Index(firstIn: self)
282+
let extent = _range(at: 0)
283+
return Index(value: extent.lowerBound, extent: extent, rangeIndex: 0, rangeCount: _rangeCount)
399284
}
400285

401286
public var endIndex: Index {
402-
// TODO: We should cache this result
287+
let rangeCount = _rangeCount
288+
let rangeIndex = rangeCount - 1
289+
let extent : Range<Int>
290+
let value : Int
291+
if rangeCount > 0 {
292+
extent = _range(at: rangeCount - 1)
293+
value = extent.upperBound // "1 past the end" position is the last range, 1 + the end of that range's extent
294+
} else {
295+
extent = 0..<0
296+
value = 0
297+
}
403298

404-
return Index(lastIn: self)
299+
return Index(value: value, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount)
405300
}
406301

407302
public subscript(index : Index) -> Element {
@@ -455,18 +350,18 @@ public struct IndexSet : ReferenceConvertible, Equatable, BidirectionalCollectio
455350
/// - parameter range: The range of integers to include.
456351
public func indexRange(in range: Range<Element>) -> Range<Index> {
457352
if range.isEmpty {
458-
let i = Index(indexSet: self, index: 0)
353+
let i = _index(ofInteger: 0)
459354
return i..<i
460355
}
461356

462357
if range.lowerBound > last || (range.upperBound - 1) < first {
463-
let i = Index(indexSet: self, index: 0)
358+
let i = _index(ofInteger: 0)
464359
return i..<i
465360
}
466361

467-
let resultFirst = Index(indexSet: self, index: integerGreaterThanOrEqualTo(range.lowerBound))
468-
let resultLast = Index(indexSet: self, index: integerLessThanOrEqualTo(range.upperBound - 1))
469-
return resultFirst..<resultLast.successor()
362+
let resultFirst = _index(ofInteger: integerGreaterThanOrEqualTo(range.lowerBound))
363+
let resultLast = _index(ofInteger: integerLessThanOrEqualTo(range.upperBound - 1))
364+
return resultFirst..<index(after: resultLast)
470365
}
471366

472367
/// Return a `Range<IndexSet.Index>` which can be used to subscript the index set.
@@ -540,19 +435,88 @@ public struct IndexSet : ReferenceConvertible, Equatable, BidirectionalCollectio
540435
// Indexable
541436

542437
public func index(after i: Index) -> Index {
543-
return i.successor()
438+
if i.value + 1 == i.extent.upperBound {
439+
// Move to the next range
440+
if i.rangeIndex + 1 == i.rangeCount {
441+
// We have no more to go; return a 'past the end' index
442+
return Index(value: i.value + 1, extent: i.extent, rangeIndex: i.rangeIndex, rangeCount: i.rangeCount)
443+
} else {
444+
let rangeIndex = i.rangeIndex + 1
445+
let rangeCount = i.rangeCount
446+
let extent = _range(at: rangeIndex)
447+
let value = extent.lowerBound
448+
return Index(value: value, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount)
449+
}
450+
} else {
451+
// Move to the next value in this range
452+
return Index(value: i.value + 1, extent: i.extent, rangeIndex: i.rangeIndex, rangeCount: i.rangeCount)
453+
}
544454
}
545455

546456
public func formIndex(after i: inout Index) {
547-
i._successorInPlace()
457+
if i.value + 1 == i.extent.upperBound {
458+
// Move to the next range
459+
if i.rangeIndex + 1 == i.rangeCount {
460+
// We have no more to go; return a 'past the end' index
461+
i.value += 1
462+
} else {
463+
i.rangeIndex += 1
464+
i.extent = _range(at: i.rangeIndex)
465+
i.value = i.extent.lowerBound
466+
}
467+
} else {
468+
// Move to the next value in this range
469+
i.value += 1
470+
}
548471
}
549472

550473
public func index(before i: Index) -> Index {
551-
return i.predecessor()
474+
if i.value == i.extent.lowerBound {
475+
// Move to the next range
476+
if i.rangeIndex == 0 {
477+
// We have no more to go
478+
return Index(value: i.value, extent: i.extent, rangeIndex: i.rangeIndex, rangeCount: i.rangeCount)
479+
} else {
480+
let rangeIndex = i.rangeIndex - 1
481+
let rangeCount = i.rangeCount
482+
let extent = _range(at: rangeIndex)
483+
let value = extent.upperBound - 1
484+
return Index(value: value, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount)
485+
}
486+
} else {
487+
// Move to the previous value in this range
488+
return Index(value: i.value - 1, extent: i.extent, rangeIndex: i.rangeIndex, rangeCount: i.rangeCount)
489+
}
552490
}
553491

554492
public func formIndex(before i: inout Index) {
555-
i._predecessorInPlace()
493+
if i.value == i.extent.lowerBound {
494+
// Move to the next range
495+
if i.rangeIndex == 0 {
496+
// We have no more to go
497+
} else {
498+
i.rangeIndex -= 1
499+
i.extent = _range(at: i.rangeIndex)
500+
i.value = i.extent.upperBound - 1
501+
}
502+
} else {
503+
// Move to the previous value in this range
504+
i.value -= 1
505+
}
506+
}
507+
508+
private func _index(ofInteger integer: Element) -> Index {
509+
let rangeCount = _rangeCount
510+
let value = integer
511+
if let rangeIndex = _indexOfRange(containing: integer) {
512+
let extent = _range(at: rangeIndex)
513+
let rangeIndex = rangeIndex
514+
return Index(value: value, extent: extent, rangeIndex: rangeIndex, rangeCount: rangeCount)
515+
} else {
516+
let extent = 0..<0
517+
let rangeIndex = 0
518+
return Index(value: value, extent: Range(extent), rangeIndex: rangeIndex, rangeCount: rangeCount)
519+
}
556520
}
557521

558522
// MARK: -

0 commit comments

Comments
 (0)