//===--- RangeSetRanges.swift ---------------------------------*- swift -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2023 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// @available(SwiftStdlib 6.0, *) extension RangeSet { /// A collection of the ranges that make up a range set. public struct Ranges { internal var _storage: ContiguousArray> @usableFromInline internal init() { _storage = [] } @usableFromInline internal init(_range: Range) { _storage = [_range] } @usableFromInline internal init(_ranges: [Range]) { _storage = ContiguousArray(_ranges) } @usableFromInline internal init(_unorderedRanges: [Range]) { _storage = ContiguousArray(_unorderedRanges) _storage.sort { $0.lowerBound < $1.lowerBound } var i = 0 while i < _storage.count { let current = _storage[i] if i > 0 { let previous = _storage[i - 1] if previous.upperBound >= current.lowerBound { let newUpper = Swift.max(previous.upperBound, current.upperBound) _storage[i - 1] = previous.lowerBound ..< newUpper _storage.remove(at: i) continue } } if current.isEmpty { _storage.remove(at: i) } else { i += 1 } } } } } @available(SwiftStdlib 6.0, *) extension RangeSet.Ranges { @usableFromInline internal func _contains(_ bound: Bound) -> Bool { let i = _storage._partitioningIndex { $0.upperBound > bound } return i == _storage.endIndex ? false : _storage[i].lowerBound <= bound } /// Returns a range indicating the existing ranges that `range` overlaps /// with. /// /// For example, if `self` is `[0..<5, 10..<15, 20..<25, 30..<35]`, then: /// /// - `_indicesOfRange(12..<14) == 1..<2` /// - `_indicesOfRange(12..<19) == 1..<2` /// - `_indicesOfRange(17..<19) == 2..<2` /// - `_indicesOfRange(12..<22) == 1..<3` @usableFromInline internal func _indicesOfRange( _ range: Range, in subranges: ContiguousArray>, includeAdjacent: Bool = true ) -> Range { guard subranges.count > 1 else { if subranges.isEmpty { return 0 ..< 0 } else { let subrange = subranges[0] if range.upperBound < subrange.lowerBound { return 0 ..< 0 } else if range.lowerBound > subrange.upperBound { return 1 ..< 1 } else { return 0 ..< 1 } } } // The beginning index for the position of `range` is the first range // with an upper bound larger than `range`'s lower bound. The range // at this position may or may not overlap `range`. let beginningIndex = subranges._partitioningIndex { if includeAdjacent { $0.upperBound >= range.lowerBound } else { $0.upperBound > range.lowerBound } } // The ending index for `range` is the first range with a lower bound // greater than `range`'s upper bound. If this is the same as // `beginningIndex`, than `range` doesn't overlap any of the existing // ranges. If this is `ranges.endIndex`, then `range` overlaps the // rest of the ranges. Otherwise, `range` overlaps one or // more ranges in the set. let endingIndex = subranges[beginningIndex...]._partitioningIndex { if includeAdjacent { $0.lowerBound > range.upperBound } else { $0.lowerBound >= range.upperBound } } return beginningIndex ..< endingIndex } // Insert a non-empty range into the storage @usableFromInline @discardableResult internal mutating func _insert(contentsOf range: Range) -> Bool { let indices = _indicesOfRange(range, in: _storage) if indices.isEmpty { _storage.insert(range, at: indices.lowerBound) return true } else { let lower = Swift.min( _storage[indices.lowerBound].lowerBound, range.lowerBound) let upper = Swift.max( _storage[indices.upperBound - 1].upperBound, range.upperBound) let newRange = lower ..< upper if indices.count == 1 && newRange == _storage[indices.lowerBound] { return false } _storage.replaceSubrange(indices, with: CollectionOfOne(newRange)) return true } } // Remove a non-empty range from the storage @usableFromInline internal mutating func _remove(contentsOf range: Range) { let indices = _indicesOfRange(range, in: _storage, includeAdjacent: false) guard !indices.isEmpty else { return } let overlapsLowerBound = range.lowerBound > _storage[indices.lowerBound].lowerBound let overlapsUpperBound = range.upperBound < _storage[indices.upperBound - 1].upperBound switch (overlapsLowerBound, overlapsUpperBound) { case (false, false): _storage.removeSubrange(indices) case (false, true): let newRange = range.upperBound..<_storage[indices.upperBound - 1].upperBound _storage.replaceSubrange(indices, with: CollectionOfOne(newRange)) case (true, false): let newRange = _storage[indices.lowerBound].lowerBound ..< range.lowerBound _storage.replaceSubrange(indices, with: CollectionOfOne(newRange)) case (true, true): _storage.replaceSubrange(indices, with: _Pair( _storage[indices.lowerBound].lowerBound..) -> Self { let indices = _indicesOfRange(bounds, in: _storage) guard !indices.isEmpty else { return Self(_range: bounds) } var result: [Range] = [] var low = bounds.lowerBound for range in _storage[indices] { let gapRange = low ..< range.lowerBound if !gapRange.isEmpty { result.append(gapRange) } low = range.upperBound } let finalRange = low ..< bounds.upperBound if !finalRange.isEmpty { result.append(finalRange) } let resultVal = Self(_ranges: result) return resultVal } @usableFromInline internal func _intersection(_ other: Self) -> Self { let left = self._storage let right = other._storage var otherRangeIndex = 0 var result: [Range] = [] // Considering these two range sets: // // self = [0..<5, 9..<14] // other = [1..<3, 4..<6, 8..<12] // // `self.intersection(other)` looks like this, where x's cover the // ranges in `self`, y's cover the ranges in `other`, and z's cover the // resulting ranges: // // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // xxxxxxxxxxxxxxxxxxx__ xxxxxxxxxxxxxxxxxxx__ // yyyyyyy__ yyyyyyy__ yyyyyyyyyyyyyyy__ // zzzzzzz__ zzz__ zzzzzzzzzzz__ // // The same, but for `other.intersection(self)`: // // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // xxxxxxx__ xxxxxxx__ xxxxxxxxxxxxxxx__ // yyyyyyyyyyyyyyyyyyy__ yyyyyyyyyyyyyyyyyyy__ // zzzzzzz__ zzz__ zzzzzzzzzzz__ for currentRange in left { // Search forward in `right` until finding either an overlapping // range or one that is strictly higher than this range. while otherRangeIndex < right.endIndex && right[otherRangeIndex].upperBound <= currentRange.lowerBound { otherRangeIndex += 1 } // For each range in `right` that overlaps with the current range // in `left`, append the intersection to the result. while otherRangeIndex < right.endIndex && right[otherRangeIndex].lowerBound < currentRange.upperBound { let overlap = right[otherRangeIndex].clamped(to: currentRange) result.append(overlap) // If the range in `right` continues past the current range in // `self`, it could overlap the next range in `self`, so break // out of examining the current range. guard currentRange.upperBound > right[otherRangeIndex].upperBound else { break } otherRangeIndex += 1 } } return Self(_ranges: result) } } @available(SwiftStdlib 6.0, *) extension RangeSet.Ranges: Sequence { public typealias Element = Range public typealias Iterator = IndexingIterator } @available(SwiftStdlib 6.0, *) extension RangeSet.Ranges: Collection { public typealias Index = Int public typealias Indices = Range public typealias SubSequence = Slice public var startIndex: Index { 0 } public var endIndex: Index { _storage.count } public var count: Int { self.endIndex } public subscript(i: Index) -> Element { _storage[i] } } @available(SwiftStdlib 6.0, *) extension RangeSet.Ranges: RandomAccessCollection {} @available(SwiftStdlib 6.0, *) extension RangeSet.Ranges: Equatable { public static func == (left: Self, right: Self) -> Bool { left._storage == right._storage } } @available(SwiftStdlib 6.0, *) extension RangeSet.Ranges: Hashable where Bound: Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(_storage) } } @available(SwiftStdlib 6.0, *) extension RangeSet.Ranges: Sendable where Bound: Sendable {} @available(SwiftStdlib 6.0, *) extension RangeSet.Ranges: CustomStringConvertible { public var description: String { _makeCollectionDescription() } } /// A collection of two elements, to avoid heap allocation when calling /// `replaceSubrange` with just two elements. internal struct _Pair: RandomAccessCollection { internal var pair: (first: Element, second: Element) internal init(_ first: Element, _ second: Element) { self.pair = (first, second) } internal var startIndex: Int { 0 } internal var endIndex: Int { 2 } internal subscript(position: Int) -> Element { get { switch position { case 0: return pair.first case 1: return pair.second default: _preconditionFailure("Index is out of range") } } } }