|
| 1 | +//===----------------------------------------------------------------------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2025 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | + |
| 13 | +@available(SwiftStdlib 6.3, *) |
| 14 | +public protocol BorrowIteratorProtocol<Element>: ~Copyable, ~Escapable { |
| 15 | + associatedtype Element: ~Copyable |
| 16 | + |
| 17 | + // FIXME: This ought to be a core requirement, but `Ref` is not a thing yet. |
| 18 | +// @_lifetime(&self) |
| 19 | +// @_lifetime(self: copy self) |
| 20 | +// mutating func next() -> Ref<Element>? |
| 21 | + |
| 22 | + /// Advance the iterator, returning an ephemeral span over the elements |
| 23 | + /// that are ready to be visited. |
| 24 | + /// |
| 25 | + /// If the underlying iterable is a container type, then the returned span |
| 26 | + /// typically directly addresses one of its storage buffers. On the other |
| 27 | + /// hand, if the underlying iterable materializes its elements on demand, |
| 28 | + /// then the returned span addresses some temporary buffer associated with |
| 29 | + /// the iterator itself. Consequently, the returned span is tied to this |
| 30 | + /// particular invocation of `nextSpan`, and it cannot survive until the next |
| 31 | + /// invocation of it. |
| 32 | + /// |
| 33 | + /// If the iterator has not yet reached the end of the underlying iterable, |
| 34 | + /// then this method returns a non-empty span of at most `maximumCount` |
| 35 | + /// elements, and updates the iterator's current position to the element |
| 36 | + /// following the last item in the returned span (or the end, if there is |
| 37 | + /// none). The `maximumCount` argument allows callers to avoid getting more |
| 38 | + /// items that they are able to process in one go, simplifying usage, and |
| 39 | + /// avoiding materializing more elements than needed. |
| 40 | + /// |
| 41 | + /// If the iterator's current position is at the end of the container, then |
| 42 | + /// this method returns an empty span without updating the position. |
| 43 | + /// |
| 44 | + /// This method can be used to efficiently process the items of a container |
| 45 | + /// in bulk, by directly iterating over its piecewise contiguous pieces of |
| 46 | + /// storage: |
| 47 | + /// |
| 48 | + /// var it = items.startBorrowIteration() |
| 49 | + /// while true { |
| 50 | + /// let span = it.nextSpan(after: &index) |
| 51 | + /// if span.isEmpty { break } |
| 52 | + /// // Process items in `span` |
| 53 | + /// } |
| 54 | + /// |
| 55 | + /// Note: The spans returned by this method are not guaranteed to be disjunct. |
| 56 | + /// Iterators that materialize elements on demand typically reuse the same |
| 57 | + /// buffer over and over again; and even some proper containers may link to a |
| 58 | + /// single storage chunk (or parts of a storage chunk) multiple times, for |
| 59 | + /// example to repeat their contents. |
| 60 | + /// |
| 61 | + /// Note: Repeatedly iterating over the same container is expected to return |
| 62 | + /// the same items (collected in similarly sized span instances), but the |
| 63 | + /// returned spans are not guaranteed to be identical. For example, this is |
| 64 | + /// the case with containers that store some of their contents within their |
| 65 | + /// direct representation. Such containers may not always have a unique |
| 66 | + /// address in memory, and so the locations of the spans exposed by this |
| 67 | + /// method may vary between different borrows of the same container.) |
| 68 | + @_lifetime(&self) |
| 69 | + @_lifetime(self: copy self) |
| 70 | + mutating func nextSpan(maximumCount: Int) -> Span<Element> |
| 71 | + |
| 72 | + /// Advance the position of this iterator by the specified offset, or until |
| 73 | + /// the end of the underlying iterable. |
| 74 | + /// |
| 75 | + /// Returns the number of items that were skipped. If this is less than |
| 76 | + /// `maximumOffset`, then the iterable did not have enough elements left to |
| 77 | + /// skip the requested number of items. In this case, the iterator's current |
| 78 | + /// position is set to the end of the iterable. |
| 79 | + /// |
| 80 | + /// `maximumOffset` must be nonnegative, unless this is a bidirectional |
| 81 | + /// or random-access iterator. |
| 82 | + @_lifetime(self: copy self) |
| 83 | + mutating func skip(by maximumOffset: Int) -> Int |
| 84 | + |
| 85 | + // FIXME: Add BidirectionalBorrowIteratorProtocol and RandomAccessBorrowIteratorProtocol. |
| 86 | + // BidirectionalBorrowIteratorProtocol would need to have a `previousSpan` |
| 87 | + // method, which considerably complicates implementation. |
| 88 | + // Perhaps these would be better left to as variants of protocol Container, |
| 89 | + // which do not need a separate iterator concept. |
| 90 | +} |
| 91 | + |
| 92 | +@available(SwiftStdlib 6.3, *) |
| 93 | +extension BorrowIteratorProtocol where Self: ~Copyable & ~Escapable { |
| 94 | + @_alwaysEmitIntoClient |
| 95 | + @_lifetime(&self) |
| 96 | + @_lifetime(self: copy self) |
| 97 | + @_transparent |
| 98 | + public mutating func nextSpan() -> Span<Element> { |
| 99 | + nextSpan(maximumCount: Int.max) |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +@available(SwiftStdlib 6.3, *) |
| 104 | +extension BorrowIteratorProtocol where Self: ~Copyable & ~Escapable { |
| 105 | + @_alwaysEmitIntoClient |
| 106 | + @_lifetime(self: copy self) |
| 107 | + public mutating func skip(by offset: Int) -> Int { |
| 108 | + var remainder = offset |
| 109 | + while remainder > 0 { |
| 110 | + let span = nextSpan(maximumCount: remainder) |
| 111 | + if span.isEmpty { break } |
| 112 | + remainder &-= span.count |
| 113 | + } |
| 114 | + return offset &- remainder |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +@available(SwiftStdlib 6.3, *) |
| 119 | +extension Span: Iterable where Element: ~Copyable { |
| 120 | + // FIXME: This simple definition cannot also be a backward (or bidirectional) |
| 121 | + // iterator, nor a random-access iterator. If we want to go in that direction, |
| 122 | + // we'll need to rather introduce a type more like `RigidArray.BorrowIterator`. |
| 123 | + public typealias BorrowIterator = Self |
| 124 | + |
| 125 | + @_alwaysEmitIntoClient |
| 126 | + @_transparent |
| 127 | + public var estimatedCount: EstimatedCount { |
| 128 | + .exactly(count) |
| 129 | + } |
| 130 | + |
| 131 | + @_alwaysEmitIntoClient |
| 132 | + @_lifetime(copy self) |
| 133 | + @_transparent |
| 134 | + public func startBorrowIteration() -> Span<Element> { |
| 135 | + self |
| 136 | + } |
| 137 | +} |
| 138 | + |
| 139 | +@available(SwiftStdlib 6.3, *) |
| 140 | +extension Span: BorrowIteratorProtocol where Element: ~Copyable { |
| 141 | + @_alwaysEmitIntoClient |
| 142 | + @_lifetime(&self) |
| 143 | + @_lifetime(self: copy self) |
| 144 | + public mutating func nextSpan(maximumCount: Int) -> Span<Element> { |
| 145 | + let result = extracting(first: maximumCount) |
| 146 | + self = extracting(droppingFirst: maximumCount) |
| 147 | + return result |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +@available(SwiftStdlib 6.3, *) |
| 152 | +extension MutableSpan: Iterable where Element: ~Copyable { |
| 153 | + public typealias BorrowIterator = Span<Element>.BorrowIterator |
| 154 | + |
| 155 | + @_alwaysEmitIntoClient |
| 156 | + @_transparent |
| 157 | + public var estimatedCount: EstimatedCount { |
| 158 | + .exactly(count) |
| 159 | + } |
| 160 | + |
| 161 | + @_alwaysEmitIntoClient |
| 162 | + @_lifetime(borrow self) |
| 163 | + @_transparent |
| 164 | + public func startBorrowIteration() -> Span<Element> { |
| 165 | + span |
| 166 | + } |
| 167 | +} |
| 168 | + |
| 169 | +@available(SwiftStdlib 6.3, *) |
| 170 | +extension OutputSpan: Iterable where Element: ~Copyable { |
| 171 | + public typealias BorrowIterator = Span<Element>.BorrowIterator |
| 172 | + |
| 173 | + @_alwaysEmitIntoClient |
| 174 | + @_transparent |
| 175 | + public var estimatedCount: EstimatedCount { |
| 176 | + .exactly(count) |
| 177 | + } |
| 178 | + |
| 179 | + @_alwaysEmitIntoClient |
| 180 | + @_lifetime(borrow self) |
| 181 | + @_transparent |
| 182 | + public func startBorrowIteration() -> Span<Element> { |
| 183 | + self.span |
| 184 | + } |
| 185 | +} |
0 commit comments