Coverage Report

Created: 2026-02-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}