diff --git a/Package.swift b/Package.swift index 613dd7478..540dbfc3c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.7 +// swift-tools-version:6.0 //===----------------------------------------------------------------------===// // // This source file is part of the Swift Collections open source project @@ -52,7 +52,17 @@ var defines: [String] = [ // "COLLECTIONS_SINGLE_MODULE", ] -var _settings: [SwiftSetting] = defines.map { .define($0) } +let _sharedSettings: [SwiftSetting] = defines.map { .define($0) } + [ + .enableExperimentalFeature("AllowUnsafeAttribute"), + .enableExperimentalFeature("BuiltinModule"), + .enableExperimentalFeature("LifetimeDependence"), + .enableExperimentalFeature("SuppressedAssociatedTypes"), +] + + +let _settings: [SwiftSetting] = _sharedSettings + [] + +let _testSettings: [SwiftSetting] = _sharedSettings + [] struct CustomTarget { enum Kind { @@ -67,6 +77,7 @@ struct CustomTarget { var dependencies: [Target.Dependency] var directory: String var exclude: [String] + var settings: [SwiftSetting] } extension CustomTarget.Kind { @@ -91,14 +102,16 @@ extension CustomTarget { name: String, dependencies: [Target.Dependency] = [], directory: String? = nil, - exclude: [String] = [] + exclude: [String] = [], + settings: [SwiftSetting]? = nil ) -> CustomTarget { CustomTarget( kind: kind, name: name, dependencies: dependencies, directory: directory ?? name, - exclude: exclude) + exclude: exclude, + settings: settings ?? (kind.isTest ? _testSettings : _settings)) } func toTarget() -> Target { @@ -114,7 +127,7 @@ extension CustomTarget { dependencies: dependencies, path: kind.path(for: directory), exclude: exclude, - swiftSettings: _settings, + swiftSettings: settings, linkerSettings: linkerSettings) case .test: return Target.testTarget( @@ -122,7 +135,7 @@ extension CustomTarget { dependencies: dependencies, path: kind.path(for: directory), exclude: exclude, - swiftSettings: _settings, + swiftSettings: settings, linkerSettings: linkerSettings) } } @@ -169,7 +182,7 @@ extension Array where Element == CustomTarget { t.exclude.map { "\(t.name)/\($0)" } }, sources: targets.map { "\($0.name)" }, - swiftSettings: _settings, + swiftSettings: _testSettings, linkerSettings: linkerSettings) } } @@ -188,9 +201,6 @@ let targets: [CustomTarget] = [ name: "InternalCollectionsUtilities", exclude: [ "CMakeLists.txt", - "Compatibility/UnsafeMutableBufferPointer+SE-0370.swift.gyb", - "Compatibility/UnsafeMutablePointer+SE-0370.swift.gyb", - "Compatibility/UnsafeRawPointer extensions.swift.gyb", "Debugging.swift.gyb", "Descriptions.swift.gyb", "IntegerTricks/FixedWidthInteger+roundUpToPowerOfTwo.swift.gyb", @@ -198,7 +208,6 @@ let targets: [CustomTarget] = [ "IntegerTricks/UInt+first and last set bit.swift.gyb", "IntegerTricks/UInt+reversed.swift.gyb", "RandomAccessCollection+Offsets.swift.gyb", - "Specialize.swift.gyb", "UnsafeBitSet/_UnsafeBitSet+Index.swift.gyb", "UnsafeBitSet/_UnsafeBitSet+_Word.swift.gyb", "UnsafeBitSet/_UnsafeBitSet.swift.gyb", @@ -206,6 +215,18 @@ let targets: [CustomTarget] = [ "UnsafeMutableBufferPointer+Extras.swift.gyb", ]), + .target( + kind: .exported, + name: "Span", + dependencies: ["InternalCollectionsUtilities"], + exclude: ["CMakeLists.txt"], + settings: _sharedSettings + [.unsafeFlags(["-Xfrontend", "-strict-memory-safety"])], + ), + .target( + kind: .test, + name: "SpanTests", + dependencies: ["Span", "_CollectionsTestSupport"]), + .target( kind: .exported, name: "BitCollections", @@ -221,7 +242,7 @@ let targets: [CustomTarget] = [ .target( kind: .exported, name: "DequeModule", - dependencies: ["InternalCollectionsUtilities"], + dependencies: ["InternalCollectionsUtilities", "Span"], exclude: ["CMakeLists.txt"]), .target( kind: .test, @@ -263,7 +284,9 @@ let targets: [CustomTarget] = [ name: "_RopeModule", dependencies: ["InternalCollectionsUtilities"], directory: "RopeModule", - exclude: ["CMakeLists.txt"]), + exclude: ["CMakeLists.txt"], + // FIXME: _modify accessors in RopeModule seem to be broken in Swift 6 mode + settings: _sharedSettings + [.swiftLanguageVersion(.v5)]), .target( kind: .test, name: "RopeModuleTests", diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt index ad39dd9be..2ada2ac20 100644 --- a/Sources/CMakeLists.txt +++ b/Sources/CMakeLists.txt @@ -56,6 +56,7 @@ else() endif() if(NOT COLLECTIONS_FOUNDATION_TOOLCHAIN_MODULE) + add_subdirectory(Future) add_subdirectory(BitCollections) add_subdirectory(DequeModule) add_subdirectory(HashTreeCollections) diff --git a/Sources/DequeModule/CMakeLists.txt b/Sources/DequeModule/CMakeLists.txt index 07364b66e..2e5cd3915 100644 --- a/Sources/DequeModule/CMakeLists.txt +++ b/Sources/DequeModule/CMakeLists.txt @@ -14,7 +14,8 @@ else() add_library(DequeModule ${COLLECTIONS_DEQUE_SOURCES}) target_link_libraries(DequeModule PRIVATE - InternalCollectionsUtilities) + InternalCollectionsUtilities + Future) set_target_properties(DequeModule PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) @@ -31,13 +32,12 @@ target_sources(${module_name} PRIVATE "Deque+ExpressibleByArrayLiteral.swift" "Deque+Extras.swift" "Deque+Hashable.swift" - "Deque+Sendable.swift" "Deque+Testing.swift" "Deque._Storage.swift" - "Deque._UnsafeHandle.swift" "Deque.swift" - "_DequeBuffer.swift" - "_DequeBufferHeader.swift" + "DynamicDeque.swift" + "RigidDeque.swift" "_DequeSlot.swift" - "_UnsafeWrappedBuffer.swift" + "_UnsafeDequeHandle.swift" + "_UnsafeDequeSegments.swift" ) diff --git a/Sources/DequeModule/Deque+Codable.swift b/Sources/DequeModule/Deque+Codable.swift index abef341ea..ee214b8cd 100644 --- a/Sources/DequeModule/Deque+Codable.swift +++ b/Sources/DequeModule/Deque+Codable.swift @@ -35,11 +35,11 @@ extension Deque: Decodable where Element: Decodable { /// - Parameter decoder: The decoder to read data from. @inlinable public init(from decoder: Decoder) throws { - self.init() - var container = try decoder.unkeyedContainer() if let count = container.count { - self.reserveCapacity(count) + self.init(minimumCapacity: count) + } else { + self.init() } while !container.isAtEnd { let element = try container.decode(Element.self) diff --git a/Sources/DequeModule/Deque+Collection.swift b/Sources/DequeModule/Deque+Collection.swift index b56772cb4..0e308724d 100644 --- a/Sources/DequeModule/Deque+Collection.swift +++ b/Sources/DequeModule/Deque+Collection.swift @@ -13,7 +13,9 @@ import InternalCollectionsUtilities #endif -extension Deque: Sequence { +import Span + +extension Deque { // Implementation note: we could also use the default `IndexingIterator` here. // This custom implementation performs direct storage access to eliminate any // and all index validation overhead. It also optimizes away repeated @@ -21,9 +23,9 @@ extension Deque: Sequence { /// An iterator over the members of a deque. @frozen - public struct Iterator: IteratorProtocol { + public struct Iterator { @usableFromInline - internal var _storage: Deque._Storage + internal var _base: Deque @usableFromInline internal var _nextSlot: _Slot @@ -32,68 +34,74 @@ extension Deque: Sequence { internal var _endSlot: _Slot @inlinable - internal init(_storage: Deque._Storage, start: _Slot, end: _Slot) { - self._storage = _storage + internal init(_base: Deque, start: _Slot, end: _Slot) { + self._base = _base self._nextSlot = start self._endSlot = end } @inlinable internal init(_base: Deque) { - self = _base._storage.read { handle in + self = _base._read { handle in let start = handle.startSlot let end = Swift.min(start.advanced(by: handle.count), handle.limSlot) - return Self(_storage: _base._storage, start: start, end: end) + return Self(_base: _base, start: start, end: end) } } @inlinable internal init(_base: Deque, from index: Int) { - self = _base._storage.read { handle in + self = _base._read { handle in assert(index >= 0 && index <= handle.count) let start = handle.slot(forOffset: index) if index == handle.count { - return Self(_storage: _base._storage, start: start, end: start) + return Self(_base: _base, start: start, end: start) } var end = handle.endSlot if start >= end { end = handle.limSlot } - return Self(_storage: _base._storage, start: start, end: end) + return Self(_base: _base, start: start, end: end) } } + } +} - @inlinable - @inline(never) - internal mutating func _swapSegment() -> Bool { - assert(_nextSlot == _endSlot) - return _storage.read { handle in - let end = handle.endSlot - if end == .zero || end == _nextSlot { - return false - } - _endSlot = end - _nextSlot = .zero - return true +extension Deque.Iterator: Sendable where Element: Sendable {} + +extension Deque.Iterator: IteratorProtocol { + @inlinable + @inline(never) + internal mutating func _swapSegment() -> Bool { + assert(_nextSlot == _endSlot) + return _base._read { handle in + let end = handle.endSlot + if end == .zero || end == _nextSlot { + return false } + _endSlot = end + _nextSlot = .zero + return true } + } - /// Advances to the next element and returns it, or `nil` if no next element - /// exists. - /// - /// Once `nil` has been returned, all subsequent calls return `nil`. - @inlinable - public mutating func next() -> Element? { - if _nextSlot == _endSlot { - guard _swapSegment() else { return nil } - } - assert(_nextSlot < _endSlot) - let slot = _nextSlot - _nextSlot = _nextSlot.advanced(by: 1) - return _storage.read { handle in - return handle.ptr(at: slot).pointee - } + /// Advances to the next element and returns it, or `nil` if no next element + /// exists. + /// + /// Once `nil` has been returned, all subsequent calls return `nil`. + @inlinable + public mutating func next() -> Element? { + if _nextSlot == _endSlot { + guard _swapSegment() else { return nil } + } + assert(_nextSlot < _endSlot) + let slot = _nextSlot + _nextSlot = _nextSlot.advanced(by: 1) + return _base._read { handle in + return handle.ptr(at: slot).pointee } } +} +extension Deque: Sequence { /// Returns an iterator over the elements of the deque. /// /// - Complexity: O(1) @@ -104,8 +112,8 @@ extension Deque: Sequence { @inlinable public __consuming func _copyToContiguousArray() -> ContiguousArray { - ContiguousArray(unsafeUninitializedCapacity: _storage.count) { target, count in - _storage.read { source in + ContiguousArray(unsafeUninitializedCapacity: count) { target, count in + _read { source in let segments = source.segments() let c = segments.first.count target[.. ) -> (Iterator, UnsafeMutableBufferPointer.Index) { - _storage.read { source in + _read { source in let segments = source.segments() let c1 = Swift.min(segments.first.count, target.count) target[..( _ body: (UnsafeBufferPointer) throws -> R ) rethrows -> R? { - return try _storage.read { handle in + return try _read { handle in + guard handle.count > 0 else { return nil } let endSlot = handle.startSlot.advanced(by: handle.count) guard endSlot.position <= handle.capacity else { return nil } return try body(handle.buffer(for: handle.startSlot ..< endSlot)) @@ -164,8 +173,6 @@ extension Deque: Sequence { } } -extension Deque.Iterator: Sendable where Element: Sendable {} - extension Deque: RandomAccessCollection { public typealias Index = Int public typealias SubSequence = Slice @@ -176,7 +183,7 @@ extension Deque: RandomAccessCollection { /// - Complexity: O(1) @inlinable @inline(__always) - public var count: Int { _storage.count } + public var count: Int { _storage.read { $0.count } } /// The position of the first element in a nonempty deque. /// @@ -354,16 +361,10 @@ extension Deque: RandomAccessCollection { @inlinable public subscript(index: Int) -> Element { get { - precondition(index >= 0 && index < count, "Index out of bounds") - return _storage.read { $0.ptr(at: $0.slot(forOffset: index)).pointee } + return _storage.read { $0[index] } } set { - precondition(index >= 0 && index < count, "Index out of bounds") - _storage.ensureUnique() - _storage.update { handle in - let slot = handle.slot(forOffset: index) - handle.ptr(at: slot).pointee = newValue - } + _storage.update { $0[index] = newValue } } @inline(__always) // https://github.com/apple/swift-collections/issues/164 _modify { @@ -378,20 +379,20 @@ extension Deque: RandomAccessCollection { @inlinable internal mutating func _prepareForModify(at index: Int) -> (_Slot, Element) { - _storage.ensureUnique() + _ensureUnique() // We technically aren't supposed to escape storage pointers out of a // managed buffer, so we escape a `(slot, value)` pair instead, leaving // the corresponding slot temporarily uninitialized. - return _storage.update { handle in + return _update { handle in let slot = handle.slot(forOffset: index) - return (slot, handle.ptr(at: slot).move()) + return (slot, handle.mutablePtr(at: slot).move()) } } @inlinable - internal mutating func _finalizeModify(_ slot: _Slot, _ value: Element) { - _storage.update { handle in - handle.ptr(at: slot).initialize(to: value) + internal mutating func _finalizeModify(_ slot: _Slot, _ value: __owned Element) { + _update { handle in + handle.mutablePtr(at: slot).initialize(to: value) } } @@ -434,8 +435,8 @@ extension Deque: MutableCollection { public mutating func swapAt(_ i: Int, _ j: Int) { precondition(i >= 0 && i < count, "Index out of bounds") precondition(j >= 0 && j < count, "Index out of bounds") - _storage.ensureUnique() - _storage.update { handle in + _ensureUnique() + _update { handle in let slot1 = handle.slot(forOffset: i) let slot2 = handle.slot(forOffset: j) handle.mutableBuffer.swapAt(slot1.position, slot2.position) @@ -466,8 +467,8 @@ extension Deque: MutableCollection { public mutating func withContiguousMutableStorageIfAvailable( _ body: (inout UnsafeMutableBufferPointer) throws -> R ) rethrows -> R? { - _storage.ensureUnique() - return try _storage.update { handle in + _ensureUnique() + return try _update { handle in let endSlot = handle.startSlot.advanced(by: handle.count) guard endSlot.position <= handle.capacity else { // FIXME: Rotate storage such that it becomes contiguous. @@ -506,7 +507,8 @@ extension Deque: RangeReplaceableCollection { /// - Complexity: O(1) @inlinable public init() { - _storage = _Storage() + // FIXME: Can we do empty singletons in this world? Should _storage become optional? + self.init(minimumCapacity: 0) } /// Reserves enough space to store the specified number of elements. @@ -522,7 +524,7 @@ extension Deque: RangeReplaceableCollection { /// - Complexity: O(`count`) @inlinable public mutating func reserveCapacity(_ minimumCapacity: Int) { - _storage.ensureUnique(minimumCapacity: minimumCapacity, linearGrowth: true) + _ensureUnique(minimumCapacity: minimumCapacity, linearGrowth: true) } /// Replaces a range of elements with the elements in the specified @@ -552,13 +554,13 @@ extension Deque: RangeReplaceableCollection { let removalCount = subrange.count let insertionCount = newElements.count let deltaCount = insertionCount - removalCount - _storage.ensureUnique(minimumCapacity: count + deltaCount) let replacementCount = Swift.min(removalCount, insertionCount) let targetCut = subrange.lowerBound + replacementCount let sourceCut = newElements.index(newElements.startIndex, offsetBy: replacementCount) - _storage.update { target in + _ensureUnique(minimumCapacity: count + deltaCount) + _update { target in target.uncheckedReplaceInPlace( inOffsets: subrange.lowerBound ..< targetCut, with: newElements[..= 0) self.init(minimumCapacity: count) - _storage.update { handle in + _update { handle in assert(handle.startSlot == .zero) if count > 0 { - handle.ptr(at: .zero).initialize(repeating: repeatedValue, count: count) + handle.mutablePtr(at: .zero).initialize( + repeating: repeatedValue, count: count) } handle.count = count } @@ -605,7 +608,7 @@ extension Deque: RangeReplaceableCollection { /// - Complexity: O(*n*), where *n* is the number of elements in the sequence. @inlinable public init(_ elements: some Sequence) { - self.init() + self.init(minimumCapacity: elements.underestimatedCount) self.append(contentsOf: elements) } @@ -618,9 +621,9 @@ extension Deque: RangeReplaceableCollection { @inlinable public init(_ elements: some Collection) { let c = elements.count - guard c > 0 else { _storage = _Storage(); return } - self._storage = _Storage(minimumCapacity: c) - _storage.update { handle in + guard c > 0 else { self.init(); return } + self.init(minimumCapacity: c) + _update { handle in assert(handle.startSlot == .zero) let target = handle.mutableBuffer(for: .zero ..< _Slot(at: c)) let done: Void? = elements.withContiguousStorageIfAvailable { source in @@ -633,6 +636,18 @@ extension Deque: RangeReplaceableCollection { } } + @inlinable + public init(_ elements: UnsafeBufferPointer) { + guard elements.count > 0 else { self.init(); return } + self.init(minimumCapacity: elements.count) + _update { handle in + assert(handle.startSlot == .zero) + let target = handle.mutableBuffer(for: .zero ..< _Slot(at: elements.count)) + target.initializeAll(fromContentsOf: elements) + handle.count = elements.count + } + } + /// Adds a new element at the end of the deque. /// /// Use this method to append a single element to the end of a deque. @@ -658,8 +673,8 @@ extension Deque: RangeReplaceableCollection { /// - SeeAlso: `prepend(_:)` @inlinable public mutating func append(_ newElement: Element) { - _storage.ensureUnique(minimumCapacity: count + 1) - _storage.update { + _ensureUnique(minimumCapacity: count + 1) + _update { $0.uncheckedAppend(newElement) } } @@ -681,24 +696,24 @@ extension Deque: RangeReplaceableCollection { @inlinable public mutating func append(contentsOf newElements: some Sequence) { let done: Void? = newElements.withContiguousStorageIfAvailable { source in - _storage.ensureUnique(minimumCapacity: count + source.count) - _storage.update { $0.uncheckedAppend(contentsOf: source) } + _ensureUnique(minimumCapacity: count + source.count) + _update { $0.uncheckedAppend(contentsOf: source) } } if done != nil { return } let underestimatedCount = newElements.underestimatedCount - _storage.ensureUnique(minimumCapacity: count + underestimatedCount) - var it = _storage.update { target in + _ensureUnique(minimumCapacity: count + underestimatedCount) + var it = _update { target in let gaps = target.availableSegments() let (it, copied) = gaps.initialize(fromSequencePrefix: newElements) target.count += copied return it } while let next = it.next() { - _storage.ensureUnique(minimumCapacity: count + 1) - _storage.update { target in + _ensureUnique(minimumCapacity: count + 1) + _update { target in target.uncheckedAppend(next) let gaps = target.availableSegments() target.count += gaps.initialize(fromPrefixOf: &it) @@ -725,15 +740,15 @@ extension Deque: RangeReplaceableCollection { contentsOf newElements: some Collection ) { let done: Void? = newElements.withContiguousStorageIfAvailable { source in - _storage.ensureUnique(minimumCapacity: count + source.count) - _storage.update { $0.uncheckedAppend(contentsOf: source) } + _ensureUnique(minimumCapacity: count + source.count) + _update { $0.uncheckedAppend(contentsOf: source) } } guard done == nil else { return } let c = newElements.count guard c > 0 else { return } - _storage.ensureUnique(minimumCapacity: count + c) - _storage.update { target in + _ensureUnique(minimumCapacity: count + c) + _update { target in let gaps = target.availableSegments().prefix(c) gaps.initialize(from: newElements) target.count += c @@ -759,19 +774,9 @@ extension Deque: RangeReplaceableCollection { public mutating func insert(_ newElement: Element, at index: Int) { precondition(index >= 0 && index <= count, "Can't insert element at invalid index") - _storage.ensureUnique(minimumCapacity: count + 1) - _storage.update { target in - if index == 0 { - target.uncheckedPrepend(newElement) - return - } - if index == count { - target.uncheckedAppend(newElement) - return - } - let gap = target.openGap(ofSize: 1, atOffset: index) - assert(gap.first.count == 1) - gap.first.baseAddress!.initialize(to: newElement) + _ensureUnique(minimumCapacity: count + 1) + _update { target in + target.uncheckedInsert(newElement, at: index) } } @@ -800,8 +805,8 @@ extension Deque: RangeReplaceableCollection { precondition(index >= 0 && index <= count, "Can't insert elements at an invalid index") let newCount = newElements.count - _storage.ensureUnique(minimumCapacity: count + newCount) - _storage.update { target in + _ensureUnique(minimumCapacity: count + newCount) + _update { target in target.uncheckedInsert(contentsOf: newElements, count: newCount, atOffset: index) } } @@ -826,11 +831,10 @@ extension Deque: RangeReplaceableCollection { public mutating func remove(at index: Int) -> Element { precondition(index >= 0 && index < self.count, "Index out of bounds") // FIXME: Implement storage shrinking - _storage.ensureUnique() - return _storage.update { target in - // FIXME: Add direct implementation & see if it makes a difference - let result = self[index] - target.uncheckedRemove(offsets: index ..< index + 1) + _ensureUnique() + return _update { target in + let result = target.mutablePtr(at: target.slot(forOffset: index)).move() + target.closeGap(offsets: index ..< index + 1) return result } } @@ -852,23 +856,23 @@ extension Deque: RangeReplaceableCollection { public mutating func removeSubrange(_ bounds: Range) { precondition(bounds.lowerBound >= 0 && bounds.upperBound <= self.count, "Index range out of bounds") - _storage.ensureUnique() - _storage.update { $0.uncheckedRemove(offsets: bounds) } + _ensureUnique() + _update { $0.uncheckedRemove(offsets: bounds) } } @inlinable public mutating func _customRemoveLast() -> Element? { precondition(!isEmpty, "Cannot remove last element of an empty Deque") - _storage.ensureUnique() - return _storage.update { $0.uncheckedRemoveLast() } + _ensureUnique() + return _update { $0.uncheckedRemoveLast() } } @inlinable public mutating func _customRemoveLast(_ n: Int) -> Bool { precondition(n >= 0, "Can't remove a negative number of elements") precondition(n <= count, "Can't remove more elements than there are in the Collection") - _storage.ensureUnique() - _storage.update { $0.uncheckedRemoveLast(n) } + _ensureUnique() + _update { $0.uncheckedRemoveLast(n) } return true } @@ -884,8 +888,8 @@ extension Deque: RangeReplaceableCollection { @discardableResult public mutating func removeFirst() -> Element { precondition(!isEmpty, "Cannot remove first element of an empty Deque") - _storage.ensureUnique() - return _storage.update { $0.uncheckedRemoveFirst() } + _ensureUnique() + return _update { $0.uncheckedRemoveFirst() } } /// Removes the specified number of elements from the beginning of the deque. @@ -900,8 +904,8 @@ extension Deque: RangeReplaceableCollection { public mutating func removeFirst(_ n: Int) { precondition(n >= 0, "Can't remove a negative number of elements") precondition(n <= count, "Can't remove more elements than there are in the Collection") - _storage.ensureUnique() - return _storage.update { $0.uncheckedRemoveFirst(n) } + _ensureUnique() + return _update { $0.uncheckedRemoveFirst(n) } } /// Removes all elements from the deque. @@ -913,8 +917,8 @@ extension Deque: RangeReplaceableCollection { @inlinable public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) { if keepCapacity { - _storage.ensureUnique() - _storage.update { $0.uncheckedRemoveAll() } + _ensureUnique() + _update { $0.uncheckedRemoveAll() } } else { self = Deque() } diff --git a/Sources/DequeModule/Deque+Container.swift b/Sources/DequeModule/Deque+Container.swift new file mode 100644 index 000000000..0b714a24d --- /dev/null +++ b/Sources/DequeModule/Deque+Container.swift @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2024 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 +// +//===----------------------------------------------------------------------===// + +#if !COLLECTIONS_SINGLE_MODULE +import Span +#endif + +extension Deque: RandomAccessContainer { + public typealias BorrowingIterator = RigidDeque.BorrowingIterator + + @inlinable + public func startBorrowingIteration() -> BorrowingIterator { + self.startBorrowingIteration(from: 0) + } + + @inlinable + public func startBorrowingIteration(from start: Int) -> BorrowingIterator { + // FIXME: This is unacceptably unsafe. We want to access `_storage.value` + // FIXME: as if it was a structural part of `self`, but there is no way + // FIXME: to express this in Swift. + BorrowingIterator( + _unsafeSegments: _storage.value._handle.segments(), + startOffset: start, + owner: self) + } + + @inlinable + public func index(at position: borrowing BorrowingIterator) -> Int { + precondition(_read { $0.segments().isIdentical(to: position._segments) }) + return position._offset + } + + @inlinable + public func formIndex( + _ index: inout Index, offsetBy distance: inout Index.Stride, limitedBy limit: Index + ) { + // Note: Range checks are deferred until element access. + index.advance(by: &distance, limitedBy: limit) + } +} diff --git a/Sources/DequeModule/Deque+Equatable.swift b/Sources/DequeModule/Deque+Equatable.swift index 4a068d7fa..0c64445e3 100644 --- a/Sources/DequeModule/Deque+Equatable.swift +++ b/Sources/DequeModule/Deque+Equatable.swift @@ -9,6 +9,8 @@ // //===----------------------------------------------------------------------===// +import Span + extension Deque: Equatable where Element: Equatable { /// Returns a Boolean value indicating whether two values are equal. Two /// deques are considered equal if they contain the same elements in the same @@ -17,16 +19,10 @@ extension Deque: Equatable where Element: Equatable { /// - Complexity: O(`min(left.count, right.count)`) @inlinable public static func ==(left: Self, right: Self) -> Bool { - let lhsCount = left.count - if lhsCount != right.count { - return false - } - - // Test referential equality. - if lhsCount == 0 || left._storage.isIdentical(to: right._storage) { - return true - } - + let count = left.count + guard count == right.count else { return false } + if count == 0 { return true } + if left._storage.isIdentical(to: right._storage) { return true } return left.elementsEqual(right) } } diff --git a/Sources/DequeModule/Deque+Extras.swift b/Sources/DequeModule/Deque+Extras.swift index 850e493fe..8f4e6fb8a 100644 --- a/Sources/DequeModule/Deque+Extras.swift +++ b/Sources/DequeModule/Deque+Extras.swift @@ -45,8 +45,8 @@ extension Deque { initializingWith initializer: (inout UnsafeMutableBufferPointer, inout Int) throws -> Void ) rethrows { - self._storage = .init(minimumCapacity: capacity) - try _storage.update { handle in + self.init(minimumCapacity: capacity) + try _update { handle in handle.startSlot = .zero var count = 0 var buffer = handle.mutableBuffer(for: .zero ..< _Slot(at: capacity)) @@ -76,8 +76,8 @@ extension Deque { // FIXME: Add this to the stdlib on BidirectionalCollection // where Self == Self.SubSequence guard count > 0 else { return nil } - _storage.ensureUnique() - return _storage.update { + _ensureUnique() + return _update { $0.uncheckedRemoveFirst() } } @@ -111,8 +111,8 @@ extension Deque { /// - SeeAlso: `append(_:)` @inlinable public mutating func prepend(_ newElement: Element) { - _storage.ensureUnique(minimumCapacity: count + 1) - return _storage.update { + _ensureUnique(minimumCapacity: count + 1) + return _update { $0.uncheckedPrepend(newElement) } } @@ -138,15 +138,15 @@ extension Deque { contentsOf newElements: some Collection ) { let done: Void? = newElements.withContiguousStorageIfAvailable { source in - _storage.ensureUnique(minimumCapacity: count + source.count) - _storage.update { $0.uncheckedPrepend(contentsOf: source) } + _ensureUnique(minimumCapacity: count + source.count) + _update { $0.uncheckedPrepend(contentsOf: source) } } guard done == nil else { return } let c = newElements.count guard c > 0 else { return } - _storage.ensureUnique(minimumCapacity: count + c) - _storage.update { target in + _ensureUnique(minimumCapacity: count + c) + _update { target in let gaps = target.availableSegments().suffix(c) gaps.initialize(from: newElements) target.count += c @@ -173,8 +173,8 @@ extension Deque { @inlinable public mutating func prepend(contentsOf newElements: some Sequence) { let done: Void? = newElements.withContiguousStorageIfAvailable { source in - _storage.ensureUnique(minimumCapacity: count + source.count) - _storage.update { $0.uncheckedPrepend(contentsOf: source) } + _ensureUnique(minimumCapacity: count + source.count) + _update { $0.uncheckedPrepend(contentsOf: source) } } guard done == nil else { return } @@ -182,7 +182,7 @@ extension Deque { self.append(contentsOf: newElements) let newCount = self.count let c = newCount - originalCount - _storage.update { target in + _update { target in target.startSlot = target.slot(forOffset: originalCount) target.count = target.capacity target.closeGap(offsets: c ..< c + (target.capacity - newCount)) diff --git a/Sources/DequeModule/Deque+Sendable.swift b/Sources/DequeModule/Deque+Sendable.swift deleted file mode 100644 index 882e921e8..000000000 --- a/Sources/DequeModule/Deque+Sendable.swift +++ /dev/null @@ -1,12 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2022 - 2024 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 -// -//===----------------------------------------------------------------------===// - -extension Deque: @unchecked Sendable where Element: Sendable {} diff --git a/Sources/DequeModule/Deque+Testing.swift b/Sources/DequeModule/Deque+Testing.swift index 0136886b9..ce4dbea65 100644 --- a/Sources/DequeModule/Deque+Testing.swift +++ b/Sources/DequeModule/Deque+Testing.swift @@ -37,8 +37,8 @@ extension Deque { /// This property isn't intended to be used outside of `Deque`'s own test /// target. @_spi(Testing) - public var _capacity: Int { - _storage.capacity + public var _unstableCapacity: Int { + _capacity } /// The number of the storage slot in this deque that holds the first element. @@ -48,8 +48,8 @@ extension Deque { /// This property isn't intended to be used outside of `Deque`'s own test /// target. @_spi(Testing) - public var _startSlot: Int { - _storage.startSlot.position + public var _unstableStartSlot: Int { + _storage.read { $0._handle.startSlot.position } } /// Constructs a deque instance of the specified contents and layout. Exposed @@ -65,26 +65,25 @@ extension Deque { precondition(capacity >= 0) precondition(startSlot >= 0 && (startSlot < capacity || (capacity == 0 && startSlot == 0))) precondition(contents.count <= capacity) + let startSlot = _Slot(at: startSlot) - let buffer = _DequeBuffer.create(minimumCapacity: capacity) { _ in - _DequeBufferHeader(capacity: capacity, count: contents.count, startSlot: startSlot) - } - let storage = Deque._Storage(unsafeDowncast(buffer, to: _DequeBuffer.self)) - if contents.count > 0 { + + var d = RigidDeque(capacity: capacity) + d._handle.count = contents.count + d._handle.startSlot = startSlot + if d._handle.count > 0 { contents.withUnsafeBufferPointer { source in - storage.update { target in - let segments = target.mutableSegments() - let c = segments.first.count - segments.first.initializeAll(fromContentsOf: source.prefix(c)) - if let second = segments.second { - second.initializeAll(fromContentsOf: source.dropFirst(c)) - } + let segments = d._handle.mutableSegments() + let c = segments.first.count + segments.first.initializeAll(fromContentsOf: source.prefix(c)) + if let second = segments.second { + second.initializeAll(fromContentsOf: source.dropFirst(c)) } } } - self.init(_storage: storage) - assert(self._capacity == capacity) - assert(self._startSlot == startSlot.position) + self.init(_storage: d) + assert(self._unstableCapacity == capacity) + assert(self._unstableStartSlot == startSlot.position) assert(self.count == contents.count) } } diff --git a/Sources/DequeModule/Deque._Storage.swift b/Sources/DequeModule/Deque._Storage.swift deleted file mode 100644 index 307801806..000000000 --- a/Sources/DequeModule/Deque._Storage.swift +++ /dev/null @@ -1,226 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2021 - 2024 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 -// -//===----------------------------------------------------------------------===// - -extension Deque { - @frozen - @usableFromInline - struct _Storage { - @usableFromInline - internal typealias _Buffer = ManagedBufferPointer<_DequeBufferHeader, Element> - - @usableFromInline - internal var _buffer: _Buffer - - @inlinable - @inline(__always) - internal init(_buffer: _Buffer) { - self._buffer = _buffer - } - } -} - -extension Deque._Storage: CustomStringConvertible { - @usableFromInline - internal var description: String { - "Deque<\(Element.self)>._Storage\(_buffer.header)" - } -} - -extension Deque._Storage { - @inlinable - internal init() { - self.init(_buffer: _Buffer(unsafeBufferObject: _emptyDequeStorage)) - } - - @inlinable - internal init(_ object: _DequeBuffer) { - self.init(_buffer: _Buffer(unsafeBufferObject: object)) - } - - @inlinable - internal init(minimumCapacity: Int) { - let object = _DequeBuffer.create( - minimumCapacity: minimumCapacity, - makingHeaderWith: { - #if os(OpenBSD) - let capacity = minimumCapacity - #else - let capacity = $0.capacity - #endif - return _DequeBufferHeader(capacity: capacity, count: 0, startSlot: .zero) - }) - self.init(_buffer: _Buffer(unsafeBufferObject: object)) - } -} - -extension Deque._Storage { - #if COLLECTIONS_INTERNAL_CHECKS - @usableFromInline @inline(never) @_effects(releasenone) - internal func _checkInvariants() { - _buffer.withUnsafeMutablePointerToHeader { $0.pointee._checkInvariants() } - } - #else - @inlinable @inline(__always) - internal func _checkInvariants() {} - #endif // COLLECTIONS_INTERNAL_CHECKS -} - -extension Deque._Storage { - @inlinable - @inline(__always) - internal var identity: AnyObject { _buffer.buffer } - - - @inlinable - @inline(__always) - internal var capacity: Int { - _buffer.withUnsafeMutablePointerToHeader { $0.pointee.capacity } - } - - @inlinable - @inline(__always) - internal var count: Int { - _buffer.withUnsafeMutablePointerToHeader { $0.pointee.count } - } - - @inlinable - @inline(__always) - internal var startSlot: _DequeSlot { - _buffer.withUnsafeMutablePointerToHeader { $0.pointee.startSlot - } - } -} - -extension Deque._Storage { - @usableFromInline - internal typealias Index = Int - - @usableFromInline - internal typealias _UnsafeHandle = Deque._UnsafeHandle - - @inlinable - @inline(__always) - internal func read(_ body: (_UnsafeHandle) throws -> R) rethrows -> R { - try _buffer.withUnsafeMutablePointers { header, elements in - let handle = _UnsafeHandle(header: header, - elements: elements, - isMutable: false) - return try body(handle) - } - } - - @inlinable - @inline(__always) - internal func update(_ body: (_UnsafeHandle) throws -> R) rethrows -> R { - try _buffer.withUnsafeMutablePointers { header, elements in - let handle = _UnsafeHandle(header: header, - elements: elements, - isMutable: true) - return try body(handle) - } - } -} - -extension Deque._Storage { - /// Return a boolean indicating whether this storage instance is known to have - /// a single unique reference. If this method returns true, then it is safe to - /// perform in-place mutations on the deque. - @inlinable - @inline(__always) - internal mutating func isUnique() -> Bool { - _buffer.isUniqueReference() - } - - /// Ensure that this storage refers to a uniquely held buffer by copying - /// elements if necessary. - @inlinable - @inline(__always) - internal mutating func ensureUnique() { - if isUnique() { return } - self._makeUniqueCopy() - } - - @inlinable - @inline(never) - internal mutating func _makeUniqueCopy() { - self = self.read { $0.copyElements() } - } - - /// The growth factor to use to increase storage size to make place for an - /// insertion. - @inlinable - @inline(__always) - internal static var growthFactor: Double { 1.5 } - - @usableFromInline - internal func _growCapacity( - to minimumCapacity: Int, - linearly: Bool - ) -> Int { - if linearly { return Swift.max(capacity, minimumCapacity) } - return Swift.max(Int((Self.growthFactor * Double(capacity)).rounded(.up)), - minimumCapacity) - } - - /// Ensure that we have a uniquely referenced buffer with enough space to - /// store at least `minimumCapacity` elements. - /// - /// - Parameter minimumCapacity: The minimum number of elements the buffer - /// needs to be able to hold on return. - /// - /// - Parameter linearGrowth: If true, then don't use an exponential growth - /// factor when reallocating the buffer -- just allocate space for the - /// requested number of elements - @inlinable - @inline(__always) - internal mutating func ensureUnique( - minimumCapacity: Int, - linearGrowth: Bool = false - ) { - let unique = isUnique() - if _slowPath(capacity < minimumCapacity || !unique) { - _ensureUnique(isUnique: unique, minimumCapacity: minimumCapacity, linearGrowth: linearGrowth) - } - } - - @inlinable - @inline(never) - internal mutating func _ensureUnique( - isUnique: Bool, - minimumCapacity: Int, - linearGrowth: Bool - ) { - if capacity >= minimumCapacity { - assert(!isUnique) - self = self.read { $0.copyElements() } - return - } - - let minimumCapacity = _growCapacity(to: minimumCapacity, linearly: linearGrowth) - if isUnique { - self = self.update { source in - source.moveElements(minimumCapacity: minimumCapacity) - } - } else { - self = self.read { source in - source.copyElements(minimumCapacity: minimumCapacity) - } - } - } -} - -extension Deque._Storage { - @inlinable - @inline(__always) - internal func isIdentical(to other: Self) -> Bool { - self._buffer.buffer === other._buffer.buffer - } -} diff --git a/Sources/DequeModule/Deque.swift b/Sources/DequeModule/Deque.swift index 9d6667a36..c52bbc0a6 100644 --- a/Sources/DequeModule/Deque.swift +++ b/Sources/DequeModule/Deque.swift @@ -9,6 +9,8 @@ // //===----------------------------------------------------------------------===// +import Span + /// A collection implementing a double-ended queue. `Deque` (pronounced "deck") /// implements an ordered random-access collection that supports efficient /// insertions and removals from both ends. @@ -85,11 +87,11 @@ public struct Deque { internal typealias _Slot = _DequeSlot @usableFromInline - internal var _storage: _Storage + internal var _storage: Shared> @inlinable - internal init(_storage: _Storage) { - self._storage = _storage + internal init(_storage: consuming RigidDeque) { + self._storage = Shared(_storage) } /// Creates and empty deque with preallocated space for at least the specified @@ -100,6 +102,137 @@ public struct Deque { /// storage buffer. @inlinable public init(minimumCapacity: Int) { - self._storage = _Storage(minimumCapacity: minimumCapacity) + self.init(_storage: RigidDeque(capacity: minimumCapacity)) + } +} + +extension Deque: @unchecked Sendable where Element: Sendable {} + +extension Deque { + @usableFromInline + internal typealias _UnsafeHandle = _UnsafeDequeHandle + + @inlinable + @inline(__always) + internal func _read( + _ body: (borrowing _UnsafeHandle) throws(E) -> R + ) throws(E) -> R { + try _storage.read { rigid throws(E) in + try body(rigid._handle) + } + } + + @inlinable + @inline(__always) + internal mutating func _update( + _ body: (inout _UnsafeHandle) throws(E) -> R + ) throws(E) -> R { + _ensureUnique() + return try _storage.update { v throws(E) in + try body(&v._handle) + } + } +} + +extension Deque { + /// Return a boolean indicating whether this storage instance is known to have + /// a single unique reference. If this method returns true, then it is safe to + /// perform in-place mutations on the deque. + @inlinable + internal mutating func _isUnique() -> Bool { + _storage.isUnique() + } + + /// Ensure that this storage refers to a uniquely held buffer by copying + /// elements if necessary. + @inlinable + @inline(__always) + internal mutating func _ensureUnique() { + _storage.ensureUnique(cloner: { $0._copy() }) + } + + /// Copy elements into a new storage instance without changing capacity or + /// layout. + @inlinable + @inline(never) + internal func _makeUniqueCopy() -> Self { + Deque(_storage: _storage.read { $0._copy() }) + } + + @inlinable + @inline(never) + internal func _makeUniqueCopy(capacity: Int) -> Self { + Deque(_storage: _storage.read { $0._copy(capacity: capacity) }) + } + + @inlinable + internal var _capacity: Int { + _storage.read { $0.capacity } + } + + @usableFromInline + internal func _growCapacity( + to minimumCapacity: Int, + linearly: Bool + ) -> Int { + if linearly { + return Swift.max(_capacity, minimumCapacity) + } + let next = (3 * _capacity + 1) / 2 + return Swift.max(next, minimumCapacity) + } + + /// Ensure that we have a uniquely referenced buffer with enough space to + /// store at least `minimumCapacity` elements. + /// + /// - Parameter minimumCapacity: The minimum number of elements the buffer + /// needs to be able to hold on return. + /// + /// - Parameter linearGrowth: If true, then don't use an exponential growth + /// factor when reallocating the buffer -- just allocate space for the + /// requested number of elements + @inlinable + @inline(__always) + internal mutating func _ensureUnique( + minimumCapacity: Int, + linearGrowth: Bool = false + ) { + let unique = _isUnique() + if _slowPath(_capacity < minimumCapacity || !unique) { + __ensureUnique( + isUnique: unique, + minimumCapacity: minimumCapacity, + linearGrowth: linearGrowth) + } + } + + @inlinable + @inline(never) + internal mutating func __ensureUnique( + isUnique: Bool, + minimumCapacity: Int, + linearGrowth: Bool + ) { + if _capacity >= minimumCapacity { + assert(!isUnique) + self = self._makeUniqueCopy() + return + } + + let c = _growCapacity(to: minimumCapacity, linearly: linearGrowth) + if isUnique { + self._storage.update { $0.resize(to: c) } + } else { + self = self._makeUniqueCopy(capacity: c) + } } } + +extension Deque { + @inlinable + @inline(__always) + internal func _isIdentical(to other: Self) -> Bool { + self._storage.isIdentical(to: other._storage) + } +} + diff --git a/Sources/DequeModule/DynamicDeque.swift b/Sources/DequeModule/DynamicDeque.swift new file mode 100644 index 000000000..58eb43039 --- /dev/null +++ b/Sources/DequeModule/DynamicDeque.swift @@ -0,0 +1,167 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2024 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 +// +//===----------------------------------------------------------------------===// + +#if !COLLECTIONS_SINGLE_MODULE +import InternalCollectionsUtilities +import Span +#endif + +@frozen +public struct DynamicDeque: ~Copyable { + @usableFromInline + internal var _storage: RigidDeque + + @inlinable + public init() { + _storage = .init(capacity: 0) + } + + @inlinable + public init(capacity: Int) { + _storage = .init(capacity: capacity) + } +} + +extension DynamicDeque: @unchecked Sendable where Element: Sendable & ~Copyable {} + +extension DynamicDeque: RandomAccessContainer where Element: ~Copyable { + public typealias BorrowingIterator = RigidDeque.BorrowingIterator + public typealias Index = Int + + public func startBorrowingIteration() -> BorrowingIterator { + _storage.startBorrowingIteration() + } + + public func startBorrowingIteration(from start: Int) -> BorrowingIterator { + _storage.startBorrowingIteration(from: start) + } + + @inlinable + public var isEmpty: Bool { _storage.isEmpty } + + @inlinable + public var count: Int { _storage.count } + + @inlinable + public var startIndex: Int { _storage.startIndex } + + @inlinable + public var endIndex: Int { _storage.endIndex } + + @inlinable + public subscript(position: Int) -> Element { + @inline(__always) + _read { + yield _storage[position] + } + @inline(__always) + _modify { + yield &_storage[position] + } + } + + public func index(at position: borrowing BorrowingIterator) -> Int { + _storage.index(at: position) + } +} + +extension DynamicDeque where Element: ~Copyable { + @inlinable + internal var _capacity: Int { _storage.capacity } + + @inlinable + internal var _freeCapacity: Int { _storage.freeCapacity } + + @inlinable + internal var _isFull: Bool { _storage.isFull } + + @inlinable + internal mutating func _grow(to minimumCapacity: Int) { + guard minimumCapacity > _capacity else { return } + let c = Swift.max(minimumCapacity, 7 * _capacity / 4) + _storage.resize(to: c) + } + + @inlinable + internal mutating func _ensureFreeCapacity(_ freeCapacity: Int) { + _grow(to: count + freeCapacity) + } +} + +extension DynamicDeque where Element: ~Copyable { + @inlinable + public mutating func append(_ newElement: consuming Element) { + _ensureFreeCapacity(1) + _storage.append(newElement) + } + + @inlinable + public mutating func prepend(_ newElement: consuming Element) { + _ensureFreeCapacity(1) + _storage.prepend(newElement) + } + + @inlinable + public mutating func insert(_ newElement: consuming Element, at index: Int) { + _ensureFreeCapacity(1) + _storage.insert(newElement, at: index) + } +} + +extension DynamicDeque where Element: ~Copyable { + @inlinable + @discardableResult + public mutating func remove(at index: Int) -> Element { + _storage.remove(at: index) + } + + @inlinable + public mutating func removeSubrange(_ bounds: Range) { + _storage.removeSubrange(bounds) + } + + @inlinable + @discardableResult + public mutating func removeFirst() -> Element { + _storage.removeFirst() + } + + @inlinable + @discardableResult + public mutating func removeLast() -> Element { + _storage.removeLast() + } + + @inlinable + public mutating func removeFirst(_ n: Int) { + _storage.removeFirst(n) + } + + @inlinable + public mutating func removeLast(_ n: Int) { + _storage.removeLast(n) + } + + @inlinable + public mutating func removeAll() { + _storage.removeAll() + } + + @inlinable + public mutating func popFirst() -> Element? { + _storage.popFirst() + } + + @inlinable + public mutating func popLast() -> Element? { + _storage.popLast() + } +} diff --git a/Sources/DequeModule/RigidDeque.swift b/Sources/DequeModule/RigidDeque.swift new file mode 100644 index 000000000..cdf3f3f43 --- /dev/null +++ b/Sources/DequeModule/RigidDeque.swift @@ -0,0 +1,264 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2024 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 +// +//===----------------------------------------------------------------------===// + +#if !COLLECTIONS_SINGLE_MODULE +import InternalCollectionsUtilities +import Span +#endif + +@frozen +public struct RigidDeque: ~Copyable { + @usableFromInline + internal typealias _Slot = _DequeSlot + + @usableFromInline + internal typealias _UnsafeHandle = _UnsafeDequeHandle + + @usableFromInline + internal var _handle: _UnsafeHandle + + @inlinable + internal init(_handle: consuming _UnsafeHandle) { + self._handle = _handle + } + + @inlinable + public init(capacity: Int) { + self.init(_handle: .allocate(capacity: capacity)) + } + + deinit { + _handle.dispose() + } +} + +extension RigidDeque: @unchecked Sendable where Element: Sendable & ~Copyable {} + +extension RigidDeque where Element: ~Copyable { +#if COLLECTIONS_INTERNAL_CHECKS + @usableFromInline @inline(never) @_effects(releasenone) + internal func _checkInvariants() { + _handle._checkInvariants() + } +#else + @inlinable @inline(__always) + internal func _checkInvariants() {} +#endif // COLLECTIONS_INTERNAL_CHECKS +} + +extension RigidDeque where Element: ~Copyable { + @usableFromInline + internal var description: String { + _handle.description + } +} + +public struct _DequeBorrowingIterator: BorrowingIteratorProtocol, ~Escapable { + @usableFromInline + internal typealias _UnsafeHandle = _UnsafeDequeHandle + + @usableFromInline + internal let _segments: _UnsafeDequeSegments + + @usableFromInline + internal var _offset: Int + + @inlinable + internal init( + _unsafeSegments segments: _UnsafeDequeSegments, + startOffset: Int, + owner: borrowing T + ) { + self._segments = segments + self._offset = startOffset + } + + @inlinable + internal init(_for handle: borrowing _UnsafeHandle, startOffset: Int) { + self.init(_unsafeSegments: handle.segments(), startOffset: startOffset, owner: handle) + } + + @inlinable + public mutating func nextChunk( + maximumCount: Int + ) -> dependsOn(scoped self) Span { + precondition(maximumCount > 0) + if _offset < _segments.first.count { + let d = Swift.min(maximumCount, _segments.first.count - _offset) + let slice = _segments.first.extracting(_offset ..< _offset + d) + _offset += d + return Span(_unsafeElements: slice) + } + guard let second = _segments.second else { + return Span(_unsafeElements: UnsafeBufferPointer._empty) + } + let o = _offset - _segments.first.count + let d = Swift.min(maximumCount, second.count - o) + let slice = second.extracting(o ..< o + d) + _offset += d + return Span(_unsafeElements: slice) + } +} + +extension RigidDeque: RandomAccessContainer where Element: ~Copyable { + public typealias BorrowingIterator = _DequeBorrowingIterator + + public func startBorrowingIteration() -> BorrowingIterator { + _handle.startBorrowingIteration() + } + + public func startBorrowingIteration(from start: Int) -> BorrowingIterator { + _handle.startBorrowingIteration(from: start) + } + + public typealias Index = Int + + @inlinable + public var isEmpty: Bool { _handle.count == 0 } + + @inlinable + public var count: Int { _handle.count } + + @inlinable + public var startIndex: Int { 0 } + + @inlinable + public var endIndex: Int { _handle.count } + + @inlinable + public subscript(position: Int) -> Element { + @inline(__always) + _read { + yield _handle[offset: position] + } + @inline(__always) + _modify { + yield &_handle[offset: position] + } + } + + public func index(at position: borrowing BorrowingIterator) -> Int { + precondition(_handle.segments().isIdentical(to: position._segments)) + return position._offset + } +} + +extension RigidDeque where Element: ~Copyable { + @inlinable + public var capacity: Int { _handle.capacity } + + @inlinable + public var freeCapacity: Int { capacity - count } + + @inlinable + public var isFull: Bool { count == capacity } + + @inlinable + public mutating func resize(to newCapacity: Int) { + _handle.reallocate(capacity: newCapacity) + } +} + +extension RigidDeque where Element: ~Copyable { + @inlinable + public mutating func append(_ newElement: consuming Element) { + precondition(!isFull, "RigidDeque is full") + _handle.uncheckedAppend(newElement) + } + + @inlinable + public mutating func prepend(_ newElement: consuming Element) { + precondition(!isFull, "RigidDeque is full") + _handle.uncheckedPrepend(newElement) + } + + @inlinable + public mutating func insert(_ newElement: consuming Element, at index: Int) { + precondition(!isFull, "RigidDeque is full") + precondition(index >= 0 && index <= count, + "Can't insert element at invalid index") + _handle.uncheckedInsert(newElement, at: index) + } +} + +extension RigidDeque where Element: ~Copyable { + @inlinable + @discardableResult + public mutating func remove(at index: Int) -> Element { + precondition(index >= 0 && index < count, + "Can't remove element at invalid index") + return _handle.uncheckedRemove(at: index) + } + + @inlinable + public mutating func removeSubrange(_ bounds: Range) { + precondition(bounds.lowerBound >= 0 && bounds.upperBound <= count, + "Index range out of bounds") + _handle.uncheckedRemove(offsets: bounds) + } + + @inlinable + @discardableResult + public mutating func removeFirst() -> Element { + precondition(!isEmpty, "Cannot remove first element of an empty RigidDeque") + return _handle.uncheckedRemoveFirst() + } + + @inlinable + @discardableResult + public mutating func removeLast() -> Element { + precondition(!isEmpty, "Cannot remove last element of an empty RigidDeque") + return _handle.uncheckedRemoveLast() + } + + @inlinable + public mutating func removeFirst(_ n: Int) { + precondition(n >= 0, "Can't remove a negative number of elements") + precondition(n <= count, "Can't remove more elements than there are in a RigidDeque") + _handle.uncheckedRemoveFirst(n) + } + + @inlinable + public mutating func removeLast(_ n: Int) { + precondition(n >= 0, "Can't remove a negative number of elements") + precondition(n <= count, "Can't remove more elements than there are in a RigidDeque") + _handle.uncheckedRemoveLast(n) + } + + @inlinable + public mutating func removeAll() { + _handle.uncheckedRemoveAll() + } + + @inlinable + public mutating func popFirst() -> Element? { + guard !isEmpty else { return nil } + return _handle.uncheckedRemoveFirst() + } + + @inlinable + public mutating func popLast() -> Element? { + guard !isEmpty else { return nil } + return _handle.uncheckedRemoveLast() + } +} + +extension RigidDeque { + @inlinable + internal func _copy() -> Self { + RigidDeque(_handle: _handle.allocateCopy()) + } + + @inlinable + internal func _copy(capacity: Int) -> Self { + RigidDeque(_handle: _handle.allocateCopy(capacity: capacity)) + } +} diff --git a/Sources/DequeModule/_DequeBuffer.swift b/Sources/DequeModule/_DequeBuffer.swift deleted file mode 100644 index cb17d1dce..000000000 --- a/Sources/DequeModule/_DequeBuffer.swift +++ /dev/null @@ -1,49 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2021 - 2024 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 -// -//===----------------------------------------------------------------------===// - -@_fixed_layout -@usableFromInline -internal final class _DequeBuffer: ManagedBuffer<_DequeBufferHeader, Element> { - @inlinable - deinit { - self.withUnsafeMutablePointers { header, elements in - header.pointee._checkInvariants() - - let capacity = header.pointee.capacity - let count = header.pointee.count - let startSlot = header.pointee.startSlot - - if startSlot.position + count <= capacity { - (elements + startSlot.position).deinitialize(count: count) - } else { - let firstRegion = capacity - startSlot.position - (elements + startSlot.position).deinitialize(count: firstRegion) - elements.deinitialize(count: count - firstRegion) - } - } - } -} - -extension _DequeBuffer: CustomStringConvertible { - @usableFromInline - internal var description: String { - withUnsafeMutablePointerToHeader { "_DequeStorage<\(Element.self)>\($0.pointee)" } - } -} - -/// The type-punned empty singleton storage instance. -@usableFromInline -internal let _emptyDequeStorage = _DequeBuffer.create( - minimumCapacity: 0, - makingHeaderWith: { _ in - _DequeBufferHeader(capacity: 0, count: 0, startSlot: .init(at: 0)) - }) - diff --git a/Sources/DequeModule/_DequeBufferHeader.swift b/Sources/DequeModule/_DequeBufferHeader.swift deleted file mode 100644 index c45f756dd..000000000 --- a/Sources/DequeModule/_DequeBufferHeader.swift +++ /dev/null @@ -1,49 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2021 - 2024 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 -// -//===----------------------------------------------------------------------===// - -@usableFromInline -internal struct _DequeBufferHeader { - @usableFromInline - var capacity: Int - - @usableFromInline - var count: Int - - @usableFromInline - var startSlot: _DequeSlot - - @usableFromInline - init(capacity: Int, count: Int, startSlot: _DequeSlot) { - self.capacity = capacity - self.count = count - self.startSlot = startSlot - _checkInvariants() - } - - #if COLLECTIONS_INTERNAL_CHECKS - @usableFromInline @inline(never) @_effects(releasenone) - internal func _checkInvariants() { - precondition(capacity >= 0) - precondition(count >= 0 && count <= capacity) - precondition(startSlot.position >= 0 && startSlot.position <= capacity) - } - #else - @inlinable @inline(__always) - internal func _checkInvariants() {} - #endif // COLLECTIONS_INTERNAL_CHECKS -} - -extension _DequeBufferHeader: CustomStringConvertible { - @usableFromInline - internal var description: String { - "(capacity: \(capacity), count: \(count), startSlot: \(startSlot))" - } -} diff --git a/Sources/DequeModule/_DequeSlot.swift b/Sources/DequeModule/_DequeSlot.swift index 2d281cb94..d1d8d5f4f 100644 --- a/Sources/DequeModule/_DequeSlot.swift +++ b/Sources/DequeModule/_DequeSlot.swift @@ -69,4 +69,10 @@ extension Range where Bound == _DequeSlot { @inlinable @inline(__always) internal var _count: Int { upperBound.position - lowerBound.position } + + @inlinable + @inline(__always) + internal var _offsets: Range { + Range(uncheckedBounds: (lowerBound.position, upperBound.position)) + } } diff --git a/Sources/DequeModule/Deque._UnsafeHandle.swift b/Sources/DequeModule/_UnsafeDequeHandle.swift similarity index 68% rename from Sources/DequeModule/Deque._UnsafeHandle.swift rename to Sources/DequeModule/_UnsafeDequeHandle.swift index 8302b635f..25d5b1bfe 100644 --- a/Sources/DequeModule/Deque._UnsafeHandle.swift +++ b/Sources/DequeModule/_UnsafeDequeHandle.swift @@ -9,106 +9,98 @@ // //===----------------------------------------------------------------------===// -extension Deque { - @frozen +#if !COLLECTIONS_SINGLE_MODULE +import InternalCollectionsUtilities +#endif + +@frozen +@usableFromInline +internal struct _UnsafeDequeHandle: ~Copyable { @usableFromInline - internal struct _UnsafeHandle { - @usableFromInline - let _header: UnsafeMutablePointer<_DequeBufferHeader> - @usableFromInline - let _elements: UnsafeMutablePointer - #if DEBUG - @usableFromInline - let _isMutable: Bool - #endif - - @inlinable - @inline(__always) - init( - header: UnsafeMutablePointer<_DequeBufferHeader>, - elements: UnsafeMutablePointer, - isMutable: Bool - ) { - self._header = header - self._elements = elements - #if DEBUG - self._isMutable = isMutable - #endif - } - } -} + internal typealias Slot = _DequeSlot -extension Deque._UnsafeHandle { - @inlinable - @inline(__always) - func assertMutable() { - #if DEBUG - assert(_isMutable) - #endif - } -} + @usableFromInline + internal var _buffer: UnsafeMutableBufferPointer -extension Deque._UnsafeHandle { @usableFromInline - internal typealias Slot = _DequeSlot + internal var count: Int - @inlinable - @inline(__always) - var header: _DequeBufferHeader { - _header.pointee - } + @usableFromInline + internal var startSlot: Slot @inlinable - @inline(__always) - var capacity: Int { - _header.pointee.capacity + internal init( + buffer: UnsafeMutableBufferPointer, + count: Int, + startSlot: _DequeSlot + ) { + self._buffer = buffer + self.count = count + self.startSlot = startSlot } @inlinable - @inline(__always) - var count: Int { - get { _header.pointee.count } - nonmutating set { _header.pointee.count = newValue } + internal consuming func dispose() { + _checkInvariants() + self.mutableSegments().deinitialize() + _buffer.deallocate() } +} +extension _UnsafeDequeHandle where Element: ~Copyable { @inlinable - @inline(__always) - var startSlot: Slot { - get { _header.pointee.startSlot } - nonmutating set { _header.pointee.startSlot = newValue } + internal static var empty: Self { + Self(buffer: ._empty, count: 0, startSlot: .zero) } @inlinable - @inline(__always) - func ptr(at slot: Slot) -> UnsafeMutablePointer { - assert(slot.position >= 0 && slot.position <= capacity) - return _elements + slot.position + internal static func allocate( + capacity: Int + ) -> Self { + Self( + buffer: capacity > 0 ? .allocate(capacity: capacity) : ._empty, + count: 0, + startSlot: .zero) } } -extension Deque._UnsafeHandle { - @inlinable - @inline(__always) - var mutableBuffer: UnsafeMutableBufferPointer { - assertMutable() - return .init(start: _elements, count: _header.pointee.capacity) +extension _UnsafeDequeHandle where Element: ~Copyable { +#if COLLECTIONS_INTERNAL_CHECKS + @usableFromInline @inline(never) @_effects(releasenone) + internal func _checkInvariants() { + precondition(capacity >= 0) + precondition(count >= 0 && count <= capacity) + precondition(startSlot.position >= 0 && startSlot.position <= capacity) + } +#else + @inlinable @inline(__always) + internal func _checkInvariants() {} +#endif // COLLECTIONS_INTERNAL_CHECKS +} + +extension _UnsafeDequeHandle where Element: ~Copyable { + @usableFromInline + internal var description: String { + "(capacity: \(capacity), count: \(count), start: \(startSlot))" } +} - @inlinable - internal func buffer(for range: Range) -> UnsafeBufferPointer { - assert(range.upperBound.position <= capacity) - return .init(start: _elements + range.lowerBound.position, count: range._count) +extension _UnsafeDequeHandle where Element: ~Copyable { + @inlinable @inline(__always) + internal var _baseAddress: UnsafeMutablePointer { + _buffer.baseAddress.unsafelyUnwrapped } @inlinable - @inline(__always) - internal func mutableBuffer(for range: Range) -> UnsafeMutableBufferPointer { - assertMutable() - return .init(mutating: buffer(for: range)) + internal var capacity: Int { + _buffer.count } } -extension Deque._UnsafeHandle { + +// MARK: Slots + +extension _UnsafeDequeHandle where Element: ~Copyable { /// The slot immediately following the last valid one. (`endSlot` refers to /// the valid slot corresponding to `endIndex`, which is a different thing /// entirely.) @@ -170,228 +162,254 @@ extension Deque._UnsafeHandle { } } -extension Deque._UnsafeHandle { - @inlinable - internal func segments() -> _UnsafeWrappedBuffer { - let wrap = capacity - startSlot.position - if count <= wrap { - return .init(start: ptr(at: startSlot), count: count) - } - return .init(first: ptr(at: startSlot), count: wrap, - second: ptr(at: .zero), count: count - wrap) +// MARK: Element Access + +extension _UnsafeDequeHandle where Element: ~Copyable { + @inlinable @inline(__always) + internal func ptr(at slot: Slot) -> UnsafePointer { + assert(slot.position >= 0 && slot.position <= capacity) + return UnsafePointer(_baseAddress + slot.position) + } + + @inlinable @inline(__always) + internal mutating func mutablePtr( + at slot: Slot + ) -> UnsafeMutablePointer { + assert(slot.position >= 0 && slot.position <= capacity) + return _baseAddress + slot.position } +} +extension _UnsafeDequeHandle where Element: ~Copyable { @inlinable - internal func segments( - forOffsets offsets: Range - ) -> _UnsafeWrappedBuffer { - assert(offsets.lowerBound >= 0 && offsets.upperBound <= count) - let lower = slot(forOffset: offsets.lowerBound) - let upper = slot(forOffset: offsets.upperBound) - if offsets.count == 0 || lower < upper { - return .init(start: ptr(at: lower), count: offsets.count) + internal subscript(offset offset: Int) -> Element { + @inline(__always) + _read { + precondition(offset >= 0 && offset < count, "Index out of bounds") + let slot = slot(forOffset: offset) + yield ptr(at: slot).pointee + } + @inline(__always) + _modify { + precondition(offset >= 0 && offset < count, "Index out of bounds") + let slot = slot(forOffset: offset) + yield &mutablePtr(at: slot).pointee } - return .init(first: ptr(at: lower), count: capacity - lower.position, - second: ptr(at: .zero), count: upper.position) } +} - @inlinable - @inline(__always) - internal func mutableSegments() -> _UnsafeMutableWrappedBuffer { - assertMutable() - return .init(mutating: segments()) +// MARK: Access to contiguous regions + +extension _UnsafeDequeHandle where Element: ~Copyable { + @inlinable @inline(__always) + internal var mutableBuffer: UnsafeMutableBufferPointer { + mutating get { + _buffer + } } @inlinable - @inline(__always) - internal func mutableSegments( - forOffsets range: Range - ) -> _UnsafeMutableWrappedBuffer { - assertMutable() - return .init(mutating: segments(forOffsets: range)) + internal func buffer(for range: Range) -> UnsafeBufferPointer { + assert(range.upperBound.position <= capacity) + return .init(_buffer._extracting(unchecked: range._offsets)) } -} -extension Deque._UnsafeHandle { - @inlinable - internal func availableSegments() -> _UnsafeMutableWrappedBuffer { - assertMutable() - let endSlot = self.endSlot - guard count < capacity else { return .init(start: ptr(at: endSlot), count: 0) } - if endSlot < startSlot { return .init(mutableBuffer(for: endSlot ..< startSlot)) } - return .init(mutableBuffer(for: endSlot ..< limSlot), - mutableBuffer(for: .zero ..< startSlot)) + @inlinable @inline(__always) + internal mutating func mutableBuffer(for range: Range) -> UnsafeMutableBufferPointer { + assert(range.upperBound.position <= capacity) + return _buffer._extracting(unchecked: range._offsets) } } - - -extension Deque._UnsafeHandle { +extension _UnsafeDequeHandle { @inlinable @discardableResult - func initialize( + internal mutating func initialize( at start: Slot, from source: UnsafeBufferPointer ) -> Slot { assert(start.position + source.count <= capacity) guard source.count > 0 else { return start } - ptr(at: start).initialize(from: source.baseAddress!, count: source.count) + mutablePtr(at: start).initialize(from: source.baseAddress!, count: source.count) return Slot(at: start.position + source.count) } +} +extension _UnsafeDequeHandle where Element: ~Copyable { @inlinable @inline(__always) @discardableResult - func moveInitialize( + internal mutating func moveInitialize( at start: Slot, from source: UnsafeMutableBufferPointer ) -> Slot { assert(start.position + source.count <= capacity) guard source.count > 0 else { return start } - ptr(at: start).moveInitialize(from: source.baseAddress!, count: source.count) + mutablePtr(at: start) + .moveInitialize(from: source.baseAddress!, count: source.count) return Slot(at: start.position + source.count) } +} + +// MARK: Access to Segments + +extension _UnsafeDequeHandle where Element: ~Copyable { + @inlinable + internal func segments() -> _UnsafeDequeSegments { + guard _buffer.baseAddress != nil else { + return .init(._empty) + } + let wrap = capacity - startSlot.position + if count <= wrap { + return .init(start: ptr(at: startSlot), count: count) + } + return .init(first: ptr(at: startSlot), count: wrap, + second: ptr(at: .zero), count: count - wrap) + } + + @inlinable + internal func segments( + forOffsets offsets: Range + ) -> _UnsafeDequeSegments { + assert(offsets.lowerBound >= 0 && offsets.upperBound <= count) + guard _buffer.baseAddress != nil else { + return .init(._empty) + } + let lower = slot(forOffset: offsets.lowerBound) + let upper = slot(forOffset: offsets.upperBound) + if offsets.count == 0 || lower < upper { + return .init(start: ptr(at: lower), count: offsets.count) + } + return .init(first: ptr(at: lower), count: capacity - lower.position, + second: ptr(at: .zero), count: upper.position) + } @inlinable @inline(__always) - @discardableResult - public func move( - from source: Slot, - to target: Slot, - count: Int - ) -> (source: Slot, target: Slot) { - assert(count >= 0) - assert(source.position + count <= self.capacity) - assert(target.position + count <= self.capacity) - guard count > 0 else { return (source, target) } - ptr(at: target).moveInitialize(from: ptr(at: source), count: count) - return (slot(source, offsetBy: count), slot(target, offsetBy: count)) + internal mutating func mutableSegments() -> _UnsafeMutableDequeSegments { + .init(mutating: segments()) } -} + @inlinable + @inline(__always) + internal mutating func mutableSegments( + forOffsets range: Range + ) -> _UnsafeMutableDequeSegments { + .init(mutating: segments(forOffsets: range)) + } + @inlinable + internal mutating func mutableSegments( + between start: Slot, + and end: Slot + ) -> _UnsafeMutableDequeSegments { + assert(start.position <= capacity) + assert(end.position <= capacity) + if start < end { + return .init( + start: mutablePtr(at: start), + count: end.position - start.position) + } + return .init( + first: mutablePtr(at: start), count: capacity - start.position, + second: mutablePtr(at: .zero), count: end.position) + } +} -extension Deque._UnsafeHandle { - /// Copy elements into a new storage instance without changing capacity or - /// layout. +extension _UnsafeDequeHandle where Element: ~Copyable { @inlinable - internal func copyElements() -> Deque._Storage { - let object = _DequeBuffer.create( - minimumCapacity: capacity, - makingHeaderWith: { _ in header }) - let result = Deque._Storage(_buffer: ManagedBufferPointer(unsafeBufferObject: object)) - guard self.count > 0 else { return result } - result.update { target in - let source = self.segments() - target.initialize(at: startSlot, from: source.first) - if let second = source.second { - target.initialize(at: .zero, from: second) - } + internal mutating func availableSegments() -> _UnsafeMutableDequeSegments { + guard _buffer.baseAddress != nil else { + return .init(._empty) } - return result + let endSlot = self.endSlot + guard count < capacity else { return .init(start: mutablePtr(at: endSlot), count: 0) } + if endSlot < startSlot { return .init(mutableBuffer(for: endSlot ..< startSlot)) } + return .init(mutableBuffer(for: endSlot ..< limSlot), + mutableBuffer(for: .zero ..< startSlot)) } +} - /// Copy elements into a new storage instance with the specified minimum - /// capacity. - @inlinable - internal func copyElements(minimumCapacity: Int) -> Deque._Storage { - assert(minimumCapacity >= count) - let object = _DequeBuffer.create( - minimumCapacity: minimumCapacity, - makingHeaderWith: { - #if os(OpenBSD) - let capacity = minimumCapacity - #else - let capacity = $0.capacity - #endif - return _DequeBufferHeader( - capacity: capacity, - count: count, - startSlot: .zero) - }) - let result = Deque._Storage(_buffer: ManagedBufferPointer(unsafeBufferObject: object)) - guard count > 0 else { return result } - result.update { target in - assert(target.count == count && target.startSlot.position == 0) - let source = self.segments() - let next = target.initialize(at: .zero, from: source.first) - if let second = source.second { - target.initialize(at: next, from: second) - } +// MARK: Wholesale Copying and Reallocation + +extension _UnsafeDequeHandle { + /// Copy elements in `handle` into a newly allocated handle without changing its + /// capacity or layout. + @inlinable + internal borrowing func allocateCopy() -> Self { + var result: _UnsafeDequeHandle = .allocate(capacity: self.capacity) + result.count = self.count + result.startSlot = self.startSlot + let src = self.segments() + result.initialize(at: self.startSlot, from: src.first) + if let second = src.second { + result.initialize(at: .zero, from: second) } return result } - /// Move elements into a new storage instance with the specified minimum - /// capacity. Existing indices in `self` won't necessarily be valid in the - /// result. `self` is left empty. - @inlinable - internal func moveElements(minimumCapacity: Int) -> Deque._Storage { - assertMutable() - let count = self.count - assert(minimumCapacity >= count) - let object = _DequeBuffer.create( - minimumCapacity: minimumCapacity, - makingHeaderWith: { - #if os(OpenBSD) - let capacity = minimumCapacity - #else - let capacity = $0.capacity - #endif - return _DequeBufferHeader( - capacity: capacity, - count: count, - startSlot: .zero) - }) - let result = Deque._Storage(_buffer: ManagedBufferPointer(unsafeBufferObject: object)) - guard count > 0 else { return result } - result.update { target in - let source = self.mutableSegments() - let next = target.moveInitialize(at: .zero, from: source.first) - if let second = source.second { - target.moveInitialize(at: next, from: second) - } + /// Copy elements in `handle` into a newly allocated handle with the specified + /// minimum capacity. This operation does not preserve layout. + @inlinable + internal borrowing func allocateCopy(capacity: Int) -> Self { + precondition(capacity >= self.count) + var result: _UnsafeDequeHandle = .allocate(capacity: capacity) + result.count = self.count + let src = self.segments() + let next = result.initialize(at: .zero, from: src.first) + if let second = src.second { + result.initialize(at: next, from: second) } - self.count = 0 return result } } -extension Deque._UnsafeHandle { +extension _UnsafeDequeHandle where Element: ~Copyable { @inlinable - internal func withUnsafeSegment( - startingAt start: Int, - maximumCount: Int?, - _ body: (UnsafeBufferPointer) throws -> R - ) rethrows -> (end: Int, result: R) { - assert(start <= count) - guard start < count else { - return try (count, body(UnsafeBufferPointer(start: nil, count: 0))) + internal mutating func reallocate(capacity newCapacity: Int) { + precondition(newCapacity >= count) + guard newCapacity != capacity else { return } + + var new = _UnsafeDequeHandle.allocate(capacity: newCapacity) + let source = self.mutableSegments() + let next = new.moveInitialize(at: .zero, from: source.first) + if let second = source.second { + new.moveInitialize(at: next, from: second) } - let endSlot = self.endSlot + _buffer.deallocate() + _buffer = new._buffer + startSlot = .zero + } +} - let segmentStart = self.slot(forOffset: start) - let segmentEnd = segmentStart < endSlot ? endSlot : limSlot - let count = Swift.min(maximumCount ?? Int.max, segmentEnd.position - segmentStart.position) - let result = try body(UnsafeBufferPointer(start: ptr(at: segmentStart), count: count)) - return (start + count, result) +// MARK: Iteration + +extension _UnsafeDequeHandle where Element: ~Copyable { + @inlinable + internal func startBorrowingIteration() -> _DequeBorrowingIterator { + .init(_for: self, startOffset: 0) + } + + @inlinable + internal func startBorrowingIteration(from start: Int) -> _DequeBorrowingIterator { + precondition(start >= 0 && start <= count) + return .init(_for: self, startOffset: start) } } // MARK: Replacement -extension Deque._UnsafeHandle { +extension _UnsafeDequeHandle { /// Replace the elements in `range` with `newElements`. The deque's count must /// not change as a result of calling this function. /// /// This function does not validate its input arguments in release builds. Nor /// does it ensure that the storage buffer is uniquely referenced. @inlinable - internal func uncheckedReplaceInPlace( + internal mutating func uncheckedReplaceInPlace( inOffsets range: Range, with newElements: C ) where C.Element == Element { - assertMutable() assert(range.upperBound <= count) assert(newElements.count == range.count) guard !range.isEmpty else { return } @@ -402,28 +420,28 @@ extension Deque._UnsafeHandle { // MARK: Appending -extension Deque._UnsafeHandle { +extension _UnsafeDequeHandle where Element: ~Copyable { /// Append `element` to this buffer. The buffer must have enough free capacity /// to insert one new element. /// /// This function does not validate its input arguments in release builds. Nor /// does it ensure that the storage buffer is uniquely referenced. @inlinable - internal func uncheckedAppend(_ element: Element) { - assertMutable() + internal mutating func uncheckedAppend(_ element: consuming Element) { assert(count < capacity) - ptr(at: endSlot).initialize(to: element) + mutablePtr(at: endSlot).initialize(to: element) count += 1 } +} +extension _UnsafeDequeHandle { /// Append the contents of `source` to this buffer. The buffer must have /// enough free capacity to insert the new elements. /// /// This function does not validate its input arguments in release builds. Nor /// does it ensure that the storage buffer is uniquely referenced. @inlinable - internal func uncheckedAppend(contentsOf source: UnsafeBufferPointer) { - assertMutable() + internal mutating func uncheckedAppend(contentsOf source: UnsafeBufferPointer) { assert(count + source.count <= capacity) guard source.count > 0 else { return } let c = self.count @@ -435,25 +453,25 @@ extension Deque._UnsafeHandle { // MARK: Prepending -extension Deque._UnsafeHandle { +extension _UnsafeDequeHandle where Element: ~Copyable { @inlinable - internal func uncheckedPrepend(_ element: Element) { - assertMutable() + internal mutating func uncheckedPrepend(_ element: consuming Element) { assert(count < capacity) let slot = self.slot(before: startSlot) - ptr(at: slot).initialize(to: element) + mutablePtr(at: slot).initialize(to: element) startSlot = slot count += 1 } +} +extension _UnsafeDequeHandle { /// Prepend the contents of `source` to this buffer. The buffer must have /// enough free capacity to insert the new elements. /// /// This function does not validate its input arguments in release builds. Nor /// does it ensure that the storage buffer is uniquely referenced. @inlinable - internal func uncheckedPrepend(contentsOf source: UnsafeBufferPointer) { - assertMutable() + internal mutating func uncheckedPrepend(contentsOf source: UnsafeBufferPointer) { assert(count + source.count <= capacity) guard source.count > 0 else { return } let oldStart = startSlot @@ -461,52 +479,29 @@ extension Deque._UnsafeHandle { startSlot = newStart count += source.count - let gap = mutableWrappedBuffer(between: newStart, and: oldStart) + let gap = mutableSegments(between: newStart, and: oldStart) gap.initialize(from: source) } } -// MARK: Insertion - -extension Deque._UnsafeHandle { - /// Insert all elements from `newElements` into this deque, starting at - /// `offset`. - /// - /// This function does not validate its input arguments in release builds. Nor - /// does it ensure that the storage buffer is uniquely referenced. - /// - /// - Parameter newElements: The elements to insert. - /// - Parameter newCount: Must be equal to `newElements.count`. Used to - /// prevent calling `count` more than once. - /// - Parameter offset: The desired offset from the start at which to place - /// the first element. - @inlinable - internal func uncheckedInsert( - contentsOf newElements: __owned C, - count newCount: Int, - atOffset offset: Int - ) where C.Element == Element { - assertMutable() - assert(offset <= count) - assert(newElements.count == newCount) - guard newCount > 0 else { return } - let gap = openGap(ofSize: newCount, atOffset: offset) - gap.initialize(from: newElements) - } +// MARK: Opening and Closing Gaps +extension _UnsafeDequeHandle where Element: ~Copyable { @inlinable - internal func mutableWrappedBuffer( - between start: Slot, - and end: Slot - ) -> _UnsafeMutableWrappedBuffer { - assert(start.position <= capacity) - assert(end.position <= capacity) - if start < end { - return .init(start: ptr(at: start), count: end.position - start.position) - } - return .init( - first: ptr(at: start), count: capacity - start.position, - second: ptr(at: .zero), count: end.position) + @inline(__always) + @discardableResult + internal mutating func move( + from source: Slot, + to target: Slot, + count: Int + ) -> (source: Slot, target: Slot) { + assert(count >= 0) + assert(source.position + count <= self.capacity) + assert(target.position + count <= self.capacity) + guard count > 0 else { return (source, target) } + mutablePtr(at: target) + .moveInitialize(from: mutablePtr(at: source), count: count) + return (slot(source, offsetBy: count), slot(target, offsetBy: count)) } /// Slide elements around so that there is a gap of uninitialized slots of @@ -520,11 +515,10 @@ extension Deque._UnsafeHandle { /// - Parameter offset: The offset from the start at which the uninitialized /// slots should start. @inlinable - internal func openGap( + internal mutating func openGap( ofSize gapSize: Int, atOffset offset: Int - ) -> _UnsafeMutableWrappedBuffer { - assertMutable() + ) -> _UnsafeMutableDequeSegments { assert(offset >= 0 && offset <= self.count) assert(self.count + gapSize <= capacity) assert(gapSize > 0) @@ -586,7 +580,7 @@ extension Deque._UnsafeHandle { move(from: gapStart, to: gapEnd, count: tailCount - gapSize - originalEnd.position) } count += gapSize - return mutableWrappedBuffer(between: gapStart, and: gapEnd.orIfZero(capacity)) + return mutableSegments(between: gapStart, and: gapEnd.orIfZero(capacity)) } // Open the gap by sliding elements to the left. @@ -642,91 +636,17 @@ extension Deque._UnsafeHandle { } startSlot = newStart count += gapSize - return mutableWrappedBuffer(between: gapStart, and: gapEnd.orIfZero(capacity)) - } -} - -// MARK: Removal - -extension Deque._UnsafeHandle { - @inlinable - internal func uncheckedRemoveFirst() -> Element { - assertMutable() - assert(count > 0) - let result = ptr(at: startSlot).move() - startSlot = slot(after: startSlot) - count -= 1 - return result - } - - @inlinable - internal func uncheckedRemoveLast() -> Element { - assertMutable() - assert(count > 0) - let slot = self.slot(forOffset: count - 1) - let result = ptr(at: slot).move() - count -= 1 - return result - } - - @inlinable - internal func uncheckedRemoveFirst(_ n: Int) { - assertMutable() - assert(count >= n) - guard n > 0 else { return } - let target = mutableSegments(forOffsets: 0 ..< n) - target.deinitialize() - startSlot = slot(startSlot, offsetBy: n) - count -= n - } - - @inlinable - internal func uncheckedRemoveLast(_ n: Int) { - assertMutable() - assert(count >= n) - guard n > 0 else { return } - let target = mutableSegments(forOffsets: count - n ..< count) - target.deinitialize() - count -= n - } - - /// Remove all elements stored in this instance, deinitializing their storage. - /// - /// This method does not ensure that the storage buffer is uniquely - /// referenced. - @inlinable - internal func uncheckedRemoveAll() { - assertMutable() - guard count > 0 else { return } - let target = mutableSegments() - target.deinitialize() - count = 0 - startSlot = .zero - } - - /// Remove all elements in `bounds`, deinitializing their storage and sliding - /// remaining elements to close the resulting gap. - /// - /// This function does not validate its input arguments in release builds. Nor - /// does it ensure that the storage buffer is uniquely referenced. - @inlinable - internal func uncheckedRemove(offsets bounds: Range) { - assertMutable() - assert(bounds.lowerBound >= 0 && bounds.upperBound <= self.count) - - // Deinitialize elements in `bounds`. - mutableSegments(forOffsets: bounds).deinitialize() - closeGap(offsets: bounds) + return mutableSegments(between: gapStart, and: gapEnd.orIfZero(capacity)) } /// Close the gap of already uninitialized elements in `bounds`, sliding - /// elements outside of the gap to eliminate it. + /// elements outside of the gap to eliminate it, and updating `count` to + /// reflect the removal. /// /// This function does not validate its input arguments in release builds. Nor /// does it ensure that the storage buffer is uniquely referenced. @inlinable - internal func closeGap(offsets bounds: Range) { - assertMutable() + internal mutating func closeGap(offsets bounds: Range) { assert(bounds.lowerBound >= 0 && bounds.upperBound <= self.count) let gapSize = bounds.count guard gapSize > 0 else { return } @@ -831,3 +751,127 @@ extension Deque._UnsafeHandle { } } } + +// MARK: Insertion + +extension _UnsafeDequeHandle where Element: ~Copyable { + @inlinable + internal mutating func uncheckedInsert( + _ newElement: consuming Element, at offset: Int + ) { + assert(count < capacity) + if offset == 0 { + uncheckedPrepend(newElement) + return + } + if offset == count { + uncheckedAppend(newElement) + return + } + let gap = openGap(ofSize: 1, atOffset: offset) + assert(gap.first.count == 1) + gap.first.baseAddress!.initialize(to: newElement) + } +} + +extension _UnsafeDequeHandle { + /// Insert all elements from `newElements` into this deque, starting at + /// `offset`. + /// + /// This function does not validate its input arguments in release builds. Nor + /// does it ensure that the storage buffer is uniquely referenced. + /// + /// - Parameter newElements: The elements to insert. + /// - Parameter newCount: Must be equal to `newElements.count`. Used to + /// prevent calling `count` more than once. + /// - Parameter offset: The desired offset from the start at which to place + /// the first element. + @inlinable + internal mutating func uncheckedInsert( + contentsOf newElements: __owned C, + count newCount: Int, + atOffset offset: Int + ) where C.Element == Element { + assert(offset <= count) + assert(newElements.count == newCount) + guard newCount > 0 else { return } + let gap = openGap(ofSize: newCount, atOffset: offset) + gap.initialize(from: newElements) + } +} + +// MARK: Removal + +extension _UnsafeDequeHandle where Element: ~Copyable { + @inlinable + internal mutating func uncheckedRemove(at offset: Int) -> Element { + let slot = self.slot(forOffset: offset) + let result = mutablePtr(at: slot).move() + closeGap(offsets: Range(uncheckedBounds: (offset, offset + 1))) + return result + } + + @inlinable + internal mutating func uncheckedRemoveFirst() -> Element { + assert(count > 0) + let result = mutablePtr(at: startSlot).move() + startSlot = slot(after: startSlot) + count -= 1 + return result + } + + @inlinable + internal mutating func uncheckedRemoveLast() -> Element { + assert(count > 0) + let slot = self.slot(forOffset: count - 1) + let result = mutablePtr(at: slot).move() + count -= 1 + return result + } + + @inlinable + internal mutating func uncheckedRemoveFirst(_ n: Int) { + assert(count >= n) + guard n > 0 else { return } + let target = mutableSegments(forOffsets: 0 ..< n) + target.deinitialize() + startSlot = slot(startSlot, offsetBy: n) + count -= n + } + + @inlinable + internal mutating func uncheckedRemoveLast(_ n: Int) { + assert(count >= n) + guard n > 0 else { return } + let target = mutableSegments(forOffsets: count - n ..< count) + target.deinitialize() + count -= n + } + + /// Remove all elements stored in this instance, deinitializing their storage. + /// + /// This method does not ensure that the storage buffer is uniquely + /// referenced. + @inlinable + internal mutating func uncheckedRemoveAll() { + guard count > 0 else { return } + let target = mutableSegments() + target.deinitialize() + count = 0 + startSlot = .zero + } + + /// Remove all elements in `bounds`, deinitializing their storage and sliding + /// remaining elements to close the resulting gap. + /// + /// This function does not validate its input arguments in release builds. Nor + /// does it ensure that the storage buffer is uniquely referenced. + @inlinable + internal mutating func uncheckedRemove(offsets bounds: Range) { + assert(bounds.lowerBound >= 0 && bounds.upperBound <= self.count) + + // Deinitialize elements in `bounds`. + mutableSegments(forOffsets: bounds).deinitialize() + closeGap(offsets: bounds) + } +} diff --git a/Sources/DequeModule/_UnsafeWrappedBuffer.swift b/Sources/DequeModule/_UnsafeDequeSegments.swift similarity index 86% rename from Sources/DequeModule/_UnsafeWrappedBuffer.swift rename to Sources/DequeModule/_UnsafeDequeSegments.swift index 1faefacc0..49d5592f9 100644 --- a/Sources/DequeModule/_UnsafeWrappedBuffer.swift +++ b/Sources/DequeModule/_UnsafeDequeSegments.swift @@ -11,11 +11,12 @@ #if !COLLECTIONS_SINGLE_MODULE import InternalCollectionsUtilities +import Span #endif @frozen @usableFromInline -internal struct _UnsafeWrappedBuffer { +internal struct _UnsafeDequeSegments { @usableFromInline internal let first: UnsafeBufferPointer @@ -54,11 +55,21 @@ internal struct _UnsafeWrappedBuffer { @inlinable internal var count: Int { first.count + (second?.count ?? 0) } + + @inlinable + internal func isIdentical(to other: Self) -> Bool { + guard self.first === other.first else { return false } + switch (self.second, other.second) { + case (nil, nil): return true + case let (a?, b?): return a === b + default: return false + } + } } @frozen @usableFromInline -internal struct _UnsafeMutableWrappedBuffer { +internal struct _UnsafeMutableDequeSegments { @usableFromInline internal let first: UnsafeMutableBufferPointer @@ -76,24 +87,6 @@ internal struct _UnsafeMutableWrappedBuffer { assert(first.count > 0 || second == nil) } - @inlinable - @inline(__always) - internal init( - _ first: UnsafeMutableBufferPointer.SubSequence, - _ second: UnsafeMutableBufferPointer? = nil - ) { - self.init(UnsafeMutableBufferPointer(rebasing: first), second) - } - - @inlinable - @inline(__always) - internal init( - _ first: UnsafeMutableBufferPointer, - _ second: UnsafeMutableBufferPointer.SubSequence - ) { - self.init(first, UnsafeMutableBufferPointer(rebasing: second)) - } - @inlinable @inline(__always) internal init( @@ -117,13 +110,33 @@ internal struct _UnsafeMutableWrappedBuffer { @inlinable @inline(__always) - internal init(mutating buffer: _UnsafeWrappedBuffer) { + internal init(mutating buffer: _UnsafeDequeSegments) { self.init(.init(mutating: buffer.first), buffer.second.map { .init(mutating: $0) }) } } -extension _UnsafeMutableWrappedBuffer { +extension _UnsafeMutableDequeSegments { + @inlinable + @inline(__always) + internal init( + _ first: UnsafeMutableBufferPointer.SubSequence, + _ second: UnsafeMutableBufferPointer? = nil + ) { + self.init(UnsafeMutableBufferPointer(rebasing: first), second) + } + + @inlinable + @inline(__always) + internal init( + _ first: UnsafeMutableBufferPointer, + _ second: UnsafeMutableBufferPointer.SubSequence + ) { + self.init(first, UnsafeMutableBufferPointer(rebasing: second)) + } +} + +extension _UnsafeMutableDequeSegments where Element: ~Copyable { @inlinable @inline(__always) internal var count: Int { first.count + (second?.count ?? 0) } @@ -135,9 +148,9 @@ extension _UnsafeMutableWrappedBuffer { return self } if n <= first.count { - return Self(first.prefix(n)) + return Self(first._extracting(first: n)) } - return Self(first, second!.prefix(n - first.count)) + return Self(first, second!._extracting(first: n - first.count)) } @inlinable @@ -147,22 +160,24 @@ extension _UnsafeMutableWrappedBuffer { return self } guard let second = second else { - return Self(first.suffix(n)) + return Self(first._extracting(last: n)) } if n <= second.count { - return Self(second.suffix(n)) + return Self(second._extracting(last: n)) } - return Self(first.suffix(n - second.count), second) + return Self(first._extracting(last: n - second.count), second) } } -extension _UnsafeMutableWrappedBuffer { +extension _UnsafeMutableDequeSegments where Element: ~Copyable { @inlinable internal func deinitialize() { first.deinitialize() second?.deinitialize() } +} +extension _UnsafeMutableDequeSegments { @inlinable @discardableResult internal func initialize( diff --git a/Sources/HashTreeCollections/HashNode/_HashNode+Storage.swift b/Sources/HashTreeCollections/HashNode/_HashNode+Storage.swift index 0aefeb848..88af96555 100644 --- a/Sources/HashTreeCollections/HashNode/_HashNode+Storage.swift +++ b/Sources/HashTreeCollections/HashNode/_HashNode+Storage.swift @@ -26,6 +26,7 @@ internal typealias _RawHashStorage = ManagedBuffer<_HashNodeHeader, _RawHashNode /// `_HashNode.Storage` subclass is to allow storage instances to properly /// clean up after themselves in their `deinit` method.) @usableFromInline +nonisolated(unsafe) internal let _emptySingleton: _RawHashStorage = _RawHashStorage.create( minimumCapacity: 0, makingHeaderWith: { _ in _HashNodeHeader(byteCapacity: 0) }) diff --git a/Sources/InternalCollectionsUtilities/CMakeLists.txt b/Sources/InternalCollectionsUtilities/CMakeLists.txt index 808e36c2e..0adb28f1c 100644 --- a/Sources/InternalCollectionsUtilities/CMakeLists.txt +++ b/Sources/InternalCollectionsUtilities/CMakeLists.txt @@ -24,12 +24,8 @@ target_sources(${module_name} PRIVATE "autogenerated/Debugging.swift" "autogenerated/Descriptions.swift" "autogenerated/RandomAccessCollection+Offsets.swift" - "autogenerated/Specialize.swift" "autogenerated/UnsafeBufferPointer+Extras.swift" "autogenerated/UnsafeMutableBufferPointer+Extras.swift" - "Compatibility/autogenerated/UnsafeMutableBufferPointer+SE-0370.swift" - "Compatibility/autogenerated/UnsafeMutablePointer+SE-0370.swift" - "Compatibility/autogenerated/UnsafeRawPointer extensions.swift" "IntegerTricks/autogenerated/FixedWidthInteger+roundUpToPowerOfTwo.swift" "IntegerTricks/autogenerated/Integer rank.swift" "IntegerTricks/autogenerated/UInt+first and last set bit.swift" diff --git a/Sources/InternalCollectionsUtilities/Compatibility/UnsafeMutableBufferPointer+SE-0370.swift.gyb b/Sources/InternalCollectionsUtilities/Compatibility/UnsafeMutableBufferPointer+SE-0370.swift.gyb deleted file mode 100644 index 8e4f6debf..000000000 --- a/Sources/InternalCollectionsUtilities/Compatibility/UnsafeMutableBufferPointer+SE-0370.swift.gyb +++ /dev/null @@ -1,424 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2022 - 2024 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 -// -//===----------------------------------------------------------------------===// - -// Note: These are adapted from SE-0370 in the Swift 5.8 Standard Library. - -%{ - from gyb_utils import * -}% -${autogenerated_warning()} - -% for modifier in visibility_levels: -${visibility_boilerplate(modifier)} -#if swift(<5.8) -extension UnsafeMutableBufferPointer { - /// Deinitializes every instance in this buffer. - /// - /// The region of memory underlying this buffer must be fully initialized. - /// After calling `deinitialize(count:)`, the memory is uninitialized, - /// but still bound to the `Element` type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Returns: A raw buffer to the same range of memory as this buffer. - /// The range of memory is still bound to `Element`. - @discardableResult - @inlinable - ${modifier} func deinitialize() -> UnsafeMutableRawBufferPointer { - guard let start = baseAddress else { return .init(start: nil, count: 0) } - start.deinitialize(count: count) - return .init(start: UnsafeMutableRawPointer(start), - count: count * MemoryLayout.stride) - } -} -#endif - -// Note: this is left unconditionally enabled because we need the SR14663 workaround. :-( -extension UnsafeMutableBufferPointer { - /// Initializes the buffer's memory with - /// every element of the source. - /// - /// Prior to calling the `initialize(fromContentsOf:)` method on a buffer, - /// the memory referenced by the buffer must be uninitialized, - /// or the `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer up to, but not including, - /// the returned index is initialized. - /// The buffer must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// buffer's `startIndex`. If `source` contains as many elements as the buffer - /// can hold, the returned index is equal to the buffer's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer - /// must not overlap. - /// - /// - Parameter source: A collection of elements to be used to - /// initialize the buffer's storage. - /// - Returns: The index one past the last element of the buffer initialized - /// by this function. - @inlinable - ${modifier} func initialize( - fromContentsOf source: C - ) -> Index - where C.Element == Element { - let count: Int? = source.withContiguousStorageIfAvailable { - guard let sourceAddress = $0.baseAddress, !$0.isEmpty else { - return 0 - } - precondition( - $0.count <= self.count, - "buffer cannot contain every element from source." - ) - baseAddress?.initialize(from: sourceAddress, count: $0.count) - return $0.count - } - if let count = count { - return startIndex.advanced(by: count) - } - - var (iterator, copied) = source._copyContents(initializing: self) - precondition( - iterator.next() == nil, - "buffer cannot contain every element from source." - ) - return startIndex.advanced(by: copied) - } -} - -#if swift(<5.8) -extension UnsafeMutableBufferPointer { - /// Moves every element of an initialized source buffer into the - /// uninitialized memory referenced by this buffer, leaving the source memory - /// uninitialized and this buffer's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, - /// the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// buffer's `startIndex`. If `source` contains as many elements as the buffer - /// can hold, the returned index is equal to the buffer's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer - /// may overlap. - /// - /// - Parameter source: A buffer containing the values to copy. The memory - /// region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer initialized - /// by this function. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func moveInitialize(fromContentsOf source: Self) -> Index { - guard let sourceAddress = source.baseAddress, !source.isEmpty else { - return startIndex - } - precondition( - source.count <= self.count, - "buffer cannot contain every element from source." - ) - baseAddress?.moveInitialize(from: sourceAddress, count: source.count) - return startIndex.advanced(by: source.count) - } - - /// Moves every element of an initialized source buffer into the - /// uninitialized memory referenced by this buffer, leaving the source memory - /// uninitialized and this buffer's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, - /// the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// buffer's `startIndex`. If `source` contains as many elements as the buffer - /// can hold, the returned index is equal to the buffer's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer - /// may overlap. - /// - /// - Parameter source: A buffer containing the values to copy. The memory - /// region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer initialized - /// by this function. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func moveInitialize(fromContentsOf source: Slice) -> Index { - return moveInitialize(fromContentsOf: Self(rebasing: source)) - } - - /// Initializes the element at `index` to the given value. - /// - /// The memory underlying the destination element must be uninitialized, - /// or `Element` must be a trivial type. After a call to `initialize(to:)`, - /// the memory underlying this element of the buffer is initialized. - /// - /// - Parameters: - /// - value: The value used to initialize the buffer element's memory. - /// - index: The index of the element to initialize - @inlinable - @_alwaysEmitIntoClient - ${modifier} func initializeElement(at index: Index, to value: Element) { - assert(startIndex <= index && index < endIndex) - let p = baseAddress.unsafelyUnwrapped.advanced(by: index) - p.initialize(to: value) - } - - /// Retrieves and returns the element at `index`, - /// leaving that element's underlying memory uninitialized. - /// - /// The memory underlying the element at `index` must be initialized. - /// After calling `moveElement(from:)`, the memory underlying this element - /// of the buffer is uninitialized, and still bound to type `Element`. - /// - /// - Parameters: - /// - index: The index of the buffer element to retrieve and deinitialize. - /// - Returns: The instance referenced by this index in this buffer. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func moveElement(from index: Index) -> Element { - assert(startIndex <= index && index < endIndex) - return baseAddress.unsafelyUnwrapped.advanced(by: index).move() - } - - /// Deinitializes the memory underlying the element at `index`. - /// - /// The memory underlying the element at `index` must be initialized. - /// After calling `deinitializeElement()`, the memory underlying this element - /// of the buffer is uninitialized, and still bound to type `Element`. - /// - /// - Parameters: - /// - index: The index of the buffer element to deinitialize. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func deinitializeElement(at index: Index) { - assert(startIndex <= index && index < endIndex) - let p = baseAddress.unsafelyUnwrapped.advanced(by: index) - p.deinitialize(count: 1) - } -} -#endif - -#if swift(<5.8) -extension Slice { - /// Initializes the buffer slice's memory with with - /// every element of the source. - /// - /// Prior to calling the `initialize(fromContentsOf:)` method - /// on a buffer slice, the memory it references must be uninitialized, - /// or the `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer slice up to, but not including, - /// the returned index is initialized. - /// The buffer slice must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the index of the next uninitialized element - /// in the buffer slice, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to - /// the buffer slice's `startIndex`. If `source` contains as many elements - /// as the buffer slice can hold, the returned index is equal to - /// to the slice's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer slice - /// must not overlap. - /// - /// - Parameter source: A collection of elements to be used to - /// initialize the buffer slice's storage. - /// - Returns: The index one past the last element of the buffer slice - /// initialized by this function. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func initialize( - fromContentsOf source: C - ) -> Index where Base == UnsafeMutableBufferPointer { - let buffer = Base(rebasing: self) - let index = buffer.initialize(fromContentsOf: source) - let distance = buffer.distance(from: buffer.startIndex, to: index) - return startIndex.advanced(by: distance) - } - - /// Moves every element of an initialized source buffer into the - /// uninitialized memory referenced by this buffer slice, leaving the - /// source memory uninitialized and this buffer slice's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a - /// buffer slice, the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer slice up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer slice must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer slice, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// slice's `startIndex`. If `source` contains as many elements as the slice - /// can hold, the returned index is equal to the slice's `endIndex`. - /// - /// - Note: The memory regions referenced by `source` and this buffer slice - /// may overlap. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Parameter source: A buffer containing the values to copy. - /// The memory region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer slice - /// initialized by this function. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func moveInitialize( - fromContentsOf source: UnsafeMutableBufferPointer - ) -> Index where Base == UnsafeMutableBufferPointer { - let buffer = Base(rebasing: self) - let index = buffer.moveInitialize(fromContentsOf: source) - let distance = buffer.distance(from: buffer.startIndex, to: index) - return startIndex.advanced(by: distance) - } - - /// Moves every element of an initialized source buffer slice into the - /// uninitialized memory referenced by this buffer slice, leaving the - /// source memory uninitialized and this buffer slice's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a - /// buffer slice, the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer slice up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer slice must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer slice, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// slice's `startIndex`. If `source` contains as many elements as the slice - /// can hold, the returned index is equal to the slice's `endIndex`. - /// - /// - Note: The memory regions referenced by `source` and this buffer slice - /// may overlap. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Parameter source: A buffer slice containing the values to copy. - /// The memory region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer slice - /// initialized by this function. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func moveInitialize( - fromContentsOf source: Slice> - ) -> Index where Base == UnsafeMutableBufferPointer { - let buffer = Base(rebasing: self) - let index = buffer.moveInitialize(fromContentsOf: source) - let distance = buffer.distance(from: buffer.startIndex, to: index) - return startIndex.advanced(by: distance) - } - - /// Deinitializes every instance in this buffer slice. - /// - /// The region of memory underlying this buffer slice must be fully - /// initialized. After calling `deinitialize(count:)`, the memory - /// is uninitialized, but still bound to the `Element` type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Returns: A raw buffer to the same range of memory as this buffer. - /// The range of memory is still bound to `Element`. - @discardableResult - @inlinable - @_alwaysEmitIntoClient - ${modifier} func deinitialize() -> UnsafeMutableRawBufferPointer - where Base == UnsafeMutableBufferPointer { - Base(rebasing: self).deinitialize() - } - - /// Initializes the element at `index` to the given value. - /// - /// The memory underlying the destination element must be uninitialized, - /// or `Element` must be a trivial type. After a call to `initialize(to:)`, - /// the memory underlying this element of the buffer slice is initialized. - /// - /// - Parameters: - /// - value: The value used to initialize the buffer element's memory. - /// - index: The index of the element to initialize - @inlinable - @_alwaysEmitIntoClient - ${modifier} func initializeElement(at index: Int, to value: Element) - where Base == UnsafeMutableBufferPointer { - assert(startIndex <= index && index < endIndex) - base.baseAddress.unsafelyUnwrapped.advanced(by: index).initialize(to: value) - } -} -#endif - -#if swift(<5.8) -extension UnsafeMutableBufferPointer { - /// Updates every element of this buffer's initialized memory. - /// - /// The buffer’s memory must be initialized or its `Element` type - /// must be a trivial type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Parameters: - /// - repeatedValue: The value used when updating this pointer's memory. - @_alwaysEmitIntoClient - ${modifier} func update(repeating repeatedValue: Element) { - guard let dstBase = baseAddress else { return } - dstBase.update(repeating: repeatedValue, count: count) - } -} -#endif - -#if swift(<5.8) -extension Slice { - /// Updates every element of this buffer slice's initialized memory. - /// - /// The buffer slice’s memory must be initialized or its `Element` - /// must be a trivial type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Parameters: - /// - repeatedValue: The value used when updating this pointer's memory. - @_alwaysEmitIntoClient - ${modifier} func update(repeating repeatedValue: Element) - where Base == UnsafeMutableBufferPointer { - Base(rebasing: self).update(repeating: repeatedValue) - } -} -#endif -% end -${visibility_boilerplate("end")} diff --git a/Sources/InternalCollectionsUtilities/Compatibility/UnsafeMutablePointer+SE-0370.swift.gyb b/Sources/InternalCollectionsUtilities/Compatibility/UnsafeMutablePointer+SE-0370.swift.gyb deleted file mode 100644 index d95199cf7..000000000 --- a/Sources/InternalCollectionsUtilities/Compatibility/UnsafeMutablePointer+SE-0370.swift.gyb +++ /dev/null @@ -1,43 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2022 - 2024 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 -// -//===----------------------------------------------------------------------===// - -%{ - from gyb_utils import * -}% -${autogenerated_warning()} - -% for modifier in visibility_levels: -${visibility_boilerplate(modifier)} -#if swift(<5.8) -extension UnsafeMutablePointer { - /// Update this pointer's initialized memory with the specified number of - /// consecutive copies of the given value. - /// - /// The region of memory starting at this pointer and covering `count` - /// instances of the pointer's `Pointee` type must be initialized or - /// `Pointee` must be a trivial type. After calling - /// `update(repeating:count:)`, the region is initialized. - /// - /// - Parameters: - /// - repeatedValue: The value used when updating this pointer's memory. - /// - count: The number of consecutive elements to update. - /// `count` must not be negative. - @_alwaysEmitIntoClient - ${modifier} func update(repeating repeatedValue: Pointee, count: Int) { - assert(count >= 0, "UnsafeMutablePointer.update(repeating:count:) with negative count") - for i in 0 ..< count { - self[i] = repeatedValue - } - } -} -#endif -% end -${visibility_boilerplate("end")} diff --git a/Sources/InternalCollectionsUtilities/Compatibility/UnsafeRawPointer extensions.swift.gyb b/Sources/InternalCollectionsUtilities/Compatibility/UnsafeRawPointer extensions.swift.gyb deleted file mode 100644 index 68c2eedcd..000000000 --- a/Sources/InternalCollectionsUtilities/Compatibility/UnsafeRawPointer extensions.swift.gyb +++ /dev/null @@ -1,89 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2022 - 2024 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 -// -//===----------------------------------------------------------------------===// - -%{ - from gyb_utils import * -}% -${autogenerated_warning()} - -% for modifier in visibility_levels: -${visibility_boilerplate(modifier)} -#if compiler(<5.7) || (os(macOS) && compiler(<5.8)) // SE-0334 -extension UnsafeRawPointer { - /// Obtain the next pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func alignedUp(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = (UInt(bitPattern: self) &+ mask) & ~mask - return Self(bitPattern: bits)! - } - - /// Obtain the preceding pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func alignedDown(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = UInt(bitPattern: self) & ~mask - return Self(bitPattern: bits)! - } -} - -extension UnsafeMutableRawPointer { - /// Obtain the next pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func alignedUp(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = (UInt(bitPattern: self) &+ mask) & ~mask - return Self(bitPattern: bits)! - } - - /// Obtain the preceding pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - ${modifier} func alignedDown(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = UInt(bitPattern: self) & ~mask - return Self(bitPattern: bits)! - } -} -#endif -% end -${visibility_boilerplate("end")} diff --git a/Sources/InternalCollectionsUtilities/Compatibility/autogenerated/UnsafeMutableBufferPointer+SE-0370.swift b/Sources/InternalCollectionsUtilities/Compatibility/autogenerated/UnsafeMutableBufferPointer+SE-0370.swift deleted file mode 100644 index 9068f8b91..000000000 --- a/Sources/InternalCollectionsUtilities/Compatibility/autogenerated/UnsafeMutableBufferPointer+SE-0370.swift +++ /dev/null @@ -1,832 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2022 - 2024 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 -// -//===----------------------------------------------------------------------===// - -// Note: These are adapted from SE-0370 in the Swift 5.8 Standard Library. - - -// ############################################################################# -// # # -// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # -// # # -// ############################################################################# - - - -// In single module mode, we need these declarations to be internal, -// but in regular builds we want them to be public. Unfortunately -// the current best way to do this is to duplicate all definitions. -#if COLLECTIONS_SINGLE_MODULE -#if swift(<5.8) -extension UnsafeMutableBufferPointer { - /// Deinitializes every instance in this buffer. - /// - /// The region of memory underlying this buffer must be fully initialized. - /// After calling `deinitialize(count:)`, the memory is uninitialized, - /// but still bound to the `Element` type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Returns: A raw buffer to the same range of memory as this buffer. - /// The range of memory is still bound to `Element`. - @discardableResult - @inlinable - internal func deinitialize() -> UnsafeMutableRawBufferPointer { - guard let start = baseAddress else { return .init(start: nil, count: 0) } - start.deinitialize(count: count) - return .init(start: UnsafeMutableRawPointer(start), - count: count * MemoryLayout.stride) - } -} -#endif - -// Note: this is left unconditionally enabled because we need the SR14663 workaround. :-( -extension UnsafeMutableBufferPointer { - /// Initializes the buffer's memory with - /// every element of the source. - /// - /// Prior to calling the `initialize(fromContentsOf:)` method on a buffer, - /// the memory referenced by the buffer must be uninitialized, - /// or the `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer up to, but not including, - /// the returned index is initialized. - /// The buffer must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// buffer's `startIndex`. If `source` contains as many elements as the buffer - /// can hold, the returned index is equal to the buffer's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer - /// must not overlap. - /// - /// - Parameter source: A collection of elements to be used to - /// initialize the buffer's storage. - /// - Returns: The index one past the last element of the buffer initialized - /// by this function. - @inlinable - internal func initialize( - fromContentsOf source: C - ) -> Index - where C.Element == Element { - let count: Int? = source.withContiguousStorageIfAvailable { - guard let sourceAddress = $0.baseAddress, !$0.isEmpty else { - return 0 - } - precondition( - $0.count <= self.count, - "buffer cannot contain every element from source." - ) - baseAddress?.initialize(from: sourceAddress, count: $0.count) - return $0.count - } - if let count = count { - return startIndex.advanced(by: count) - } - - var (iterator, copied) = source._copyContents(initializing: self) - precondition( - iterator.next() == nil, - "buffer cannot contain every element from source." - ) - return startIndex.advanced(by: copied) - } -} - -#if swift(<5.8) -extension UnsafeMutableBufferPointer { - /// Moves every element of an initialized source buffer into the - /// uninitialized memory referenced by this buffer, leaving the source memory - /// uninitialized and this buffer's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, - /// the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// buffer's `startIndex`. If `source` contains as many elements as the buffer - /// can hold, the returned index is equal to the buffer's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer - /// may overlap. - /// - /// - Parameter source: A buffer containing the values to copy. The memory - /// region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer initialized - /// by this function. - @inlinable - @_alwaysEmitIntoClient - internal func moveInitialize(fromContentsOf source: Self) -> Index { - guard let sourceAddress = source.baseAddress, !source.isEmpty else { - return startIndex - } - precondition( - source.count <= self.count, - "buffer cannot contain every element from source." - ) - baseAddress?.moveInitialize(from: sourceAddress, count: source.count) - return startIndex.advanced(by: source.count) - } - - /// Moves every element of an initialized source buffer into the - /// uninitialized memory referenced by this buffer, leaving the source memory - /// uninitialized and this buffer's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, - /// the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// buffer's `startIndex`. If `source` contains as many elements as the buffer - /// can hold, the returned index is equal to the buffer's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer - /// may overlap. - /// - /// - Parameter source: A buffer containing the values to copy. The memory - /// region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer initialized - /// by this function. - @inlinable - @_alwaysEmitIntoClient - internal func moveInitialize(fromContentsOf source: Slice) -> Index { - return moveInitialize(fromContentsOf: Self(rebasing: source)) - } - - /// Initializes the element at `index` to the given value. - /// - /// The memory underlying the destination element must be uninitialized, - /// or `Element` must be a trivial type. After a call to `initialize(to:)`, - /// the memory underlying this element of the buffer is initialized. - /// - /// - Parameters: - /// - value: The value used to initialize the buffer element's memory. - /// - index: The index of the element to initialize - @inlinable - @_alwaysEmitIntoClient - internal func initializeElement(at index: Index, to value: Element) { - assert(startIndex <= index && index < endIndex) - let p = baseAddress.unsafelyUnwrapped.advanced(by: index) - p.initialize(to: value) - } - - /// Retrieves and returns the element at `index`, - /// leaving that element's underlying memory uninitialized. - /// - /// The memory underlying the element at `index` must be initialized. - /// After calling `moveElement(from:)`, the memory underlying this element - /// of the buffer is uninitialized, and still bound to type `Element`. - /// - /// - Parameters: - /// - index: The index of the buffer element to retrieve and deinitialize. - /// - Returns: The instance referenced by this index in this buffer. - @inlinable - @_alwaysEmitIntoClient - internal func moveElement(from index: Index) -> Element { - assert(startIndex <= index && index < endIndex) - return baseAddress.unsafelyUnwrapped.advanced(by: index).move() - } - - /// Deinitializes the memory underlying the element at `index`. - /// - /// The memory underlying the element at `index` must be initialized. - /// After calling `deinitializeElement()`, the memory underlying this element - /// of the buffer is uninitialized, and still bound to type `Element`. - /// - /// - Parameters: - /// - index: The index of the buffer element to deinitialize. - @inlinable - @_alwaysEmitIntoClient - internal func deinitializeElement(at index: Index) { - assert(startIndex <= index && index < endIndex) - let p = baseAddress.unsafelyUnwrapped.advanced(by: index) - p.deinitialize(count: 1) - } -} -#endif - -#if swift(<5.8) -extension Slice { - /// Initializes the buffer slice's memory with with - /// every element of the source. - /// - /// Prior to calling the `initialize(fromContentsOf:)` method - /// on a buffer slice, the memory it references must be uninitialized, - /// or the `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer slice up to, but not including, - /// the returned index is initialized. - /// The buffer slice must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the index of the next uninitialized element - /// in the buffer slice, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to - /// the buffer slice's `startIndex`. If `source` contains as many elements - /// as the buffer slice can hold, the returned index is equal to - /// to the slice's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer slice - /// must not overlap. - /// - /// - Parameter source: A collection of elements to be used to - /// initialize the buffer slice's storage. - /// - Returns: The index one past the last element of the buffer slice - /// initialized by this function. - @inlinable - @_alwaysEmitIntoClient - internal func initialize( - fromContentsOf source: C - ) -> Index where Base == UnsafeMutableBufferPointer { - let buffer = Base(rebasing: self) - let index = buffer.initialize(fromContentsOf: source) - let distance = buffer.distance(from: buffer.startIndex, to: index) - return startIndex.advanced(by: distance) - } - - /// Moves every element of an initialized source buffer into the - /// uninitialized memory referenced by this buffer slice, leaving the - /// source memory uninitialized and this buffer slice's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a - /// buffer slice, the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer slice up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer slice must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer slice, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// slice's `startIndex`. If `source` contains as many elements as the slice - /// can hold, the returned index is equal to the slice's `endIndex`. - /// - /// - Note: The memory regions referenced by `source` and this buffer slice - /// may overlap. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Parameter source: A buffer containing the values to copy. - /// The memory region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer slice - /// initialized by this function. - @inlinable - @_alwaysEmitIntoClient - internal func moveInitialize( - fromContentsOf source: UnsafeMutableBufferPointer - ) -> Index where Base == UnsafeMutableBufferPointer { - let buffer = Base(rebasing: self) - let index = buffer.moveInitialize(fromContentsOf: source) - let distance = buffer.distance(from: buffer.startIndex, to: index) - return startIndex.advanced(by: distance) - } - - /// Moves every element of an initialized source buffer slice into the - /// uninitialized memory referenced by this buffer slice, leaving the - /// source memory uninitialized and this buffer slice's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a - /// buffer slice, the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer slice up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer slice must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer slice, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// slice's `startIndex`. If `source` contains as many elements as the slice - /// can hold, the returned index is equal to the slice's `endIndex`. - /// - /// - Note: The memory regions referenced by `source` and this buffer slice - /// may overlap. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Parameter source: A buffer slice containing the values to copy. - /// The memory region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer slice - /// initialized by this function. - @inlinable - @_alwaysEmitIntoClient - internal func moveInitialize( - fromContentsOf source: Slice> - ) -> Index where Base == UnsafeMutableBufferPointer { - let buffer = Base(rebasing: self) - let index = buffer.moveInitialize(fromContentsOf: source) - let distance = buffer.distance(from: buffer.startIndex, to: index) - return startIndex.advanced(by: distance) - } - - /// Deinitializes every instance in this buffer slice. - /// - /// The region of memory underlying this buffer slice must be fully - /// initialized. After calling `deinitialize(count:)`, the memory - /// is uninitialized, but still bound to the `Element` type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Returns: A raw buffer to the same range of memory as this buffer. - /// The range of memory is still bound to `Element`. - @discardableResult - @inlinable - @_alwaysEmitIntoClient - internal func deinitialize() -> UnsafeMutableRawBufferPointer - where Base == UnsafeMutableBufferPointer { - Base(rebasing: self).deinitialize() - } - - /// Initializes the element at `index` to the given value. - /// - /// The memory underlying the destination element must be uninitialized, - /// or `Element` must be a trivial type. After a call to `initialize(to:)`, - /// the memory underlying this element of the buffer slice is initialized. - /// - /// - Parameters: - /// - value: The value used to initialize the buffer element's memory. - /// - index: The index of the element to initialize - @inlinable - @_alwaysEmitIntoClient - internal func initializeElement(at index: Int, to value: Element) - where Base == UnsafeMutableBufferPointer { - assert(startIndex <= index && index < endIndex) - base.baseAddress.unsafelyUnwrapped.advanced(by: index).initialize(to: value) - } -} -#endif - -#if swift(<5.8) -extension UnsafeMutableBufferPointer { - /// Updates every element of this buffer's initialized memory. - /// - /// The buffer’s memory must be initialized or its `Element` type - /// must be a trivial type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Parameters: - /// - repeatedValue: The value used when updating this pointer's memory. - @_alwaysEmitIntoClient - internal func update(repeating repeatedValue: Element) { - guard let dstBase = baseAddress else { return } - dstBase.update(repeating: repeatedValue, count: count) - } -} -#endif - -#if swift(<5.8) -extension Slice { - /// Updates every element of this buffer slice's initialized memory. - /// - /// The buffer slice’s memory must be initialized or its `Element` - /// must be a trivial type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Parameters: - /// - repeatedValue: The value used when updating this pointer's memory. - @_alwaysEmitIntoClient - internal func update(repeating repeatedValue: Element) - where Base == UnsafeMutableBufferPointer { - Base(rebasing: self).update(repeating: repeatedValue) - } -} -#endif -#else // !COLLECTIONS_SINGLE_MODULE -#if swift(<5.8) -extension UnsafeMutableBufferPointer { - /// Deinitializes every instance in this buffer. - /// - /// The region of memory underlying this buffer must be fully initialized. - /// After calling `deinitialize(count:)`, the memory is uninitialized, - /// but still bound to the `Element` type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Returns: A raw buffer to the same range of memory as this buffer. - /// The range of memory is still bound to `Element`. - @discardableResult - @inlinable - public func deinitialize() -> UnsafeMutableRawBufferPointer { - guard let start = baseAddress else { return .init(start: nil, count: 0) } - start.deinitialize(count: count) - return .init(start: UnsafeMutableRawPointer(start), - count: count * MemoryLayout.stride) - } -} -#endif - -// Note: this is left unconditionally enabled because we need the SR14663 workaround. :-( -extension UnsafeMutableBufferPointer { - /// Initializes the buffer's memory with - /// every element of the source. - /// - /// Prior to calling the `initialize(fromContentsOf:)` method on a buffer, - /// the memory referenced by the buffer must be uninitialized, - /// or the `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer up to, but not including, - /// the returned index is initialized. - /// The buffer must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// buffer's `startIndex`. If `source` contains as many elements as the buffer - /// can hold, the returned index is equal to the buffer's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer - /// must not overlap. - /// - /// - Parameter source: A collection of elements to be used to - /// initialize the buffer's storage. - /// - Returns: The index one past the last element of the buffer initialized - /// by this function. - @inlinable - public func initialize( - fromContentsOf source: C - ) -> Index - where C.Element == Element { - let count: Int? = source.withContiguousStorageIfAvailable { - guard let sourceAddress = $0.baseAddress, !$0.isEmpty else { - return 0 - } - precondition( - $0.count <= self.count, - "buffer cannot contain every element from source." - ) - baseAddress?.initialize(from: sourceAddress, count: $0.count) - return $0.count - } - if let count = count { - return startIndex.advanced(by: count) - } - - var (iterator, copied) = source._copyContents(initializing: self) - precondition( - iterator.next() == nil, - "buffer cannot contain every element from source." - ) - return startIndex.advanced(by: copied) - } -} - -#if swift(<5.8) -extension UnsafeMutableBufferPointer { - /// Moves every element of an initialized source buffer into the - /// uninitialized memory referenced by this buffer, leaving the source memory - /// uninitialized and this buffer's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, - /// the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// buffer's `startIndex`. If `source` contains as many elements as the buffer - /// can hold, the returned index is equal to the buffer's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer - /// may overlap. - /// - /// - Parameter source: A buffer containing the values to copy. The memory - /// region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer initialized - /// by this function. - @inlinable - @_alwaysEmitIntoClient - public func moveInitialize(fromContentsOf source: Self) -> Index { - guard let sourceAddress = source.baseAddress, !source.isEmpty else { - return startIndex - } - precondition( - source.count <= self.count, - "buffer cannot contain every element from source." - ) - baseAddress?.moveInitialize(from: sourceAddress, count: source.count) - return startIndex.advanced(by: source.count) - } - - /// Moves every element of an initialized source buffer into the - /// uninitialized memory referenced by this buffer, leaving the source memory - /// uninitialized and this buffer's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, - /// the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// buffer's `startIndex`. If `source` contains as many elements as the buffer - /// can hold, the returned index is equal to the buffer's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer - /// may overlap. - /// - /// - Parameter source: A buffer containing the values to copy. The memory - /// region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer initialized - /// by this function. - @inlinable - @_alwaysEmitIntoClient - public func moveInitialize(fromContentsOf source: Slice) -> Index { - return moveInitialize(fromContentsOf: Self(rebasing: source)) - } - - /// Initializes the element at `index` to the given value. - /// - /// The memory underlying the destination element must be uninitialized, - /// or `Element` must be a trivial type. After a call to `initialize(to:)`, - /// the memory underlying this element of the buffer is initialized. - /// - /// - Parameters: - /// - value: The value used to initialize the buffer element's memory. - /// - index: The index of the element to initialize - @inlinable - @_alwaysEmitIntoClient - public func initializeElement(at index: Index, to value: Element) { - assert(startIndex <= index && index < endIndex) - let p = baseAddress.unsafelyUnwrapped.advanced(by: index) - p.initialize(to: value) - } - - /// Retrieves and returns the element at `index`, - /// leaving that element's underlying memory uninitialized. - /// - /// The memory underlying the element at `index` must be initialized. - /// After calling `moveElement(from:)`, the memory underlying this element - /// of the buffer is uninitialized, and still bound to type `Element`. - /// - /// - Parameters: - /// - index: The index of the buffer element to retrieve and deinitialize. - /// - Returns: The instance referenced by this index in this buffer. - @inlinable - @_alwaysEmitIntoClient - public func moveElement(from index: Index) -> Element { - assert(startIndex <= index && index < endIndex) - return baseAddress.unsafelyUnwrapped.advanced(by: index).move() - } - - /// Deinitializes the memory underlying the element at `index`. - /// - /// The memory underlying the element at `index` must be initialized. - /// After calling `deinitializeElement()`, the memory underlying this element - /// of the buffer is uninitialized, and still bound to type `Element`. - /// - /// - Parameters: - /// - index: The index of the buffer element to deinitialize. - @inlinable - @_alwaysEmitIntoClient - public func deinitializeElement(at index: Index) { - assert(startIndex <= index && index < endIndex) - let p = baseAddress.unsafelyUnwrapped.advanced(by: index) - p.deinitialize(count: 1) - } -} -#endif - -#if swift(<5.8) -extension Slice { - /// Initializes the buffer slice's memory with with - /// every element of the source. - /// - /// Prior to calling the `initialize(fromContentsOf:)` method - /// on a buffer slice, the memory it references must be uninitialized, - /// or the `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer slice up to, but not including, - /// the returned index is initialized. - /// The buffer slice must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the index of the next uninitialized element - /// in the buffer slice, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to - /// the buffer slice's `startIndex`. If `source` contains as many elements - /// as the buffer slice can hold, the returned index is equal to - /// to the slice's `endIndex`. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Note: The memory regions referenced by `source` and this buffer slice - /// must not overlap. - /// - /// - Parameter source: A collection of elements to be used to - /// initialize the buffer slice's storage. - /// - Returns: The index one past the last element of the buffer slice - /// initialized by this function. - @inlinable - @_alwaysEmitIntoClient - public func initialize( - fromContentsOf source: C - ) -> Index where Base == UnsafeMutableBufferPointer { - let buffer = Base(rebasing: self) - let index = buffer.initialize(fromContentsOf: source) - let distance = buffer.distance(from: buffer.startIndex, to: index) - return startIndex.advanced(by: distance) - } - - /// Moves every element of an initialized source buffer into the - /// uninitialized memory referenced by this buffer slice, leaving the - /// source memory uninitialized and this buffer slice's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a - /// buffer slice, the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer slice up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer slice must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer slice, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// slice's `startIndex`. If `source` contains as many elements as the slice - /// can hold, the returned index is equal to the slice's `endIndex`. - /// - /// - Note: The memory regions referenced by `source` and this buffer slice - /// may overlap. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Parameter source: A buffer containing the values to copy. - /// The memory region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer slice - /// initialized by this function. - @inlinable - @_alwaysEmitIntoClient - public func moveInitialize( - fromContentsOf source: UnsafeMutableBufferPointer - ) -> Index where Base == UnsafeMutableBufferPointer { - let buffer = Base(rebasing: self) - let index = buffer.moveInitialize(fromContentsOf: source) - let distance = buffer.distance(from: buffer.startIndex, to: index) - return startIndex.advanced(by: distance) - } - - /// Moves every element of an initialized source buffer slice into the - /// uninitialized memory referenced by this buffer slice, leaving the - /// source memory uninitialized and this buffer slice's memory initialized. - /// - /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a - /// buffer slice, the memory it references must be uninitialized, - /// or its `Element` type must be a trivial type. After the call, - /// the memory referenced by the buffer slice up to, but not including, - /// the returned index is initialized. The memory referenced by - /// `source` is uninitialized after the function returns. - /// The buffer slice must reference enough memory to accommodate - /// `source.count` elements. - /// - /// The returned index is the position of the next uninitialized element - /// in the buffer slice, one past the index of the last element written. - /// If `source` contains no elements, the returned index is equal to the - /// slice's `startIndex`. If `source` contains as many elements as the slice - /// can hold, the returned index is equal to the slice's `endIndex`. - /// - /// - Note: The memory regions referenced by `source` and this buffer slice - /// may overlap. - /// - /// - Precondition: `self.count` >= `source.count` - /// - /// - Parameter source: A buffer slice containing the values to copy. - /// The memory region underlying `source` must be initialized. - /// - Returns: The index one past the last element of the buffer slice - /// initialized by this function. - @inlinable - @_alwaysEmitIntoClient - public func moveInitialize( - fromContentsOf source: Slice> - ) -> Index where Base == UnsafeMutableBufferPointer { - let buffer = Base(rebasing: self) - let index = buffer.moveInitialize(fromContentsOf: source) - let distance = buffer.distance(from: buffer.startIndex, to: index) - return startIndex.advanced(by: distance) - } - - /// Deinitializes every instance in this buffer slice. - /// - /// The region of memory underlying this buffer slice must be fully - /// initialized. After calling `deinitialize(count:)`, the memory - /// is uninitialized, but still bound to the `Element` type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Returns: A raw buffer to the same range of memory as this buffer. - /// The range of memory is still bound to `Element`. - @discardableResult - @inlinable - @_alwaysEmitIntoClient - public func deinitialize() -> UnsafeMutableRawBufferPointer - where Base == UnsafeMutableBufferPointer { - Base(rebasing: self).deinitialize() - } - - /// Initializes the element at `index` to the given value. - /// - /// The memory underlying the destination element must be uninitialized, - /// or `Element` must be a trivial type. After a call to `initialize(to:)`, - /// the memory underlying this element of the buffer slice is initialized. - /// - /// - Parameters: - /// - value: The value used to initialize the buffer element's memory. - /// - index: The index of the element to initialize - @inlinable - @_alwaysEmitIntoClient - public func initializeElement(at index: Int, to value: Element) - where Base == UnsafeMutableBufferPointer { - assert(startIndex <= index && index < endIndex) - base.baseAddress.unsafelyUnwrapped.advanced(by: index).initialize(to: value) - } -} -#endif - -#if swift(<5.8) -extension UnsafeMutableBufferPointer { - /// Updates every element of this buffer's initialized memory. - /// - /// The buffer’s memory must be initialized or its `Element` type - /// must be a trivial type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Parameters: - /// - repeatedValue: The value used when updating this pointer's memory. - @_alwaysEmitIntoClient - public func update(repeating repeatedValue: Element) { - guard let dstBase = baseAddress else { return } - dstBase.update(repeating: repeatedValue, count: count) - } -} -#endif - -#if swift(<5.8) -extension Slice { - /// Updates every element of this buffer slice's initialized memory. - /// - /// The buffer slice’s memory must be initialized or its `Element` - /// must be a trivial type. - /// - /// - Note: All buffer elements must already be initialized. - /// - /// - Parameters: - /// - repeatedValue: The value used when updating this pointer's memory. - @_alwaysEmitIntoClient - public func update(repeating repeatedValue: Element) - where Base == UnsafeMutableBufferPointer { - Base(rebasing: self).update(repeating: repeatedValue) - } -} -#endif -#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/InternalCollectionsUtilities/Compatibility/autogenerated/UnsafeMutablePointer+SE-0370.swift b/Sources/InternalCollectionsUtilities/Compatibility/autogenerated/UnsafeMutablePointer+SE-0370.swift deleted file mode 100644 index 2e89d5aa0..000000000 --- a/Sources/InternalCollectionsUtilities/Compatibility/autogenerated/UnsafeMutablePointer+SE-0370.swift +++ /dev/null @@ -1,72 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2022 - 2024 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 -// -//===----------------------------------------------------------------------===// - - -// ############################################################################# -// # # -// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # -// # # -// ############################################################################# - - - -// In single module mode, we need these declarations to be internal, -// but in regular builds we want them to be public. Unfortunately -// the current best way to do this is to duplicate all definitions. -#if COLLECTIONS_SINGLE_MODULE -#if swift(<5.8) -extension UnsafeMutablePointer { - /// Update this pointer's initialized memory with the specified number of - /// consecutive copies of the given value. - /// - /// The region of memory starting at this pointer and covering `count` - /// instances of the pointer's `Pointee` type must be initialized or - /// `Pointee` must be a trivial type. After calling - /// `update(repeating:count:)`, the region is initialized. - /// - /// - Parameters: - /// - repeatedValue: The value used when updating this pointer's memory. - /// - count: The number of consecutive elements to update. - /// `count` must not be negative. - @_alwaysEmitIntoClient - internal func update(repeating repeatedValue: Pointee, count: Int) { - assert(count >= 0, "UnsafeMutablePointer.update(repeating:count:) with negative count") - for i in 0 ..< count { - self[i] = repeatedValue - } - } -} -#endif -#else // !COLLECTIONS_SINGLE_MODULE -#if swift(<5.8) -extension UnsafeMutablePointer { - /// Update this pointer's initialized memory with the specified number of - /// consecutive copies of the given value. - /// - /// The region of memory starting at this pointer and covering `count` - /// instances of the pointer's `Pointee` type must be initialized or - /// `Pointee` must be a trivial type. After calling - /// `update(repeating:count:)`, the region is initialized. - /// - /// - Parameters: - /// - repeatedValue: The value used when updating this pointer's memory. - /// - count: The number of consecutive elements to update. - /// `count` must not be negative. - @_alwaysEmitIntoClient - public func update(repeating repeatedValue: Pointee, count: Int) { - assert(count >= 0, "UnsafeMutablePointer.update(repeating:count:) with negative count") - for i in 0 ..< count { - self[i] = repeatedValue - } - } -} -#endif -#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/InternalCollectionsUtilities/Compatibility/autogenerated/UnsafeRawPointer extensions.swift b/Sources/InternalCollectionsUtilities/Compatibility/autogenerated/UnsafeRawPointer extensions.swift deleted file mode 100644 index c389967eb..000000000 --- a/Sources/InternalCollectionsUtilities/Compatibility/autogenerated/UnsafeRawPointer extensions.swift +++ /dev/null @@ -1,164 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2022 - 2024 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 -// -//===----------------------------------------------------------------------===// - - -// ############################################################################# -// # # -// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # -// # # -// ############################################################################# - - - -// In single module mode, we need these declarations to be internal, -// but in regular builds we want them to be public. Unfortunately -// the current best way to do this is to duplicate all definitions. -#if COLLECTIONS_SINGLE_MODULE -#if compiler(<5.7) || (os(macOS) && compiler(<5.8)) // SE-0334 -extension UnsafeRawPointer { - /// Obtain the next pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - internal func alignedUp(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = (UInt(bitPattern: self) &+ mask) & ~mask - return Self(bitPattern: bits)! - } - - /// Obtain the preceding pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - internal func alignedDown(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = UInt(bitPattern: self) & ~mask - return Self(bitPattern: bits)! - } -} - -extension UnsafeMutableRawPointer { - /// Obtain the next pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - internal func alignedUp(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = (UInt(bitPattern: self) &+ mask) & ~mask - return Self(bitPattern: bits)! - } - - /// Obtain the preceding pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - internal func alignedDown(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = UInt(bitPattern: self) & ~mask - return Self(bitPattern: bits)! - } -} -#endif -#else // !COLLECTIONS_SINGLE_MODULE -#if compiler(<5.7) || (os(macOS) && compiler(<5.8)) // SE-0334 -extension UnsafeRawPointer { - /// Obtain the next pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - public func alignedUp(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = (UInt(bitPattern: self) &+ mask) & ~mask - return Self(bitPattern: bits)! - } - - /// Obtain the preceding pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - public func alignedDown(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = UInt(bitPattern: self) & ~mask - return Self(bitPattern: bits)! - } -} - -extension UnsafeMutableRawPointer { - /// Obtain the next pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - public func alignedUp(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = (UInt(bitPattern: self) &+ mask) & ~mask - return Self(bitPattern: bits)! - } - - /// Obtain the preceding pointer properly aligned to store a value of type `T`. - /// - /// If `self` is properly aligned for accessing `T`, - /// this function returns `self`. - /// - /// - Parameters: - /// - type: the type to be stored at the returned address. - /// - Returns: a pointer properly aligned to store a value of type `T`. - @inlinable - @_alwaysEmitIntoClient - public func alignedDown(for type: T.Type) -> Self { - let mask = UInt(MemoryLayout.alignment) &- 1 - let bits = UInt(bitPattern: self) & ~mask - return Self(bitPattern: bits)! - } -} -#endif -#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/InternalCollectionsUtilities/Specialize.swift.gyb b/Sources/InternalCollectionsUtilities/Specialize.swift.gyb deleted file mode 100644 index c654d78cc..000000000 --- a/Sources/InternalCollectionsUtilities/Specialize.swift.gyb +++ /dev/null @@ -1,34 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2023 - 2024 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 -// -//===----------------------------------------------------------------------===// - -%{ - from gyb_utils import * -}% -${autogenerated_warning()} - -% for modifier in visibility_levels: -${visibility_boilerplate(modifier)} -/// Returns `x` as its concrete type `U`, or `nil` if `x` has a different -/// concrete type. -/// -/// This cast can be useful for dispatching to specializations of generic -/// functions. -@_transparent -@inlinable -${modifier} func _specialize(_ x: T, for: U.Type) -> U? { - // Note: this was ported from recent versions of the Swift stdlib. - guard T.self == U.self else { - return nil - } - return _identityCast(x, to: U.self) -} -% end -${visibility_boilerplate("end")} diff --git a/Sources/InternalCollectionsUtilities/UnsafeBufferPointer+Extras.swift.gyb b/Sources/InternalCollectionsUtilities/UnsafeBufferPointer+Extras.swift.gyb index 603e3047e..014c9d304 100644 --- a/Sources/InternalCollectionsUtilities/UnsafeBufferPointer+Extras.swift.gyb +++ b/Sources/InternalCollectionsUtilities/UnsafeBufferPointer+Extras.swift.gyb @@ -16,13 +16,44 @@ ${autogenerated_warning()} % for modifier in visibility_levels: ${visibility_boilerplate(modifier)} -extension UnsafeBufferPointer { +extension UnsafeBufferPointer where Element: ~Copyable { + @inlinable + @inline(__always) + ${modifier} static var _empty: Self { + .init(start: nil, count: 0) + } + @inlinable @inline(__always) ${modifier} func _ptr(at index: Int) -> UnsafePointer { assert(index >= 0 && index < count) return baseAddress.unsafelyUnwrapped + index } + + @_alwaysEmitIntoClient + ${modifier} func _extracting(unchecked bounds: Range) -> Self { + assert(bounds.lowerBound >= 0 && bounds.upperBound <= count, + "Index out of range") + guard let start = self.baseAddress else { + return Self(start: nil, count: 0) + } + return Self(start: start + bounds.lowerBound, count: bounds.count) + } + + @inlinable + ${modifier} func _extracting(first n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (0, n))) + } + + @inlinable + ${modifier} func _extracting(last n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (count - n, count))) + } } + % end ${visibility_boilerplate("end")} diff --git a/Sources/InternalCollectionsUtilities/UnsafeMutableBufferPointer+Extras.swift.gyb b/Sources/InternalCollectionsUtilities/UnsafeMutableBufferPointer+Extras.swift.gyb index c7c6ee0fb..a2f840f7d 100644 --- a/Sources/InternalCollectionsUtilities/UnsafeMutableBufferPointer+Extras.swift.gyb +++ b/Sources/InternalCollectionsUtilities/UnsafeMutableBufferPointer+Extras.swift.gyb @@ -16,9 +16,48 @@ ${autogenerated_warning()} % for modifier in visibility_levels: ${visibility_boilerplate(modifier)} +extension UnsafeMutableBufferPointer where Element: ~Copyable { + @inlinable + @inline(__always) + ${modifier} static var _empty: Self { + .init(start: nil, count: 0) + } + + @inlinable + @inline(__always) + ${modifier} func _ptr(at index: Int) -> UnsafeMutablePointer { + assert(index >= 0 && index < count) + return baseAddress.unsafelyUnwrapped + index + } + + @_alwaysEmitIntoClient + ${modifier} func _extracting(unchecked bounds: Range) -> Self { + assert(bounds.lowerBound >= 0 && bounds.upperBound <= count, + "Index out of range") + guard let start = self.baseAddress else { + return Self(start: nil, count: 0) + } + return Self(start: start + bounds.lowerBound, count: bounds.count) + } + + @inlinable + ${modifier} func _extracting(first n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (0, n))) + } + + @inlinable + ${modifier} func _extracting(last n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (count - n, count))) + } +} + extension UnsafeMutableBufferPointer { @inlinable - public func initialize(fromContentsOf source: Self) -> Index { + ${modifier} func initialize(fromContentsOf source: Self) -> Index { guard source.count > 0 else { return 0 } precondition( source.count <= self.count, @@ -30,7 +69,7 @@ extension UnsafeMutableBufferPointer { } @inlinable - public func initialize(fromContentsOf source: Slice) -> Index { + ${modifier} func initialize(fromContentsOf source: Slice) -> Index { let sourceCount = source.count guard sourceCount > 0 else { return 0 } precondition( @@ -45,7 +84,7 @@ extension UnsafeMutableBufferPointer { extension Slice { @inlinable @inline(__always) - public func initialize( + ${modifier} func initialize( fromContentsOf source: UnsafeMutableBufferPointer ) -> Index where Base == UnsafeMutableBufferPointer @@ -56,7 +95,7 @@ extension Slice { } @inlinable @inline(__always) - public func initialize( + ${modifier} func initialize( fromContentsOf source: Slice> ) -> Index where Base == UnsafeMutableBufferPointer @@ -69,7 +108,7 @@ extension Slice { extension UnsafeMutableBufferPointer { @inlinable @inline(__always) - public func initializeAll( + ${modifier} func initializeAll( fromContentsOf source: C ) where C.Element == Element { let i = self.initialize(fromContentsOf: source) @@ -77,25 +116,41 @@ extension UnsafeMutableBufferPointer { } @inlinable @inline(__always) - public func initializeAll(fromContentsOf source: Self) { - let i = self.initialize(fromContentsOf: source) - assert(i == self.endIndex) + ${modifier} func initializeAll(fromContentsOf source: UnsafeBufferPointer) { + assert(self.count == source.count) + guard source.count > 0 else { return } + self.baseAddress.unsafelyUnwrapped.initialize( + from: source.baseAddress.unsafelyUnwrapped, + count: source.count) } @inlinable @inline(__always) - public func initializeAll(fromContentsOf source: Slice) { - let i = self.initialize(fromContentsOf: source) - assert(i == self.endIndex) + ${modifier} func initializeAll(fromContentsOf source: Slice>) { + self.initializeAll(fromContentsOf: .init(rebasing: source)) } @inlinable @inline(__always) - public func moveInitializeAll(fromContentsOf source: Self) { + ${modifier} func initializeAll(fromContentsOf source: Self) { + self.initializeAll(fromContentsOf: UnsafeBufferPointer(source)) + } + + @inlinable @inline(__always) + ${modifier} func initializeAll(fromContentsOf source: Slice) { + self.initializeAll(fromContentsOf: UnsafeMutableBufferPointer(rebasing: source)) + } +} + +extension UnsafeMutableBufferPointer where Element: ~Copyable { + @inlinable @inline(__always) + ${modifier} func moveInitializeAll(fromContentsOf source: Self) { let i = self.moveInitialize(fromContentsOf: source) assert(i == self.endIndex) } +} +extension UnsafeMutableBufferPointer { @inlinable @inline(__always) - public func moveInitializeAll(fromContentsOf source: Slice) { + ${modifier} func moveInitializeAll(fromContentsOf source: Slice) { let i = self.moveInitialize(fromContentsOf: source) assert(i == self.endIndex) } @@ -103,7 +158,7 @@ extension UnsafeMutableBufferPointer { extension Slice { @inlinable @inline(__always) - public func initializeAll( + ${modifier} func initializeAll( fromContentsOf source: C ) where Base == UnsafeMutableBufferPointer { let i = self.initialize(fromContentsOf: source) @@ -111,7 +166,7 @@ extension Slice { } @inlinable @inline(__always) - public func initializeAll( + ${modifier} func initializeAll( fromContentsOf source: UnsafeMutableBufferPointer ) where Base == UnsafeMutableBufferPointer { let target = UnsafeMutableBufferPointer(rebasing: self) @@ -119,7 +174,7 @@ extension Slice { } @inlinable @inline(__always) - public func initializeAll( + ${modifier} func initializeAll( fromContentsOf source: Slice> ) where Base == UnsafeMutableBufferPointer { let target = UnsafeMutableBufferPointer(rebasing: self) @@ -127,7 +182,7 @@ extension Slice { } @inlinable @inline(__always) - public func moveInitializeAll( + ${modifier} func moveInitializeAll( fromContentsOf source: UnsafeMutableBufferPointer ) where Base == UnsafeMutableBufferPointer { let target = UnsafeMutableBufferPointer(rebasing: self) @@ -135,7 +190,7 @@ extension Slice { } @inlinable @inline(__always) - public func moveInitializeAll( + ${modifier} func moveInitializeAll( fromContentsOf source: Slice> ) where Base == UnsafeMutableBufferPointer { let target = UnsafeMutableBufferPointer(rebasing: self) diff --git a/Sources/InternalCollectionsUtilities/autogenerated/Specialize.swift b/Sources/InternalCollectionsUtilities/autogenerated/Specialize.swift deleted file mode 100644 index 07087af24..000000000 --- a/Sources/InternalCollectionsUtilities/autogenerated/Specialize.swift +++ /dev/null @@ -1,54 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift Collections open source project -// -// Copyright (c) 2023 - 2024 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 -// -//===----------------------------------------------------------------------===// - - -// ############################################################################# -// # # -// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. # -// # # -// ############################################################################# - - - -// In single module mode, we need these declarations to be internal, -// but in regular builds we want them to be public. Unfortunately -// the current best way to do this is to duplicate all definitions. -#if COLLECTIONS_SINGLE_MODULE -/// Returns `x` as its concrete type `U`, or `nil` if `x` has a different -/// concrete type. -/// -/// This cast can be useful for dispatching to specializations of generic -/// functions. -@_transparent -@inlinable -internal func _specialize(_ x: T, for: U.Type) -> U? { - // Note: this was ported from recent versions of the Swift stdlib. - guard T.self == U.self else { - return nil - } - return _identityCast(x, to: U.self) -} -#else // !COLLECTIONS_SINGLE_MODULE -/// Returns `x` as its concrete type `U`, or `nil` if `x` has a different -/// concrete type. -/// -/// This cast can be useful for dispatching to specializations of generic -/// functions. -@_transparent -@inlinable -public func _specialize(_ x: T, for: U.Type) -> U? { - // Note: this was ported from recent versions of the Swift stdlib. - guard T.self == U.self else { - return nil - } - return _identityCast(x, to: U.self) -} -#endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/InternalCollectionsUtilities/autogenerated/UnsafeBufferPointer+Extras.swift b/Sources/InternalCollectionsUtilities/autogenerated/UnsafeBufferPointer+Extras.swift index 749f17bc8..ec6a6f824 100644 --- a/Sources/InternalCollectionsUtilities/autogenerated/UnsafeBufferPointer+Extras.swift +++ b/Sources/InternalCollectionsUtilities/autogenerated/UnsafeBufferPointer+Extras.swift @@ -22,21 +22,83 @@ // but in regular builds we want them to be public. Unfortunately // the current best way to do this is to duplicate all definitions. #if COLLECTIONS_SINGLE_MODULE -extension UnsafeBufferPointer { +extension UnsafeBufferPointer where Element: ~Copyable { + @inlinable + @inline(__always) + internal static var _empty: Self { + .init(start: nil, count: 0) + } + @inlinable @inline(__always) internal func _ptr(at index: Int) -> UnsafePointer { assert(index >= 0 && index < count) return baseAddress.unsafelyUnwrapped + index } + + @_alwaysEmitIntoClient + internal func _extracting(unchecked bounds: Range) -> Self { + assert(bounds.lowerBound >= 0 && bounds.upperBound <= count, + "Index out of range") + guard let start = self.baseAddress else { + return Self(start: nil, count: 0) + } + return Self(start: start + bounds.lowerBound, count: bounds.count) + } + + @inlinable + internal func _extracting(first n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (0, n))) + } + + @inlinable + internal func _extracting(last n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (count - n, count))) + } } + #else // !COLLECTIONS_SINGLE_MODULE -extension UnsafeBufferPointer { +extension UnsafeBufferPointer where Element: ~Copyable { + @inlinable + @inline(__always) + public static var _empty: Self { + .init(start: nil, count: 0) + } + @inlinable @inline(__always) public func _ptr(at index: Int) -> UnsafePointer { assert(index >= 0 && index < count) return baseAddress.unsafelyUnwrapped + index } + + @_alwaysEmitIntoClient + public func _extracting(unchecked bounds: Range) -> Self { + assert(bounds.lowerBound >= 0 && bounds.upperBound <= count, + "Index out of range") + guard let start = self.baseAddress else { + return Self(start: nil, count: 0) + } + return Self(start: start + bounds.lowerBound, count: bounds.count) + } + + @inlinable + public func _extracting(first n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (0, n))) + } + + @inlinable + public func _extracting(last n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (count - n, count))) + } } + #endif // COLLECTIONS_SINGLE_MODULE diff --git a/Sources/InternalCollectionsUtilities/autogenerated/UnsafeMutableBufferPointer+Extras.swift b/Sources/InternalCollectionsUtilities/autogenerated/UnsafeMutableBufferPointer+Extras.swift index 2661d8ca1..5c8dd9489 100644 --- a/Sources/InternalCollectionsUtilities/autogenerated/UnsafeMutableBufferPointer+Extras.swift +++ b/Sources/InternalCollectionsUtilities/autogenerated/UnsafeMutableBufferPointer+Extras.swift @@ -22,9 +22,48 @@ // but in regular builds we want them to be public. Unfortunately // the current best way to do this is to duplicate all definitions. #if COLLECTIONS_SINGLE_MODULE +extension UnsafeMutableBufferPointer where Element: ~Copyable { + @inlinable + @inline(__always) + internal static var _empty: Self { + .init(start: nil, count: 0) + } + + @inlinable + @inline(__always) + internal func _ptr(at index: Int) -> UnsafeMutablePointer { + assert(index >= 0 && index < count) + return baseAddress.unsafelyUnwrapped + index + } + + @_alwaysEmitIntoClient + internal func _extracting(unchecked bounds: Range) -> Self { + assert(bounds.lowerBound >= 0 && bounds.upperBound <= count, + "Index out of range") + guard let start = self.baseAddress else { + return Self(start: nil, count: 0) + } + return Self(start: start + bounds.lowerBound, count: bounds.count) + } + + @inlinable + internal func _extracting(first n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (0, n))) + } + + @inlinable + internal func _extracting(last n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (count - n, count))) + } +} + extension UnsafeMutableBufferPointer { @inlinable - public func initialize(fromContentsOf source: Self) -> Index { + internal func initialize(fromContentsOf source: Self) -> Index { guard source.count > 0 else { return 0 } precondition( source.count <= self.count, @@ -36,7 +75,7 @@ extension UnsafeMutableBufferPointer { } @inlinable - public func initialize(fromContentsOf source: Slice) -> Index { + internal func initialize(fromContentsOf source: Slice) -> Index { let sourceCount = source.count guard sourceCount > 0 else { return 0 } precondition( @@ -51,7 +90,7 @@ extension UnsafeMutableBufferPointer { extension Slice { @inlinable @inline(__always) - public func initialize( + internal func initialize( fromContentsOf source: UnsafeMutableBufferPointer ) -> Index where Base == UnsafeMutableBufferPointer @@ -62,7 +101,7 @@ extension Slice { } @inlinable @inline(__always) - public func initialize( + internal func initialize( fromContentsOf source: Slice> ) -> Index where Base == UnsafeMutableBufferPointer @@ -75,7 +114,7 @@ extension Slice { extension UnsafeMutableBufferPointer { @inlinable @inline(__always) - public func initializeAll( + internal func initializeAll( fromContentsOf source: C ) where C.Element == Element { let i = self.initialize(fromContentsOf: source) @@ -83,25 +122,41 @@ extension UnsafeMutableBufferPointer { } @inlinable @inline(__always) - public func initializeAll(fromContentsOf source: Self) { - let i = self.initialize(fromContentsOf: source) - assert(i == self.endIndex) + internal func initializeAll(fromContentsOf source: UnsafeBufferPointer) { + assert(self.count == source.count) + guard source.count > 0 else { return } + self.baseAddress.unsafelyUnwrapped.initialize( + from: source.baseAddress.unsafelyUnwrapped, + count: source.count) } @inlinable @inline(__always) - public func initializeAll(fromContentsOf source: Slice) { - let i = self.initialize(fromContentsOf: source) - assert(i == self.endIndex) + internal func initializeAll(fromContentsOf source: Slice>) { + self.initializeAll(fromContentsOf: .init(rebasing: source)) } @inlinable @inline(__always) - public func moveInitializeAll(fromContentsOf source: Self) { + internal func initializeAll(fromContentsOf source: Self) { + self.initializeAll(fromContentsOf: UnsafeBufferPointer(source)) + } + + @inlinable @inline(__always) + internal func initializeAll(fromContentsOf source: Slice) { + self.initializeAll(fromContentsOf: UnsafeMutableBufferPointer(rebasing: source)) + } +} + +extension UnsafeMutableBufferPointer where Element: ~Copyable { + @inlinable @inline(__always) + internal func moveInitializeAll(fromContentsOf source: Self) { let i = self.moveInitialize(fromContentsOf: source) assert(i == self.endIndex) } +} +extension UnsafeMutableBufferPointer { @inlinable @inline(__always) - public func moveInitializeAll(fromContentsOf source: Slice) { + internal func moveInitializeAll(fromContentsOf source: Slice) { let i = self.moveInitialize(fromContentsOf: source) assert(i == self.endIndex) } @@ -109,7 +164,7 @@ extension UnsafeMutableBufferPointer { extension Slice { @inlinable @inline(__always) - public func initializeAll( + internal func initializeAll( fromContentsOf source: C ) where Base == UnsafeMutableBufferPointer { let i = self.initialize(fromContentsOf: source) @@ -117,7 +172,7 @@ extension Slice { } @inlinable @inline(__always) - public func initializeAll( + internal func initializeAll( fromContentsOf source: UnsafeMutableBufferPointer ) where Base == UnsafeMutableBufferPointer { let target = UnsafeMutableBufferPointer(rebasing: self) @@ -125,7 +180,7 @@ extension Slice { } @inlinable @inline(__always) - public func initializeAll( + internal func initializeAll( fromContentsOf source: Slice> ) where Base == UnsafeMutableBufferPointer { let target = UnsafeMutableBufferPointer(rebasing: self) @@ -133,7 +188,7 @@ extension Slice { } @inlinable @inline(__always) - public func moveInitializeAll( + internal func moveInitializeAll( fromContentsOf source: UnsafeMutableBufferPointer ) where Base == UnsafeMutableBufferPointer { let target = UnsafeMutableBufferPointer(rebasing: self) @@ -141,7 +196,7 @@ extension Slice { } @inlinable @inline(__always) - public func moveInitializeAll( + internal func moveInitializeAll( fromContentsOf source: Slice> ) where Base == UnsafeMutableBufferPointer { let target = UnsafeMutableBufferPointer(rebasing: self) @@ -149,6 +204,45 @@ extension Slice { } } #else // !COLLECTIONS_SINGLE_MODULE +extension UnsafeMutableBufferPointer where Element: ~Copyable { + @inlinable + @inline(__always) + public static var _empty: Self { + .init(start: nil, count: 0) + } + + @inlinable + @inline(__always) + public func _ptr(at index: Int) -> UnsafeMutablePointer { + assert(index >= 0 && index < count) + return baseAddress.unsafelyUnwrapped + index + } + + @_alwaysEmitIntoClient + public func _extracting(unchecked bounds: Range) -> Self { + assert(bounds.lowerBound >= 0 && bounds.upperBound <= count, + "Index out of range") + guard let start = self.baseAddress else { + return Self(start: nil, count: 0) + } + return Self(start: start + bounds.lowerBound, count: bounds.count) + } + + @inlinable + public func _extracting(first n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (0, n))) + } + + @inlinable + public func _extracting(last n: Int) -> Self { + assert(n >= 0) + if n >= count { return self } + return extracting(Range(uncheckedBounds: (count - n, count))) + } +} + extension UnsafeMutableBufferPointer { @inlinable public func initialize(fromContentsOf source: Self) -> Index { @@ -209,24 +303,40 @@ extension UnsafeMutableBufferPointer { assert(i == self.endIndex) } + @inlinable @inline(__always) + public func initializeAll(fromContentsOf source: UnsafeBufferPointer) { + assert(self.count == source.count) + guard source.count > 0 else { return } + self.baseAddress.unsafelyUnwrapped.initialize( + from: source.baseAddress.unsafelyUnwrapped, + count: source.count) + } + + @inlinable @inline(__always) + public func initializeAll(fromContentsOf source: Slice>) { + self.initializeAll(fromContentsOf: .init(rebasing: source)) + } + @inlinable @inline(__always) public func initializeAll(fromContentsOf source: Self) { - let i = self.initialize(fromContentsOf: source) - assert(i == self.endIndex) + self.initializeAll(fromContentsOf: UnsafeBufferPointer(source)) } @inlinable @inline(__always) public func initializeAll(fromContentsOf source: Slice) { - let i = self.initialize(fromContentsOf: source) - assert(i == self.endIndex) + self.initializeAll(fromContentsOf: UnsafeMutableBufferPointer(rebasing: source)) } +} +extension UnsafeMutableBufferPointer where Element: ~Copyable { @inlinable @inline(__always) public func moveInitializeAll(fromContentsOf source: Self) { let i = self.moveInitialize(fromContentsOf: source) assert(i == self.endIndex) } +} +extension UnsafeMutableBufferPointer { @inlinable @inline(__always) public func moveInitializeAll(fromContentsOf source: Slice) { let i = self.moveInitialize(fromContentsOf: source) diff --git a/Sources/Span/CMakeLists.txt b/Sources/Span/CMakeLists.txt new file mode 100644 index 000000000..b85b0b5e3 --- /dev/null +++ b/Sources/Span/CMakeLists.txt @@ -0,0 +1,27 @@ +#[[ +This source file is part of the Swift Collections Open Source Project + +Copyright (c) 2024 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 +#]] + +add_library(Future + "LifetimeOverride.swift" + "MutableRawSpan.swift" + "MutableSpan.swift" + "MutableSpanSlicing.swift" + "OutputSpan.swift" + "SpanExtensions.swift" + "StdlibOutputSpanExtensions.swift" + "UnsafeBufferPointer+Additions.swift" +) + +target_link_libraries(Future PRIVATE + InternalCollectionsUtilities) +set_target_properties(Future PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) + +_install_target(Future) +set_property(GLOBAL APPEND PROPERTY SWIFT_COLLECTIONS_EXPORTS Future) diff --git a/Sources/Span/LifetimeOverride.swift b/Sources/Span/LifetimeOverride.swift new file mode 100644 index 000000000..ed416cfa9 --- /dev/null +++ b/Sources/Span/LifetimeOverride.swift @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 2025 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 +// +//===----------------------------------------------------------------------===// + +/// Unsafely discard any lifetime dependency on the `dependent` argument. +/// Return a value identical to `dependent` with a lifetime dependency +/// on the caller's borrow scope of the `source` argument. +@unsafe +@_unsafeNonescapableResult +@_alwaysEmitIntoClient +@_transparent +@lifetime(borrow source) +internal func _overrideLifetime< + T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable +>( + _ dependent: consuming T, borrowing source: borrowing U +) -> T { + // TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence + // should be expressed by a builtin that is hidden within the function body. + dependent +} + +/// Unsafely discard any lifetime dependency on the `dependent` argument. +/// Return a value identical to `dependent` that inherits +/// all lifetime dependencies from the `source` argument. +@unsafe +@_unsafeNonescapableResult +@_alwaysEmitIntoClient +@_transparent +@lifetime(source) +internal func _overrideLifetime< + T: ~Copyable & ~Escapable, U: ~Copyable & ~Escapable +>( + _ dependent: consuming T, copying source: borrowing U +) -> T { + // TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence + // should be expressed by a builtin that is hidden within the function body. + dependent +} + +/// Unsafely discard any lifetime dependency on the `dependent` argument. +/// Return a value identical to `dependent` with a lifetime dependency +/// on the caller's exclusive borrow scope of the `source` argument. +@unsafe +@_unsafeNonescapableResult +@_alwaysEmitIntoClient +@_transparent +@lifetime(borrow source) +public func _overrideLifetime< + T: ~Copyable & ~Escapable, + U: ~Copyable & ~Escapable +>( + _ dependent: consuming T, + mutating source: inout U +) -> T { + // TODO: Remove @_unsafeNonescapableResult. Instead, the unsafe dependence + // should be expressed by a builtin that is hidden within the function body. + dependent +} diff --git a/Sources/Span/MutableRawSpan.swift b/Sources/Span/MutableRawSpan.swift new file mode 100644 index 000000000..3a2770a14 --- /dev/null +++ b/Sources/Span/MutableRawSpan.swift @@ -0,0 +1,630 @@ +//===--- MutableRawSpan.swift ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 2025 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 +// +//===----------------------------------------------------------------------===// + +// A MutableRawSpan represents a span of memory which +// contains initialized `Element` instances. +@safe +@frozen +@available(macOS 9999, *) +public struct MutableRawSpan: ~Copyable & ~Escapable { + @usableFromInline + internal let _pointer: UnsafeMutableRawPointer? + + @usableFromInline + internal let _count: Int + + @_alwaysEmitIntoClient + internal func _start() -> UnsafeMutableRawPointer { + unsafe _pointer.unsafelyUnwrapped + } + + @_alwaysEmitIntoClient + @lifetime(borrow pointer) + internal init( + _unchecked pointer: UnsafeMutableRawPointer?, + byteCount: Int + ) { + _pointer = unsafe pointer + _count = byteCount + } +} + +@available(macOS 9999, *) +extension MutableRawSpan: @unchecked Sendable {} + +@available(macOS 9999, *) +extension MutableRawSpan { + + @_alwaysEmitIntoClient + @lifetime(borrow bytes) + public init( + _unsafeBytes bytes: UnsafeMutableRawBufferPointer + ) { + let baseAddress = bytes.baseAddress + let span = MutableRawSpan(_unchecked: baseAddress, byteCount: bytes.count) + self = unsafe _overrideLifetime(span, borrowing: bytes) + } + + @_alwaysEmitIntoClient + @lifetime(borrow bytes) + public init( + _unsafeBytes bytes: borrowing Slice + ) { + let rebased = unsafe UnsafeMutableRawBufferPointer(rebasing: bytes) + let span = MutableRawSpan(_unsafeBytes: rebased) + self = unsafe _overrideLifetime(span, borrowing: bytes) + } + + @_alwaysEmitIntoClient + @lifetime(borrow pointer) + public init( + _unsafeStart pointer: UnsafeMutableRawPointer, + byteCount: Int + ) { + precondition(byteCount >= 0, "Count must not be negative") + self.init(_unchecked: pointer, byteCount: byteCount) + } + + @_alwaysEmitIntoClient + @lifetime(borrow elements) + public init( + _unsafeElements elements: UnsafeMutableBufferPointer + ) { + let bytes = UnsafeMutableRawBufferPointer(elements) + let span = MutableRawSpan(_unsafeBytes: bytes) + self = unsafe _overrideLifetime(span, borrowing: elements) + } + + @_alwaysEmitIntoClient + @lifetime(borrow elements) + public init( + _unsafeElements elements: borrowing Slice> + ) { + let rebased = unsafe UnsafeMutableBufferPointer(rebasing: elements) + let span = MutableRawSpan(_unsafeElements: rebased) + self = unsafe _overrideLifetime(span, borrowing: elements) + } + + @_alwaysEmitIntoClient + @lifetime(elements) + public init( + _elements elements: consuming MutableSpan + ) { + let bytes = unsafe UnsafeMutableRawBufferPointer( + start: elements._pointer, + count: elements.count &* MemoryLayout.stride + ) + let span = MutableRawSpan(_unsafeBytes: bytes) + self = unsafe _overrideLifetime(span, copying: elements) + } +} + +@available(macOS 9999, *) +extension MutableRawSpan { + @_alwaysEmitIntoClient + public var byteCount: Int { _count } + + @_alwaysEmitIntoClient + public var isEmpty: Bool { byteCount == 0 } + + @_alwaysEmitIntoClient + public var byteOffsets: Range { + unsafe Range(uncheckedBounds: (0, byteCount)) + } +} + +@available(macOS 9999, *) +extension MutableRawSpan { + + @_alwaysEmitIntoClient + public func withUnsafeBytes( + _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + guard let pointer = _pointer, _count > 0 else { + return try unsafe body(.init(start: nil, count: 0)) + } + return try unsafe body(.init(start: pointer, count: _count)) + } + + @_alwaysEmitIntoClient + public mutating func withUnsafeMutableBytes( + _ body: (UnsafeMutableRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + guard let pointer = _pointer, _count > 0 else { + return try unsafe body(.init(start: nil, count: 0)) + } + return try unsafe body(.init(start: pointer, count: _count)) + } +} + +@available(macOS 9999, *) +extension RawSpan { + + @_alwaysEmitIntoClient + @lifetime(borrow mutableSpan) + public init(_unsafeMutableRawSpan mutableSpan: borrowing MutableRawSpan) { + let start = mutableSpan._start() + let span = RawSpan(_unsafeStart: start, byteCount: mutableSpan.byteCount) + self = unsafe _overrideLifetime(span, borrowing: mutableSpan) + } +} + +@available(macOS 9999, *) +extension MutableRawSpan { + + public var bytes: RawSpan { + @_alwaysEmitIntoClient + @lifetime(borrow self) + borrowing get { + return RawSpan(_unsafeMutableRawSpan: self) + } + } + + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + public borrowing func _unsafeView( + as type: T.Type + ) -> Span { + let bytes = unsafe UnsafeRawBufferPointer(start: _pointer, count: _count) + let span = Span(_unsafeBytes: bytes) + return unsafe _overrideLifetime(span, borrowing: self) + } + + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + public mutating func _unsafeMutableView( + as type: T.Type + ) -> MutableSpan { + let bytes = unsafe UnsafeMutableRawBufferPointer( + start: _pointer, count: _count + ) + let span = MutableSpan(_unsafeBytes: bytes) + return unsafe _overrideLifetime(span, mutating: &self) + } +} + +@available(macOS 9999, *) +extension MutableRawSpan { + + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be properly aligned for + /// accessing `T` and initialized to `T` or another type that is layout + /// compatible with `T`. + /// + /// This is an unsafe operation. Failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance is memory-managed and unassociated + /// with the value in the memory referenced by this pointer. + @unsafe + @_alwaysEmitIntoClient + public func unsafeLoad( + fromByteOffset offset: Int = 0, as: T.Type + ) -> T { + precondition( + UInt(bitPattern: offset) <= UInt(bitPattern: _count) && + MemoryLayout.size <= (_count &- offset), + "Byte offset range out of bounds" + ) + return unsafe unsafeLoad(fromUncheckedByteOffset: offset, as: T.self) + } + + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be properly aligned for + /// accessing `T` and initialized to `T` or another type that is layout + /// compatible with `T`. + /// + /// This is an unsafe operation. This function does not validate the bounds + /// of the memory access, and failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance is memory-managed and unassociated + /// with the value in the memory referenced by this pointer. + @unsafe + @_alwaysEmitIntoClient + public func unsafeLoad( + fromUncheckedByteOffset offset: Int, as: T.Type + ) -> T { + unsafe _start().load(fromByteOffset: offset, as: T.self) + } + + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be initialized to `T` + /// or another type that is layout compatible with `T`. + /// + /// This is an unsafe operation. Failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance isn't associated + /// with the value in the range of memory referenced by this pointer. + @unsafe + @_alwaysEmitIntoClient + public func unsafeLoadUnaligned( + fromByteOffset offset: Int = 0, as: T.Type + ) -> T { + precondition( + UInt(bitPattern: offset) <= UInt(bitPattern: _count) && + MemoryLayout.size <= (_count &- offset), + "Byte offset range out of bounds" + ) + return unsafe unsafeLoadUnaligned(fromUncheckedByteOffset: offset, as: T.self) + } + + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be initialized to `T` + /// or another type that is layout compatible with `T`. + /// + /// This is an unsafe operation. This function does not validate the bounds + /// of the memory access, and failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance isn't associated + /// with the value in the range of memory referenced by this pointer. + @unsafe + @_alwaysEmitIntoClient + public func unsafeLoadUnaligned( + fromUncheckedByteOffset offset: Int, as: T.Type + ) -> T { + unsafe _start().loadUnaligned(fromByteOffset: offset, as: T.self) + } + + @_alwaysEmitIntoClient + public mutating func storeBytes( + of value: T, toByteOffset offset: Int = 0, as type: T.Type + ) { + precondition( + UInt(bitPattern: offset) <= UInt(bitPattern: _count) && + MemoryLayout.size <= (_count &- offset), + "Byte offset range out of bounds" + ) + unsafe storeBytes(of: value, toUncheckedByteOffset: offset, as: type) + } + + @unsafe + @_alwaysEmitIntoClient + public mutating func storeBytes( + of value: T, toUncheckedByteOffset offset: Int, as type: T.Type + ) { + unsafe _start().storeBytes(of: value, toByteOffset: offset, as: type) + } +} + +//MARK: copyMemory +@available(macOS 9999, *) +extension MutableRawSpan { + + @_alwaysEmitIntoClient + public mutating func update( + from source: S + ) -> (unwritten: S.Iterator, byteOffset: Int) where S.Element: BitwiseCopyable { + var iterator = source.makeIterator() + let offset = update(from: &iterator) + return (iterator, offset) + } + + @_alwaysEmitIntoClient + public mutating func update( + from elements: inout some IteratorProtocol + ) -> Int { + var offset = 0 + while offset + MemoryLayout.stride <= _count { + guard let element = elements.next() else { break } + unsafe storeBytes( + of: element, toUncheckedByteOffset: offset, as: Element.self + ) + offset &+= MemoryLayout.stride + } + return offset + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: C + ) -> Int where C.Element: BitwiseCopyable { + let newOffset = source.withContiguousStorageIfAvailable { + self.update(fromContentsOf: RawSpan(_unsafeElements: $0)) + } + if let newOffset { return newOffset } + + var elements = source.makeIterator() + let lastOffset = update(from: &elements) + precondition( + elements.next() == nil, + "destination span cannot contain every element from source." + ) + return lastOffset + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: Span + ) -> Int { +// update(from: source.bytes) + source.withUnsafeBytes { + update(fromContentsOf: $0) + } + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: borrowing MutableSpan + ) -> Int { +// update(from: source.span.bytes) + source.withUnsafeBytes { + update(fromContentsOf: $0) + } + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: RawSpan + ) -> Int { + if source.byteCount == 0 { return 0 } + source.withUnsafeBytes { + unsafe _start().copyMemory(from: $0.baseAddress!, byteCount: $0.count) + } + return source.byteCount + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: borrowing MutableRawSpan + ) -> Int { + update(fromContentsOf: source.bytes) + } +} + +// MARK: sub-spans +@available(macOS 9999, *) +extension MutableRawSpan { + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(_ bounds: Range) -> Self { + precondition( + UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && + UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), + "Index range out of bounds" + ) + return unsafe _extracting(unchecked: bounds) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// This function does not validate `bounds`; this is an unsafe operation. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(unchecked bounds: Range) -> Self { + let newStart = unsafe _pointer?.advanced(by: bounds.lowerBound) + let newSpan = Self(_unchecked: newStart, byteCount: bounds.count) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting( + _ bounds: some RangeExpression + ) -> Self { + _extracting(bounds.relative(to: byteOffsets)) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// This function does not validate `bounds`; this is an unsafe operation. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(unchecked bounds: ClosedRange) -> Self { + let range = unsafe Range( + uncheckedBounds: (bounds.lowerBound, bounds.upperBound+1) + ) + return unsafe _extracting(unchecked: range) + } + + /// Constructs a new span over all the items of this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Returns: A `MutableSpan` over all the items of this span. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(_: UnboundedRange) -> Self { + let newSpan = Self(_unchecked: _start(), byteCount: _count) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } +} + +// MARK: prefixes and suffixes +@available(macOS 9999, *) +extension MutableRawSpan { + + /// Returns a span containing the initial elements of this span, + /// up to the specified maximum length. + /// + /// If the maximum length exceeds the length of this span, + /// the result contains all the elements. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter maxLength: The maximum number of elements to return. + /// `maxLength` must be greater than or equal to zero. + /// - Returns: A span with at most `maxLength` elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(first maxLength: Int) -> Self { + precondition(maxLength >= 0, "Can't have a prefix of negative length") + let newCount = min(maxLength, byteCount) + let newSpan = Self(_unchecked: _pointer, byteCount: newCount) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } + + /// Returns a span over all but the given number of trailing elements. + /// + /// If the number of elements to drop exceeds the number of elements in + /// the span, the result is an empty span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter k: The number of elements to drop off the end of + /// the span. `k` must be greater than or equal to zero. + /// - Returns: A span leaving off the specified number of elements at the end. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(droppingLast k: Int) -> Self { + precondition(k >= 0, "Can't drop a negative number of elements") + let dropped = min(k, byteCount) + let newSpan = Self(_unchecked: _pointer, byteCount: byteCount &- dropped) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } + + /// Returns a span containing the final elements of the span, + /// up to the given maximum length. + /// + /// If the maximum length exceeds the length of this span, + /// the result contains all the elements. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter maxLength: The maximum number of elements to return. + /// `maxLength` must be greater than or equal to zero. + /// - Returns: A span with at most `maxLength` elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(last maxLength: Int) -> Self { + precondition(maxLength >= 0, "Can't have a suffix of negative length") + let newCount = min(maxLength, byteCount) + let newStart = unsafe _pointer?.advanced(by: byteCount &- newCount) + let newSpan = Self(_unchecked: newStart, byteCount: newCount) + return unsafe _overrideLifetime(newSpan, copying: self) + } + + /// Returns a span over all but the given number of initial elements. + /// + /// If the number of elements to drop exceeds the number of elements in + /// the span, the result is an empty span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter k: The number of elements to drop from the beginning of + /// the span. `k` must be greater than or equal to zero. + /// - Returns: A span starting after the specified number of elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(droppingFirst k: Int) -> Self { + precondition(k >= 0, "Can't drop a negative number of bytes") + let dropped = min(k, byteCount) + let newStart = unsafe _pointer?.advanced(by: dropped) + let newSpan = Self(_unchecked: newStart, byteCount: byteCount &- dropped) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } +} diff --git a/Sources/Span/MutableSpan.swift b/Sources/Span/MutableSpan.swift new file mode 100644 index 000000000..d552a93f1 --- /dev/null +++ b/Sources/Span/MutableSpan.swift @@ -0,0 +1,860 @@ +//===--- MutableSpan.swift ------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 2025 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 +// +//===----------------------------------------------------------------------===// + +import Builtin + +// A MutableSpan represents a span of memory which +// contains initialized `Element` instances. +@safe +@frozen +@available(macOS 9999, *) +public struct MutableSpan +: ~Copyable, ~Escapable { + @usableFromInline + internal let _pointer: UnsafeMutableRawPointer? + + @usableFromInline + internal let _count: Int + + @_alwaysEmitIntoClient + internal func _start() -> UnsafeMutableRawPointer { + unsafe _pointer.unsafelyUnwrapped + } + + @_alwaysEmitIntoClient + @lifetime(borrow start) + internal init( + _unchecked start: UnsafeMutableRawPointer?, + count: Int + ) { + _pointer = unsafe start + _count = count + } +} + +@available(macOS 9999, *) +extension MutableSpan: @unchecked Sendable where Element: Sendable {} + +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + @usableFromInline + @lifetime(borrow elements) + internal init( + _unchecked elements: UnsafeMutableBufferPointer + ) { + _pointer = .init(elements.baseAddress) + _count = elements.count + } + + @_alwaysEmitIntoClient + @lifetime(borrow buffer) + public init( + _unsafeElements buffer: UnsafeMutableBufferPointer + ) { + precondition( + ((Int(bitPattern: buffer.baseAddress) & + (MemoryLayout.alignment &- 1)) == 0), + "baseAddress must be properly aligned to access Element" + ) + let ms = MutableSpan(_unchecked: buffer) + self = unsafe _overrideLifetime(ms, borrowing: buffer) + } + + @_alwaysEmitIntoClient + @lifetime(borrow start) + public init( + _unsafeStart start: UnsafeMutablePointer, + count: Int + ) { + precondition(count >= 0, "Count must not be negative") + let buffer = unsafe UnsafeMutableBufferPointer(start: start, count: count) + let ms = MutableSpan(_unsafeElements: buffer) + self = unsafe _overrideLifetime(ms, borrowing: start) + } +} + +@available(macOS 9999, *) +extension MutableSpan { + + @_alwaysEmitIntoClient + @lifetime(borrow elements) + public init( + _unsafeElements elements: borrowing Slice> + ) { + let rb = unsafe UnsafeMutableBufferPointer(rebasing: elements) + let ms = MutableSpan(_unsafeElements: rb) + self = unsafe _overrideLifetime(ms, borrowing: elements) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: BitwiseCopyable { + + @_alwaysEmitIntoClient + @lifetime(borrow buffer) + public init( + _unsafeBytes buffer: UnsafeMutableRawBufferPointer + ) { + precondition( + ((Int(bitPattern: buffer.baseAddress) & + (MemoryLayout.alignment &- 1)) == 0), + "baseAddress must be properly aligned to access Element" + ) + let (byteCount, stride) = (buffer.count, MemoryLayout.stride) + let (count, remainder) = byteCount.quotientAndRemainder(dividingBy: stride) + precondition(remainder == 0, "Span must contain a whole number of elements") + let elements = unsafe UnsafeMutableBufferPointer( + start: buffer.baseAddress?.assumingMemoryBound(to: Element.self), + count: count + ) + let ms = MutableSpan(_unsafeElements: elements) + self = unsafe _overrideLifetime(ms, borrowing: buffer) + } + + @_alwaysEmitIntoClient + @lifetime(borrow pointer) + public init( + _unsafeStart pointer: UnsafeMutableRawPointer, + byteCount: Int + ) { + precondition(byteCount >= 0, "Count must not be negative") + let bytes = unsafe UnsafeMutableRawBufferPointer( + start: pointer, count: byteCount + ) + let ms = MutableSpan(_unsafeBytes: bytes) + self = unsafe _overrideLifetime(ms, borrowing: pointer) + } + + @_alwaysEmitIntoClient + @lifetime(borrow buffer) + public init( + _unsafeBytes buffer: borrowing Slice + ) { + let bytes = unsafe UnsafeMutableRawBufferPointer(rebasing: buffer) + let ms = MutableSpan(_unsafeBytes: bytes) + self = unsafe _overrideLifetime(ms, borrowing: buffer) + } +} + +@available(macOS 9999, *) +extension Span where Element: ~Copyable { + + @_alwaysEmitIntoClient + @lifetime(borrow mutableSpan) + public init(_unsafeMutableSpan mutableSpan: borrowing MutableSpan) { + let pointer = + unsafe mutableSpan._pointer?.assumingMemoryBound(to: Element.self) + let buffer = unsafe UnsafeBufferPointer( + start: pointer, count: mutableSpan.count + ) + let span = Span(_unsafeElements: buffer) + self = unsafe _overrideLifetime(span, borrowing: mutableSpan) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + public var span: Span { + @lifetime(borrow self) + borrowing get { + Span(_unsafeMutableSpan: self) + } + } +} + +@available(macOS 9999, *) +extension RawSpan { + + @_alwaysEmitIntoClient + public init( + _unsafeMutableSpan mutableSpan: borrowing MutableSpan + ) { + let pointer = mutableSpan._pointer + let byteCount = mutableSpan.count &* MemoryLayout.stride + let buffer = unsafe UnsafeRawBufferPointer(start: pointer, count: byteCount) + let rawSpan = RawSpan(_unsafeBytes: buffer) + self = unsafe _overrideLifetime(rawSpan, borrowing: mutableSpan) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: Equatable { + + @_alwaysEmitIntoClient + public func _elementsEqual(_ other: borrowing Self) -> Bool { + _elementsEqual(Span(_unsafeMutableSpan: other)) + } + + @_alwaysEmitIntoClient + public func _elementsEqual(_ other: Span) -> Bool { + Span(_unsafeMutableSpan: self)._elementsEqual(other) + } + + @_alwaysEmitIntoClient + public func _elementsEqual(_ other: some Collection) -> Bool { + Span(_unsafeMutableSpan: self)._elementsEqual(other) + } + + @_alwaysEmitIntoClient + public func _elementsEqual(_ other: some Sequence) -> Bool { + Span(_unsafeMutableSpan: self)._elementsEqual(other) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + public var _description: String { + let addr = String( + UInt(bitPattern: _pointer), radix: 16, uppercase: false + ) + return "(0x\(addr), \(_count))" + } +} + +//MARK: Collection, RandomAccessCollection +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable & ~Escapable { + + @_alwaysEmitIntoClient + public var count: Int { _count } + + @_alwaysEmitIntoClient + public var isEmpty: Bool { _count == 0 } + + public typealias Index = Int + + @_alwaysEmitIntoClient + public var indices: Range { + unsafe Range(uncheckedBounds: (0, _count)) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: BitwiseCopyable { + + /// Construct a RawSpan over the memory represented by this span + /// + /// - Returns: a RawSpan over the memory represented by this span + @_alwaysEmitIntoClient + public var bytes: RawSpan { + @lifetime(borrow self) + borrowing get { + RawSpan(_unsafeMutableSpan: self) + } + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + /// Accesses the element at the specified position in the `Span`. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + public subscript(_ position: Index) -> Element { + unsafeAddress { + precondition(indices.contains(position), "index out of bounds") + return unsafe UnsafePointer(_unsafeAddressOfElement(unchecked: position)) + } + unsafeMutableAddress { + precondition(indices.contains(position), "index out of bounds") + return unsafe _unsafeAddressOfElement(unchecked: position) + } + } + + /// Accesses the element at the specified position in the `Span`. + /// + /// This subscript does not validate `position`; this is an unsafe operation. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. + /// + /// - Complexity: O(1) + @unsafe + @_alwaysEmitIntoClient + public subscript(unchecked position: Index) -> Element { + unsafeAddress { + unsafe UnsafePointer(_unsafeAddressOfElement(unchecked: position)) + } + unsafeMutableAddress { + unsafe _unsafeAddressOfElement(unchecked: position) + } + } + + @unsafe + @_alwaysEmitIntoClient + internal func _unsafeAddressOfElement( + unchecked position: Index + ) -> UnsafeMutablePointer { + let elementOffset = position &* MemoryLayout.stride + let address = unsafe _start().advanced(by: elementOffset) + return unsafe address.assumingMemoryBound(to: Element.self) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + public mutating func swapAt(_ i: Index, _ j: Index) { + precondition(indices.contains(Index(i))) + precondition(indices.contains(Index(j))) + unsafe swapAt(unchecked: i, unchecked: j) + } + + @unsafe + @_alwaysEmitIntoClient + public mutating func swapAt(unchecked i: Index, unchecked j: Index) { + let pi = unsafe _unsafeAddressOfElement(unchecked: i) + let pj = unsafe _unsafeAddressOfElement(unchecked: j) + let temporary = unsafe pi.move() + unsafe pi.initialize(to: pj.move()) + unsafe pj.initialize(to: consume temporary) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: BitwiseCopyable { + + /// Accesses the element at the specified position in the `Span`. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + public subscript(_ position: Index) -> Element { + get { + precondition(indices.contains(position), "index out of bounds") + return unsafe self[unchecked: position] + } + set { + precondition(indices.contains(position), "index out of bounds") + unsafe self[unchecked: position] = newValue + } + } + + /// Accesses the element at the specified position in the `Span`. + /// + /// This subscript does not validate `position`; this is an unsafe operation. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. + /// + /// - Complexity: O(1) + @unsafe + @_alwaysEmitIntoClient + public subscript(unchecked position: Index) -> Element { + get { + let offset = position&*MemoryLayout.stride + return unsafe _start().loadUnaligned( + fromByteOffset: offset, as: Element.self + ) + } + set { + let offset = position&*MemoryLayout.stride + unsafe _start().storeBytes( + of: newValue, toByteOffset: offset, as: Element.self + ) + } + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + //FIXME: mark closure parameter as non-escaping + @_alwaysEmitIntoClient + public func withUnsafeBufferPointer( + _ body: (_ buffer: UnsafeBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + try Span(_unsafeMutableSpan: self).withUnsafeBufferPointer(body) + } + + //FIXME: mark closure parameter as non-escaping + @_alwaysEmitIntoClient + public mutating func withUnsafeMutableBufferPointer< + E: Error, Result: ~Copyable + >( + _ body: (UnsafeMutableBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + guard let pointer = _pointer, count > 0 else { + return try unsafe body(.init(start: nil, count: 0)) + } + // bind memory by hand to sidestep alignment concerns + let binding = Builtin.bindMemory( + pointer._rawValue, count._builtinWordValue, Element.self + ) + defer { Builtin.rebindMemory(pointer._rawValue, binding) } + return try unsafe body(.init(start: .init(pointer._rawValue), count: count)) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: BitwiseCopyable { + + //FIXME: mark closure parameter as non-escaping + @_alwaysEmitIntoClient + public func withUnsafeBytes( + _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + try RawSpan(_unsafeMutableSpan: self).withUnsafeBytes(body) + } + + //FIXME: mark closure parameter as non-escaping + @_alwaysEmitIntoClient + public mutating func withUnsafeMutableBytes( + _ body: (_ buffer: UnsafeMutableRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result { + let bytes = unsafe UnsafeMutableRawBufferPointer( + start: (_count == 0) ? nil : _start(), + count: _count &* MemoryLayout.stride + ) + return try unsafe body(bytes) + } +} + +//MARK: bulk-update functions +@available(macOS 9999, *) +extension MutableSpan { + + @_alwaysEmitIntoClient + public mutating func update(repeating repeatedValue: consuming Element) { + unsafe _start().withMemoryRebound(to: Element.self, capacity: count) { + unsafe $0.update(repeating: repeatedValue, count: count) + } + } + + @_alwaysEmitIntoClient + public mutating func update( + from source: S + ) -> (unwritten: S.Iterator, index: Index) where S.Element == Element { + var iterator = source.makeIterator() + let index = update(from: &iterator) + return (iterator, index) + } + + @_alwaysEmitIntoClient + public mutating func update( + from elements: inout some IteratorProtocol + ) -> Index { + var index = 0 + while index < _count { + guard let element = elements.next() else { break } + unsafe self[unchecked: index] = element + index &+= 1 + } + return index + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: some Collection + ) -> Index { + let updated = source.withContiguousStorageIfAvailable { + self.update(fromContentsOf: Span(_unsafeElements: $0)) + } + if let updated { + return updated + } + + //TODO: use _copyContents here + + var iterator = source.makeIterator() + let index = update(from: &iterator) + precondition( + iterator.next() == nil, + "destination buffer view cannot contain every element from source." + ) + return index + } + + @_alwaysEmitIntoClient + public mutating func update(fromContentsOf source: Span) -> Index { + guard !source.isEmpty else { return 0 } + precondition( + source.count <= self.count, + "destination span cannot contain every element from source." + ) + unsafe _start().withMemoryRebound( + to: Element.self, capacity: source.count + ) { dest in + source.withUnsafeBufferPointer { + unsafe dest.update(from: $0.baseAddress!, count: $0.count) + } + } + return source.count + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: borrowing MutableSpan + ) -> Index { + update(fromContentsOf: source.span) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + public mutating func moveUpdate( + fromContentsOf source: consuming OutputSpan + ) -> Index { + guard !source.isEmpty else { return 0 } + precondition( + source.count <= self.count, + "destination span cannot contain every element from source." + ) + let buffer = unsafe source.relinquishBorrowedMemory() + // we must now deinitialize the returned UMBP + unsafe _start().moveInitializeMemory( + as: Element.self, from: buffer.baseAddress!, count: buffer.count + ) + return buffer.count + } + + @_alwaysEmitIntoClient + public mutating func moveUpdate( + fromContentsOf source: UnsafeMutableBufferPointer + ) -> Index { + let s = OutputSpan(_initializing: source, initialized: source.count) + return self.moveUpdate(fromContentsOf: s) + } +} + +@available(macOS 9999, *) +extension MutableSpan { + + @_alwaysEmitIntoClient + public mutating func moveUpdate( + fromContentsOf source: Slice> + ) -> Index { + self.moveUpdate(fromContentsOf: unsafe .init(rebasing: source)) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: BitwiseCopyable { + + @_alwaysEmitIntoClient + public mutating func update( + repeating repeatedValue: Element + ) where Element: BitwiseCopyable { + guard count > 0 else { return } + // rebind _start manually in order to avoid assumptions about alignment. + let rp = _start()._rawValue + let binding = Builtin.bindMemory(rp, count._builtinWordValue, Element.self) + let rebound = unsafe UnsafeMutablePointer(rp) + unsafe rebound.update(repeating: repeatedValue, count: count) + Builtin.rebindMemory(rp, binding) + } + + @_alwaysEmitIntoClient + public mutating func update( + from source: S + ) -> (unwritten: S.Iterator, index: Index) + where S.Element == Element, Element: BitwiseCopyable { + var iterator = source.makeIterator() + let index = update(from: &iterator) + return (iterator, index) + } + + @_alwaysEmitIntoClient + public mutating func update( + from elements: inout some IteratorProtocol + ) -> Index { + var index = 0 + while index < _count { + guard let element = elements.next() else { break } + unsafe self[unchecked: index] = element + index &+= 1 + } + return index + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: some Collection + ) -> Index where Element: BitwiseCopyable { + let updated = source.withContiguousStorageIfAvailable { + self.update(fromContentsOf: Span(_unsafeElements: $0)) + } + if let updated { + return updated + } + + //TODO: use _copyContents here + + var iterator = source.makeIterator() + let index = update(from: &iterator) + precondition( + iterator.next() == nil, + "destination buffer view cannot contain every element from source." + ) + return index + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: Span + ) -> Index where Element: BitwiseCopyable { + guard !source.isEmpty else { return 0 } + precondition( + source.count <= self.count, + "destination span cannot contain every element from source." + ) + source.withUnsafeBufferPointer { + unsafe _start().copyMemory( + from: $0.baseAddress!, + byteCount: $0.count &* MemoryLayout.stride + ) + } + return source.count + } + + @_alwaysEmitIntoClient + public mutating func update( + fromContentsOf source: borrowing MutableSpan + ) -> Index where Element: BitwiseCopyable { + update(fromContentsOf: source.span) + } +} + +// MARK: sub-spans +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(_ bounds: Range) -> Self { + precondition( + UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && + UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), + "Index range out of bounds" + ) + return unsafe _extracting(unchecked: bounds) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// This function does not validate `bounds`; this is an unsafe operation. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(unchecked bounds: Range) -> Self { + let delta = bounds.lowerBound &* MemoryLayout.stride + let newStart = unsafe _pointer?.advanced(by: delta) + let newSpan = Self(_unchecked: newStart, count: bounds.count) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting( + _ bounds: some RangeExpression + ) -> Self { + _extracting(bounds.relative(to: indices)) + } + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// This function does not validate `bounds`; this is an unsafe operation. + /// + /// - Parameter bounds: A valid range of positions. Every position in + /// this range must be within the bounds of this `MutableSpan`. + /// + /// - Returns: A `MutableSpan` over the items within `bounds` + /// + /// - Complexity: O(1) + @unsafe + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting( + unchecked bounds: ClosedRange + ) -> Self { + let range = unsafe Range( + uncheckedBounds: (bounds.lowerBound, bounds.upperBound&+1) + ) + return unsafe _extracting(unchecked: range) + } + + /// Constructs a new span over all the items of this span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Returns: A `MutableSpan` over all the items of this span. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(_: UnboundedRange) -> Self { + let newSpan = Self(_unchecked: _start(), count: _count) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } +} + +// MARK: prefixes and suffixes +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + /// Returns a span containing the initial elements of this span, + /// up to the specified maximum length. + /// + /// If the maximum length exceeds the length of this span, + /// the result contains all the elements. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter maxLength: The maximum number of elements to return. + /// `maxLength` must be greater than or equal to zero. + /// - Returns: A span with at most `maxLength` elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(first maxLength: Int) -> Self { + precondition(maxLength >= 0, "Can't have a prefix of negative length") + let newCount = min(maxLength, count) + let newSpan = Self(_unchecked: _pointer, count: newCount) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } + + /// Returns a span over all but the given number of trailing elements. + /// + /// If the number of elements to drop exceeds the number of elements in + /// the span, the result is an empty span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter k: The number of elements to drop off the end of + /// the span. `k` must be greater than or equal to zero. + /// - Returns: A span leaving off the specified number of elements at the end. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(droppingLast k: Int) -> Self { + precondition(k >= 0, "Can't drop a negative number of elements") + let droppedCount = min(k, count) + let newSpan = Self(_unchecked: _pointer, count: count &- droppedCount) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } + + /// Returns a span containing the final elements of the span, + /// up to the given maximum length. + /// + /// If the maximum length exceeds the length of this span, + /// the result contains all the elements. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter maxLength: The maximum number of elements to return. + /// `maxLength` must be greater than or equal to zero. + /// - Returns: A span with at most `maxLength` elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(last maxLength: Int) -> Self { + precondition(maxLength >= 0, "Can't have a suffix of negative length") + let newCount = min(maxLength, count) + let offset = (count &- newCount) * MemoryLayout.stride + let newStart = unsafe _pointer?.advanced(by: offset) + let newSpan = Self(_unchecked: newStart, count: newCount) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } + + /// Returns a span over all but the given number of initial elements. + /// + /// If the number of elements to drop exceeds the number of elements in + /// the span, the result is an empty span. + /// + /// The returned span's first item is always at offset 0; unlike buffer + /// slices, extracted spans do not share their indices with the + /// span from which they are extracted. + /// + /// - Parameter k: The number of elements to drop from the beginning of + /// the span. `k` must be greater than or equal to zero. + /// - Returns: A span starting after the specified number of elements. + /// + /// - Complexity: O(1) + @_alwaysEmitIntoClient + @lifetime(borrow self) + mutating public func _extracting(droppingFirst k: Int) -> Self { + precondition(k >= 0, "Can't drop a negative number of elements") + let droppedCount = min(k, count) + let offset = droppedCount * MemoryLayout.stride + let newStart = unsafe _pointer?.advanced(by: offset) + let newSpan = Self(_unchecked: newStart, count: count &- droppedCount) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } +} diff --git a/Sources/Span/MutableSpanSlicing.swift b/Sources/Span/MutableSpanSlicing.swift new file mode 100644 index 000000000..7db6229b9 --- /dev/null +++ b/Sources/Span/MutableSpanSlicing.swift @@ -0,0 +1,165 @@ +//===--- MutableSpanSlicing.swift -----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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 +// +//===----------------------------------------------------------------------===// + +//MARK: Option 1 extracting() function +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + @lifetime(borrow self) + public mutating func extracting(_ bounds: Range) -> Self { + precondition( + UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && + UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), + "Index range out of bounds" + ) + let newSpan = MutableSpan(_unchecked: _start(), uncheckedBounds: bounds) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } +} + +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + @_alwaysEmitIntoClient + @lifetime(borrow pointer) + fileprivate init( + _unchecked pointer: UnsafeMutableRawPointer, + uncheckedBounds bounds: Range + ) { + let delta = bounds.lowerBound &* MemoryLayout.stride + let newStart = unsafe pointer.advanced(by: delta) + let newSpan = Self( + _unchecked: newStart, count: bounds.upperBound &- bounds.lowerBound + ) + self = unsafe _overrideLifetime(newSpan, borrowing: pointer) + } +} + +@available(macOS 9999, *) +extension Span where Element: ~Copyable { + @_alwaysEmitIntoClient + @lifetime(borrow pointer) + fileprivate init( + _unchecked pointer: UnsafeMutableRawPointer, + uncheckedBounds bounds: Range + ) { + let mut = MutableSpan(_unchecked: pointer, uncheckedBounds: bounds) + let newSpan = mut.span + self = unsafe _overrideLifetime(newSpan, borrowing: pointer) + } +} + +//MARK: Option 2 extracting subscript +@available(macOS 9999, *) +extension MutableSpan where Element: ~Copyable { + + public subscript(extracting bounds: Range) -> Self { + @lifetime(borrow self) + mutating get { + precondition( + UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && + UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), + "Index range out of bounds" + ) + let newSpan = MutableSpan(_unchecked: _start(), uncheckedBounds: bounds) + return unsafe _overrideLifetime(newSpan, mutating: &self) + } + } +} + +//MARK: Option 3 specific slicing wrapper type +@available(macOS 9999, *) +public struct SubMutableSpan +: ~Copyable, ~Escapable { + public typealias Index = MutableSpan.Index + + public fileprivate(set) var offset: MutableSpan.Index + public /*exclusive*/ var base: MutableSpan + + init(_ _offset: Index, _ _base: consuming MutableSpan) { + offset = _offset + base = _base + } + + public var count: Int { base.count - offset } +} + +@available(macOS 9999, *) +extension SubMutableSpan where Element: ~Copyable { + + public subscript (index: Index) -> Element { + unsafeAddress { + precondition(offset <= index && index < base.count) + return unsafe UnsafePointer(base._unsafeAddressOfElement(unchecked: index)) + } + unsafeMutableAddress { + precondition(offset <= index && index < base.count) + return unsafe base._unsafeAddressOfElement(unchecked: index) + } + } + + public var span: Span { + @lifetime(borrow self) + borrowing get { + let newSpan = Span( + _unchecked: base._start(), uncheckedBounds: offset..) -> SubMutableSpan { + @lifetime(borrow self) + mutating get { + precondition( + UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && + UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), + "Index range out of bounds" + ) + let prefix = Self( + _unchecked: _start(), uncheckedBounds: 0..(bounds.lowerBound, prefix) + return unsafe _overrideLifetime(subSpan, mutating: &self) + } + } +} + +//MARK: Option 4 range parameters to bulk mutation functions +@available(macOS 9999, *) +extension MutableSpan where Element: Copyable { + + public mutating func update( + in bounds: Range, + repeating repeatedValue: consuming Element + ) { + precondition( + UInt(bitPattern: bounds.lowerBound) <= UInt(bitPattern: _count) && + UInt(bitPattern: bounds.upperBound) <= UInt(bitPattern: _count), + "Index range out of bounds" + ) + let start = unsafe _start() + bounds.lowerBound &* MemoryLayout.stride + unsafe start.withMemoryRebound(to: Element.self, capacity: bounds.count) { + unsafe $0.update(repeating: repeatedValue, count: bounds.count) + } + } +} diff --git a/Sources/Span/OutputSpan.swift b/Sources/Span/OutputSpan.swift new file mode 100644 index 000000000..4df8661b6 --- /dev/null +++ b/Sources/Span/OutputSpan.swift @@ -0,0 +1,395 @@ +//===--- OutputSpan.swift -------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 2025 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 +// +//===----------------------------------------------------------------------===// + +// OutputSpan represents a span of memory which contains +// a variable number of `Element` instances, followed by uninitialized memory. +@safe +@frozen +@available(macOS 9999, *) +public struct OutputSpan: ~Copyable, ~Escapable { + @usableFromInline + internal let _pointer: UnsafeMutableRawPointer? + + public let capacity: Int + + @usableFromInline + internal var _initialized: Int = 0 + + @_alwaysEmitIntoClient + internal func _start() -> UnsafeMutableRawPointer { + unsafe _pointer.unsafelyUnwrapped + } + + @_alwaysEmitIntoClient + public var available: Int { capacity &- _initialized } + + @_alwaysEmitIntoClient + public var count: Int { _initialized } + + @_alwaysEmitIntoClient + public var isEmpty: Bool { _initialized == 0 } + + deinit { + if _initialized > 0 { + unsafe _start().withMemoryRebound( + to: Element.self, capacity: _initialized + ) { + [ workaround = _initialized ] in + _ = unsafe $0.deinitialize(count: workaround) + } + } + } + + @_alwaysEmitIntoClient + @lifetime(borrow start) + internal init( + _unchecked start: UnsafeMutableRawPointer?, + capacity: Int, + initialized: Int + ) { + _pointer = unsafe start + self.capacity = capacity + _initialized = initialized + } +} + +@available(macOS 9999, *) +@available(*, unavailable) +extension OutputSpan: Sendable {} + +@available(macOS 9999, *) +extension OutputSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + @lifetime(borrow buffer) + internal init( + _unchecked buffer: UnsafeMutableBufferPointer, + initialized: Int + ) { + _pointer = .init(buffer.baseAddress) + capacity = buffer.count + _initialized = initialized + } + + @_alwaysEmitIntoClient + @lifetime(borrow buffer) + public init( + _initializing buffer: UnsafeMutableBufferPointer, + initialized: Int = 0 + ) { + precondition( + ((Int(bitPattern: buffer.baseAddress) & + (MemoryLayout.alignment&-1)) == 0), + "baseAddress must be properly aligned to access Element" + ) + self.init(_unchecked: buffer, initialized: initialized) + } + + @_alwaysEmitIntoClient + @lifetime(borrow pointer) + public init( + _initializing pointer: UnsafeMutablePointer, + capacity: Int, + initialized: Int = 0 + ) { + precondition(capacity >= 0, "Capacity must be 0 or greater") + let buf = unsafe UnsafeMutableBufferPointer(start: pointer, count: capacity) + let os = OutputSpan(_initializing: buf, initialized: initialized) + self = unsafe _overrideLifetime(os, borrowing: pointer) + } +} + +@available(macOS 9999, *) +extension OutputSpan { + + @_alwaysEmitIntoClient + @lifetime(borrow buffer) + public init( + _initializing buffer: borrowing Slice>, + initialized: Int = 0 + ) { + let rebased = unsafe UnsafeMutableBufferPointer(rebasing: buffer) + let os = OutputSpan(_initializing: rebased, initialized: 0) + self = unsafe unsafe _overrideLifetime(os, borrowing: buffer) + } +} + +@available(macOS 9999, *) +extension OutputSpan where Element: BitwiseCopyable { + + @_alwaysEmitIntoClient + @lifetime(borrow bytes) + public init( + _initializing bytes: UnsafeMutableRawBufferPointer, + initialized: Int = 0 + ) { + precondition( + ((Int(bitPattern: bytes.baseAddress) & + (MemoryLayout.alignment&-1)) == 0), + "baseAddress must be properly aligned to access Element" + ) + let (byteCount, stride) = (bytes.count, MemoryLayout.stride) + let (count, remainder) = byteCount.quotientAndRemainder(dividingBy: stride) + precondition(remainder == 0, "Span must contain a whole number of elements") + let pointer = bytes.baseAddress + let os = OutputSpan( + _unchecked: pointer, capacity: count, initialized: initialized + ) + self = unsafe _overrideLifetime(os, borrowing: bytes) + } + + @_alwaysEmitIntoClient + @lifetime(borrow pointer) + public init( + _initializing pointer: UnsafeMutableRawPointer, + capacity: Int, + initialized: Int = 0 + ) { + precondition(capacity >= 0, "Capacity must be 0 or greater") + let buf = unsafe UnsafeMutableRawBufferPointer(start: pointer, count: capacity) + let os = OutputSpan(_initializing: buf, initialized: initialized) + self = unsafe _overrideLifetime(os, borrowing: pointer) + } + + @_alwaysEmitIntoClient + @lifetime(borrow buffer) + public init( + _initializing buffer: borrowing Slice, + initialized: Int = 0 + ) { + let rebased = unsafe UnsafeMutableRawBufferPointer(rebasing: buffer) + let os = OutputSpan(_initializing: rebased, initialized: initialized) + self = unsafe _overrideLifetime(os, borrowing: buffer) + } +} + +@available(macOS 9999, *) +extension OutputSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + public mutating func append(_ value: consuming Element) { + precondition(_initialized < capacity, "Output buffer overflow") + let p = unsafe _start().advanced(by: _initialized&*MemoryLayout.stride) + unsafe p.initializeMemory(as: Element.self, to: value) + _initialized &+= 1 + } + + @_alwaysEmitIntoClient + public mutating func removeLast() -> Element? { + guard _initialized > 0 else { return nil } + _initialized &-= 1 + let p = unsafe _start().advanced(by: _initialized&*MemoryLayout.stride) + return unsafe p.withMemoryRebound(to: Element.self, capacity: 1, { unsafe $0.move() }) + } + + @_alwaysEmitIntoClient + public mutating func removeAll() { + _ = unsafe _start().withMemoryRebound(to: Element.self, capacity: _initialized) { + unsafe $0.deinitialize(count: _initialized) + } + _initialized = 0 + } +} + +//MARK: bulk-update functions +@available(macOS 9999, *) +extension OutputSpan { + + @_alwaysEmitIntoClient + public mutating func append(repeating repeatedValue: Element, count: Int) { + let available = capacity &- _initialized + precondition( + count <= available, + "destination span cannot contain number of elements requested." + ) + let offset = _initialized&*MemoryLayout.stride + let p = unsafe _start().advanced(by: offset) + unsafe p.withMemoryRebound(to: Element.self, capacity: count) { + unsafe $0.initialize(repeating: repeatedValue, count: count) + } + _initialized &+= count + } + + @_alwaysEmitIntoClient + public mutating func append( + from elements: S + ) -> S.Iterator where S: Sequence, S.Element == Element { + var iterator = elements.makeIterator() + append(from: &iterator) + return iterator + } + + @_alwaysEmitIntoClient + public mutating func append( + from elements: inout some IteratorProtocol + ) { + while _initialized < capacity { + guard let element = elements.next() else { break } + let p = unsafe _start().advanced(by: _initialized&*MemoryLayout.stride) + unsafe p.initializeMemory(as: Element.self, to: element) + _initialized &+= 1 + } + } + + @_alwaysEmitIntoClient + public mutating func append( + fromContentsOf source: some Collection + ) { + let void: Void? = source.withContiguousStorageIfAvailable { + append(fromContentsOf: Span(_unsafeElements: $0)) + } + if void != nil { + return + } + + let available = capacity &- _initialized + let tail = unsafe _start().advanced(by: _initialized&*MemoryLayout.stride) + var (iterator, copied) = + unsafe tail.withMemoryRebound(to: Element.self, capacity: available) { + let suffix = unsafe UnsafeMutableBufferPointer(start: $0, count: available) + return unsafe source._copyContents(initializing: suffix) + } + precondition( + iterator.next() == nil, + "destination span cannot contain every element from source." + ) + assert(_initialized + copied <= capacity) // invariant check + _initialized &+= copied + } + + @_alwaysEmitIntoClient + public mutating func append( + fromContentsOf source: Span + ) { + guard !source.isEmpty else { return } + precondition( + source.count <= available, + "destination span cannot contain every element from source." + ) + let tail = unsafe _start().advanced(by: _initialized&*MemoryLayout.stride) + _ = source.withUnsafeBufferPointer { + unsafe tail.initializeMemory( + as: Element.self, from: $0.baseAddress!, count: $0.count + ) + } + _initialized += source.count + } + + @_alwaysEmitIntoClient + public mutating func append(fromContentsOf source: borrowing MutableSpan) { + source.withUnsafeBufferPointer { append(fromContentsOf: $0) } + } +} + +@available(macOS 9999, *) +extension OutputSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + public mutating func moveAppend( + fromContentsOf source: consuming Self + ) { + guard !source.isEmpty else { return } + precondition( + source.count <= available, + "buffer cannot contain every element from source." + ) + let buffer = unsafe source.relinquishBorrowedMemory() + // we must now deinitialize the returned UMBP + let tail = unsafe _start().advanced(by: _initialized&*MemoryLayout.stride) + unsafe tail.moveInitializeMemory( + as: Element.self, from: buffer.baseAddress!, count: buffer.count + ) + _initialized &+= buffer.count + } + + @_alwaysEmitIntoClient + public mutating func moveAppend( + fromContentsOf source: UnsafeMutableBufferPointer + ) { + let source = OutputSpan(_initializing: source, initialized: source.count) + moveAppend(fromContentsOf: source) + } +} + +@available(macOS 9999, *) +extension OutputSpan { + + @_alwaysEmitIntoClient + public mutating func moveAppend( + fromContentsOf source: Slice> + ) { + moveAppend( + fromContentsOf: unsafe UnsafeMutableBufferPointer(rebasing: source) + ) + } +} + +@available(macOS 9999, *) +extension OutputSpan where Element: BitwiseCopyable { +// TODO: alternative append() implementations for BitwiseCopyable elements +} + +@available(macOS 9999, *) +extension OutputSpan where Element: ~Copyable { + + @_alwaysEmitIntoClient + public var span: Span { + @lifetime(borrow self) + borrowing get { + let pointer = unsafe _pointer?.assumingMemoryBound(to: Element.self) + let buffer = unsafe UnsafeBufferPointer(start: pointer, count: _initialized) + let span = Span(_unsafeElements: buffer) + return unsafe _overrideLifetime(span, borrowing: self) + } + } + + @_alwaysEmitIntoClient + public var mutableSpan: MutableSpan { + @lifetime(borrow self) + mutating get { + let pointer = unsafe _pointer?.assumingMemoryBound(to: Element.self) + let buffer = unsafe UnsafeMutableBufferPointer( + start: pointer, count: _initialized + ) + let span = MutableSpan(_unsafeElements: buffer) + return unsafe _overrideLifetime(span, mutating: &self) + } + } +} + +@available(macOS 9999, *) +extension OutputSpan where Element: ~Copyable { + + @unsafe + @_alwaysEmitIntoClient + public consuming func relinquishBorrowedMemory( + ) -> UnsafeMutableBufferPointer { + let (start, count) = (self._pointer, self._initialized) + discard self + let typed = unsafe start?.bindMemory(to: Element.self, capacity: count) + return unsafe UnsafeMutableBufferPointer(start: typed, count: count) + } +} + +@available(macOS 9999, *) +extension OutputSpan where Element: BitwiseCopyable { + + @unsafe + @_alwaysEmitIntoClient + public consuming func relinquishBorrowedBytes( + ) -> UnsafeMutableRawBufferPointer { + let (start, count) = (self._pointer, self._initialized) + discard self + let byteCount = count&*MemoryLayout.stride + return unsafe UnsafeMutableRawBufferPointer(start: start, count: byteCount) + } +} diff --git a/Sources/Span/SpanExtensions.swift b/Sources/Span/SpanExtensions.swift new file mode 100644 index 000000000..c08065470 --- /dev/null +++ b/Sources/Span/SpanExtensions.swift @@ -0,0 +1,110 @@ +//===--- SpanExtensions.swift ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 2025 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 +// +//===----------------------------------------------------------------------===// + +private let immortalThing: [Void] = [] + +@available(macOS 9999, *) +extension Span { + + @available(macOS 9999, *) + public static var empty: Span { + @lifetime(immortal) + get { + let empty = unsafe UnsafeBufferPointer(start: nil, count: 0) + let span = Span(_unsafeElements: empty) + return unsafe _overrideLifetime(span, borrowing: immortalThing) + } + } + + @available(macOS 9999, *) + @lifetime(immortal) + public init() { + let empty = unsafe UnsafeBufferPointer(start: nil, count: 0) + let span = Span(_unsafeElements: empty) + self = unsafe _overrideLifetime(span, borrowing: immortalThing) + } +} + +@available(macOS 9999, *) +extension Span where Element: Equatable { + + /// Returns a Boolean value indicating whether this and another span + /// contain equal elements in the same order. + /// + /// - Parameters: + /// - other: A span to compare to this one. + /// - Returns: `true` if this sequence and `other` contain equivalent items, + /// using `areEquivalent` as the equivalence test; otherwise, `false.` + /// + /// - Complexity: O(*m*), where *m* is the lesser of the length of the + /// sequence and the length of `other`. + @_alwaysEmitIntoClient + public func _elementsEqual(_ other: Self) -> Bool { + guard count == other.count else { return false } + if count == 0 { return true } + + //FIXME: This could be short-cut + // with a layout constraint where stride equals size, + // as long as there is at most 1 unused bit pattern. + // if Element is BitwiseEquatable { + // return _swift_stdlib_memcmp(lhs.baseAddress, rhs.baseAddress, count) == 0 + // } + for o in 0..) -> Bool { + let equal = other.withContiguousStorageIfAvailable { + _elementsEqual(Span(_unsafeElements: $0)) + } + if let equal { return equal } + + guard count == other.count else { return false } + if count == 0 { return true } + + return _elementsEqual(AnySequence(other)) + } + + /// Returns a Boolean value indicating whether this span and a Sequence + /// contain equal elements in the same order. + /// + /// - Parameters: + /// - other: A Sequence to compare to this span. + /// - Returns: `true` if this sequence and `other` contain equivalent items, + /// using `areEquivalent` as the equivalence test; otherwise, `false.` + /// + /// - Complexity: O(*m*), where *m* is the lesser of the length of the + /// sequence and the length of `other`. + @_alwaysEmitIntoClient + public func _elementsEqual(_ other: some Sequence) -> Bool { + var offset = 0 + for otherElement in other { + if offset >= count { return false } + if unsafe self[unchecked: offset] != otherElement { return false } + offset += 1 + } + return offset == count + } +} diff --git a/Sources/Span/StdlibOutputSpanExtensions.swift b/Sources/Span/StdlibOutputSpanExtensions.swift new file mode 100644 index 000000000..fce41a049 --- /dev/null +++ b/Sources/Span/StdlibOutputSpanExtensions.swift @@ -0,0 +1,88 @@ +//===--- StdlibOutputSpanExtensions.swift ---------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 2025 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 +// +//===----------------------------------------------------------------------===// + +extension Array { + + @available(macOS 9999, *) + public init( + capacity: Int, + initializingWith initializer: (inout OutputSpan) throws -> Void + ) rethrows { + try unsafe self.init( + unsafeUninitializedCapacity: capacity, + initializingWith: { (buffer, count) in + let pointer = unsafe buffer.baseAddress.unsafelyUnwrapped + var output = OutputSpan( + _initializing: pointer, capacity: buffer.count + ) + try initializer(&output) + let initialized = unsafe output.relinquishBorrowedMemory() + unsafe assert(initialized.baseAddress == buffer.baseAddress) + count = initialized.count + } + ) + } +} + +extension String { + + // also see https://github.com/apple/swift/pull/23050 + // and `final class __SharedStringStorage` + + @available(macOS 9999, *) + public init( + utf8Capacity capacity: Int, + initializingWith initializer: (inout OutputSpan) throws -> Void + ) rethrows { + try unsafe self.init( + unsafeUninitializedCapacity: capacity, + initializingUTF8With: { buffer in + let pointer = unsafe buffer.baseAddress.unsafelyUnwrapped + var output = OutputSpan( + _initializing: pointer, capacity: buffer.count + ) + try initializer(&output) + let initialized = unsafe output.relinquishBorrowedMemory() + unsafe assert(initialized.baseAddress == buffer.baseAddress) + return initialized.count + } + ) + } +} + +import Foundation + +extension Data { + + @available(macOS 9999, *) + public init( + capacity: Int, + initializingWith initializer: (inout OutputSpan) throws -> Void + ) rethrows { + self = Data(count: capacity) // initialized with zeroed buffer + let count = unsafe try self.withUnsafeMutableBytes { rawBuffer in + unsafe try rawBuffer.withMemoryRebound(to: UInt8.self) { buffer in + unsafe buffer.deinitialize() + let pointer = unsafe buffer.baseAddress.unsafelyUnwrapped + var output = OutputSpan( + _initializing: pointer, capacity: capacity + ) + try initializer(&output) + let initialized = unsafe output.relinquishBorrowedMemory() + unsafe assert(initialized.baseAddress == buffer.baseAddress) + return initialized.count + } + } + assert(count <= self.count) + self.replaceSubrange(count.. Bool { + (a.baseAddress == b.baseAddress) && (a.count == b.count) + } +} + +extension UnsafeMutableBufferPointer where Element: ~Copyable { + /// Returns a Boolean value indicating whether two + /// `UnsafeMutableBufferPointer` instances refer to the same region in + /// memory. + @inlinable @inline(__always) + public static func ===(_ a: Self, _ b: Self) -> Bool { + (a.baseAddress == b.baseAddress) && (a.count == b.count) + } +} + +extension UnsafeRawBufferPointer { + /// Returns a Boolean value indicating whether two `UnsafeRawBufferPointer` + /// instances refer to the same region in memory. + @inlinable @inline(__always) + public static func ===(_ a: Self, _ b: Self) -> Bool { + (a.baseAddress == b.baseAddress) && (a.count == b.count) + } +} + +extension UnsafeMutableRawBufferPointer { + /// Returns a Boolean value indicating whether two + /// `UnsafeMutableRawBufferPointer` instances refer to the same region in + /// memory. + @inlinable @inline(__always) + public static func ===(_ a: Self, _ b: Self) -> Bool { + (a.baseAddress == b.baseAddress) && (a.count == b.count) + } +} diff --git a/Tests/DequeTests/DequeTests.swift b/Tests/DequeTests/DequeTests.swift index 34c947fe2..310832567 100644 --- a/Tests/DequeTests/DequeTests.swift +++ b/Tests/DequeTests/DequeTests.swift @@ -17,6 +17,11 @@ import _CollectionsTestSupport @_spi(Testing) import DequeModule #endif +extension Deque { + var _capacity: Int { _unstableCapacity } + var _startSlot: Int { _unstableStartSlot } +} + final class DequeTests: CollectionTestCase { func test_testingSPIs() { let deque = Deque(_capacity: 5, startSlot: 2, contents: [10, 20, 30, 40]) @@ -176,16 +181,18 @@ final class DequeTests: CollectionTestCase { let d1 = Deque>( unsafeUninitializedCapacity: cap, initializingWith: { target, c in - expectNotNil(target.baseAddress) expectEqual(target.count, cap) expectEqual(c, 0) - contents.withUnsafeBufferPointer { source in - precondition(source.count <= target.count) - target.baseAddress!.initialize( - from: source.baseAddress!, - count: source.count) + if target.count > 0 { + expectNotNil(target.baseAddress) + contents.withUnsafeBufferPointer { source in + precondition(source.count <= target.count) + target.baseAddress!.initialize( + from: source.baseAddress!, + count: source.count) + } + c = count } - c = count }) expectEqualElements(d1, contents) } @@ -206,16 +213,18 @@ final class DequeTests: CollectionTestCase { try Deque>( unsafeUninitializedCapacity: cap, initializingWith: { target, c in - expectNotNil(target.baseAddress) expectEqual(target.count, cap) expectEqual(c, 0) - contents.withUnsafeBufferPointer { source in - precondition(source.count <= target.count) - target.baseAddress!.initialize( - from: source.baseAddress!, - count: source.count) + if target.count > 0 { + expectNotNil(target.baseAddress) + contents.withUnsafeBufferPointer { source in + precondition(source.count <= target.count) + target.baseAddress!.initialize( + from: source.baseAddress!, + count: source.count) + } + c = count } - c = count throw TestError(count) }) ) { error in diff --git a/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet Diffing Tests.swift b/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet Diffing Tests.swift index b5ea03f20..9d012a8f0 100644 --- a/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet Diffing Tests.swift +++ b/Tests/OrderedCollectionsTests/OrderedSet/OrderedSet Diffing Tests.swift @@ -18,13 +18,13 @@ import _CollectionsTestSupport #endif class MeasuringHashable: Hashable { - static var equalityChecks = 0 + nonisolated(unsafe) static var equalityChecks = 0 static func == (lhs: MeasuringHashable, rhs: MeasuringHashable) -> Bool { MeasuringHashable.equalityChecks += 1 return lhs._inner == rhs._inner } - static var hashChecks = 0 + nonisolated(unsafe) static var hashChecks = 0 func hash(into hasher: inout Hasher) { MeasuringHashable.hashChecks += 1 _inner.hash(into: &hasher) diff --git a/Tests/SpanTests/MutableRawSpanTests.swift b/Tests/SpanTests/MutableRawSpanTests.swift new file mode 100644 index 000000000..2dd1a4367 --- /dev/null +++ b/Tests/SpanTests/MutableRawSpanTests.swift @@ -0,0 +1,356 @@ +//===--- MutableSpanTests.swift -------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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 +// +//===----------------------------------------------------------------------===// + +import XCTest +import Span + +@available(macOS 9999, *) +final class MutableRawSpanTests: XCTestCase { + + func testBasicInitializer() { + var s = Array("\(#file)+\(#function)--\(Int.random(in: 1000...9999))".utf8) + s.withUnsafeMutableBytes { + let b = MutableRawSpan(_unsafeBytes: $0) + XCTAssertEqual(b.byteCount, $0.count) + XCTAssertFalse(b.isEmpty) + XCTAssertEqual(b.byteOffsets, 0..<$0.count) + } + } + + func testIsEmpty() { + var array = [0, 1, 2] + array.withUnsafeMutableBufferPointer { + var span = MutableRawSpan(_unsafeElements: $0) + XCTAssertFalse(span.isEmpty) + + let e = $0.extracting(0..<0) + span = MutableRawSpan(_unsafeElements: e) + XCTAssertTrue(span.isEmpty) + } + } + + func testIndices() { + let capacity = 4 as UInt8 + var a = Array(0...stride + + let s0 = span.unsafeLoad(as: String.self) + XCTAssertEqual(s0.contains("0"), true) + let s1 = span.unsafeLoad(fromByteOffset: stride, as: String.self) + XCTAssertEqual(s1.contains("1"), true) + let s2 = span.unsafeLoad(fromUncheckedByteOffset: 2*stride, as: String.self) + XCTAssertEqual(s2.contains("2"), true) + } + } + + func testLoadUnaligned() { + let capacity = 64 + var a = Array(0...stride/2, as: UInt.self + ) + } + XCTAssertEqual(a[0].littleEndian & 0xffff, 0xff) + XCTAssertEqual(a[0].bigEndian & 0xffff, 0xffff) + } + + public func testUpdateFromSequence() { + let capacity = 8 + var a = Array(repeating: Int.max, count: capacity) + XCTAssertEqual(a.allSatisfy({ $0 == .max }), true) + a.withUnsafeMutableBufferPointer { + let empty = UnsafeMutableBufferPointer(start: nil, count: 0) + var span = MutableRawSpan(_unsafeElements: empty) + var (iterator, updated) = span.update(from: 0..<0) + XCTAssertNil(iterator.next()) + XCTAssertEqual(updated, 0) + + span = MutableRawSpan(_unsafeElements: $0) + (iterator, updated) = span.update(from: 0..<0) + XCTAssertNil(iterator.next()) + XCTAssertEqual(updated, 0) + + (iterator, updated) = span.update(from: 0..<10000) + XCTAssertNotNil(iterator.next()) + XCTAssertEqual(updated, capacity*MemoryLayout.stride) + } + XCTAssertEqual(a.elementsEqual(0..()) + XCTAssertEqual(a.allSatisfy({ $0 == .max }), true) + a.withUnsafeMutableBytes { + let emptyPrefix = $0.prefix(0) + var span = MutableRawSpan(_unsafeBytes: emptyPrefix) + var updated = span.update(fromContentsOf: e) + XCTAssertEqual(updated, 0) + + + updated = span.update(fromContentsOf: AnyCollection(e)) + XCTAssertEqual(updated, 0) + + span = MutableRawSpan(_unsafeBytes: $0) + updated = span.update(fromContentsOf: 0...stride) + } + XCTAssertEqual(a.elementsEqual(0...stride) + } + XCTAssertEqual(a.elementsEqual(0...stride) + } + } + XCTAssertEqual(a.allSatisfy({ $0 == Int.min }), true) + + a.withUnsafeMutableBytes { + var span = MutableRawSpan(_unsafeBytes: $0) + let array = Array(0...stride) + } + } + XCTAssertEqual(a.elementsEqual(0..) { + s[0] += 100 + } + + public func testSliceAsExtractingFunction() { + let count = 8 + let b = UnsafeMutableBufferPointer.allocate(capacity: count) + _ = b.initialize(fromContentsOf: 0...allocate(capacity: count) + _ = b.initialize(fromContentsOf: 0..) { + s[s.offset] += 100 + } + + public func testSliceAsWrapperType() { + let count = 8 + let b = UnsafeMutableBufferPointer.allocate(capacity: count) + _ = b.initialize(fromContentsOf: 0...allocate(capacity: count) + _ = b.initialize(fromContentsOf: 0..(_unsafeStart: rp, byteCount: bc) + XCTAssertEqual(b.count, capacity) + + let stride = MemoryLayout.stride + let r = MutableSpan(_unsafeBytes: $0.dropFirst(stride)) + XCTAssertEqual(r.count, (capacity-1)*stride) + XCTAssertEqual(r.count, bc-stride) + } + + let v = UnsafeMutableRawBufferPointer(start: nil, count: 0) + let m = MutableSpan(_unsafeBytes: v) + XCTAssertEqual(m.count, 0) + } + + func testIsEmpty() { + var array = [0, 1, 2] + array.withUnsafeMutableBufferPointer { + let span = MutableSpan(_unsafeElements: $0) + let e = span.isEmpty + XCTAssertFalse(e) + } + + array = [] + array.withUnsafeMutableBufferPointer { + let span = MutableSpan(_unsafeElements: $0) + let e = span.isEmpty + XCTAssertTrue(e) + } + } + + func testSpanFromMutableSpan() { + var array = [0, 1, 2] + array.withUnsafeMutableBufferPointer { + let mutable = MutableSpan(_unsafeElements: $0) + let immutable = Span(_unsafeMutableSpan: mutable) + XCTAssertEqual(mutable.count, immutable.count) + } + } + + func testRawSpanFromMutableSpan() { + let count = 4 + var array = Array(0...stride) + } + } + + func testSpanIndices() { + let capacity = 4 + var a = Array(0..(unsafeUninitializedCapacity: capacity) { + for i in $0.indices { + $0.initializeElement(at: i, to: .random(in: 0..<10)) + } + $1 = $0.count + } + a.withUnsafeMutableBufferPointer { + var v1 = MutableSpan(_unsafeElements: $0) + + XCTAssertEqual(v1._elementsEqual(Span(_unsafeMutableSpan: v1)._extracting(first: 1)), false) + XCTAssertEqual(Span(_unsafeMutableSpan: v1)._extracting(first: 0)._elementsEqual(Span(_unsafeMutableSpan: v1)._extracting(last: 0)), true) + XCTAssertEqual(v1._elementsEqual(v1), true) + XCTAssertEqual(Span(_unsafeMutableSpan: v1)._extracting(first: 3)._elementsEqual(Span(_unsafeMutableSpan: v1)._extracting(last: 3)), false) + + v1[0] = 0 + + let s = v1.span + var b = s.withUnsafeBufferPointer { Array($0) } + b.withUnsafeMutableBufferPointer { + let v2 = MutableSpan(_unsafeElements: $0) + let equal = v1._elementsEqual(v2) + XCTAssertEqual(equal, true) + } + } + } + + func testElementsEqualCollection() { + let capacity = 4 + var a = Array(0..(start: nil, count: 0) + var span = MutableSpan(_unsafeElements: empty) + var (iterator, updated) = span.update(from: 0..<0) + XCTAssertNil(iterator.next()) + XCTAssertEqual(updated, 0) + + span = MutableSpan(_unsafeElements: $0) + (iterator, updated) = span.update(from: 0..<0) + XCTAssertNil(iterator.next()) + XCTAssertEqual(updated, 0) + + (iterator, updated) = span.update(from: 0..<10000) + XCTAssertNotNil(iterator.next()) + XCTAssertEqual(updated, capacity) + } + XCTAssertEqual(a.elementsEqual(0...allocate(capacity: capacity) + + a.withUnsafeMutableBufferPointer { + var span = MutableSpan(_unsafeElements: $0) + + var o = OutputSpan(_initializing: b) + o.append(fromContentsOf: (0...allocate(capacity: count) + _ = b.initialize(fromContentsOf: 0..(start: nil, count: 0) + defer { _ = e } + + var m = MutableSpan(_unsafeElements: b) + m[0] = 100 + XCTAssertEqual(m.count, count) + XCTAssertEqual(m[0], 100) + + var s = m.span + XCTAssertEqual(s.count, m.count) + XCTAssertEqual(s[0], m[0]) + + // we're done using `s` before it gets reassigned + m.update(repeating: 7) + + s = m.span + +// m[0] = -1 // exclusivity violation + + XCTAssertEqual(s.count, m.count) + XCTAssertEqual(s[0], m[0]) + } + + public func testSwapAt() { + let count = 8 + var array = Array(0.. + var span = MutableSpan(_unsafeElements: $0) + XCTAssertEqual(span.count, capacity) + + prefix = span._extracting(first: 1) + XCTAssertEqual(prefix[0], 0) + + prefix = span._extracting(first: capacity) + XCTAssertEqual(prefix[capacity-1], UInt8(capacity-1)) + + prefix = span._extracting(droppingLast: capacity) + XCTAssertEqual(prefix.isEmpty, true) + + prefix = span._extracting(droppingLast: 1) + XCTAssertEqual(prefix[capacity-2], UInt8(capacity-2)) + } + + do { + let b = UnsafeMutableBufferPointer(start: nil, count: 0) + var span = MutableSpan(_unsafeElements: b) + XCTAssertEqual(span.count, b.count) + XCTAssertEqual(span._extracting(first: 1).count, b.count) + XCTAssertEqual(span._extracting(droppingLast: 1).count, b.count) + } + } + + func testSuffix() { + let capacity = 4 + var a = Array(0.. + var span = MutableSpan(_unsafeElements: $0) + XCTAssertEqual(span.count, capacity) + + suffix = span._extracting(last: capacity) + XCTAssertEqual(suffix[0], 0) + + suffix = span._extracting(last: capacity-1) + XCTAssertEqual(suffix[0], 1) + + suffix = span._extracting(last: 1) + XCTAssertEqual(suffix[0], UInt8(capacity-1)) + + suffix = span._extracting(droppingFirst: capacity) + XCTAssertTrue(suffix.isEmpty) + + suffix = span._extracting(droppingFirst: 1) + XCTAssertEqual(suffix[0], 1) + } + + do { + let b = UnsafeMutableBufferPointer(start: nil, count: 0) + var span = MutableSpan(_unsafeElements: b) + XCTAssertEqual(span.count, b.count) + XCTAssertEqual(span._extracting(last: 1).count, b.count) + XCTAssertEqual(span._extracting(droppingFirst: 1).count, b.count) + } + } +} diff --git a/Tests/SpanTests/OutputSpanTests.swift b/Tests/SpanTests/OutputSpanTests.swift new file mode 100644 index 000000000..fcac8ce0e --- /dev/null +++ b/Tests/SpanTests/OutputSpanTests.swift @@ -0,0 +1,266 @@ +//===--- OutputSpanTests.swift --------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 2025 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 +// +//===----------------------------------------------------------------------===// + +import XCTest +import Span + +@available(macOS 9999, *) +struct Allocation: ~Copyable { + let allocation: UnsafeMutablePointer + let capacity: Int + var count: Int? = nil + + init(of count: Int = 1, _ t: T.Type) { + precondition(count >= 0) + capacity = count + allocation = UnsafeMutablePointer.allocate(capacity: capacity) + } + + var isEmpty: Bool { (count ?? 0) == 0 } + + mutating func initialize( + _ body: (/* mutating */ inout OutputSpan) throws(E) -> Void + ) throws(E) { + if count != nil { fatalError() } + var outputBuffer = OutputSpan( + _initializing: allocation, capacity: capacity + ) + do { + try body(&outputBuffer) + let initialized = outputBuffer.relinquishBorrowedMemory() + assert(initialized.baseAddress == allocation) + count = initialized.count + } + catch { + outputBuffer.removeAll() + let empty = outputBuffer.relinquishBorrowedMemory() + assert(empty.baseAddress == allocation) + assert(empty.count == 0) + throw error + } + } + + borrowing func withSpan( + _ body: (borrowing Span) throws(E) -> R + ) throws(E) -> R { + try body(Span(_unsafeStart: allocation, count: count ?? 0)) + } + + deinit { + if let count { + allocation.deinitialize(count: count) + } + allocation.deallocate() + } +} + +enum MyTestError: Error { case error } + +@available(macOS 9999, *) +final class OutputSpanTests: XCTestCase { + + func testOutputBufferCreation() { + let c = 48 + let allocation = UnsafeMutablePointer.allocate(capacity: c) + defer { allocation.deallocate() } + + let ob = OutputSpan(_initializing: allocation, capacity: c) + let initialized = ob.relinquishBorrowedMemory() + XCTAssertNotNil(initialized.baseAddress) + XCTAssertEqual(initialized.count, 0) + } + + func testOutputBufferDeinitWithoutRelinquishingMemory() { + let c = 48 + let allocation = UnsafeMutableBufferPointer.allocate(capacity: c) + defer { allocation.deallocate() } + + var ob = OutputSpan(_initializing: Slice(base: allocation, bounds: 0..(_initializing: allocation[8...]) + let r = Int16.max>>2 + ob.append(r) + _ = ob.relinquishBorrowedBytes() + + let o = allocation.load(fromByteOffset: 8, as: Int16.self) + XCTAssertEqual(o, r) + } + + func testInitializeBufferByAppendingRepeatedElements() { + var a = Allocation(of: 48, Int.self) + let c = 10 + a.initialize { + $0.append(repeating: c, count: c) + let oops = $0.removeLast() + XCTAssertEqual(oops, c) + XCTAssertEqual($0.count, c-1) + } + a.withSpan { span in + XCTAssertEqual(span.count, c-1) + XCTAssert(span._elementsEqual(Array(repeating: c, count: c-1))) + } + } + + func testInitializeBufferFromSequence() { + var a = Allocation(of: 48, Int.self) + a.initialize { + var it = $0.append(from: 0..<18) + XCTAssertNil(it.next()) + } + a.withSpan { span in + XCTAssertEqual(span.count, 18) + XCTAssert(span._elementsEqual(0..<18)) + } + } + + func testInitializeBufferFromCollectionNotContiguous() { + var a = Allocation(of: 48, Int.self) + let c = 24 + a.initialize { + $0.append(fromContentsOf: 0...allocate(capacity: c) + for i in 0.. 0) + throw MyTestError.error + } + } + catch MyTestError.error { + XCTAssertEqual(a.isEmpty, true) + } + } + + func testMutateOutputSpan() throws { + let b = UnsafeMutableBufferPointer.allocate(capacity: 10) + defer { b.deallocate() } + + var span = OutputSpan(_initializing: b) + XCTAssertEqual(span.count, 0) + span.append(fromContentsOf: 1...9) + XCTAssertEqual(span.count, 9) + + var mutable = span.mutableSpan +// span.append(20) // exclusivity violation + for i in 0...stride) + XCTAssertFalse(span.isEmpty) + } + } + + func testInitWithEmptySpanOfIntegers() { + let a: [Int] = [] + a.withUnsafeBufferPointer { + let intSpan = Span(_unsafeElements: $0) + let span = RawSpan(_elements: intSpan) + XCTAssertTrue(span.isEmpty) + } + } + + func testInitWithRawBytes() { + let capacity = 4 + var a = Array(0...stride) + } + + a.withUnsafeMutableBytes { + let span = RawSpan(_unsafeBytes: $0) + XCTAssertEqual(span.byteCount, capacity*MemoryLayout.stride) + } + } + + func testWithRawPointer() { + let capacity = 4 + var a = Array(0...stride + ) + XCTAssertEqual(span.byteCount, $0.count) + } + + a.withUnsafeMutableBytes { + let pointer = $0.baseAddress! + let span = RawSpan( + _unsafeStart: pointer, + byteCount: capacity*MemoryLayout.stride + ) + XCTAssertEqual(span.byteCount, $0.count) + } + } + + func testLoad() { + let capacity = 4 + let s = (0...stride + + let s0 = span.unsafeLoad(as: String.self) + XCTAssertEqual(s0.contains("0"), true) + let s1 = span.unsafeLoad(fromByteOffset: stride, as: String.self) + XCTAssertEqual(s1.contains("1"), true) + let s2 = span.unsafeLoad(fromUncheckedByteOffset: 2*stride, as: String.self) + XCTAssertEqual(s2.contains("2"), true) + } + } + + func testLoadUnaligned() { + let capacity = 64 + let a = Array(0..? + bounds = span.byteOffsets(of: subSpan1) + XCTAssertEqual(bounds, span.byteOffsets.prefix(6)) + bounds = span.byteOffsets(of: subSpan2) + XCTAssertEqual(bounds, span.byteOffsets.suffix(6)) + bounds = subSpan2.byteOffsets(of: subSpan1) + XCTAssertNil(bounds) + bounds = subSpan1.byteOffsets(of: subSpan2) + XCTAssertNil(bounds) + bounds = subSpan2.byteOffsets(of: span) + XCTAssertNil(bounds) + bounds = nilSpan.byteOffsets(of: emptySpan) + XCTAssertNil(bounds) + bounds = span.byteOffsets(of: nilSpan) + XCTAssertNil(bounds) + bounds = nilSpan.byteOffsets(of: nilSpan) + XCTAssertEqual(bounds, 0..<0) + } +} diff --git a/Tests/SpanTests/SpanTests.swift b/Tests/SpanTests/SpanTests.swift new file mode 100644 index 000000000..596547111 --- /dev/null +++ b/Tests/SpanTests/SpanTests.swift @@ -0,0 +1,415 @@ +//===--- SpanTests.swift --------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 2025 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 +// +//===----------------------------------------------------------------------===// + +import XCTest +import Span + +@available(macOS 9999, *) +final class SpanTests: XCTestCase { + + func testInitWithOrdinaryElement() { + let capacity = 4 + var s = (0..(_unsafeBytes: $0) + XCTAssertEqual(b.count, capacity) + + let r = Span(_unsafeBytes: $0) + XCTAssertEqual(r.count, capacity*MemoryLayout.stride) + + let p = $0.baseAddress! + let span = Span( + _unsafeStart: p, byteCount: capacity*MemoryLayout.stride + ) + XCTAssertEqual(span.count, capacity) + } + + a.withUnsafeMutableBytes { + let b = Span(_unsafeBytes: $0) + XCTAssertEqual(b.count, capacity) + + let p = $0.baseAddress! + let span = Span( + _unsafeStart: p, byteCount: capacity*MemoryLayout.stride + ) + XCTAssertEqual(span.count, capacity) + } + } + + func testIsEmpty() { + let capacity = 4 + let a = Array(0...stride) + } + } + + func testSpanIndices() { + let capacity = 4 + let a = Array(0..(unsafeUninitializedCapacity: capacity) { + for i in $0.indices { + $0.initializeElement(at: i, to: .random(in: 0..<10)) + } + $1 = $0.count + } + a.withUnsafeBufferPointer { + let span = Span(_unsafeElements: $0) + + XCTAssertEqual(span._elementsEqual(span._extracting(first: 1)), false) + XCTAssertEqual(span._extracting(0..<0)._elementsEqual(span._extracting(last: 0)), true) + XCTAssertEqual(span._elementsEqual(span), true) + XCTAssertEqual(span._extracting(0..<3)._elementsEqual(span._extracting(last: 3)), false) + + let copy = span.withUnsafeBufferPointer(Array.init) + copy.withUnsafeBufferPointer { + let spanOfCopy = Span(_unsafeElements: $0) + XCTAssertTrue(span._elementsEqual(spanOfCopy)) + } + } + } + + func testElementsEqualCollection() { + let capacity = 4 + let a = Array(0..(start: nil, count: 0) + let span = Span(_unsafeElements: b) + XCTAssertEqual(span.count, b.count) + XCTAssertEqual(span._extracting(first: 1).count, b.count) + XCTAssertEqual(span._extracting(droppingLast: 1).count, b.count) + } + } + + func testSuffix() { + let capacity = 4 + let a = Array(0..(_unsafeElements: $0) + XCTAssertEqual(span.count, capacity) + XCTAssertEqual(span._extracting(last: capacity)[0], 0) + XCTAssertEqual(span._extracting(last: capacity-1)[0], 1) + XCTAssertEqual(span._extracting(last: 1)[0], capacity-1) + XCTAssertEqual(span._extracting(droppingFirst: capacity).count, 0) + XCTAssertEqual(span._extracting(droppingFirst: 1)[0], 1) + } + + do { + let b = UnsafeBufferPointer(start: nil, count: 0) + let span = Span(_unsafeElements: b) + XCTAssertEqual(span.count, b.count) + XCTAssertEqual(span._extracting(last: 1).count, b.count) + XCTAssertEqual(span._extracting(droppingFirst: 1).count, b.count) + } + } + + public func testWithUnsafeBuffer() { + let capacity: UInt8 = 64 + let a = Array(0...allocate(capacity: 8) + _ = b.initialize(fromContentsOf: 0..<8) + defer { b.deallocate() } + + let span = Span(_unsafeElements: b) + let pre = span._extracting(first: 6) + let suf = span._extracting(last: 6) + + XCTAssertFalse( + pre.isIdentical(to: suf) + ) + XCTAssertFalse( + pre.isIdentical(to: span) + ) + XCTAssertTrue( + pre._extracting(last: 4).isIdentical(to: suf._extracting(first: 4)) + ) + } + + func testIndicesOf() { + let b = UnsafeMutableBufferPointer.allocate(capacity: 8) + _ = b.initialize(fromContentsOf: 0..<8) + defer { b.deallocate() } + + let span = Span(_unsafeElements: b) + let subSpan1 = span._extracting(first: 6) + let subSpan2 = span._extracting(last: 6) + let emptySpan = span._extracting(first: 0) +/* This isn't relevant until we can support unaligned spans + let unalignedSpan = RawSpan(_unsafeSpan: span) + ._extracting(droppingFirst: 6) + ._extracting(droppingLast: 2) + .unsafeView(as: Int.self) +*/ + let nilBuffer = UnsafeBufferPointer(start: nil, count: 0) + let nilSpan = Span(_unsafeElements: nilBuffer) + + var bounds: Range? + bounds = span.indices(of: subSpan1) + XCTAssertEqual(bounds, span.indices.prefix(6)) + bounds = span.indices(of: subSpan2) + XCTAssertEqual(bounds, span.indices.suffix(6)) + bounds = subSpan2.indices(of: subSpan1) + XCTAssertNil(bounds) + bounds = subSpan1.indices(of: subSpan2) + XCTAssertNil(bounds) + bounds = subSpan2.indices(of: span) + XCTAssertNil(bounds) + bounds = nilSpan.indices(of: emptySpan) + XCTAssertNil(bounds) + bounds = span.indices(of: nilSpan) + XCTAssertNil(bounds) + bounds = nilSpan.indices(of: nilSpan) + XCTAssertEqual(bounds, 0..<0) + } + +// func testSpanIterator() { +// class C { +// let id: Int +// init(id: Int) { self.id = id } +// } +// +// let b = UnsafeMutableBufferPointer.allocate(capacity: 8) +// _ = b.initialize(fromContentsOf: (0..<8).map(C.init(id:))) +// defer { +// b.deinitialize() +// b.deallocate() +// } +// +// let span = Span(_unsafeElements: b) +// var iterator = Span.Iterator(from: span) +// var i = 0 +// while let c = iterator.next() { +// XCTAssertEqual(i, c.id) +// i += 1 +// } +// } + + func testTypeErasedSpanOfBitwiseCopyable() { + let b = UnsafeMutableRawBufferPointer.allocate(byteCount: 64, alignment: 8) + defer { b.deallocate() } + let initialized = b.initializeMemory(as: UInt8.self, fromContentsOf: 0..<64) + XCTAssertEqual(initialized.count, 64) + defer { initialized.deinitialize() } + + func test(_ span: Span) -> T { + span[0] + } + + // let span = Span(_unsafeBytes: b.dropFirst().dropLast(7)) + + let suffix = b.dropFirst(4) + let span = Span(_unsafeBytes: suffix) + let first = test(span) + XCTAssertEqual(first, 0x07060504) + } +} diff --git a/Tests/SpanTests/StdlibOutputSpanExtensionTests.swift b/Tests/SpanTests/StdlibOutputSpanExtensionTests.swift new file mode 100644 index 000000000..a8f883226 --- /dev/null +++ b/Tests/SpanTests/StdlibOutputSpanExtensionTests.swift @@ -0,0 +1,54 @@ +//===--- StdlibOutputSpanExtensionTests.swift -----------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 2025 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 +// +//===----------------------------------------------------------------------===// + +import XCTest +import Foundation +import Span + +@available(macOS 9999, *) +final class StdlibOutputSpanExtensionTests: XCTestCase { + + func testArrayInitializationExample() { + var array: [UInt8] + array = Array(capacity: 32, initializingWith: { output in + for i in 0..<(output.capacity/2) { + output.append(UInt8(clamping: i)) + } + }) + XCTAssertEqual(array.count, 16) + XCTAssert(array.elementsEqual(0..<16)) + XCTAssertGreaterThanOrEqual(array.capacity, 32) + } + + func testDataInitializationExample() { + var data: Data + data = Data(capacity: 32, initializingWith: { output in + for i in 0..<(output.capacity/2) { + output.append(UInt8(clamping: i)) + } + }) + XCTAssertEqual(data.count, 16) + XCTAssert(data.elementsEqual(0..<16)) + } + + func testStringInitializationExample() { + var string: String + let c = UInt8(ascii: "A") + string = String(utf8Capacity: 32, initializingWith: { output in + for i in 0..<(output.capacity/2) { + output.append(c + UInt8(clamping: i)) + } + }) + XCTAssertEqual(string.utf8.count, 16) + XCTAssert(string.utf8.elementsEqual(c..<(c+16))) + } +} diff --git a/Tests/_CollectionsTestSupport/AssertionContexts/Assertions.swift b/Tests/_CollectionsTestSupport/AssertionContexts/Assertions.swift index 7224be07c..554a4c935 100644 --- a/Tests/_CollectionsTestSupport/AssertionContexts/Assertions.swift +++ b/Tests/_CollectionsTestSupport/AssertionContexts/Assertions.swift @@ -15,7 +15,7 @@ import XCTest public func expectFailure( _ message: @autoclosure () -> String = "", trapping: Bool = false, - file: StaticString = #file, + file: StaticString = (#file), line: UInt = #line ) { let message = message() diff --git a/Tests/_CollectionsTestSupport/AssertionContexts/TestContext.swift b/Tests/_CollectionsTestSupport/AssertionContexts/TestContext.swift index ad3b89af4..d5eaef556 100644 --- a/Tests/_CollectionsTestSupport/AssertionContexts/TestContext.swift +++ b/Tests/_CollectionsTestSupport/AssertionContexts/TestContext.swift @@ -20,7 +20,7 @@ public final class TestContext { internal var _trace: [Entry] = [] // FIXME: This ought to be a thread-local variable. - internal static var _current: TestContext? + nonisolated(unsafe) internal static var _current: TestContext? public init() {} } @@ -208,7 +208,7 @@ extension TestContext { @inline(never) public func debuggerBreak( _ message: String, - file: StaticString = #file, + file: StaticString = (#file), line: UInt = #line ) { XCTFail(message, file: file, line: line) diff --git a/Xcode/Collections.xcodeproj/project.pbxproj b/Xcode/Collections.xcodeproj/project.pbxproj index 2eaac6343..637baf8bf 100644 --- a/Xcode/Collections.xcodeproj/project.pbxproj +++ b/Xcode/Collections.xcodeproj/project.pbxproj @@ -7,13 +7,27 @@ objects = { /* Begin PBXBuildFile section */ + 7D380E052C6ABF6800AD8F58 /* NewArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D380E042C6ABF6000AD8F58 /* NewArray.swift */; }; + 7D380E072C6BFB9B00AD8F58 /* LifetimeOverride.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D380E062C6BFB9500AD8F58 /* LifetimeOverride.swift */; }; + 7D380E0D2C6D565300AD8F58 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D380E0C2C6D565000AD8F58 /* Shared.swift */; }; + 7D9514292C750E08009C37DD /* RigidDeque.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC142C5AB45600A1BF15 /* RigidDeque.swift */; }; + 7D95142A2C753862009C37DD /* Deque.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBB29CA70F3004483EB /* Deque.swift */; }; + 7D95142B2C753955009C37DD /* Deque+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC829CA70F3004483EB /* Deque+Equatable.swift */; }; + 7D95142C2C7539B9009C37DD /* Deque+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB829CA70F3004483EB /* Deque+Collection.swift */; }; + 7D95142D2C753C1B009C37DD /* Deque+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC129CA70F3004483EB /* Deque+Codable.swift */; }; + 7D95142E2C753C2E009C37DD /* Deque+CustomReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBA29CA70F3004483EB /* Deque+CustomReflectable.swift */; }; + 7D95142F2C753C34009C37DD /* Deque+Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC529CA70F3004483EB /* Deque+Descriptions.swift */; }; + 7D9514302C753C45009C37DD /* Deque+ExpressibleByArrayLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC729CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift */; }; + 7D9514312C753C4A009C37DD /* Deque+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC629CA70F3004483EB /* Deque+Extras.swift */; }; + 7D9514322C753C55009C37DD /* Deque+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBD29CA70F3004483EB /* Deque+Hashable.swift */; }; + 7D9514332C753C5B009C37DD /* Deque+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC229CA70F3004483EB /* Deque+Testing.swift */; }; + 7D9514342C753C63009C37DD /* DynamicDeque.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC132C5AB45600A1BF15 /* DynamicDeque.swift */; }; + 7D9514352C753FC1009C37DD /* Deque+Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D380E022C6AAA9300AD8F58 /* Deque+Container.swift */; }; 7D9B859729E4F74400B291CD /* BitArray+Shifts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9B859129E4F74400B291CD /* BitArray+Shifts.swift */; }; 7D9B859829E4F74400B291CD /* BitArray+Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9B859229E4F74400B291CD /* BitArray+Descriptions.swift */; }; 7D9B859929E4F74400B291CD /* BitArray+LosslessStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9B859329E4F74400B291CD /* BitArray+LosslessStringConvertible.swift */; }; 7D9B859A29E4F74400B291CD /* BitArray+RandomBits.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9B859429E4F74400B291CD /* BitArray+RandomBits.swift */; }; 7D9B859B29E4F74400B291CD /* BitArray+ExpressibleByStringLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9B859529E4F74400B291CD /* BitArray+ExpressibleByStringLiteral.swift */; }; - 7DB0AE762B6E06B300602A20 /* Specialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DB0AE752B6E06B300602A20 /* Specialize.swift */; }; - 7DB0AE772B6E06B300602A20 /* Specialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DB0AE752B6E06B300602A20 /* Specialize.swift */; }; 7DE91B2F29CA6721004483EB /* Collections.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DE91B2629CA6721004483EB /* Collections.framework */; }; 7DE9200D29CA70F3004483EB /* OrderedDictionary+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7529CA70F3004483EB /* OrderedDictionary+Equatable.swift */; }; 7DE9200E29CA70F3004483EB /* OrderedDictionary+ExpressibleByDictionaryLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91E7629CA70F3004483EB /* OrderedDictionary+ExpressibleByDictionaryLiteral.swift */; }; @@ -76,23 +90,8 @@ 7DE9204729CA70F3004483EB /* _HashTable+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB229CA70F3004483EB /* _HashTable+Testing.swift */; }; 7DE9204829CA70F3004483EB /* _HashTable+CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB329CA70F3004483EB /* _HashTable+CustomStringConvertible.swift */; }; 7DE9204929CA70F3004483EB /* _HashTable+BucketIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB429CA70F3004483EB /* _HashTable+BucketIterator.swift */; }; - 7DE9204B29CA70F3004483EB /* _DequeBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB729CA70F3004483EB /* _DequeBuffer.swift */; }; - 7DE9204C29CA70F3004483EB /* Deque+Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EB829CA70F3004483EB /* Deque+Collection.swift */; }; - 7DE9204E29CA70F3004483EB /* Deque+CustomReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBA29CA70F3004483EB /* Deque+CustomReflectable.swift */; }; - 7DE9204F29CA70F3004483EB /* Deque.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBB29CA70F3004483EB /* Deque.swift */; }; 7DE9205029CA70F3004483EB /* _DequeSlot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBC29CA70F3004483EB /* _DequeSlot.swift */; }; - 7DE9205129CA70F3004483EB /* Deque+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBD29CA70F3004483EB /* Deque+Hashable.swift */; }; - 7DE9205229CA70F3004483EB /* _UnsafeWrappedBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBE29CA70F3004483EB /* _UnsafeWrappedBuffer.swift */; }; - 7DE9205329CA70F3004483EB /* _DequeBufferHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBF29CA70F3004483EB /* _DequeBufferHeader.swift */; }; - 7DE9205429CA70F3004483EB /* Deque+Sendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC029CA70F3004483EB /* Deque+Sendable.swift */; }; - 7DE9205529CA70F3004483EB /* Deque+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC129CA70F3004483EB /* Deque+Codable.swift */; }; - 7DE9205629CA70F3004483EB /* Deque+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC229CA70F3004483EB /* Deque+Testing.swift */; }; - 7DE9205829CA70F3004483EB /* Deque._UnsafeHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC429CA70F3004483EB /* Deque._UnsafeHandle.swift */; }; - 7DE9205929CA70F3004483EB /* Deque+Descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC529CA70F3004483EB /* Deque+Descriptions.swift */; }; - 7DE9205A29CA70F3004483EB /* Deque+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC629CA70F3004483EB /* Deque+Extras.swift */; }; - 7DE9205B29CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC729CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift */; }; - 7DE9205C29CA70F3004483EB /* Deque+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC829CA70F3004483EB /* Deque+Equatable.swift */; }; - 7DE9205D29CA70F4004483EB /* Deque._Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EC929CA70F3004483EB /* Deque._Storage.swift */; }; + 7DE9205229CA70F3004483EB /* _UnsafeDequeSegments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91EBE29CA70F3004483EB /* _UnsafeDequeSegments.swift */; }; 7DE9205E29CA70F4004483EB /* BigString+Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ECD29CA70F3004483EB /* BigString+Metrics.swift */; }; 7DE9205F29CA70F4004483EB /* BigString+Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ECE29CA70F3004483EB /* BigString+Index.swift */; }; 7DE9206029CA70F4004483EB /* BigString+Summary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91ECF29CA70F3004483EB /* BigString+Summary.swift */; }; @@ -354,9 +353,6 @@ 7DE9221C29CA8576004483EB /* RangeReplaceableCollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921F729CA8576004483EB /* RangeReplaceableCollectionTests.swift */; }; 7DE9221D29CA8576004483EB /* DequeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921F829CA8576004483EB /* DequeTests.swift */; }; 7DE9221E29CA8576004483EB /* MutableCollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE921F929CA8576004483EB /* MutableCollectionTests.swift */; }; - 7DEBDAF229CBEE5300ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDACD29CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift */; }; - 7DEBDAF429CBEE5300ADC226 /* UnsafeMutablePointer+SE-0370.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDACF29CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift */; }; - 7DEBDAF529CBEE5300ADC226 /* UnsafeRawPointer extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD029CBEE5200ADC226 /* UnsafeRawPointer extensions.swift */; }; 7DEBDAF829CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD429CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift */; }; 7DEBDAF929CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD529CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift */; }; 7DEBDAFA29CBEE5300ADC226 /* RandomAccessCollection+Offsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD629CBEE5300ADC226 /* RandomAccessCollection+Offsets.swift */; }; @@ -373,9 +369,6 @@ 7DEBDB0F29CBF68900ADC226 /* UInt+reversed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE829CBEE5300ADC226 /* UInt+reversed.swift */; }; 7DEBDB1029CBF68900ADC226 /* UInt+first and last set bit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAEA29CBEE5300ADC226 /* UInt+first and last set bit.swift */; }; 7DEBDB1129CBF68900ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAEB29CBEE5300ADC226 /* FixedWidthInteger+roundUpToPowerOfTwo.swift */; }; - 7DEBDB1229CBF68D00ADC226 /* UnsafeMutablePointer+SE-0370.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDACF29CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift */; }; - 7DEBDB1429CBF68D00ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDACD29CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift */; }; - 7DEBDB1529CBF68D00ADC226 /* UnsafeRawPointer extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAD029CBEE5200ADC226 /* UnsafeRawPointer extensions.swift */; }; 7DEBDB1629CBF69F00ADC226 /* _UnsafeBitSet+Index.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDADF29CBEE5300ADC226 /* _UnsafeBitSet+Index.swift */; }; 7DEBDB1729CBF69F00ADC226 /* _UnsafeBitSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE029CBEE5300ADC226 /* _UnsafeBitSet.swift */; }; 7DEBDB1829CBF69F00ADC226 /* _UnsafeBitSet+_Word.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDAE129CBEE5300ADC226 /* _UnsafeBitSet+_Word.swift */; }; @@ -422,6 +415,25 @@ 7DEBDB9129CCE44A00ADC226 /* CollectionTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2429CCE43600ADC226 /* CollectionTestCase.swift */; }; 7DEBDB9229CCE44A00ADC226 /* StringConvertibleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBDB2B29CCE43600ADC226 /* StringConvertibleValue.swift */; }; 7DEBDB9D29CCE73D00ADC226 /* Collections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DE91F7B29CA70F3004483EB /* Collections.swift */; }; + 7DEBEC172C5AB45600A1BF15 /* _UnsafeDequeHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC122C5AB45600A1BF15 /* _UnsafeDequeHandle.swift */; }; + 7DEBEC252C5AE43C00A1BF15 /* CMakeLists.txt in Resources */ = {isa = PBXBuildFile; fileRef = 7DEBEC1E2C5AE43C00A1BF15 /* CMakeLists.txt */; }; + 7DEBEC262C5AE43C00A1BF15 /* RigidArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC1A2C5AE43C00A1BF15 /* RigidArray.swift */; }; + 7DEBEC272C5AE43C00A1BF15 /* DynamicArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC192C5AE43C00A1BF15 /* DynamicArray.swift */; }; + 7DEBEC282C5AE43C00A1BF15 /* Inout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC202C5AE43C00A1BF15 /* Inout.swift */; }; + 7DEBEC292C5AE43C00A1BF15 /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC182C5AE43C00A1BF15 /* Container.swift */; }; + 7DEBEC2A2C5AE43C00A1BF15 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC1C2C5AE43C00A1BF15 /* Box.swift */; }; + 7DEBEC2B2C5AE43C00A1BF15 /* UnsafeBufferPointer+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC232C5AE43C00A1BF15 /* UnsafeBufferPointer+Additions.swift */; }; + 7DEBEC2C2C5AE43C00A1BF15 /* RawSpan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC212C5AE43C00A1BF15 /* RawSpan.swift */; }; + 7DEBEC2D2C5AE43C00A1BF15 /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC1D2C5AE43C00A1BF15 /* Cell.swift */; }; + 7DEBEC2E2C5AE43C00A1BF15 /* Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC222C5AE43C00A1BF15 /* Span.swift */; }; + 7DEBEC2F2C5AE43C00A1BF15 /* ContiguousStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DEBEC1F2C5AE43C00A1BF15 /* ContiguousStorage.swift */; }; + BBD683962C7F857900B567A9 /* SpanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD683942C7F857900B567A9 /* SpanTests.swift */; }; + BBD683972C7F857900B567A9 /* Inout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD683922C7F857900B567A9 /* Inout.swift */; }; + BBD683982C7F857900B567A9 /* BoxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD6838E2C7F857900B567A9 /* BoxTests.swift */; }; + BBD683992C7F857900B567A9 /* CellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD6838F2C7F857900B567A9 /* CellTests.swift */; }; + BBD6839A2C7F857900B567A9 /* RawSpanTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD683932C7F857900B567A9 /* RawSpanTests.swift */; }; + BBD6839B2C7F857900B567A9 /* DynamicArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD683912C7F857900B567A9 /* DynamicArrayTests.swift */; }; + BBD6839C2C7F857900B567A9 /* ContiguousStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD683902C7F857900B567A9 /* ContiguousStorageTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -435,6 +447,10 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 7D380E022C6AAA9300AD8F58 /* Deque+Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Deque+Container.swift"; sourceTree = ""; }; + 7D380E042C6ABF6000AD8F58 /* NewArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewArray.swift; sourceTree = ""; }; + 7D380E062C6BFB9500AD8F58 /* LifetimeOverride.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LifetimeOverride.swift; sourceTree = ""; }; + 7D380E0C2C6D565000AD8F58 /* Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shared.swift; sourceTree = ""; }; 7D5A64D229CCE8CC00CB2595 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 7D5A64D329CCEE9A00CB2595 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 7D5A64D429CCEF1500CB2595 /* generate-sources.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "generate-sources.sh"; sourceTree = ""; }; @@ -448,8 +464,6 @@ 7D9B859429E4F74400B291CD /* BitArray+RandomBits.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+RandomBits.swift"; sourceTree = ""; }; 7D9B859529E4F74400B291CD /* BitArray+ExpressibleByStringLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BitArray+ExpressibleByStringLiteral.swift"; sourceTree = ""; }; 7D9B859C29E4F77D00B291CD /* Collections.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Collections.xctestplan; sourceTree = ""; }; - 7DB0AE712B6E067A00602A20 /* Specialize.swift.gyb */ = {isa = PBXFileReference; lastKnownFileType = text; path = Specialize.swift.gyb; sourceTree = ""; }; - 7DB0AE752B6E06B300602A20 /* Specialize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Specialize.swift; sourceTree = ""; }; 7DE91B2629CA6721004483EB /* Collections.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Collections.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7DE91B2E29CA6721004483EB /* CollectionsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CollectionsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 7DE91E7229CA70F3004483EB /* OrderedCollections.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = OrderedCollections.docc; sourceTree = ""; }; @@ -516,25 +530,20 @@ 7DE91EB329CA70F3004483EB /* _HashTable+CustomStringConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashTable+CustomStringConvertible.swift"; sourceTree = ""; }; 7DE91EB429CA70F3004483EB /* _HashTable+BucketIterator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "_HashTable+BucketIterator.swift"; sourceTree = ""; }; 7DE91EB529CA70F3004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; - 7DE91EB729CA70F3004483EB /* _DequeBuffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _DequeBuffer.swift; sourceTree = ""; }; 7DE91EB829CA70F3004483EB /* Deque+Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Collection.swift"; sourceTree = ""; }; 7DE91EB929CA70F3004483EB /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; 7DE91EBA29CA70F3004483EB /* Deque+CustomReflectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+CustomReflectable.swift"; sourceTree = ""; }; 7DE91EBB29CA70F3004483EB /* Deque.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deque.swift; sourceTree = ""; }; 7DE91EBC29CA70F3004483EB /* _DequeSlot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _DequeSlot.swift; sourceTree = ""; }; 7DE91EBD29CA70F3004483EB /* Deque+Hashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Hashable.swift"; sourceTree = ""; }; - 7DE91EBE29CA70F3004483EB /* _UnsafeWrappedBuffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _UnsafeWrappedBuffer.swift; sourceTree = ""; }; - 7DE91EBF29CA70F3004483EB /* _DequeBufferHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _DequeBufferHeader.swift; sourceTree = ""; }; - 7DE91EC029CA70F3004483EB /* Deque+Sendable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Sendable.swift"; sourceTree = ""; }; + 7DE91EBE29CA70F3004483EB /* _UnsafeDequeSegments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _UnsafeDequeSegments.swift; sourceTree = ""; }; 7DE91EC129CA70F3004483EB /* Deque+Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Codable.swift"; sourceTree = ""; }; 7DE91EC229CA70F3004483EB /* Deque+Testing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Testing.swift"; sourceTree = ""; }; 7DE91EC329CA70F3004483EB /* DequeModule.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = DequeModule.docc; sourceTree = ""; }; - 7DE91EC429CA70F3004483EB /* Deque._UnsafeHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deque._UnsafeHandle.swift; sourceTree = ""; }; 7DE91EC529CA70F3004483EB /* Deque+Descriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Descriptions.swift"; sourceTree = ""; }; 7DE91EC629CA70F3004483EB /* Deque+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Extras.swift"; sourceTree = ""; }; 7DE91EC729CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+ExpressibleByArrayLiteral.swift"; sourceTree = ""; }; 7DE91EC829CA70F3004483EB /* Deque+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Deque+Equatable.swift"; sourceTree = ""; }; - 7DE91EC929CA70F3004483EB /* Deque._Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Deque._Storage.swift; sourceTree = ""; }; 7DE91ECD29CA70F3004483EB /* BigString+Metrics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Metrics.swift"; sourceTree = ""; }; 7DE91ECE29CA70F3004483EB /* BigString+Index.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Index.swift"; sourceTree = ""; }; 7DE91ECF29CA70F3004483EB /* BigString+Summary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BigString+Summary.swift"; sourceTree = ""; }; @@ -815,12 +824,6 @@ 7DE921F729CA8576004483EB /* RangeReplaceableCollectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RangeReplaceableCollectionTests.swift; sourceTree = ""; }; 7DE921F829CA8576004483EB /* DequeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DequeTests.swift; sourceTree = ""; }; 7DE921F929CA8576004483EB /* MutableCollectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutableCollectionTests.swift; sourceTree = ""; }; - 7DEBDAC829CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UnsafeMutablePointer+SE-0370.swift.gyb"; sourceTree = ""; }; - 7DEBDAC929CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UnsafeMutableBufferPointer+SE-0370.swift.gyb"; sourceTree = ""; }; - 7DEBDACB29CBEE5200ADC226 /* UnsafeRawPointer extensions.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "UnsafeRawPointer extensions.swift.gyb"; sourceTree = ""; }; - 7DEBDACD29CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeMutableBufferPointer+SE-0370.swift"; sourceTree = ""; }; - 7DEBDACF29CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeMutablePointer+SE-0370.swift"; sourceTree = ""; }; - 7DEBDAD029CBEE5200ADC226 /* UnsafeRawPointer extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeRawPointer extensions.swift"; sourceTree = ""; }; 7DEBDAD129CBEE5200ADC226 /* Descriptions.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Descriptions.swift.gyb; sourceTree = ""; }; 7DEBDAD229CBEE5200ADC226 /* RandomAccessCollection+Offsets.swift.gyb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "RandomAccessCollection+Offsets.swift.gyb"; sourceTree = ""; }; 7DEBDAD429CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeMutableBufferPointer+Extras.swift"; sourceTree = ""; }; @@ -885,6 +888,27 @@ 7DEBDB9729CCE4A600ADC226 /* CollectionsTests.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = CollectionsTests.xcconfig; sourceTree = ""; }; 7DEBDB9829CCE4A600ADC226 /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = ""; }; 7DEBDB9929CCE4A600ADC226 /* Collections.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Collections.xcconfig; sourceTree = ""; }; + 7DEBEC122C5AB45600A1BF15 /* _UnsafeDequeHandle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _UnsafeDequeHandle.swift; sourceTree = ""; }; + 7DEBEC132C5AB45600A1BF15 /* DynamicDeque.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicDeque.swift; sourceTree = ""; }; + 7DEBEC142C5AB45600A1BF15 /* RigidDeque.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RigidDeque.swift; sourceTree = ""; }; + 7DEBEC182C5AE43C00A1BF15 /* Container.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = ""; }; + 7DEBEC192C5AE43C00A1BF15 /* DynamicArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicArray.swift; sourceTree = ""; }; + 7DEBEC1A2C5AE43C00A1BF15 /* RigidArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RigidArray.swift; sourceTree = ""; }; + 7DEBEC1C2C5AE43C00A1BF15 /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; + 7DEBEC1D2C5AE43C00A1BF15 /* Cell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cell.swift; sourceTree = ""; }; + 7DEBEC1E2C5AE43C00A1BF15 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 7DEBEC1F2C5AE43C00A1BF15 /* ContiguousStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContiguousStorage.swift; sourceTree = ""; }; + 7DEBEC202C5AE43C00A1BF15 /* Inout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Inout.swift; sourceTree = ""; }; + 7DEBEC212C5AE43C00A1BF15 /* RawSpan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawSpan.swift; sourceTree = ""; }; + 7DEBEC222C5AE43C00A1BF15 /* Span.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Span.swift; sourceTree = ""; }; + 7DEBEC232C5AE43C00A1BF15 /* UnsafeBufferPointer+Additions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UnsafeBufferPointer+Additions.swift"; sourceTree = ""; }; + BBD6838E2C7F857900B567A9 /* BoxTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxTests.swift; sourceTree = ""; }; + BBD6838F2C7F857900B567A9 /* CellTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellTests.swift; sourceTree = ""; }; + BBD683902C7F857900B567A9 /* ContiguousStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContiguousStorageTests.swift; sourceTree = ""; }; + BBD683912C7F857900B567A9 /* DynamicArrayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicArrayTests.swift; sourceTree = ""; }; + BBD683922C7F857900B567A9 /* Inout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Inout.swift; sourceTree = ""; }; + BBD683932C7F857900B567A9 /* RawSpanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawSpanTests.swift; sourceTree = ""; }; + BBD683942C7F857900B567A9 /* SpanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpanTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -938,13 +962,14 @@ children = ( 7DE91F2229CA70F3004483EB /* InternalCollectionsUtilities */, 7DE91F3929CA70F3004483EB /* BitCollections */, + 7DE91EB529CA70F3004483EB /* CMakeLists.txt */, 7DE91F7929CA70F3004483EB /* Collections */, 7DE91EB629CA70F3004483EB /* DequeModule */, + 7DEBEC242C5AE43C00A1BF15 /* Future */, 7DE91FA729CA70F3004483EB /* HashTreeCollections */, 7DE9200229CA70F3004483EB /* HeapModule */, 7DE91E7129CA70F3004483EB /* OrderedCollections */, 7DE91ECA29CA70F3004483EB /* RopeModule */, - 7DE91EB529CA70F3004483EB /* CMakeLists.txt */, ); name = Sources; path = ../Sources; @@ -1055,25 +1080,24 @@ 7DE91EB629CA70F3004483EB /* DequeModule */ = { isa = PBXGroup; children = ( - 7DE91EB729CA70F3004483EB /* _DequeBuffer.swift */, - 7DE91EBF29CA70F3004483EB /* _DequeBufferHeader.swift */, 7DE91EBC29CA70F3004483EB /* _DequeSlot.swift */, - 7DE91EBE29CA70F3004483EB /* _UnsafeWrappedBuffer.swift */, - 7DE91EC929CA70F3004483EB /* Deque._Storage.swift */, - 7DE91EC429CA70F3004483EB /* Deque._UnsafeHandle.swift */, + 7DEBEC122C5AB45600A1BF15 /* _UnsafeDequeHandle.swift */, + 7DE91EBE29CA70F3004483EB /* _UnsafeDequeSegments.swift */, + 7DE91EB929CA70F3004483EB /* CMakeLists.txt */, 7DE91EBB29CA70F3004483EB /* Deque.swift */, 7DE91EC129CA70F3004483EB /* Deque+Codable.swift */, 7DE91EB829CA70F3004483EB /* Deque+Collection.swift */, + 7D380E022C6AAA9300AD8F58 /* Deque+Container.swift */, 7DE91EBA29CA70F3004483EB /* Deque+CustomReflectable.swift */, 7DE91EC529CA70F3004483EB /* Deque+Descriptions.swift */, 7DE91EC829CA70F3004483EB /* Deque+Equatable.swift */, 7DE91EC729CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift */, 7DE91EC629CA70F3004483EB /* Deque+Extras.swift */, 7DE91EBD29CA70F3004483EB /* Deque+Hashable.swift */, - 7DE91EC029CA70F3004483EB /* Deque+Sendable.swift */, 7DE91EC229CA70F3004483EB /* Deque+Testing.swift */, 7DE91EC329CA70F3004483EB /* DequeModule.docc */, - 7DE91EB929CA70F3004483EB /* CMakeLists.txt */, + 7DEBEC132C5AB45600A1BF15 /* DynamicDeque.swift */, + 7DEBEC142C5AB45600A1BF15 /* RigidDeque.swift */, ); path = DequeModule; sourceTree = ""; @@ -1253,7 +1277,6 @@ isa = PBXGroup; children = ( 7DEBDAD329CBEE5300ADC226 /* autogenerated */, - 7DEBDAC729CBEE5200ADC226 /* Compatibility */, 7DEBDAE229CBEE5300ADC226 /* IntegerTricks */, 7DEBDADA29CBEE5300ADC226 /* UnsafeBitSet */, 7DE91F3029CA70F3004483EB /* _SortedCollection.swift */, @@ -1261,7 +1284,6 @@ 7DEBDAED29CBEE5300ADC226 /* Debugging.swift.gyb */, 7DEBDAD129CBEE5200ADC226 /* Descriptions.swift.gyb */, 7DEBDAD229CBEE5200ADC226 /* RandomAccessCollection+Offsets.swift.gyb */, - 7DB0AE712B6E067A00602A20 /* Specialize.swift.gyb */, 7DEBDAD929CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift.gyb */, 7DEBDAEC29CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift.gyb */, 7DE91F2E29CA70F3004483EB /* CMakeLists.txt */, @@ -1524,6 +1546,7 @@ 7DE921CA29CA8575004483EB /* Tests */ = { isa = PBXGroup; children = ( + BBD683952C7F857900B567A9 /* FutureTests */, 7DEBDB1F29CCE43600ADC226 /* _CollectionsTestSupport */, 7DE921DA29CA8575004483EB /* BitCollectionsTests */, 7DE921CB29CA8575004483EB /* CollectionsTestSupportTests */, @@ -1647,34 +1670,12 @@ path = DequeTests; sourceTree = ""; }; - 7DEBDAC729CBEE5200ADC226 /* Compatibility */ = { - isa = PBXGroup; - children = ( - 7DEBDACC29CBEE5200ADC226 /* autogenerated */, - 7DEBDAC929CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift.gyb */, - 7DEBDAC829CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift.gyb */, - 7DEBDACB29CBEE5200ADC226 /* UnsafeRawPointer extensions.swift.gyb */, - ); - path = Compatibility; - sourceTree = ""; - }; - 7DEBDACC29CBEE5200ADC226 /* autogenerated */ = { - isa = PBXGroup; - children = ( - 7DEBDACD29CBEE5200ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift */, - 7DEBDACF29CBEE5200ADC226 /* UnsafeMutablePointer+SE-0370.swift */, - 7DEBDAD029CBEE5200ADC226 /* UnsafeRawPointer extensions.swift */, - ); - path = autogenerated; - sourceTree = ""; - }; 7DEBDAD329CBEE5300ADC226 /* autogenerated */ = { isa = PBXGroup; children = ( 7DEBDAD829CBEE5300ADC226 /* Debugging.swift */, 7DEBDAD729CBEE5300ADC226 /* Descriptions.swift */, 7DEBDAD629CBEE5300ADC226 /* RandomAccessCollection+Offsets.swift */, - 7DB0AE752B6E06B300602A20 /* Specialize.swift */, 7DEBDAD529CBEE5300ADC226 /* UnsafeBufferPointer+Extras.swift */, 7DEBDAD429CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift */, ); @@ -1813,6 +1814,49 @@ name = Xcode; sourceTree = ""; }; + 7DEBEC1B2C5AE43C00A1BF15 /* Containers */ = { + isa = PBXGroup; + children = ( + 7DEBEC182C5AE43C00A1BF15 /* Container.swift */, + 7D380E0C2C6D565000AD8F58 /* Shared.swift */, + 7DEBEC192C5AE43C00A1BF15 /* DynamicArray.swift */, + 7DEBEC1A2C5AE43C00A1BF15 /* RigidArray.swift */, + 7D380E042C6ABF6000AD8F58 /* NewArray.swift */, + ); + path = Containers; + sourceTree = ""; + }; + 7DEBEC242C5AE43C00A1BF15 /* Future */ = { + isa = PBXGroup; + children = ( + 7DEBEC1B2C5AE43C00A1BF15 /* Containers */, + 7DEBEC1C2C5AE43C00A1BF15 /* Box.swift */, + 7D380E062C6BFB9500AD8F58 /* LifetimeOverride.swift */, + 7DEBEC1D2C5AE43C00A1BF15 /* Cell.swift */, + 7DEBEC1E2C5AE43C00A1BF15 /* CMakeLists.txt */, + 7DEBEC1F2C5AE43C00A1BF15 /* ContiguousStorage.swift */, + 7DEBEC202C5AE43C00A1BF15 /* Inout.swift */, + 7DEBEC212C5AE43C00A1BF15 /* RawSpan.swift */, + 7DEBEC222C5AE43C00A1BF15 /* Span.swift */, + 7DEBEC232C5AE43C00A1BF15 /* UnsafeBufferPointer+Additions.swift */, + ); + path = Future; + sourceTree = ""; + }; + BBD683952C7F857900B567A9 /* FutureTests */ = { + isa = PBXGroup; + children = ( + BBD6838E2C7F857900B567A9 /* BoxTests.swift */, + BBD6838F2C7F857900B567A9 /* CellTests.swift */, + BBD683902C7F857900B567A9 /* ContiguousStorageTests.swift */, + BBD683912C7F857900B567A9 /* DynamicArrayTests.swift */, + BBD683922C7F857900B567A9 /* Inout.swift */, + BBD683932C7F857900B567A9 /* RawSpanTests.swift */, + BBD683942C7F857900B567A9 /* SpanTests.swift */, + ); + path = FutureTests; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1870,7 +1914,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1430; - LastUpgradeCheck = 1520; + LastUpgradeCheck = 1600; TargetAttributes = { 7DE91B2529CA6721004483EB = { CreatedOnToolsVersion = 14.3; @@ -1904,6 +1948,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7DEBEC252C5AE43C00A1BF15 /* CMakeLists.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1924,12 +1969,11 @@ 7DE920F129CA70F4004483EB /* BitArray+Initializers.swift in Sources */, 7DE9214529CA70F4004483EB /* _HashNode+Structural symmetricDifference.swift in Sources */, 7DE9201029CA70F3004483EB /* OrderedDictionary+Deprecations.swift in Sources */, - 7DE9205929CA70F3004483EB /* Deque+Descriptions.swift in Sources */, - 7DEBDAF529CBEE5300ADC226 /* UnsafeRawPointer extensions.swift in Sources */, 7DE9212729CA70F4004483EB /* TreeSet+Equatable.swift in Sources */, 7DE9213429CA70F4004483EB /* TreeSet+ExpressibleByArrayLiteral.swift in Sources */, 7DE9209729CA70F4004483EB /* _RopePath.swift in Sources */, 7DE9216629CA70F4004483EB /* _HashNode+Storage.swift in Sources */, + 7D9514312C753C4A009C37DD /* Deque+Extras.swift in Sources */, 7DE920CE29CA70F4004483EB /* BitSet+CustomReflectable.swift in Sources */, 7DE9215029CA70F4004483EB /* _UnmanagedHashNode.swift in Sources */, 7DE920EF29CA70F4004483EB /* BitArray+Codable.swift in Sources */, @@ -1944,7 +1988,6 @@ 7DE9216229CA70F4004483EB /* _AncestorHashSlots.swift in Sources */, 7DE9216B29CA70F4004483EB /* TreeDictionary+Codable.swift in Sources */, 7DE9201A29CA70F3004483EB /* OrderedDictionary+Sendable.swift in Sources */, - 7DE9205D29CA70F4004483EB /* Deque._Storage.swift in Sources */, 7DE9203B29CA70F3004483EB /* OrderedSet+Partial SetAlgebra subtracting.swift in Sources */, 7DE9213A29CA70F4004483EB /* TreeSet.swift in Sources */, 7DE9213829CA70F4004483EB /* TreeSet+Filter.swift in Sources */, @@ -1953,6 +1996,7 @@ 7DE9213629CA70F4004483EB /* TreeSet+SetAlgebra basics.swift in Sources */, 7DE9202C29CA70F3004483EB /* OrderedSet+Initializers.swift in Sources */, 7DE9213229CA70F4004483EB /* TreeSet+SetAlgebra formSymmetricDifference.swift in Sources */, + 7D380E072C6BFB9B00AD8F58 /* LifetimeOverride.swift in Sources */, 7DE9203429CA70F3004483EB /* OrderedSet+Partial SetAlgebra formUnion.swift in Sources */, 7DE9214A29CA70F4004483EB /* _HashNode+Subtree Insertions.swift in Sources */, 7DE9215829CA70F4004483EB /* _HashNode+Structural union.swift in Sources */, @@ -1966,15 +2010,17 @@ 7DE9203129CA70F3004483EB /* OrderedSet+Partial MutableCollection.swift in Sources */, 7DE9209B29CA70F4004483EB /* Rope+Split.swift in Sources */, 7DE9206629CA70F4004483EB /* BigString+Debugging.swift in Sources */, - 7DE9205229CA70F3004483EB /* _UnsafeWrappedBuffer.swift in Sources */, + 7DE9205229CA70F3004483EB /* _UnsafeDequeSegments.swift in Sources */, 7DE9201129CA70F3004483EB /* OrderedDictionary+Initializers.swift in Sources */, 7DE9212B29CA70F4004483EB /* TreeSet+SetAlgebra subtracting.swift in Sources */, 7DE920F629CA70F4004483EB /* BitArray+Equatable.swift in Sources */, 7D9B859829E4F74400B291CD /* BitArray+Descriptions.swift in Sources */, + 7D95142C2C7539B9009C37DD /* Deque+Collection.swift in Sources */, 7DE9212329CA70F4004483EB /* TreeSet+SetAlgebra isEqualSet.swift in Sources */, 7DE9214629CA70F4004483EB /* _HashSlot.swift in Sources */, 7DEBDB0129CBEE5300ADC226 /* _UnsafeBitSet+Index.swift in Sources */, 7DE9204129CA70F3004483EB /* _UnsafeBitset.swift in Sources */, + 7D9514352C753FC1009C37DD /* Deque+Container.swift in Sources */, 7DE9208F29CA70F4004483EB /* RopeElement.swift in Sources */, 7DE9207229CA70F4004483EB /* BigString+Managing Breaks.swift in Sources */, 7DE9213F29CA70F4004483EB /* TreeSet+SetAlgebra isDisjoint.swift in Sources */, @@ -1995,6 +2041,7 @@ 7DE9209A29CA70F4004483EB /* Rope+Append.swift in Sources */, 7DE9203F29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isDisjoint.swift in Sources */, 7DE9208629CA70F4004483EB /* BigString+LosslessStringConvertible.swift in Sources */, + 7D95142B2C753955009C37DD /* Deque+Equatable.swift in Sources */, 7DE9204329CA70F3004483EB /* _HashTable+Bucket.swift in Sources */, 7DE9216A29CA70F4004483EB /* TreeDictionary.swift in Sources */, 7DEBDB9D29CCE73D00ADC226 /* Collections.swift in Sources */, @@ -2005,7 +2052,6 @@ 7DE9217629CA70F4004483EB /* TreeDictionary+Initializers.swift in Sources */, 7DE9207E29CA70F4004483EB /* BigString+UTF16View.swift in Sources */, 7DEBDAFA29CBEE5300ADC226 /* RandomAccessCollection+Offsets.swift in Sources */, - 7DB0AE762B6E06B300602A20 /* Specialize.swift in Sources */, 7DE9216F29CA70F4004483EB /* TreeDictionary+CustomReflectable.swift in Sources */, 7DE920D729CA70F4004483EB /* BitSet+SetAlgebra isSuperset.swift in Sources */, 7DE920F429CA70F4004483EB /* BitArray+Hashable.swift in Sources */, @@ -2013,6 +2059,7 @@ 7DE9206A29CA70F4004483EB /* BigString+Chunk+Counts.swift in Sources */, 7DE9207529CA70F4004483EB /* BigString+Insert.swift in Sources */, 7DE9216129CA70F4004483EB /* _HashNode+Structural merge.swift in Sources */, + 7D9514332C753C5B009C37DD /* Deque+Testing.swift in Sources */, 7DE9202F29CA70F3004483EB /* OrderedSet+ExpressibleByArrayLiteral.swift in Sources */, 7DEBDAFC29CBEE5300ADC226 /* Debugging.swift in Sources */, 7DE9202B29CA70F3004483EB /* OrderedSet+Equatable.swift in Sources */, @@ -2026,8 +2073,11 @@ 7DE9215129CA70F4004483EB /* _HashNode+Subtree Modify.swift in Sources */, 7DE9204729CA70F3004483EB /* _HashTable+Testing.swift in Sources */, 7DE920E929CA70F4004483EB /* BitArray+Invariants.swift in Sources */, + 7D95142D2C753C1B009C37DD /* Deque+Codable.swift in Sources */, 7DE9215429CA70F4004483EB /* _HashNode+UnsafeHandle.swift in Sources */, + 7D9514302C753C45009C37DD /* Deque+ExpressibleByArrayLiteral.swift in Sources */, 7DE9209829CA70F4004483EB /* Rope+_Node.swift in Sources */, + 7DEBEC172C5AB45600A1BF15 /* _UnsafeDequeHandle.swift in Sources */, 7DE9204529CA70F3004483EB /* _HashTable+Constants.swift in Sources */, 7DE9214229CA70F4004483EB /* _HashStack.swift in Sources */, 7DE9206F29CA70F4004483EB /* BigString+Chunk+Breaks.swift in Sources */, @@ -2036,12 +2086,10 @@ 7DE920B529CA70F4004483EB /* _SortedCollection.swift in Sources */, 7DE9206929CA70F4004483EB /* BigString+Chunk+Indexing by UTF16.swift in Sources */, 7DE9202A29CA70F3004483EB /* OrderedSet+Partial SetAlgebra isSuperset.swift in Sources */, - 7DEBDAF229CBEE5300ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift in Sources */, 7DE9207D29CA70F4004483EB /* BigSubstring+UTF16View.swift in Sources */, 7DEBDAF829CBEE5300ADC226 /* UnsafeMutableBufferPointer+Extras.swift in Sources */, 7DE9217229CA70F4004483EB /* TreeDictionary+Sendable.swift in Sources */, 7DE9207729CA70F4004483EB /* Range+BigString.swift in Sources */, - 7DE9204B29CA70F3004483EB /* _DequeBuffer.swift in Sources */, 7DE920DF29CA70F4004483EB /* Slice+Utilities.swift in Sources */, 7DE9215729CA70F4004483EB /* _RawHashNode.swift in Sources */, 7DE9209529CA70F4004483EB /* RopeSummary.swift in Sources */, @@ -2067,13 +2115,13 @@ 7DE920EB29CA70F4004483EB /* BitArray+Fill.swift in Sources */, 7DE9201929CA70F3004483EB /* OrderedDictionary+Values.swift in Sources */, 7DE9213929CA70F4004483EB /* TreeSet+SetAlgebra Initializers.swift in Sources */, + 7D9514292C750E08009C37DD /* RigidDeque.swift in Sources */, 7DE9217729CA70F4004483EB /* TreeDictionary+Keys.swift in Sources */, 7DE920DA29CA70F4004483EB /* BitSet._UnsafeHandle.swift in Sources */, 7DE920D129CA70F4004483EB /* BitSet.Index.swift in Sources */, 7DE9207429CA70F4004483EB /* BigString+ReplaceSubrange.swift in Sources */, 7DE920DC29CA70F4004483EB /* BitSet+Invariants.swift in Sources */, 7DE9216729CA70F4004483EB /* TreeDictionary+Equatable.swift in Sources */, - 7DE9204C29CA70F3004483EB /* Deque+Collection.swift in Sources */, 7DE9201F29CA70F3004483EB /* OrderedSet+RandomAccessCollection.swift in Sources */, 7DE9202E29CA70F3004483EB /* OrderedSet+UnorderedView.swift in Sources */, 7DE9215F29CA70F4004483EB /* _UnsafePath.swift in Sources */, @@ -2101,14 +2149,24 @@ 7DE9203229CA70F3004483EB /* OrderedSet+Sendable.swift in Sources */, 7DE9206E29CA70F4004483EB /* BigString+Chunk+Splitting.swift in Sources */, 7DE920D829CA70F4004483EB /* BitSet+SetAlgebra subtracting.swift in Sources */, + 7DEBEC262C5AE43C00A1BF15 /* RigidArray.swift in Sources */, + 7DEBEC272C5AE43C00A1BF15 /* DynamicArray.swift in Sources */, + 7DEBEC282C5AE43C00A1BF15 /* Inout.swift in Sources */, + 7DEBEC292C5AE43C00A1BF15 /* Container.swift in Sources */, + 7DEBEC2A2C5AE43C00A1BF15 /* Box.swift in Sources */, + 7DEBEC2B2C5AE43C00A1BF15 /* UnsafeBufferPointer+Additions.swift in Sources */, + 7DEBEC2C2C5AE43C00A1BF15 /* RawSpan.swift in Sources */, + 7DEBEC2D2C5AE43C00A1BF15 /* Cell.swift in Sources */, + 7DEBEC2E2C5AE43C00A1BF15 /* Span.swift in Sources */, + 7DEBEC2F2C5AE43C00A1BF15 /* ContiguousStorage.swift in Sources */, 7DE9209E29CA70F4004483EB /* Rope+ForEachWhile.swift in Sources */, 7DE9217B29CA70F4004483EB /* Heap+Invariants.swift in Sources */, 7DE9212F29CA70F4004483EB /* TreeSet+SetAlgebra formIntersection.swift in Sources */, 7DE920A329CA70F4004483EB /* Rope+Index.swift in Sources */, 7DE9207329CA70F4004483EB /* BigString+RemoveSubrange.swift in Sources */, - 7DE9205829CA70F3004483EB /* Deque._UnsafeHandle.swift in Sources */, 7DE9213529CA70F4004483EB /* TreeSet+SetAlgebra symmetricDifference.swift in Sources */, 7DE920D429CA70F4004483EB /* BitSet+SetAlgebra union.swift in Sources */, + 7D9514322C753C55009C37DD /* Deque+Hashable.swift in Sources */, 7DE920C329CA70F4004483EB /* BitSet+SetAlgebra formUnion.swift in Sources */, 7DE9217429CA70F4004483EB /* TreeDictionary+Collection.swift in Sources */, 7DE9214729CA70F4004483EB /* _HashNode+Primitive Replacement.swift in Sources */, @@ -2134,6 +2192,7 @@ 7DE9212929CA70F4004483EB /* TreeSet+SetAlgebra formUnion.swift in Sources */, 7DE920C129CA70F4004483EB /* BitSet+SetAlgebra subtract.swift in Sources */, 7DE9201B29CA70F3004483EB /* OrderedDictionary+Descriptions.swift in Sources */, + 7D95142A2C753862009C37DD /* Deque.swift in Sources */, 7DE9213C29CA70F4004483EB /* TreeSet+Codable.swift in Sources */, 7DE9207129CA70F4004483EB /* BigString+Split.swift in Sources */, 7DE9214F29CA70F4004483EB /* _Bucket.swift in Sources */, @@ -2146,14 +2205,15 @@ 7DE9207A29CA70F4004483EB /* BigString+UTF8View.swift in Sources */, 7DE9201329CA70F3004483EB /* OrderedDictionary+CustomReflectable.swift in Sources */, 7DE9201C29CA70F3004483EB /* OrderedDictionary+Partial MutableCollection.swift in Sources */, + 7D9514342C753C63009C37DD /* DynamicDeque.swift in Sources */, + 7D380E0D2C6D565300AD8F58 /* Shared.swift in Sources */, 7DE9202729CA70F3004483EB /* OrderedSet+Insertions.swift in Sources */, - 7DE9205B29CA70F3004483EB /* Deque+ExpressibleByArrayLiteral.swift in Sources */, 7DE9208729CA70F4004483EB /* BigString+Sequence.swift in Sources */, 7DE9202229CA70F3004483EB /* OrderedSet+Diffing.swift in Sources */, 7DE920CA29CA70F4004483EB /* BitSet+CustomStringConvertible.swift in Sources */, + 7D380E052C6ABF6800AD8F58 /* NewArray.swift in Sources */, 7DE9212E29CA70F4004483EB /* TreeSet+SetAlgebra isStrictSuperset.swift in Sources */, 7DE920A829CA70F4004483EB /* String.Index+ABI.swift in Sources */, - 7DE9204F29CA70F3004483EB /* Deque.swift in Sources */, 7DE9214129CA70F4004483EB /* _HashNode+Initializers.swift in Sources */, 7D9B859929E4F74400B291CD /* BitArray+LosslessStringConvertible.swift in Sources */, 7DE9202D29CA70F3004483EB /* OrderedSet.swift in Sources */, @@ -2161,15 +2221,12 @@ 7DE9216329CA70F4004483EB /* _HashLevel.swift in Sources */, 7D9B859729E4F74400B291CD /* BitArray+Shifts.swift in Sources */, 7DE9216C29CA70F4004483EB /* TreeDictionary+Descriptions.swift in Sources */, - 7DE9205429CA70F3004483EB /* Deque+Sendable.swift in Sources */, 7DE920A229CA70F4004483EB /* Rope+RemoveSubrange.swift in Sources */, + 7D95142F2C753C34009C37DD /* Deque+Descriptions.swift in Sources */, 7DE920C729CA70F4004483EB /* BitSet+ExpressibleByArrayLiteral.swift in Sources */, 7DE9214929CA70F4004483EB /* _HashNode+Structural isEqualSet.swift in Sources */, 7DE9206729CA70F4004483EB /* BigString+Builder.swift in Sources */, 7DE9208B29CA70F4004483EB /* RopeMetric.swift in Sources */, - 7DE9205A29CA70F3004483EB /* Deque+Extras.swift in Sources */, - 7DEBDAF429CBEE5300ADC226 /* UnsafeMutablePointer+SE-0370.swift in Sources */, - 7DE9205129CA70F3004483EB /* Deque+Hashable.swift in Sources */, 7DE9209929CA70F4004483EB /* Rope+Extract.swift in Sources */, 7DE9202629CA70F3004483EB /* OrderedSet+Partial SetAlgebra isStrictSubset.swift in Sources */, 7DE920E729CA70F4004483EB /* BitArray+CustomReflectable.swift in Sources */, @@ -2178,14 +2235,12 @@ 7DEBDB0229CBEE5300ADC226 /* _UnsafeBitSet.swift in Sources */, 7DE9214329CA70F4004483EB /* _HashNode+Subtree Removals.swift in Sources */, 7DE9207929CA70F4004483EB /* BigString+UnicodeScalarView.swift in Sources */, - 7DE9205329CA70F3004483EB /* _DequeBufferHeader.swift in Sources */, 7DE9202429CA70F3004483EB /* OrderedSet+Invariants.swift in Sources */, 7DE920A729CA70F4004483EB /* _CharacterRecognizer.swift in Sources */, 7DE920A429CA70F4004483EB /* Rope+Sequence.swift in Sources */, 7DE9201829CA70F3004483EB /* OrderedDictionary+Invariants.swift in Sources */, 7DE920CD29CA70F4004483EB /* BitSet+CustomDebugStringConvertible.swift in Sources */, 7DE9212529CA70F4004483EB /* TreeSet+SetAlgebra isStrictSubset.swift in Sources */, - 7DE9205C29CA70F3004483EB /* Deque+Equatable.swift in Sources */, 7DE9209229CA70F4004483EB /* Rope+_Storage.swift in Sources */, 7DE9203929CA70F3004483EB /* OrderedSet+Partial RangeReplaceableCollection.swift in Sources */, 7DE9206329CA70F4004483EB /* BigString+Contents.swift in Sources */, @@ -2193,10 +2248,10 @@ 7DE920CC29CA70F4004483EB /* BitSet.Counted.swift in Sources */, 7DE9207F29CA70F4004483EB /* BigSubstring+UTF8View.swift in Sources */, 7DE920A129CA70F4004483EB /* Rope+Remove.swift in Sources */, + 7D95142E2C753C2E009C37DD /* Deque+CustomReflectable.swift in Sources */, 7DE9215929CA70F4004483EB /* _HashNode+Structural isSubset.swift in Sources */, 7DE920C629CA70F4004483EB /* BitSet+SetAlgebra formSymmetricDifference.swift in Sources */, 7DE9202129CA70F3004483EB /* OrderedSet+Partial SetAlgebra isEqualSet.swift in Sources */, - 7DE9205529CA70F3004483EB /* Deque+Codable.swift in Sources */, 7DE9206229CA70F4004483EB /* BigString+Iterators.swift in Sources */, 7DE9206029CA70F4004483EB /* BigString+Summary.swift in Sources */, 7DE9203829CA70F3004483EB /* OrderedSet+Partial SetAlgebra formIntersection.swift in Sources */, @@ -2235,7 +2290,6 @@ 7DE9206C29CA70F4004483EB /* BigString+Chunk.swift in Sources */, 7DE920F329CA70F4004483EB /* BitArray+Copy.swift in Sources */, 7DE9214029CA70F4004483EB /* _Hash.swift in Sources */, - 7DE9205629CA70F3004483EB /* Deque+Testing.swift in Sources */, 7DEBDB0829CBEE5300ADC226 /* UInt+reversed.swift in Sources */, 7DE9209329CA70F4004483EB /* Rope+Invariants.swift in Sources */, 7DE920EE29CA70F4004483EB /* BitArray+ExpressibleByArrayLiteral.swift in Sources */, @@ -2243,7 +2297,6 @@ 7DE9214C29CA70F4004483EB /* _HashNode+Primitive Insertions.swift in Sources */, 7DE920BE29CA70F4004483EB /* BitSet+SetAlgebra formIntersection.swift in Sources */, 7DE9217329CA70F4004483EB /* TreeDictionary+Merge.swift in Sources */, - 7DE9204E29CA70F3004483EB /* Deque+CustomReflectable.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2259,7 +2312,6 @@ 7DE9221429CA8576004483EB /* OrderedSet Diffing Tests.swift in Sources */, 7DEBDB1D29CBF6B200ADC226 /* RandomAccessCollection+Offsets.swift in Sources */, 7DEBDB8529CCE44A00ADC226 /* AllOnesRandomNumberGenerator.swift in Sources */, - 7DEBDB1429CBF68D00ADC226 /* UnsafeMutableBufferPointer+SE-0370.swift in Sources */, 7DE921FD29CA8576004483EB /* CombinatoricsChecks.swift in Sources */, 7DEBDB8B29CCE44A00ADC226 /* _CollectionState.swift in Sources */, 7DE9220229CA8576004483EB /* TreeDictionary Tests.swift in Sources */, @@ -2288,12 +2340,10 @@ 7DEBDB7D29CCE44A00ADC226 /* MinimalRangeReplaceableRandomAccessCollection.swift in Sources */, 7DEBDB8E29CCE44A00ADC226 /* Integer Square Root.swift in Sources */, 7DE9221E29CA8576004483EB /* MutableCollectionTests.swift in Sources */, - 7DEBDB1229CBF68D00ADC226 /* UnsafeMutablePointer+SE-0370.swift in Sources */, 7DE9221529CA8576004483EB /* RandomAccessCollection+Extras.swift in Sources */, 7DEBDB7929CCE44A00ADC226 /* LifetimeTracked.swift in Sources */, 7DE921FC29CA8576004483EB /* IndexRangeCollectionTests.swift in Sources */, 7DE9221229CA8576004483EB /* OrderedDictionary+Elements Tests.swift in Sources */, - 7DB0AE772B6E06B300602A20 /* Specialize.swift in Sources */, 7DEBDB7629CCE44A00ADC226 /* MinimalIndex.swift in Sources */, 7DE9221329CA8576004483EB /* OrderedSetInternals.swift in Sources */, 7DE9220329CA8576004483EB /* Colliders.swift in Sources */, @@ -2325,12 +2375,18 @@ 7DEBDB1829CBF69F00ADC226 /* _UnsafeBitSet+_Word.swift in Sources */, 7DE921B929CA81DC004483EB /* _SortedCollection.swift in Sources */, 7DEBDB1629CBF69F00ADC226 /* _UnsafeBitSet+Index.swift in Sources */, - 7DEBDB1529CBF68D00ADC226 /* UnsafeRawPointer extensions.swift in Sources */, 7DEBDB7B29CCE44A00ADC226 /* IndexRangeCollection.swift in Sources */, 7DE9221929CA8576004483EB /* HeapTests.swift in Sources */, 7DEBDB1729CBF69F00ADC226 /* _UnsafeBitSet.swift in Sources */, 7DEBDB7E29CCE44A00ADC226 /* CheckComparable.swift in Sources */, 7DEBDB7C29CCE44A00ADC226 /* MinimalDecoder.swift in Sources */, + BBD683962C7F857900B567A9 /* SpanTests.swift in Sources */, + BBD683972C7F857900B567A9 /* Inout.swift in Sources */, + BBD683982C7F857900B567A9 /* BoxTests.swift in Sources */, + BBD683992C7F857900B567A9 /* CellTests.swift in Sources */, + BBD6839A2C7F857900B567A9 /* RawSpanTests.swift in Sources */, + BBD6839B2C7F857900B567A9 /* DynamicArrayTests.swift in Sources */, + BBD6839C2C7F857900B567A9 /* ContiguousStorageTests.swift in Sources */, 7DE9220829CA8576004483EB /* BitSetTests.swift in Sources */, 7DEBDB7329CCE44A00ADC226 /* _MinimalCollectionCore.swift in Sources */, 7DE921C829CA81DC004483EB /* _UniqueCollection.swift in Sources */, @@ -2388,7 +2444,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7DEBDB9729CCE4A600ADC226 /* CollectionsTests.xcconfig */; buildSettings = { - DEAD_CODE_STRIPPING = YES; }; name = Debug; }; @@ -2396,7 +2451,6 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7DEBDB9729CCE4A600ADC226 /* CollectionsTests.xcconfig */; buildSettings = { - DEAD_CODE_STRIPPING = YES; }; name = Release; }; diff --git a/Xcode/Collections.xcodeproj/xcshareddata/xcschemes/Collections.xcscheme b/Xcode/Collections.xcodeproj/xcshareddata/xcschemes/Collections.xcscheme index 3266c5aac..88230e523 100644 --- a/Xcode/Collections.xcodeproj/xcshareddata/xcschemes/Collections.xcscheme +++ b/Xcode/Collections.xcodeproj/xcshareddata/xcschemes/Collections.xcscheme @@ -1,6 +1,6 @@