@@ -37,25 +37,57 @@ extension RangeSet {
37
37
_storage. sort {
38
38
$0. lowerBound < $1. lowerBound
39
39
}
40
- var i = 0
41
- while i < _storage. count {
42
- let current = _storage [ i]
43
- if i > 0 {
44
- let previous = _storage [ i - 1 ]
45
- if previous. upperBound >= current. lowerBound {
46
- let newUpper = Swift . max ( previous. upperBound, current. upperBound)
47
- _storage [ i - 1 ] = previous. lowerBound ..< newUpper
48
- _storage. remove ( at: i)
49
- continue
50
- }
51
- }
52
-
53
- if current. isEmpty {
54
- _storage. remove ( at: i)
40
+
41
+ // Find the index of the first non-empty range. If all ranges are empty,
42
+ // the result is empty.
43
+ guard let firstNonEmpty = _storage. firstIndex ( where: { $0. isEmpty == false } ) else {
44
+ _storage = [ ]
45
+ return
46
+ }
47
+
48
+ // Swap that non-empty range to be first. (This and the swap in the loop
49
+ // might be no-ops, if no empty or overlapping ranges have been found.)
50
+ _storage. swapAt ( 0 , firstNonEmpty)
51
+
52
+ // That single range is now a valid range set, so we set up three sections
53
+ // of the storage array:
54
+ //
55
+ // 1: a processed, valid range set (0...lastValid)
56
+ // 2: ranges to discard (lastValid + 1 ..< current)
57
+ // 3: unprocessed ranges (current ..< _storage.count)
58
+ //
59
+ // Section 2 is made up of ranges that are either empty or that overlap
60
+ // with the ranges in section 1. By waiting to remove these ranges until
61
+ // we've processed the entire array, we avoid needing to constantly
62
+ // reshuffle the elements during processing.
63
+ var lastValid = 0
64
+ var current = firstNonEmpty + 1
65
+
66
+ while current < _storage. count {
67
+ defer { current += 1 }
68
+
69
+ // Skip over empty ranges.
70
+ if _storage [ current] . isEmpty { continue }
71
+
72
+ // If the last valid range overlaps with the current range, extend the
73
+ // last valid range to cover the current.
74
+ if _storage [ lastValid] . upperBound >= _storage [ current] . lowerBound {
75
+ let newUpper = Swift . max (
76
+ _storage [ lastValid] . upperBound,
77
+ _storage [ current] . upperBound)
78
+ _storage [ lastValid] = Range (
79
+ uncheckedBounds: ( _storage [ lastValid] . lowerBound, newUpper) )
55
80
} else {
56
- i += 1
81
+ // Otherwise, this is a valid new range to add to the range set:
82
+ // swap it into place at the end of the valid section.
83
+ lastValid += 1
84
+ _storage. swapAt ( current, lastValid)
57
85
}
58
86
}
87
+
88
+ // Now that we've processed the whole array, remove anything left after
89
+ // the valid section.
90
+ _storage. removeSubrange ( ( lastValid + 1 ) ..< _storage. count)
59
91
}
60
92
}
61
93
}
0 commit comments