swift-nio/Sources/NIOCore/CircularBuffer.swift

835 lines
32 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
/// An automatically expanding ring buffer implementation backed by a `ContiguousArray`. Even though this implementation
/// will automatically expand if more elements than `initialCapacity` are stored, it's advantageous to prevent
/// expansions from happening frequently. Expansions will always force an allocation and a copy to happen.
public struct CircularBuffer<Element>: CustomStringConvertible {
@usableFromInline
internal private(set) var _buffer: ContiguousArray<Element?>
@usableFromInline
internal private(set) var headBackingIndex: Int
@usableFromInline
internal private(set) var tailBackingIndex: Int
@inlinable
internal var mask: Int {
return self._buffer.count &- 1
}
@inlinable
internal mutating func advanceHeadIdx(by: Int) {
self.headBackingIndex = indexAdvanced(index: self.headBackingIndex, by: by)
}
@inlinable
internal mutating func advanceTailIdx(by: Int) {
self.tailBackingIndex = indexAdvanced(index: self.tailBackingIndex, by: by)
}
@inlinable
internal func indexBeforeHeadIdx() -> Int {
return self.indexAdvanced(index: self.headBackingIndex, by: -1)
}
@inlinable
internal func indexBeforeTailIdx() -> Int {
return self.indexAdvanced(index: self.tailBackingIndex, by: -1)
}
@inlinable
internal func indexAdvanced(index: Int, by: Int) -> Int {
return (index &+ by) & self.mask
}
/// An opaque `CircularBuffer` index.
///
/// You may get indices offset from other indices by using `CircularBuffer.index(:offsetBy:)`,
/// `CircularBuffer.index(before:)`, or `CircularBuffer.index(after:)`.
///
/// - note: Every index is invalidated as soon as you perform a length-changing operating on the `CircularBuffer`
/// but remains valid when you replace one item by another using the subscript.
public struct Index: Comparable, Sendable {
@usableFromInline private(set) var _backingIndex: UInt32
@usableFromInline private(set) var _backingCheck: _UInt24
@usableFromInline private(set) var isIndexGEQHeadIndex: Bool
@inlinable
internal var backingIndex: Int {
return Int(self._backingIndex)
}
@inlinable
internal init(backingIndex: Int, backingCount: Int, backingIndexOfHead: Int) {
self.isIndexGEQHeadIndex = backingIndex >= backingIndexOfHead
self._backingCheck = .max
self._backingIndex = UInt32(backingIndex)
debugOnly {
// if we can, we store the check for the backing here
self._backingCheck = backingCount < Int(_UInt24.max) ? _UInt24(UInt32(backingCount)) : .max
}
}
@inlinable
public static func == (lhs: Index, rhs: Index) -> Bool {
return lhs._backingIndex == rhs._backingIndex &&
lhs._backingCheck == rhs._backingCheck &&
lhs.isIndexGEQHeadIndex == rhs.isIndexGEQHeadIndex
}
@inlinable
public static func < (lhs: Index, rhs: Index) -> Bool {
if lhs.isIndexGEQHeadIndex && rhs.isIndexGEQHeadIndex {
return lhs.backingIndex < rhs.backingIndex
} else if lhs.isIndexGEQHeadIndex && !rhs.isIndexGEQHeadIndex {
return true
} else if !lhs.isIndexGEQHeadIndex && rhs.isIndexGEQHeadIndex {
return false
} else {
return lhs.backingIndex < rhs.backingIndex
}
}
@usableFromInline
internal func isValidIndex(for ring: CircularBuffer<Element>) -> Bool {
return self._backingCheck == _UInt24.max || Int(self._backingCheck) == ring.count
}
}
}
// MARK: Collection/MutableCollection implementation
extension CircularBuffer: Collection, MutableCollection {
public typealias Element = Element
public typealias Indices = DefaultIndices<CircularBuffer<Element>>
public typealias RangeType<Bound> = Range<Bound> where Bound: Strideable, Bound.Stride: SignedInteger
public typealias SubSequence = CircularBuffer<Element>
/// Returns the position immediately after the given index.
///
/// The successor of an index must be well defined. For an index `i` into a
/// collection `c`, calling `c.index(after: i)` returns the same index every
/// time.
///
/// - Parameter i: A valid index of the collection. `i` must be less than
/// `endIndex`.
/// - Returns: The index value immediately after `i`.
@inlinable
public func index(after: Index) -> Index {
return self.index(after, offsetBy: 1)
}
/// Returns the index before `index`.
@inlinable
public func index(before: Index) -> Index {
return self.index(before, offsetBy: -1)
}
/// Accesses the element at the specified index.
///
/// You can subscript `CircularBuffer` with any valid index other than the
/// `CircularBuffer`'s end index. The end index refers to the position one
/// past the last element of a collection, so it doesn't correspond with an
/// element.
///
/// - Parameter position: The position of the element to access. `position`
/// must be a valid index of the collection that is not equal to the
/// `endIndex` property.
///
/// - Complexity: O(1)
@inlinable
public subscript(position: Index) -> Element {
get {
assert(position.isValidIndex(for: self),
"illegal index used, index was for CircularBuffer with count \(position._backingCheck), " +
"but actual count is \(self.count)")
return self._buffer[position.backingIndex]!
}
set {
assert(position.isValidIndex(for: self),
"illegal index used, index was for CircularBuffer with count \(position._backingCheck), " +
"but actual count is \(self.count)")
self._buffer[position.backingIndex] = newValue
}
}
/// The position of the first element in a nonempty `CircularBuffer`.
///
/// If the `CircularBuffer` is empty, `startIndex` is equal to `endIndex`.
@inlinable
public var startIndex: Index {
return .init(backingIndex: self.headBackingIndex,
backingCount: self.count,
backingIndexOfHead: self.headBackingIndex)
}
/// The `CircularBuffer`'s "past the end" position---that is, the position one
/// greater than the last valid subscript argument.
///
/// When you need a range that includes the last element of a collection, use
/// the half-open range operator (`..<`) with `endIndex`. The `..<` operator
/// creates a range that doesn't include the upper bound, so it's always
/// safe to use with `endIndex`.
///
/// If the `CircularBuffer` is empty, `endIndex` is equal to `startIndex`.
@inlinable
public var endIndex: Index {
return .init(backingIndex: self.tailBackingIndex,
backingCount: self.count,
backingIndexOfHead: self.headBackingIndex)
}
/// Returns the distance between two indices.
///
/// Unless the collection conforms to the `BidirectionalCollection` protocol,
/// `start` must be less than or equal to `end`.
///
/// - Parameters:
/// - start: A valid index of the collection.
/// - end: Another valid index of the collection. If `end` is equal to
/// `start`, the result is zero.
/// - Returns: The distance between `start` and `end`. The result can be
/// negative only if the collection conforms to the
/// `BidirectionalCollection` protocol.
///
/// - Complexity: O(1) if the collection conforms to
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the
/// resulting distance.
@inlinable
public func distance(from start: CircularBuffer<Element>.Index, to end: CircularBuffer<Element>.Index) -> Int {
let backingCount = self._buffer.count
switch (start.isIndexGEQHeadIndex, end.isIndexGEQHeadIndex) {
case (true, true):
return end.backingIndex &- start.backingIndex
case (true, false):
return backingCount &- (start.backingIndex &- end.backingIndex)
case (false, true):
return -(backingCount &- (end.backingIndex &- start.backingIndex))
case (false, false):
return end.backingIndex &- start.backingIndex
}
}
@inlinable
public func _copyContents(
initializing buffer: UnsafeMutableBufferPointer<Element>
) -> (Iterator, UnsafeMutableBufferPointer<Element>.Index) {
precondition(buffer.count >= self.count)
guard var ptr = buffer.baseAddress else {
return (self.makeIterator(), buffer.startIndex)
}
if self.tailBackingIndex >= self.headBackingIndex {
for index in self.headBackingIndex..<self.tailBackingIndex {
ptr.initialize(to: self._buffer[index]!)
ptr += 1
}
} else {
for index in self.headBackingIndex..<self._buffer.endIndex {
ptr.initialize(to: self._buffer[index]!)
ptr += 1
}
for index in 0..<self.tailBackingIndex {
ptr.initialize(to: self._buffer[index]!)
ptr += 1
}
}
return (self[self.endIndex..<self.endIndex].makeIterator(), self.count)
}
// These are implemented as no-ops for performance reasons.
@inlinable
public func _failEarlyRangeCheck(_ index: Index, bounds: Range<Index>) {}
@inlinable
public func _failEarlyRangeCheck(_ index: Index, bounds: ClosedRange<Index>) {}
@inlinable
public func _failEarlyRangeCheck(_ range: Range<Index>, bounds: Range<Index>) {}
}
// MARK: RandomAccessCollection implementation
extension CircularBuffer: RandomAccessCollection {
/// Returns the index offset by `distance` from `index`.
///
/// The following example obtains an index advanced four positions from a
/// string's starting index and then prints the character at that position.
///
/// let s = "Swift"
/// let i = s.index(s.startIndex, offsetBy: 4)
/// print(s[i])
/// // Prints "t"
///
/// The value passed as `distance` must not offset `i` beyond the bounds of
/// the collection.
///
/// - Parameters:
/// - i: A valid index of the collection.
/// - distance: The distance to offset `i`. `distance` must not be negative
/// unless the collection conforms to the `BidirectionalCollection`
/// protocol.
/// - Returns: An index offset by `distance` from the index `i`. If
/// `distance` is positive, this is the same value as the result of
/// `distance` calls to `index(after:)`. If `distance` is negative, this
/// is the same value as the result of `abs(distance)` calls to
/// `index(before:)`.
///
/// - Complexity: O(1) if the collection conforms to
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the absolute
/// value of `distance`.
@inlinable
public func index(_ i: Index, offsetBy distance: Int) -> Index {
return .init(backingIndex: (i.backingIndex &+ distance) & self.mask,
backingCount: self.count,
backingIndexOfHead: self.headBackingIndex)
}
@inlinable
public subscript(bounds: Range<Index>) -> SubSequence {
get {
precondition(self.distance(from: self.startIndex, to: bounds.lowerBound) >= 0)
precondition(self.distance(from: bounds.upperBound, to: self.endIndex) >= 0)
var newRing = self
newRing.headBackingIndex = bounds.lowerBound.backingIndex
newRing.tailBackingIndex = bounds.upperBound.backingIndex
return newRing
}
set {
precondition(self.distance(from: self.startIndex, to: bounds.lowerBound) >= 0)
precondition(self.distance(from: bounds.upperBound, to: self.endIndex) >= 0)
self.replaceSubrange(bounds, with: newValue)
}
}
}
extension CircularBuffer {
/// Allocates a buffer that can hold up to `initialCapacity` elements and initialise an empty ring backed by
/// the buffer. When the ring grows to more than `initialCapacity` elements the buffer will be expanded.
@inlinable
public init(initialCapacity: Int) {
let capacity = Int(UInt32(initialCapacity).nextPowerOf2())
self.headBackingIndex = 0
self.tailBackingIndex = 0
self._buffer = ContiguousArray<Element?>(repeating: nil, count: capacity)
assert(self._buffer.count == capacity)
}
/// Allocates an empty buffer.
@inlinable
public init() {
self = .init(initialCapacity: 16)
}
/// Append an element to the end of the ring buffer.
///
/// Amortized *O(1)*
@inlinable
public mutating func append(_ value: Element) {
self._buffer[self.tailBackingIndex] = value
self.advanceTailIdx(by: 1)
if self.headBackingIndex == self.tailBackingIndex {
// No more room left for another append so grow the buffer now.
self._doubleCapacity()
}
}
/// Prepend an element to the front of the ring buffer.
///
/// Amortized *O(1)*
@inlinable
public mutating func prepend(_ value: Element) {
let idx = self.indexBeforeHeadIdx()
self._buffer[idx] = value
self.advanceHeadIdx(by: -1)
if self.headBackingIndex == self.tailBackingIndex {
// No more room left for another append so grow the buffer now.
self._doubleCapacity()
}
}
/// Double the capacity of the buffer and adjust the headIdx and tailIdx.
///
/// Must only be called when buffer is full.
@inlinable
internal mutating func _doubleCapacity() {
// Double the storage. This can't use _resizeAndFlatten because the buffer is
// full at this stage. That's ok: we have some optimised code paths for this use-case.
let newCapacity = self.capacity << 1
assert(self.headBackingIndex == self.tailBackingIndex)
var newBacking: ContiguousArray<Element?> = []
precondition(newCapacity > 0, "Can't change capacity to \(newCapacity)")
assert(newCapacity % 2 == 0)
assert(newCapacity > self.capacity)
newBacking.reserveCapacity(newCapacity)
newBacking.append(contentsOf: self._buffer[self.headBackingIndex...])
newBacking.append(contentsOf: self._buffer[..<self.tailBackingIndex])
let newTailIndex = newBacking.count
let paddingCount = newCapacity &- newTailIndex
newBacking.append(contentsOf: repeatElement(nil, count: paddingCount))
self.headBackingIndex = 0
self.tailBackingIndex = newTailIndex
self._buffer = newBacking
assert(self.verifyInvariants())
}
/// Resizes and flatten this buffer.
///
/// Capacities are always powers of 2.
@inlinable
internal mutating func _resizeAndFlatten(newCapacity: Int) {
var newBacking: ContiguousArray<Element?> = []
precondition(newCapacity > 0, "Can't change capacity to \(newCapacity)")
assert(newCapacity % 2 == 0)
assert(newCapacity > self.capacity)
newBacking.reserveCapacity(newCapacity)
if self.tailBackingIndex >= self.headBackingIndex {
newBacking.append(contentsOf: self._buffer[self.headBackingIndex..<self.tailBackingIndex])
} else {
newBacking.append(contentsOf: self._buffer[self.headBackingIndex...])
newBacking.append(contentsOf: self._buffer[..<self.tailBackingIndex])
}
let newTailIndex = newBacking.count
let paddingCount = newCapacity &- newTailIndex
newBacking.append(contentsOf: repeatElement(nil, count: paddingCount))
self.headBackingIndex = 0
self.tailBackingIndex = newTailIndex
self._buffer = newBacking
assert(self.verifyInvariants())
}
/// Return element `offset` from first element.
///
/// *O(1)*
@inlinable
public subscript(offset offset: Int) -> Element {
get {
return self[self.index(self.startIndex, offsetBy: offset)]
}
set {
self[self.index(self.startIndex, offsetBy: offset)] = newValue
}
}
/// Returns whether the ring is empty.
@inlinable
public var isEmpty: Bool {
return self.headBackingIndex == self.tailBackingIndex
}
/// Returns the number of element in the ring.
@inlinable
public var count: Int {
if self.tailBackingIndex >= self.headBackingIndex {
return self.tailBackingIndex &- self.headBackingIndex
} else {
return self._buffer.count &- (self.headBackingIndex &- self.tailBackingIndex)
}
}
/// The total number of elements that the ring can contain without allocating new storage.
@inlinable
public var capacity: Int {
return self._buffer.count
}
/// Removes all members from the circular buffer whist keeping the capacity.
@inlinable
public mutating func removeAll(keepingCapacity: Bool = false) {
if keepingCapacity {
self.removeFirst(self.count)
} else {
self._buffer.removeAll(keepingCapacity: false)
self._buffer.append(nil)
}
self.headBackingIndex = 0
self.tailBackingIndex = 0
assert(self.verifyInvariants())
}
/// Modify the element at `index`.
///
/// This function exists to provide a method of modifying the element in its underlying backing storage, instead
/// of copying it out, modifying it, and copying it back in. This emulates the behaviour of the `_modify` accessor
/// that is part of the generalized accessors work. That accessor is currently underscored and not safe to use, so
/// this is the next best thing.
///
/// Note that this function is not guaranteed to be fast. In particular, as it is both generic and accepts a closure
/// it is possible that it will be slower than using the get/modify/set path that occurs with the subscript. If you
/// are interested in using this function for performance you *must* test and verify that the optimisation applies
/// correctly in your situation.
///
/// - parameters:
/// - index: The index of the object that should be modified. If this index is invalid this function will trap.
/// - modifyFunc: The function to apply to the modified object.
@inlinable
public mutating func modify<Result>(_ index: Index, _ modifyFunc: (inout Element) throws -> Result) rethrows -> Result {
return try modifyFunc(&self._buffer[index.backingIndex]!)
}
// MARK: CustomStringConvertible implementation
/// Returns a human readable description of the ring.
public var description: String {
var desc = "[ "
for el in self._buffer.enumerated() {
if el.0 == self.headBackingIndex {
desc += "<"
} else if el.0 == self.tailBackingIndex {
desc += ">"
}
desc += el.1.map { "\($0) " } ?? "_ "
}
desc += "]"
desc += " (bufferCapacity: \(self._buffer.count), ringLength: \(self.count))"
return desc
}
}
// MARK: - RangeReplaceableCollection
extension CircularBuffer: RangeReplaceableCollection {
/// Removes and returns the first element of the `CircularBuffer`.
///
/// Calling this method may invalidate all saved indices of this
/// `CircularBuffer`. Do not rely on a previously stored index value after
/// altering a `CircularBuffer` with any operation that can change its length.
///
/// - Returns: The first element of the `CircularBuffer` if the `CircularBuffer` is not
/// empty; otherwise, `nil`.
///
/// - Complexity: O(1)
@inlinable
public mutating func popFirst() -> Element? {
if count > 0 {
return self.removeFirst()
} else {
return nil
}
}
/// Removes and returns the last element of the `CircularBuffer`.
///
/// Calling this method may invalidate all saved indices of this
/// `CircularBuffer`. Do not rely on a previously stored index value after
/// altering a `CircularBuffer` with any operation that can change its length.
///
/// - Returns: The last element of the `CircularBuffer` if the `CircularBuffer` is not
/// empty; otherwise, `nil`.
///
/// - Complexity: O(1)
@inlinable
public mutating func popLast() -> Element? {
if count > 0 {
return self.removeLast()
} else {
return nil
}
}
/// Removes the specified number of elements from the end of the
/// `CircularBuffer`.
///
/// Attempting to remove more elements than exist in the `CircularBuffer`
/// triggers a runtime error.
///
/// Calling this method may invalidate all saved indices of this
/// `CircularBuffer`. Do not rely on a previously stored index value after
/// altering a `CircularBuffer` with any operation that can change its length.
///
/// - Parameter k: The number of elements to remove from the `CircularBuffer`.
/// `k` must be greater than or equal to zero and must not exceed the
/// number of elements in the `CircularBuffer`.
///
/// - Complexity: O(*k*), where *k* is the specified number of elements.
@inlinable
public mutating func removeLast(_ k: Int) {
precondition(k <= self.count, "Number of elements to drop bigger than the amount of elements in the buffer.")
var idx = self.tailBackingIndex
for _ in 0 ..< k {
idx = self.indexAdvanced(index: idx, by: -1)
self._buffer[idx] = nil
}
self.tailBackingIndex = idx
}
/// Removes the specified number of elements from the beginning of the
/// `CircularBuffer`.
///
/// Calling this method may invalidate any existing indices for use with this
/// `CircularBuffer`.
///
/// - Parameter k: The number of elements to remove.
/// `k` must be greater than or equal to zero and must not exceed the
/// number of elements in the `CircularBuffer`.
///
/// - Complexity: O(*k*), where *k* is the specified number of elements.
@inlinable
public mutating func removeFirst(_ k: Int) {
precondition(k <= self.count, "Number of elements to drop bigger than the amount of elements in the buffer.")
var idx = self.headBackingIndex
for _ in 0 ..< k {
self._buffer[idx] = nil
idx = self.indexAdvanced(index: idx, by: 1)
}
self.headBackingIndex = idx
}
/// Removes and returns the first element of the `CircularBuffer`.
///
/// The `CircularBuffer` must not be empty.
///
/// Calling this method may invalidate any existing indices for use with this
/// `CircularBuffer`.
///
/// - Returns: The removed element.
///
/// - Complexity: O(*1*)
@discardableResult
@inlinable
public mutating func removeFirst() -> Element {
defer {
self.removeFirst(1)
}
return self.first!
}
/// Removes and returns the last element of the `CircularBuffer`.
///
/// The `CircularBuffer` must not be empty.
///
/// Calling this method may invalidate all saved indices of this
/// `CircularBuffer`. Do not rely on a previously stored index value after
/// altering the `CircularBuffer` with any operation that can change its length.
///
/// - Returns: The last element of the `CircularBuffer`.
///
/// - Complexity: O(*1*)
@discardableResult
@inlinable
public mutating func removeLast() -> Element {
defer {
self.removeLast(1)
}
return self.last!
}
/// Replaces the specified subrange of elements with the given `CircularBuffer`.
///
/// - Parameter subrange: The subrange of the collection to replace. The bounds of the range must be valid indices
/// of the `CircularBuffer`.
///
/// - Parameter newElements: The new elements to add to the `CircularBuffer`.
///
/// *O(n)* where _n_ is the length of the new elements collection if the subrange equals to _n_
///
/// *O(m)* where _m_ is the combined length of the collection and _newElements_
@inlinable
public mutating func replaceSubrange<C: Collection>(_ subrange: Range<Index>, with newElements: C) where Element == C.Element {
precondition(subrange.lowerBound >= self.startIndex && subrange.upperBound <= self.endIndex,
"Subrange out of bounds")
assert(subrange.lowerBound.isValidIndex(for: self),
"illegal index used, index was for CircularBuffer with count \(subrange.lowerBound._backingCheck), " +
"but actual count is \(self.count)")
assert(subrange.upperBound.isValidIndex(for: self),
"illegal index used, index was for CircularBuffer with count \(subrange.upperBound._backingCheck), " +
"but actual count is \(self.count)")
let subrangeCount = self.distance(from: subrange.lowerBound, to: subrange.upperBound)
if subrangeCount == newElements.count {
var index = subrange.lowerBound
for element in newElements {
self._buffer[index.backingIndex] = element
index = self.index(after: index)
}
} else if subrangeCount == self.count && newElements.isEmpty {
self.removeSubrange(subrange)
} else {
var newBuffer: ContiguousArray<Element?> = []
let neededNewCapacity = self.count + newElements.count - subrangeCount + 1 /* always one spare */
let newCapacity = Swift.max(self.capacity, neededNewCapacity.nextPowerOf2())
newBuffer.reserveCapacity(newCapacity)
// This mapping is required due to an inconsistent ability to append sequences of non-optional
// to optional sequences.
// https://bugs.swift.org/browse/SR-7921
newBuffer.append(contentsOf: self[self.startIndex ..< subrange.lowerBound].lazy.map { $0 })
newBuffer.append(contentsOf: newElements.lazy.map { $0 })
newBuffer.append(contentsOf: self[subrange.upperBound..<self.endIndex].lazy.map { $0 })
let repetitionCount = newCapacity &- newBuffer.count
if repetitionCount > 0 {
newBuffer.append(contentsOf: repeatElement(nil, count: repetitionCount))
}
self._buffer = newBuffer
self.headBackingIndex = 0
self.tailBackingIndex = newBuffer.count &- repetitionCount
}
assert(self.verifyInvariants())
}
/// Removes the elements in the specified subrange from the circular buffer.
///
/// - Parameter bounds: The range of the circular buffer to be removed. The bounds of the range must be valid indices of the collection.
@inlinable
public mutating func removeSubrange(_ bounds: Range<Index>) {
precondition(bounds.upperBound >= self.startIndex && bounds.upperBound <= self.endIndex, "Invalid bounds.")
let boundsCount = self.distance(from: bounds.lowerBound, to: bounds.upperBound)
switch boundsCount {
case 1:
remove(at: bounds.lowerBound)
case self.count:
self = .init(initialCapacity: self._buffer.count)
default:
replaceSubrange(bounds, with: [])
}
assert(self.verifyInvariants())
}
/// Removes & returns the item at `position` from the buffer
///
/// - Parameter position: The index of the item to be removed from the buffer.
///
/// *O(1)* if the position is `headIdx` or `tailIdx`.
/// otherwise
/// *O(n)* where *n* is the number of elements between `position` and `tailIdx`.
@discardableResult
@inlinable
public mutating func remove(at position: Index) -> Element {
assert(position.isValidIndex(for: self),
"illegal index used, index was for CircularBuffer with count \(position._backingCheck), " +
"but actual count is \(self.count)")
defer {
assert(self.verifyInvariants())
}
precondition(self.indices.contains(position), "Position out of bounds.")
var bufferIndex = position.backingIndex
let element = self._buffer[bufferIndex]!
switch bufferIndex {
case self.headBackingIndex:
self.advanceHeadIdx(by: 1)
self._buffer[bufferIndex] = nil
case self.indexBeforeTailIdx():
self.advanceTailIdx(by: -1)
self._buffer[bufferIndex] = nil
default:
self._buffer[bufferIndex] = nil
var nextIndex = self.indexAdvanced(index: bufferIndex, by: 1)
while nextIndex != self.tailBackingIndex {
self._buffer.swapAt(bufferIndex, nextIndex)
bufferIndex = nextIndex
nextIndex = self.indexAdvanced(index: bufferIndex, by: 1)
}
self.advanceTailIdx(by: -1)
}
return element
}
/// The first `Element` of the `CircularBuffer` (or `nil` if empty).
@inlinable
public var first: Element? {
// We implement this here to work around https://bugs.swift.org/browse/SR-14516
guard !self.isEmpty else {
return nil
}
return self[self.startIndex]
}
/// Prepares the `CircularBuffer` to store the specified number of elements.
@inlinable
public mutating func reserveCapacity(_ minimumCapacity: Int) {
if self.capacity >= minimumCapacity {
// Already done, do nothing.
return
}
// We need to allocate a larger buffer. We take this opportunity to make ourselves contiguous
// again as needed.
let targetCapacity = minimumCapacity.nextPowerOf2()
self._resizeAndFlatten(newCapacity: targetCapacity)
}
}
extension CircularBuffer {
@usableFromInline
internal func verifyInvariants() -> Bool {
var index = self.headBackingIndex
while index != self.tailBackingIndex {
if self._buffer[index] == nil {
return false
}
index = self.indexAdvanced(index: index, by: 1)
}
return true
}
// this is not a general invariant (not true for CircularBuffer that have been sliced)
private func unreachableAreNil() -> Bool {
var index = self.tailBackingIndex
while index != self.headBackingIndex {
if self._buffer[index] != nil {
return false
}
index = self.indexAdvanced(index: index, by: 1)
}
return true
}
internal func testOnly_verifyInvariantsForNonSlices() -> Bool {
return self.verifyInvariants() && self.unreachableAreNil()
}
}
extension CircularBuffer: Equatable where Element: Equatable {
public static func ==(lhs: CircularBuffer, rhs: CircularBuffer) -> Bool {
return lhs.count == rhs.count && zip(lhs, rhs).allSatisfy(==)
}
}
extension CircularBuffer: Hashable where Element: Hashable {
public func hash(into hasher: inout Hasher) {
for element in self {
hasher.combine(element)
}
}
}
extension CircularBuffer: Sendable where Element: Sendable {}
extension CircularBuffer: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: Element...) {
self.init(elements)
}
}