1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
/// A circular buffer that allows one object at a time to be "marked" and easily identified and retrieved later.
///
/// This object is used extensively within SwiftNIO to handle flushable buffers. It can be used to store buffered
/// writes and mark how far through the buffer the user has flushed, and therefore how far through the buffer is
/// safe to write.
public struct MarkedCircularBuffer<Element>: CustomStringConvertible {
@usableFromInline internal var _buffer: CircularBuffer<Element>
@usableFromInline internal var _markedIndexOffset: Int? /* nil: nothing marked */
/// Create a new instance.
///
/// - paramaters:
/// - initialCapacity: The initial capacity of the internal storage.
@inlinable
public init(initialCapacity: Int) {
self._buffer = CircularBuffer(initialCapacity: initialCapacity)
}
// MARK: Forwarding
/// Appends an entry to the buffer, expanding it if needed.
@inlinable
public mutating func append(_ value: Element) {
self._buffer.append(value)
}
/// Removes the first element from the buffer.
@inlinable
public mutating func removeFirst() -> Element {
assert(self._buffer.count > 0)
return self.popFirst()!
}
@inlinable
public mutating func popFirst() -> Element? {
if let markedIndexOffset = self._markedIndexOffset {
if markedIndexOffset > 0 {
self._markedIndexOffset = markedIndexOffset - 1
} else {
self._markedIndexOffset = nil
}
}
return self._buffer.popFirst()
}
/// The first element in the buffer.
@inlinable
public var first: Element? {
return self._buffer.first
}
/// If the buffer is empty.
@inlinable
public var isEmpty: Bool {
return self._buffer.isEmpty
}
/// The number of elements in the buffer.
@inlinable
public var count: Int {
return self._buffer.count
}
@inlinable
public var description: String {
return self._buffer.description
}
// MARK: Marking
/// Marks the buffer at the current index, making the last index in the buffer marked.
@inlinable
public mutating func mark() {
let count = self._buffer.count
if count > 0 {
self._markedIndexOffset = count - 1
} else {
assert(self._markedIndexOffset == nil, "marked index is \(self._markedIndexOffset.debugDescription)")
}
}
/// Returns true if the buffer is currently marked at the given index.
@inlinable
public func isMarked(index: Index) -> Bool {
assert(index >= self.startIndex, "index must not be negative")
precondition(index < self.endIndex, "index \(index) out of range (0..<\(self._buffer.count))")
if let markedIndexOffset = self._markedIndexOffset {
return self.index(self.startIndex, offsetBy: markedIndexOffset) == index
} else {
return false
}
}
/// Returns the index of the marked element.
@inlinable
public var markedElementIndex: Index? {
if let markedIndexOffset = self._markedIndexOffset {
assert(markedIndexOffset >= 0)
return self.index(self.startIndex, offsetBy: markedIndexOffset)
} else {
return nil
}
}
/// Returns the marked element.
@inlinable
public var markedElement: Element? {
return self.markedElementIndex.map { self._buffer[$0] }
}
/// Returns true if the buffer has been marked at all.
@inlinable
public var hasMark: Bool {
return self._markedIndexOffset != nil
}
}
extension MarkedCircularBuffer: Collection, MutableCollection {
public typealias RangeType<Bound> = Range<Bound> where Bound: Strideable, Bound.Stride: SignedInteger
public typealias Index = CircularBuffer<Element>.Index
public typealias SubSequence = CircularBuffer<Element>
@inlinable
public func index(after i: Index) -> Index {
return self._buffer.index(after: i)
}
@inlinable
public var startIndex: Index { return self._buffer.startIndex }
@inlinable
public var endIndex: Index { return self._buffer.endIndex }
/// Retrieves the element at the given index from the buffer, without removing it.
@inlinable
public subscript(index: Index) -> Element {
get {
return self._buffer[index]
}
set {
self._buffer[index] = newValue
}
}
@inlinable
public subscript(bounds: Range<Index>) -> SubSequence {
get {
return self._buffer[bounds]
}
set {
var index = bounds.lowerBound
var iterator = newValue.makeIterator()
while let newElement = iterator.next(), index != bounds.upperBound {
self._buffer[index] = newElement
formIndex(after: &index)
}
precondition(iterator.next() == nil && index == bounds.upperBound)
}
}
}
extension MarkedCircularBuffer: RandomAccessCollection {
@inlinable
public func index(_ i: Index, offsetBy distance: Int) -> Index {
return self._buffer.index(i, offsetBy: distance)
}
@inlinable
public func distance(from start: Index, to end: Index) -> Int {
return self._buffer.distance(from: start, to: end)
}
@inlinable
public func index(before i: Index) -> Index {
return self._buffer.index(before: i)
}
}
|