//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2021 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 // //===----------------------------------------------------------------------===// // Loosely adapted from https://github.com/apple/swift/tree/main/stdlib/private/StdlibCollectionUnittest /// A minimal implementation of `RandomAccessCollection & MutableCollection` with extra checks. public struct MinimalMutableRandomAccessCollection { internal var _core: _MinimalCollectionCore public let timesMakeIteratorCalled = ResettableValue(0) public let timesUnderestimatedCountCalled = ResettableValue(0) public let timesRangeChecksCalled = ResettableValue(0) public let timesIndexNavigationCalled = ResettableValue(0) public let timesSubscriptGetterCalled = ResettableValue(0) public let timesSubscriptSetterCalled = ResettableValue(0) public let timesRangeSubscriptGetterCalled = ResettableValue(0) public let timesRangeSubscriptSetterCalled = ResettableValue(0) public let timesSwapCalled = ResettableValue(0) public let timesPartitionCalled = ResettableValue(0) public init( _ elements: S, context: TestContext = TestContext.current, underestimatedCount: UnderestimatedCountBehavior = .value(0) ) where S.Element == Element { self._core = _MinimalCollectionCore(context: context, elements: elements, underestimatedCount: underestimatedCount) } var _context: TestContext { _core.context } } extension MinimalMutableRandomAccessCollection: Sequence { public typealias Iterator = MinimalIterator public func makeIterator() -> MinimalIterator { timesMakeIteratorCalled.increment() return MinimalIterator(_core.elements) } public var underestimatedCount: Int { timesUnderestimatedCountCalled.increment() return _core.underestimatedCount } } extension MinimalMutableRandomAccessCollection: RandomAccessCollection { public typealias Index = MinimalIndex public typealias SubSequence = Slice public typealias Indices = DefaultIndices public var startIndex: MinimalIndex { timesIndexNavigationCalled.increment() return _core.startIndex } public var endIndex: MinimalIndex { timesIndexNavigationCalled.increment() return _core.endIndex } public var isEmpty: Bool { timesIndexNavigationCalled.increment() return _core.isEmpty } public var count: Int { timesIndexNavigationCalled.increment() return _core.count } public func _failEarlyRangeCheck( _ index: MinimalIndex, bounds: Range ) { timesRangeChecksCalled.increment() _core._failEarlyRangeCheck(index, bounds: bounds) } public func _failEarlyRangeCheck( _ range: Range, bounds: Range ) { timesRangeChecksCalled.increment() _core._failEarlyRangeCheck(range, bounds: bounds) } public func index(after i: MinimalIndex) -> MinimalIndex { timesIndexNavigationCalled.increment() return _core.index(after: i) } public func index(before i: MinimalIndex) -> MinimalIndex { timesIndexNavigationCalled.increment() return _core.index(before: i) } public func distance(from start: MinimalIndex, to end: MinimalIndex) -> Int { timesIndexNavigationCalled.increment() return _core.distance(from: start, to: end) } public func index(_ i: Index, offsetBy n: Int) -> Index { timesIndexNavigationCalled.increment() return _core.index(i, offsetBy: n) } public subscript(i: MinimalIndex) -> Element { get { timesSubscriptGetterCalled.increment() return _core[i] } set { timesSubscriptSetterCalled.increment() _core[i] = newValue } } public subscript(bounds: Range) -> SubSequence { get { timesRangeSubscriptGetterCalled.increment() _core.assertValid(bounds) return Slice(base: self, bounds: bounds) } set { timesRangeSubscriptSetterCalled.increment() _core.assertValid(bounds) expectEqual(bounds.upperBound._offset - bounds.lowerBound._offset, newValue.endIndex._offset - newValue.startIndex._offset, "Range assignment cannot change number of elements in MutableCollection") _core.elements[bounds.lowerBound._offset ..< bounds.upperBound._offset] = Self._coreSlice(from: newValue) // Don't invalidate indices. } } static func _coreSlice(from slice: SubSequence) -> ArraySlice { slice.base._core.assertValid(slice.startIndex ..< slice.endIndex) return slice.base._core.elements[slice.startIndex._offset ..< slice.endIndex._offset] } } extension MinimalMutableRandomAccessCollection: MutableCollection { public mutating func partition( by belongsInSecondPartition: (Element) throws -> Bool ) rethrows -> Index { timesPartitionCalled.increment() let pivot = try _core.elements.partition(by: belongsInSecondPartition) return _core.index(at: pivot) } public mutating func swapAt(_ i: Index, _ j: Index) { _core.assertValidIndexBeforeEnd(i) _core.assertValidIndexBeforeEnd(j) timesSwapCalled.increment() _core.elements.swapAt(i._offset, j._offset) // Don't invalidate indices. } public mutating func _withUnsafeMutableBufferPointerIfSupported( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R? { return nil } public mutating func withContiguousMutableStorageIfAvailable( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R? { return nil } }