//===----------------------------------------------------------*- swift -*-===// // // This source file is part of the Swift open source project // // Copyright (c) 2020 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 // //===----------------------------------------------------------------------===// struct _RangeSetStorage { fileprivate enum _Storage { case empty case singleRange(high: Int, low: Int32) case variadic([Range]) } fileprivate var _storage: _Storage init() { _storage = .empty } init(_ range: Range) { if let intRange = range as? Range, let lowerBound = Int32(exactly: intRange.lowerBound) { _storage = .singleRange(high: intRange.upperBound, low: lowerBound) } else { _storage = .variadic([range]) } } init(_ ranges: [Range]) { _storage = .variadic(ranges) } func unsafeRange(low: Int32, high: Int) -> Range { unsafeBitCast(Int(low)...self) } } // _RangeSetStorage has custom Equatable (and therefore Hashable) // conformance, since the same "value" can be represented by different // storage structures. For example, `.empty` and `.variadic([])` are // equivalent, but the synthesized conformance treats them as distinct. // The same holds with the `singleRange` representation and `variadic` // with a single-element array. extension _RangeSetStorage: Equatable { static func == (lhs: _RangeSetStorage, rhs: _RangeSetStorage) -> Bool { switch (lhs._storage, rhs._storage) { case (.empty, .empty): return true case (.empty, .singleRange), (.singleRange, .empty): return false case let (.empty, .variadic(ranges)), let (.variadic(ranges), .empty): return ranges.isEmpty case let (.singleRange(lhsHigh, lhsLow), .singleRange(rhsHigh, rhsLow)): return (lhsLow, lhsHigh) == (rhsLow, rhsHigh) case let (.singleRange(high, low), .variadic(ranges)), let (.variadic(ranges), .singleRange(high, low)): return ranges.count == 1 && (ranges[0] as! Range) == Int(low).. Range { get { switch _storage { case .empty: fatalError("Can't access elements of empty storage") case let .singleRange(high, low): assert(T.self == Int.self) return unsafeRange(low: low, high: high) case let .variadic(ranges): return ranges[i] } } set { switch _storage { case .empty: fatalError("Can't access elements of empty storage") case .singleRange: assert(T.self == Int.self) let intRange = newValue as! Range if let lowerBound = Int32(exactly: intRange.lowerBound) { _storage = .singleRange(high: intRange.upperBound, low: lowerBound) } else { _storage = .variadic([newValue]) } case .variadic(var ranges): // Temporarily set `_storage` to empty so that `ranges` // remains uniquely referenced while mutating. _storage = .empty ranges[i] = newValue _storage = .variadic(ranges) } } } var count: Int { switch _storage { case .empty: return 0 case .singleRange: return 1 case let .variadic(ranges): return ranges.count } } } extension _RangeSetStorage: RangeReplaceableCollection { mutating func replaceSubrange(_ subrange: Range, with newElements: C) where C : Collection, C.Element == Element { switch _storage { case .empty: if !newElements.isEmpty { _storage = .variadic(Array(newElements)) } case .singleRange(high: let high, low: let low): switch (subrange.isEmpty, newElements.isEmpty) { case (false, true): // Replacing the single range with an empty collection. _storage = .empty case (false, false): // Replacing the single range with a non-empty collection; // promote to a variadic container. _storage = .variadic(Array(newElements)) case (true, true): // Inserting an empty collection; no-op. break case (true, false): // Inserting a non-empty collection either before or after // the existing single element. var ranges: [Range] if subrange.lowerBound == 0 { ranges = Array(newElements) ranges.append(unsafeRange(low: low, high: high)) } else { ranges = [unsafeRange(low: low, high: high)] ranges.append(contentsOf: newElements) } _storage = .variadic(ranges) } case .variadic(var ranges): // Temporarily set `_storage` to empty so that `ranges` // remains uniquely referenced while mutating. _storage = .empty ranges.replaceSubrange(subrange, with: newElements) _storage = .variadic(ranges) } } }