/src/swift-nio/Sources/NIOCore/CircularBuffer.swift
Line | Count | Source |
1 | | //===----------------------------------------------------------------------===// |
2 | | // |
3 | | // This source file is part of the SwiftNIO open source project |
4 | | // |
5 | | // Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors |
6 | | // Licensed under Apache License v2.0 |
7 | | // |
8 | | // See LICENSE.txt for license information |
9 | | // See CONTRIBUTORS.txt for the list of SwiftNIO project authors |
10 | | // |
11 | | // SPDX-License-Identifier: Apache-2.0 |
12 | | // |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | /// An automatically expanding ring buffer implementation backed by a `ContiguousArray`. Even though this implementation |
16 | | /// will automatically expand if more elements than `initialCapacity` are stored, it's advantageous to prevent |
17 | | /// expansions from happening frequently. Expansions will always force an allocation and a copy to happen. |
18 | | public struct CircularBuffer<Element>: CustomStringConvertible { |
19 | | @usableFromInline |
20 | | internal private(set) var _buffer: ContiguousArray<Element?> |
21 | | |
22 | | @usableFromInline |
23 | | internal private(set) var headBackingIndex: Int |
24 | | |
25 | | @usableFromInline |
26 | | internal private(set) var tailBackingIndex: Int |
27 | | |
28 | | @inlinable |
29 | 312M | internal var mask: Int { |
30 | 312M | self._buffer.count &- 1 |
31 | 312M | } |
32 | | |
33 | | @inlinable |
34 | 67.2k | internal mutating func advanceHeadIdx(by: Int) { |
35 | 67.2k | self.headBackingIndex = indexAdvanced(index: self.headBackingIndex, by: by) |
36 | 67.2k | } |
37 | | |
38 | | @inlinable |
39 | 116M | internal mutating func advanceTailIdx(by: Int) { |
40 | 116M | self.tailBackingIndex = indexAdvanced(index: self.tailBackingIndex, by: by) |
41 | 116M | } |
42 | | |
43 | | @inlinable |
44 | 33.1k | internal func indexBeforeHeadIdx() -> Int { |
45 | 33.1k | self.indexAdvanced(index: self.headBackingIndex, by: -1) |
46 | 33.1k | } |
47 | | |
48 | | @inlinable |
49 | 0 | internal func indexBeforeTailIdx() -> Int { |
50 | 0 | self.indexAdvanced(index: self.tailBackingIndex, by: -1) |
51 | 0 | } |
52 | | |
53 | | @inlinable |
54 | 266M | internal func indexAdvanced(index: Int, by: Int) -> Int { |
55 | 266M | (index &+ by) & self.mask |
56 | 266M | } |
57 | | |
58 | | /// An opaque `CircularBuffer` index. |
59 | | /// |
60 | | /// You may get indices offset from other indices by using `CircularBuffer.index(:offsetBy:)`, |
61 | | /// `CircularBuffer.index(before:)`, or `CircularBuffer.index(after:)`. |
62 | | /// |
63 | | /// - Note: Every index is invalidated as soon as you perform a length-changing operating on the `CircularBuffer` |
64 | | /// but remains valid when you replace one item by another using the subscript. |
65 | | public struct Index: Comparable, Sendable { |
66 | | @usableFromInline private(set) var _backingIndex: UInt32 |
67 | | @usableFromInline private(set) var _backingCheck: _UInt24 |
68 | | @usableFromInline private(set) var isIndexGEQHeadIndex: Bool |
69 | | |
70 | | @inlinable |
71 | 8.45M | internal var backingIndex: Int { |
72 | 8.45M | Int(self._backingIndex) |
73 | 8.45M | } |
74 | | |
75 | | @inlinable |
76 | 5.96M | internal init(backingIndex: Int, backingCount: Int, backingIndexOfHead: Int) { |
77 | 5.96M | self.isIndexGEQHeadIndex = backingIndex >= backingIndexOfHead |
78 | 5.96M | self._backingCheck = .max |
79 | 5.96M | self._backingIndex = UInt32(backingIndex) |
80 | 5.96M | debugOnly { |
81 | 33.5k | // if we can, we store the check for the backing here |
82 | 33.5k | self._backingCheck = backingCount < Int(_UInt24.max) ? _UInt24(UInt32(backingCount)) : .max |
83 | 33.5k | } |
84 | 5.96M | } |
85 | | |
86 | | @inlinable |
87 | 4.20k | public static func == (lhs: Index, rhs: Index) -> Bool { |
88 | 4.20k | lhs._backingIndex == rhs._backingIndex && lhs._backingCheck == rhs._backingCheck |
89 | 4.20k | && lhs.isIndexGEQHeadIndex == rhs.isIndexGEQHeadIndex |
90 | 4.20k | } |
91 | | |
92 | | @inlinable |
93 | 109k | public static func < (lhs: Index, rhs: Index) -> Bool { |
94 | 109k | if lhs.isIndexGEQHeadIndex && rhs.isIndexGEQHeadIndex { |
95 | 109k | return lhs.backingIndex < rhs.backingIndex |
96 | 109k | } else if lhs.isIndexGEQHeadIndex && !rhs.isIndexGEQHeadIndex { |
97 | 0 | return true |
98 | 0 | } else if !lhs.isIndexGEQHeadIndex && rhs.isIndexGEQHeadIndex { |
99 | 0 | return false |
100 | 0 | } else { |
101 | 0 | return lhs.backingIndex < rhs.backingIndex |
102 | 0 | } |
103 | 109k | } |
104 | | |
105 | | @usableFromInline |
106 | 8.33k | internal func isValidIndex(for ring: CircularBuffer<Element>) -> Bool { |
107 | 8.33k | self._backingCheck == _UInt24.max || Int(self._backingCheck) == ring.count |
108 | 8.33k | } |
109 | | } |
110 | | } |
111 | | |
112 | | // MARK: Collection/MutableCollection implementation |
113 | | extension CircularBuffer: Collection, MutableCollection { |
114 | | public typealias Element = Element |
115 | | public typealias Indices = DefaultIndices<CircularBuffer<Element>> |
116 | | public typealias RangeType<Bound> = Range<Bound> where Bound: Strideable, Bound.Stride: SignedInteger |
117 | | public typealias SubSequence = CircularBuffer<Element> |
118 | | |
119 | | /// Returns the position immediately after the given index. |
120 | | /// |
121 | | /// The successor of an index must be well defined. For an index `i` into a |
122 | | /// collection `c`, calling `c.index(after: i)` returns the same index every |
123 | | /// time. |
124 | | /// |
125 | | /// - Parameter after: A valid index of the collection. `i` must be less than |
126 | | /// `endIndex`. |
127 | | /// - Returns: The index value immediately after `i`. |
128 | | @inlinable |
129 | 0 | public func index(after: Index) -> Index { |
130 | 0 | self.index(after, offsetBy: 1) |
131 | 0 | } |
132 | | |
133 | | /// Returns the index before `index`. |
134 | | @inlinable |
135 | 0 | public func index(before: Index) -> Index { |
136 | 0 | self.index(before, offsetBy: -1) |
137 | 0 | } |
138 | | |
139 | | /// Accesses the element at the specified index. |
140 | | /// |
141 | | /// You can subscript `CircularBuffer` with any valid index other than the |
142 | | /// `CircularBuffer`'s end index. The end index refers to the position one |
143 | | /// past the last element of a collection, so it doesn't correspond with an |
144 | | /// element. |
145 | | /// |
146 | | /// - Parameter position: The position of the element to access. `position` |
147 | | /// must be a valid index of the collection that is not equal to the |
148 | | /// `endIndex` property. |
149 | | /// |
150 | | /// - Complexity: O(1) |
151 | | @inlinable |
152 | | public subscript(position: Index) -> Element { |
153 | 1.57M | get { |
154 | 1.57M | assert( |
155 | 1.57M | position.isValidIndex(for: self), |
156 | 1.57M | "illegal index used, index was for CircularBuffer with count \(position._backingCheck), " |
157 | 0 | + "but actual count is \(self.count)" |
158 | 1.57M | ) |
159 | 1.57M | return self._buffer[position.backingIndex]! |
160 | 1.57M | } |
161 | 0 | set { |
162 | 0 | assert( |
163 | 0 | position.isValidIndex(for: self), |
164 | 0 | "illegal index used, index was for CircularBuffer with count \(position._backingCheck), " |
165 | 0 | + "but actual count is \(self.count)" |
166 | 0 | ) |
167 | 0 | self._buffer[position.backingIndex] = newValue |
168 | 0 | } |
169 | | } |
170 | | |
171 | | /// The position of the first element in a nonempty `CircularBuffer`. |
172 | | /// |
173 | | /// If the `CircularBuffer` is empty, `startIndex` is equal to `endIndex`. |
174 | | @inlinable |
175 | 3.47M | public var startIndex: Index { |
176 | 3.47M | .init( |
177 | 3.47M | backingIndex: self.headBackingIndex, |
178 | 3.47M | backingCount: self.count, |
179 | 3.47M | backingIndexOfHead: self.headBackingIndex |
180 | 3.47M | ) |
181 | 3.47M | } |
182 | | |
183 | | /// The `CircularBuffer`'s "past the end" position---that is, the position one |
184 | | /// greater than the last valid subscript argument. |
185 | | /// |
186 | | /// When you need a range that includes the last element of a collection, use |
187 | | /// the half-open range operator (`..<`) with `endIndex`. The `..<` operator |
188 | | /// creates a range that doesn't include the upper bound, so it's always |
189 | | /// safe to use with `endIndex`. |
190 | | /// |
191 | | /// If the `CircularBuffer` is empty, `endIndex` is equal to `startIndex`. |
192 | | @inlinable |
193 | 1.03M | public var endIndex: Index { |
194 | 1.03M | .init( |
195 | 1.03M | backingIndex: self.tailBackingIndex, |
196 | 1.03M | backingCount: self.count, |
197 | 1.03M | backingIndexOfHead: self.headBackingIndex |
198 | 1.03M | ) |
199 | 1.03M | } |
200 | | |
201 | | /// Returns the distance between two indices. |
202 | | /// |
203 | | /// Unless the collection conforms to the `BidirectionalCollection` protocol, |
204 | | /// `start` must be less than or equal to `end`. |
205 | | /// |
206 | | /// - Parameters: |
207 | | /// - start: A valid index of the collection. |
208 | | /// - end: Another valid index of the collection. If `end` is equal to |
209 | | /// `start`, the result is zero. |
210 | | /// - Returns: The distance between `start` and `end`. The result can be |
211 | | /// negative only if the collection conforms to the |
212 | | /// `BidirectionalCollection` protocol. |
213 | | /// |
214 | | /// - Complexity: O(1) if the collection conforms to |
215 | | /// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the |
216 | | /// resulting distance. |
217 | | @inlinable |
218 | 311k | public func distance(from start: CircularBuffer<Element>.Index, to end: CircularBuffer<Element>.Index) -> Int { |
219 | 311k | let backingCount = self._buffer.count |
220 | 311k | |
221 | 311k | switch (start.isIndexGEQHeadIndex, end.isIndexGEQHeadIndex) { |
222 | 311k | case (true, true): |
223 | 311k | return end.backingIndex &- start.backingIndex |
224 | 311k | case (true, false): |
225 | 0 | return backingCount &- (start.backingIndex &- end.backingIndex) |
226 | 311k | case (false, true): |
227 | 0 | return -(backingCount &- (end.backingIndex &- start.backingIndex)) |
228 | 311k | case (false, false): |
229 | 0 | return end.backingIndex &- start.backingIndex |
230 | 311k | } |
231 | 311k | } |
232 | | |
233 | | @inlinable |
234 | | public func _copyContents( |
235 | | initializing buffer: UnsafeMutableBufferPointer<Element> |
236 | 71.5k | ) -> (Iterator, UnsafeMutableBufferPointer<Element>.Index) { |
237 | 71.5k | precondition(buffer.count >= self.count) |
238 | 71.5k | |
239 | 71.5k | guard var ptr = buffer.baseAddress else { |
240 | 0 | return (self.makeIterator(), buffer.startIndex) |
241 | 71.5k | } |
242 | 71.5k | |
243 | 71.5k | if self.tailBackingIndex >= self.headBackingIndex { |
244 | 30.3M | for index in self.headBackingIndex..<self.tailBackingIndex { |
245 | 30.3M | ptr.initialize(to: self._buffer[index]!) |
246 | 30.3M | ptr += 1 |
247 | 30.3M | } |
248 | 71.5k | } else { |
249 | 0 | for index in self.headBackingIndex..<self._buffer.endIndex { |
250 | 0 | ptr.initialize(to: self._buffer[index]!) |
251 | 0 | ptr += 1 |
252 | 0 | } |
253 | 0 | for index in 0..<self.tailBackingIndex { |
254 | 0 | ptr.initialize(to: self._buffer[index]!) |
255 | 0 | ptr += 1 |
256 | 0 | } |
257 | 0 | } |
258 | 71.5k | |
259 | 71.5k | return (self[self.endIndex..<self.endIndex].makeIterator(), self.count) |
260 | 71.5k | } |
261 | | |
262 | | // These are implemented as no-ops for performance reasons. |
263 | | @inlinable |
264 | 0 | public func _failEarlyRangeCheck(_ index: Index, bounds: Range<Index>) {} |
265 | | |
266 | | @inlinable |
267 | 0 | public func _failEarlyRangeCheck(_ index: Index, bounds: ClosedRange<Index>) {} |
268 | | |
269 | | @inlinable |
270 | 0 | public func _failEarlyRangeCheck(_ range: Range<Index>, bounds: Range<Index>) {} |
271 | | } |
272 | | |
273 | | // MARK: RandomAccessCollection implementation |
274 | | extension CircularBuffer: RandomAccessCollection { |
275 | | /// Returns the index offset by `distance` from `index`. |
276 | | /// |
277 | | /// The following example obtains an index advanced four positions from a |
278 | | /// string's starting index and then prints the character at that position. |
279 | | /// |
280 | | /// let s = "Swift" |
281 | | /// let i = s.index(s.startIndex, offsetBy: 4) |
282 | | /// print(s[i]) |
283 | | /// // Prints "t" |
284 | | /// |
285 | | /// The value passed as `distance` must not offset `i` beyond the bounds of |
286 | | /// the collection. |
287 | | /// |
288 | | /// - Parameters: |
289 | | /// - i: A valid index of the collection. |
290 | | /// - distance: The distance to offset `i`. `distance` must not be negative |
291 | | /// unless the collection conforms to the `BidirectionalCollection` |
292 | | /// protocol. |
293 | | /// - Returns: An index offset by `distance` from the index `i`. If |
294 | | /// `distance` is positive, this is the same value as the result of |
295 | | /// `distance` calls to `index(after:)`. If `distance` is negative, this |
296 | | /// is the same value as the result of `abs(distance)` calls to |
297 | | /// `index(before:)`. |
298 | | /// |
299 | | /// - Complexity: O(1) if the collection conforms to |
300 | | /// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the absolute |
301 | | /// value of `distance`. |
302 | | @inlinable |
303 | 0 | public func index(_ i: Index, offsetBy distance: Int) -> Index { |
304 | 0 | .init( |
305 | 0 | backingIndex: (i.backingIndex &+ distance) & self.mask, |
306 | 0 | backingCount: self.count, |
307 | 0 | backingIndexOfHead: self.headBackingIndex |
308 | 0 | ) |
309 | 0 | } |
310 | | |
311 | | @inlinable |
312 | | public subscript(bounds: Range<Index>) -> SubSequence { |
313 | 109k | get { |
314 | 109k | precondition(self.distance(from: self.startIndex, to: bounds.lowerBound) >= 0) |
315 | 109k | precondition(self.distance(from: bounds.upperBound, to: self.endIndex) >= 0) |
316 | 109k | |
317 | 109k | var newRing = self |
318 | 109k | newRing.headBackingIndex = bounds.lowerBound.backingIndex |
319 | 109k | newRing.tailBackingIndex = bounds.upperBound.backingIndex |
320 | 109k | return newRing |
321 | 109k | } |
322 | 0 | set { |
323 | 0 | precondition(self.distance(from: self.startIndex, to: bounds.lowerBound) >= 0) |
324 | 0 | precondition(self.distance(from: bounds.upperBound, to: self.endIndex) >= 0) |
325 | 0 |
|
326 | 0 | self.replaceSubrange(bounds, with: newValue) |
327 | 0 | } |
328 | | } |
329 | | } |
330 | | |
331 | | extension CircularBuffer { |
332 | | |
333 | | /// Allocates a buffer that can hold up to `initialCapacity` elements and initialise an empty ring backed by |
334 | | /// the buffer. When the ring grows to more than `initialCapacity` elements the buffer will be expanded. |
335 | | @inlinable |
336 | 6.95M | public init(initialCapacity: Int) { |
337 | 6.95M | let capacity = Int(UInt32(initialCapacity).nextPowerOf2()) |
338 | 6.95M | self.headBackingIndex = 0 |
339 | 6.95M | self.tailBackingIndex = 0 |
340 | 6.95M | self._buffer = ContiguousArray<Element?>(repeating: nil, count: capacity) |
341 | 6.95M | assert(self._buffer.count == capacity) |
342 | 6.95M | } |
343 | | |
344 | | /// Allocates an empty buffer. |
345 | | @inlinable |
346 | 681k | public init() { |
347 | 681k | self = .init(initialCapacity: 16) |
348 | 681k | } |
349 | | |
350 | | /// Append an element to the end of the ring buffer. |
351 | | /// |
352 | | /// Amortized *O(1)* |
353 | | @inlinable |
354 | 89.6M | public mutating func append(_ value: Element) { |
355 | 89.6M | self._buffer[self.tailBackingIndex] = value |
356 | 89.6M | self.advanceTailIdx(by: 1) |
357 | 89.6M | |
358 | 89.6M | if self.headBackingIndex == self.tailBackingIndex { |
359 | 158k | // No more room left for another append so grow the buffer now. |
360 | 158k | self._doubleCapacity() |
361 | 158k | } |
362 | 89.6M | } |
363 | | |
364 | | /// Prepend an element to the front of the ring buffer. |
365 | | /// |
366 | | /// Amortized *O(1)* |
367 | | @inlinable |
368 | 33.1k | public mutating func prepend(_ value: Element) { |
369 | 33.1k | let idx = self.indexBeforeHeadIdx() |
370 | 33.1k | self._buffer[idx] = value |
371 | 33.1k | self.advanceHeadIdx(by: -1) |
372 | 33.1k | |
373 | 33.1k | if self.headBackingIndex == self.tailBackingIndex { |
374 | 0 | // No more room left for another append so grow the buffer now. |
375 | 0 | self._doubleCapacity() |
376 | 0 | } |
377 | 33.1k | } |
378 | | |
379 | | /// Double the capacity of the buffer and adjust the headIdx and tailIdx. |
380 | | /// |
381 | | /// Must only be called when buffer is full. |
382 | | @inlinable |
383 | 117k | internal mutating func _doubleCapacity() { |
384 | 117k | // Double the storage. This can't use _resizeAndFlatten because the buffer is |
385 | 117k | // full at this stage. That's ok: we have some optimised code paths for this use-case. |
386 | 117k | let newCapacity = self.capacity << 1 |
387 | 117k | assert(self.headBackingIndex == self.tailBackingIndex) |
388 | 117k | |
389 | 117k | var newBacking: ContiguousArray<Element?> = [] |
390 | 117k | precondition(newCapacity > 0, "Can't change capacity to \(newCapacity)") |
391 | 117k | assert(newCapacity % 2 == 0) |
392 | 117k | assert(newCapacity > self.capacity) |
393 | 117k | |
394 | 117k | newBacking.reserveCapacity(newCapacity) |
395 | 117k | newBacking.append(contentsOf: self._buffer[self.headBackingIndex...]) |
396 | 117k | newBacking.append(contentsOf: self._buffer[..<self.tailBackingIndex]) |
397 | 117k | |
398 | 117k | let newTailIndex = newBacking.count |
399 | 117k | let paddingCount = newCapacity &- newTailIndex |
400 | 117k | newBacking.append(contentsOf: repeatElement(nil, count: paddingCount)) |
401 | 117k | |
402 | 117k | self.headBackingIndex = 0 |
403 | 117k | self.tailBackingIndex = newTailIndex |
404 | 117k | self._buffer = newBacking |
405 | 117k | assert(self.verifyInvariants()) |
406 | 117k | } |
407 | | |
408 | | /// Resizes and flatten this buffer. |
409 | | /// |
410 | | /// Capacities are always powers of 2. |
411 | | @inlinable |
412 | 0 | internal mutating func _resizeAndFlatten(newCapacity: Int) { |
413 | 0 | var newBacking: ContiguousArray<Element?> = [] |
414 | 0 | precondition(newCapacity > 0, "Can't change capacity to \(newCapacity)") |
415 | 0 | assert(newCapacity % 2 == 0) |
416 | 0 | assert(newCapacity > self.capacity) |
417 | 0 |
|
418 | 0 | newBacking.reserveCapacity(newCapacity) |
419 | 0 |
|
420 | 0 | if self.tailBackingIndex >= self.headBackingIndex { |
421 | 0 | newBacking.append(contentsOf: self._buffer[self.headBackingIndex..<self.tailBackingIndex]) |
422 | 0 | } else { |
423 | 0 | newBacking.append(contentsOf: self._buffer[self.headBackingIndex...]) |
424 | 0 | newBacking.append(contentsOf: self._buffer[..<self.tailBackingIndex]) |
425 | 0 | } |
426 | 0 |
|
427 | 0 | let newTailIndex = newBacking.count |
428 | 0 | let paddingCount = newCapacity &- newTailIndex |
429 | 0 | newBacking.append(contentsOf: repeatElement(nil, count: paddingCount)) |
430 | 0 |
|
431 | 0 | self.headBackingIndex = 0 |
432 | 0 | self.tailBackingIndex = newTailIndex |
433 | 0 | self._buffer = newBacking |
434 | 0 | assert(self.verifyInvariants()) |
435 | 0 | } |
436 | | |
437 | | /// Return element `offset` from first element. |
438 | | /// |
439 | | /// *O(1)* |
440 | | @inlinable |
441 | | public subscript(offset offset: Int) -> Element { |
442 | 0 | get { |
443 | 0 | self[self.index(self.startIndex, offsetBy: offset)] |
444 | 0 | } |
445 | 0 | set { |
446 | 0 | self[self.index(self.startIndex, offsetBy: offset)] = newValue |
447 | 0 | } |
448 | | } |
449 | | |
450 | | /// Returns whether the ring is empty. |
451 | | @inlinable |
452 | 4.22M | public var isEmpty: Bool { |
453 | 4.22M | self.headBackingIndex == self.tailBackingIndex |
454 | 4.22M | } |
455 | | |
456 | | /// Returns the number of element in the ring. |
457 | | @inlinable |
458 | 25.4M | public var count: Int { |
459 | 25.4M | if self.tailBackingIndex >= self.headBackingIndex { |
460 | 23.5M | return self.tailBackingIndex &- self.headBackingIndex |
461 | 23.5M | } else { |
462 | 1.87M | return self._buffer.count &- (self.headBackingIndex &- self.tailBackingIndex) |
463 | 1.87M | } |
464 | 25.4M | } |
465 | | |
466 | | /// The total number of elements that the ring can contain without allocating new storage. |
467 | | @inlinable |
468 | 810k | public var capacity: Int { |
469 | 810k | self._buffer.count |
470 | 810k | } |
471 | | |
472 | | /// Removes all members from the circular buffer whist keeping the capacity. |
473 | | @inlinable |
474 | 156k | public mutating func removeAll(keepingCapacity: Bool = false) { |
475 | 156k | if keepingCapacity { |
476 | 156k | self.removeFirst(self.count) |
477 | 156k | } else { |
478 | 0 | self._buffer.removeAll(keepingCapacity: false) |
479 | 0 | self._buffer.append(nil) |
480 | 0 | } |
481 | 156k | self.headBackingIndex = 0 |
482 | 156k | self.tailBackingIndex = 0 |
483 | 156k | assert(self.verifyInvariants()) |
484 | 156k | } |
485 | | |
486 | | /// Modify the element at `index`. |
487 | | /// |
488 | | /// This function exists to provide a method of modifying the element in its underlying backing storage, instead |
489 | | /// of copying it out, modifying it, and copying it back in. This emulates the behaviour of the `_modify` accessor |
490 | | /// that is part of the generalized accessors work. That accessor is currently underscored and not safe to use, so |
491 | | /// this is the next best thing. |
492 | | /// |
493 | | /// Note that this function is not guaranteed to be fast. In particular, as it is both generic and accepts a closure |
494 | | /// it is possible that it will be slower than using the get/modify/set path that occurs with the subscript. If you |
495 | | /// are interested in using this function for performance you *must* test and verify that the optimisation applies |
496 | | /// correctly in your situation. |
497 | | /// |
498 | | /// - Parameters: |
499 | | /// - index: The index of the object that should be modified. If this index is invalid this function will trap. |
500 | | /// - modifyFunc: The function to apply to the modified object. |
501 | | @inlinable |
502 | | public mutating func modify<Result>( |
503 | | _ index: Index, |
504 | | _ modifyFunc: (inout Element) throws -> Result |
505 | 0 | ) rethrows -> Result { |
506 | 0 | try modifyFunc(&self._buffer[index.backingIndex]!) |
507 | 0 | } |
508 | | |
509 | | // MARK: CustomStringConvertible implementation |
510 | | /// Returns a human readable description of the ring. |
511 | 0 | public var description: String { |
512 | 0 | var desc = "[ " |
513 | 0 | for el in self._buffer.enumerated() { |
514 | 0 | if el.0 == self.headBackingIndex { |
515 | 0 | desc += "<" |
516 | 0 | } else if el.0 == self.tailBackingIndex { |
517 | 0 | desc += ">" |
518 | 0 | } |
519 | 0 | desc += el.1.map { "\($0) " } ?? "_ " |
520 | 0 | } |
521 | 0 | desc += "]" |
522 | 0 | desc += " (bufferCapacity: \(self._buffer.count), ringLength: \(self.count))" |
523 | 0 | return desc |
524 | 0 | } |
525 | | } |
526 | | |
527 | | // MARK: - RangeReplaceableCollection |
528 | | extension CircularBuffer: RangeReplaceableCollection { |
529 | | /// Removes and returns the first element of the `CircularBuffer`. |
530 | | /// |
531 | | /// Calling this method may invalidate all saved indices of this |
532 | | /// `CircularBuffer`. Do not rely on a previously stored index value after |
533 | | /// altering a `CircularBuffer` with any operation that can change its length. |
534 | | /// |
535 | | /// - Returns: The first element of the `CircularBuffer` if the `CircularBuffer` is not |
536 | | /// empty; otherwise, `nil`. |
537 | | /// |
538 | | /// - Complexity: O(1) |
539 | | @inlinable |
540 | 0 | public mutating func popFirst() -> Element? { |
541 | 0 | if count > 0 { |
542 | 0 | return self.removeFirst() |
543 | 0 | } else { |
544 | 0 | return nil |
545 | 0 | } |
546 | 0 | } |
547 | | |
548 | | /// Removes and returns the last element of the `CircularBuffer`. |
549 | | /// |
550 | | /// Calling this method may invalidate all saved indices of this |
551 | | /// `CircularBuffer`. Do not rely on a previously stored index value after |
552 | | /// altering a `CircularBuffer` with any operation that can change its length. |
553 | | /// |
554 | | /// - Returns: The last element of the `CircularBuffer` if the `CircularBuffer` is not |
555 | | /// empty; otherwise, `nil`. |
556 | | /// |
557 | | /// - Complexity: O(1) |
558 | | @inlinable |
559 | 0 | public mutating func popLast() -> Element? { |
560 | 0 | if count > 0 { |
561 | 0 | return self.removeLast() |
562 | 0 | } else { |
563 | 0 | return nil |
564 | 0 | } |
565 | 0 | } |
566 | | |
567 | | /// Removes the specified number of elements from the end of the |
568 | | /// `CircularBuffer`. |
569 | | /// |
570 | | /// Attempting to remove more elements than exist in the `CircularBuffer` |
571 | | /// triggers a runtime error. |
572 | | /// |
573 | | /// Calling this method may invalidate all saved indices of this |
574 | | /// `CircularBuffer`. Do not rely on a previously stored index value after |
575 | | /// altering a `CircularBuffer` with any operation that can change its length. |
576 | | /// |
577 | | /// - Parameter k: The number of elements to remove from the `CircularBuffer`. |
578 | | /// `k` must be greater than or equal to zero and must not exceed the |
579 | | /// number of elements in the `CircularBuffer`. |
580 | | /// |
581 | | /// - Complexity: O(*k*), where *k* is the specified number of elements. |
582 | | @inlinable |
583 | 0 | public mutating func removeLast(_ k: Int) { |
584 | 0 | precondition(k <= self.count, "Number of elements to drop bigger than the amount of elements in the buffer.") |
585 | 0 | var idx = self.tailBackingIndex |
586 | 0 | for _ in 0..<k { |
587 | 0 | idx = self.indexAdvanced(index: idx, by: -1) |
588 | 0 | self._buffer[idx] = nil |
589 | 0 | } |
590 | 0 | self.tailBackingIndex = idx |
591 | 0 | } |
592 | | |
593 | | /// Removes the specified number of elements from the beginning of the |
594 | | /// `CircularBuffer`. |
595 | | /// |
596 | | /// Calling this method may invalidate any existing indices for use with this |
597 | | /// `CircularBuffer`. |
598 | | /// |
599 | | /// - Parameter k: The number of elements to remove. |
600 | | /// `k` must be greater than or equal to zero and must not exceed the |
601 | | /// number of elements in the `CircularBuffer`. |
602 | | /// |
603 | | /// - Complexity: O(*k*), where *k* is the specified number of elements. |
604 | | @inlinable |
605 | 1.86M | public mutating func removeFirst(_ k: Int) { |
606 | 1.86M | precondition(k <= self.count, "Number of elements to drop bigger than the amount of elements in the buffer.") |
607 | 1.86M | var idx = self.headBackingIndex |
608 | 1.86M | for _ in 0..<k { |
609 | 933k | self._buffer[idx] = nil |
610 | 933k | idx = self.indexAdvanced(index: idx, by: 1) |
611 | 1.86M | } |
612 | 1.86M | self.headBackingIndex = idx |
613 | 1.86M | } |
614 | | |
615 | | /// Removes and returns the first element of the `CircularBuffer`. |
616 | | /// |
617 | | /// The `CircularBuffer` must not be empty. |
618 | | /// |
619 | | /// Calling this method may invalidate any existing indices for use with this |
620 | | /// `CircularBuffer`. |
621 | | /// |
622 | | /// - Returns: The removed element. |
623 | | /// |
624 | | /// - Complexity: O(*1*) |
625 | | @discardableResult |
626 | | @inlinable |
627 | 1.12M | public mutating func removeFirst() -> Element { |
628 | 1.12M | defer { |
629 | 1.12M | self.removeFirst(1) |
630 | 1.12M | } |
631 | 1.12M | return self.first! |
632 | 1.12M | } |
633 | | |
634 | | /// Removes and returns the last element of the `CircularBuffer`. |
635 | | /// |
636 | | /// The `CircularBuffer` must not be empty. |
637 | | /// |
638 | | /// Calling this method may invalidate all saved indices of this |
639 | | /// `CircularBuffer`. Do not rely on a previously stored index value after |
640 | | /// altering the `CircularBuffer` with any operation that can change its length. |
641 | | /// |
642 | | /// - Returns: The last element of the `CircularBuffer`. |
643 | | /// |
644 | | /// - Complexity: O(*1*) |
645 | | @discardableResult |
646 | | @inlinable |
647 | 0 | public mutating func removeLast() -> Element { |
648 | 0 | defer { |
649 | 0 | self.removeLast(1) |
650 | 0 | } |
651 | 0 | return self.last! |
652 | 0 | } |
653 | | |
654 | | /// Replaces the specified subrange of elements with the given `CircularBuffer`. |
655 | | /// |
656 | | /// - Parameter subrange: The subrange of the collection to replace. The bounds of the range must be valid indices |
657 | | /// of the `CircularBuffer`. |
658 | | /// |
659 | | /// - Parameter newElements: The new elements to add to the `CircularBuffer`. |
660 | | /// |
661 | | /// *O(n)* where _n_ is the length of the new elements collection if the subrange equals to _n_ |
662 | | /// |
663 | | /// *O(m)* where _m_ is the combined length of the collection and _newElements_ |
664 | | @inlinable |
665 | | public mutating func replaceSubrange<C: Collection>(_ subrange: Range<Index>, with newElements: C) |
666 | 0 | where Element == C.Element { |
667 | 0 | precondition( |
668 | 0 | subrange.lowerBound >= self.startIndex && subrange.upperBound <= self.endIndex, |
669 | 0 | "Subrange out of bounds" |
670 | 0 | ) |
671 | 0 | assert( |
672 | 0 | subrange.lowerBound.isValidIndex(for: self), |
673 | 0 | "illegal index used, index was for CircularBuffer with count \(subrange.lowerBound._backingCheck), " |
674 | 0 | + "but actual count is \(self.count)" |
675 | 0 | ) |
676 | 0 | assert( |
677 | 0 | subrange.upperBound.isValidIndex(for: self), |
678 | 0 | "illegal index used, index was for CircularBuffer with count \(subrange.upperBound._backingCheck), " |
679 | 0 | + "but actual count is \(self.count)" |
680 | 0 | ) |
681 | 0 |
|
682 | 0 | let subrangeCount = self.distance(from: subrange.lowerBound, to: subrange.upperBound) |
683 | 0 |
|
684 | 0 | if subrangeCount == newElements.count { |
685 | 0 | var index = subrange.lowerBound |
686 | 0 | for element in newElements { |
687 | 0 | self._buffer[index.backingIndex] = element |
688 | 0 | index = self.index(after: index) |
689 | 0 | } |
690 | 0 | } else if subrangeCount == self.count && newElements.isEmpty { |
691 | 0 | self.removeSubrange(subrange) |
692 | 0 | } else { |
693 | 0 | var newBuffer: ContiguousArray<Element?> = [] |
694 | 0 | let neededNewCapacity = self.count + newElements.count - subrangeCount + 1 // always one spare |
695 | 0 | let newCapacity = Swift.max(self.capacity, neededNewCapacity.nextPowerOf2()) |
696 | 0 | newBuffer.reserveCapacity(newCapacity) |
697 | 0 |
|
698 | 0 | // This mapping is required due to an inconsistent ability to append sequences of non-optional |
699 | 0 | // to optional sequences. |
700 | 0 | // https://bugs.swift.org/browse/SR-7921 |
701 | 0 | newBuffer.append(contentsOf: self[self.startIndex..<subrange.lowerBound].lazy.map { $0 }) |
702 | 0 | newBuffer.append(contentsOf: newElements.lazy.map { $0 }) |
703 | 0 | newBuffer.append(contentsOf: self[subrange.upperBound..<self.endIndex].lazy.map { $0 }) |
704 | 0 |
|
705 | 0 | let repetitionCount = newCapacity &- newBuffer.count |
706 | 0 | if repetitionCount > 0 { |
707 | 0 | newBuffer.append(contentsOf: repeatElement(nil, count: repetitionCount)) |
708 | 0 | } |
709 | 0 | self._buffer = newBuffer |
710 | 0 | self.headBackingIndex = 0 |
711 | 0 | self.tailBackingIndex = newBuffer.count &- repetitionCount |
712 | 0 | } |
713 | 0 | assert(self.verifyInvariants()) |
714 | 0 | } |
715 | | |
716 | | /// Removes the elements in the specified subrange from the circular buffer. |
717 | | /// |
718 | | /// - Parameter bounds: The range of the circular buffer to be removed. The bounds of the range must be valid indices of the collection. |
719 | | @inlinable |
720 | 0 | public mutating func removeSubrange(_ bounds: Range<Index>) { |
721 | 0 | precondition(bounds.upperBound >= self.startIndex && bounds.upperBound <= self.endIndex, "Invalid bounds.") |
722 | 0 |
|
723 | 0 | let boundsCount = self.distance(from: bounds.lowerBound, to: bounds.upperBound) |
724 | 0 | switch boundsCount { |
725 | 0 | case 1: |
726 | 0 | remove(at: bounds.lowerBound) |
727 | 0 | case self.count: |
728 | 0 | self = .init(initialCapacity: self._buffer.count) |
729 | 0 | default: |
730 | 0 | replaceSubrange(bounds, with: []) |
731 | 0 | } |
732 | 0 | assert(self.verifyInvariants()) |
733 | 0 | } |
734 | | |
735 | | /// Removes & returns the item at `position` from the buffer |
736 | | /// |
737 | | /// - Parameter position: The index of the item to be removed from the buffer. |
738 | | /// |
739 | | /// *O(1)* if the position is `headIdx` or `tailIdx`. |
740 | | /// otherwise |
741 | | /// *O(n)* where *n* is the number of elements between `position` and `tailIdx`. |
742 | | @discardableResult |
743 | | @inlinable |
744 | 0 | public mutating func remove(at position: Index) -> Element { |
745 | 0 | assert( |
746 | 0 | position.isValidIndex(for: self), |
747 | 0 | "illegal index used, index was for CircularBuffer with count \(position._backingCheck), " |
748 | 0 | + "but actual count is \(self.count)" |
749 | 0 | ) |
750 | 0 | defer { |
751 | 0 | assert(self.verifyInvariants()) |
752 | 0 | } |
753 | 0 | precondition(self.indices.contains(position), "Position out of bounds.") |
754 | 0 | var bufferIndex = position.backingIndex |
755 | 0 | let element = self._buffer[bufferIndex]! |
756 | 0 |
|
757 | 0 | switch bufferIndex { |
758 | 0 | case self.headBackingIndex: |
759 | 0 | self.advanceHeadIdx(by: 1) |
760 | 0 | self._buffer[bufferIndex] = nil |
761 | 0 | case self.indexBeforeTailIdx(): |
762 | 0 | self.advanceTailIdx(by: -1) |
763 | 0 | self._buffer[bufferIndex] = nil |
764 | 0 | default: |
765 | 0 | self._buffer[bufferIndex] = nil |
766 | 0 | var nextIndex = self.indexAdvanced(index: bufferIndex, by: 1) |
767 | 0 | while nextIndex != self.tailBackingIndex { |
768 | 0 | self._buffer.swapAt(bufferIndex, nextIndex) |
769 | 0 | bufferIndex = nextIndex |
770 | 0 | nextIndex = self.indexAdvanced(index: bufferIndex, by: 1) |
771 | 0 | } |
772 | 0 | self.advanceTailIdx(by: -1) |
773 | 0 | } |
774 | 0 |
|
775 | 0 | return element |
776 | 0 | } |
777 | | |
778 | | /// The first `Element` of the `CircularBuffer` (or `nil` if empty). |
779 | | @inlinable |
780 | 1.12M | public var first: Element? { |
781 | 1.12M | // We implement this here to work around https://bugs.swift.org/browse/SR-14516 |
782 | 1.12M | guard !self.isEmpty else { |
783 | 0 | return nil |
784 | 1.12M | } |
785 | 1.12M | return self[self.startIndex] |
786 | 1.12M | } |
787 | | |
788 | | /// Prepares the `CircularBuffer` to store the specified number of elements. |
789 | | @inlinable |
790 | 0 | public mutating func reserveCapacity(_ minimumCapacity: Int) { |
791 | 0 | if self.capacity >= minimumCapacity { |
792 | 0 | // Already done, do nothing. |
793 | 0 | return |
794 | 0 | } |
795 | 0 |
|
796 | 0 | // We need to allocate a larger buffer. We take this opportunity to make ourselves contiguous |
797 | 0 | // again as needed. |
798 | 0 | let targetCapacity = minimumCapacity.nextPowerOf2() |
799 | 0 | self._resizeAndFlatten(newCapacity: targetCapacity) |
800 | 0 | } |
801 | | } |
802 | | |
803 | | extension CircularBuffer { |
804 | | @usableFromInline |
805 | 11.1k | internal func verifyInvariants() -> Bool { |
806 | 11.1k | var index = self.headBackingIndex |
807 | 1.39M | while index != self.tailBackingIndex { |
808 | 1.38M | if self._buffer[index] == nil { |
809 | 0 | return false |
810 | 1.38M | } |
811 | 1.38M | index = self.indexAdvanced(index: index, by: 1) |
812 | 1.38M | } |
813 | 11.1k | return true |
814 | 11.1k | } |
815 | | |
816 | | // this is not a general invariant (not true for CircularBuffer that have been sliced) |
817 | 0 | private func unreachableAreNil() -> Bool { |
818 | 0 | var index = self.tailBackingIndex |
819 | 0 | while index != self.headBackingIndex { |
820 | 0 | if self._buffer[index] != nil { |
821 | 0 | return false |
822 | 0 | } |
823 | 0 | index = self.indexAdvanced(index: index, by: 1) |
824 | 0 | } |
825 | 0 | return true |
826 | 0 | } |
827 | | |
828 | 0 | internal func testOnly_verifyInvariantsForNonSlices() -> Bool { |
829 | 0 | self.verifyInvariants() && self.unreachableAreNil() |
830 | 0 | } |
831 | | } |
832 | | |
833 | | extension CircularBuffer: Equatable where Element: Equatable { |
834 | 0 | public static func == (lhs: CircularBuffer, rhs: CircularBuffer) -> Bool { |
835 | 0 | lhs.count == rhs.count && zip(lhs, rhs).allSatisfy(==) |
836 | 0 | } |
837 | | } |
838 | | |
839 | | extension CircularBuffer: Hashable where Element: Hashable { |
840 | 0 | public func hash(into hasher: inout Hasher) { |
841 | 0 | for element in self { |
842 | 0 | hasher.combine(element) |
843 | 0 | } |
844 | 0 | } |
845 | | } |
846 | | |
847 | | extension CircularBuffer: Sendable where Element: Sendable {} |
848 | | |
849 | | extension CircularBuffer: ExpressibleByArrayLiteral { |
850 | 0 | public init(arrayLiteral elements: Element...) { |
851 | 0 | self.init(elements) |
852 | 0 | } |
853 | | } |