///|
/// A failure raised while constructing a fixed-capacity ring buffer.
pub(all) suberror RingBufferError {
/// The requested capacity was zero or negative.
InvalidCapacity(Int)
} derive(Eq, Debug)
///|
/// A fixed-capacity FIFO collection that overwrites its oldest value when full.
///
/// The representation is private so callers cannot bypass the fixed-capacity
/// and insertion-order invariants through directional deque operations.
/// Arbitrary position-based insertion or removal, reordering, capacity changes,
/// and multi-buffer operations are intentionally excluded from the public API.
/// Predicate-based retain operations remain available because they preserve
/// the relative insertion order of every retained value.
/// Nested collection operations and `FromJson` are also excluded because a
/// fixed capacity cannot be inferred for their resulting buffers.
pub struct RingBuffer[A] {
priv deque : @deque.Deque[A]
}
///|
fn[A] empty_ring_buffer(capacity : Int) -> RingBuffer[A] {
let deque : @deque.Deque[A] = @deque.Deque([], capacity~)
RingBuffer::{ deque, }
}
///|
/// Constructs an empty ring buffer with a positive fixed capacity.
pub fn[A] RingBuffer::RingBuffer(
capacity : Int,
) -> RingBuffer[A] raise RingBufferError {
guard capacity > 0 else { raise RingBufferError::InvalidCapacity(capacity) }
empty_ring_buffer(capacity)
}
///|
/// Constructs a ring buffer by pushing values in order.
///
/// When `values` contains more elements than `capacity`, only the newest
/// `capacity` elements are retained.
pub fn[A] RingBuffer::from_array(
values : ArrayView[A],
capacity~ : Int,
) -> RingBuffer[A] raise RingBufferError {
let buffer = RingBuffer::RingBuffer(capacity)
for value in values {
buffer.push(value)
}
buffer
}
///|
/// Constructs a ring buffer by consuming values in order.
///
/// When the iterator yields more elements than `capacity`, only the newest
/// `capacity` elements are retained.
pub fn[A] RingBuffer::from_iter(
values : Iter[A],
capacity~ : Int,
) -> RingBuffer[A] raise RingBufferError {
let buffer = RingBuffer::RingBuffer(capacity)
for value in values {
buffer.push(value)
}
buffer
}
///|
/// Adds the newest value, overwriting the oldest value when the buffer is full.
pub fn[A] RingBuffer::push(self : RingBuffer[A], value : A) -> Unit {
if self.is_full() {
self.deque.pop_front() |> ignore
}
self.deque.push_back(value)
}
///|
/// Removes and returns the oldest value, or returns `None` when empty.
pub fn[A] RingBuffer::pop(self : RingBuffer[A]) -> A? {
self.deque.pop_front()
}
///|
/// Returns the oldest value without removing it, or returns `None` when empty.
pub fn[A] RingBuffer::peek(self : RingBuffer[A]) -> A? {
self.deque.front()
}
///|
/// Returns the newest value without removing it, or returns `None` when empty.
pub fn[A] RingBuffer::peek_latest(self : RingBuffer[A]) -> A? {
self.deque.back()
}
///|
/// Returns the fixed maximum number of values held by the buffer.
pub fn[A] RingBuffer::capacity(self : RingBuffer[A]) -> Int {
self.deque.capacity()
}
///|
/// Returns the current number of values.
pub fn[A] RingBuffer::length(self : RingBuffer[A]) -> Int {
self.deque.length()
}
///|
/// Returns whether the buffer contains no values.
pub fn[A] RingBuffer::is_empty(self : RingBuffer[A]) -> Bool {
self.deque.is_empty()
}
///|
/// Returns whether the current length has reached the fixed capacity.
pub fn[A] RingBuffer::is_full(self : RingBuffer[A]) -> Bool {
self.length() == self.capacity()
}
///|
/// Removes every value without changing the fixed capacity.
pub fn[A] RingBuffer::clear(self : RingBuffer[A]) -> Unit {
self.deque.clear()
}
///|
/// Returns the value at a logical insertion-order index, or `None` when out of bounds.
pub fn[A] RingBuffer::get(self : RingBuffer[A], index : Int) -> A? {
self.deque.get(index)
}
///|
/// Returns the value at a logical insertion-order index.
///
/// Panics when `index` is negative or not less than `length()`.
pub fn[A] RingBuffer::at(self : RingBuffer[A], index : Int) -> A {
self.deque[index]
}
///|
/// Provides indexed access in logical insertion order.
#alias("_[_]")
pub fn[A] RingBuffer::indexed_get(self : RingBuffer[A], index : Int) -> A {
self.at(index)
}
///|
/// Replaces the value at a logical insertion-order index without changing order.
///
/// Panics when `index` is negative or not less than `length()`.
pub fn[A] RingBuffer::set(self : RingBuffer[A], index : Int, value : A) -> Unit {
self.deque[index] = value
}
///|
/// Provides indexed replacement in logical insertion order.
#alias("_[_]=_")
pub fn[A] RingBuffer::indexed_set(
self : RingBuffer[A],
index : Int,
value : A,
) -> Unit {
self.set(index, value)
}
///|
/// Returns an iterator from the oldest value to the newest value.
pub fn[A] RingBuffer::iter(self : RingBuffer[A]) -> Iter[A] {
self.deque.iter()
}
///|
/// Returns an iterator of logical indices and values from oldest to newest.
pub fn[A] RingBuffer::iter2(self : RingBuffer[A]) -> Iter2[Int, A] {
self.deque.iter2()
}
///|
/// Calls `action` for each value from oldest to newest.
pub fn[A] RingBuffer::each(self : RingBuffer[A], action : (A) -> Unit) -> Unit {
self.deque.each(action)
}
///|
/// Calls `action` for each logical index and value from oldest to newest.
pub fn[A] RingBuffer::eachi(
self : RingBuffer[A],
action : (Int, A) -> Unit,
) -> Unit {
self.deque.eachi(action)
}
///|
/// Returns an iterator from the newest value to the oldest value.
pub fn[A] RingBuffer::rev_iter(self : RingBuffer[A]) -> Iter[A] {
self.deque.rev_iter()
}
///|
/// Returns an iterator of logical indices and values from newest to oldest.
pub fn[A] RingBuffer::rev_iter2(self : RingBuffer[A]) -> Iter2[Int, A] {
self.deque.rev_iter2()
}
///|
/// Calls `action` for each value from newest to oldest.
pub fn[A] RingBuffer::rev_each(
self : RingBuffer[A],
action : (A) -> Unit,
) -> Unit {
self.deque.rev_each(action)
}
///|
/// Calls `action` for each logical index and value from newest to oldest.
pub fn[A] RingBuffer::rev_eachi(
self : RingBuffer[A],
action : (Int, A) -> Unit,
) -> Unit {
self.deque.rev_eachi(action)
}
///|
/// Returns two borrowed views that together contain values from oldest to newest.
///
/// The first view starts at the physical head. The second view contains any
/// wrapped values from the physical start. Mutating the buffer may invalidate
/// either view.
pub fn[A] RingBuffer::as_views(
self : RingBuffer[A],
) -> (ArrayView[A], ArrayView[A]) {
self.deque.as_views()
}
///|
/// Copies the current values into an array ordered from oldest to newest.
pub fn[A] RingBuffer::to_array(self : RingBuffer[A]) -> Array[A] {
self.deque.to_array()
}
///|
/// Returns whether any current value equals `value`.
pub fn[A : Eq] RingBuffer::contains(self : RingBuffer[A], value : A) -> Bool {
self.deque.contains(value)
}
///|
/// Returns the logical index of the first matching value, or `None` when absent.
pub fn[A : Eq] RingBuffer::search(self : RingBuffer[A], value : A) -> Int? {
self.deque.search(value)
}
///|
/// Searches logically ordered values and returns a matching index or insertion index.
///
/// The values must already be sorted according to `Compare`.
pub fn[A : Compare] RingBuffer::binary_search(
self : RingBuffer[A],
value : A,
) -> Result[Int, Int] {
self.deque.binary_search(value)
}
///|
/// Searches logically ordered values with a caller-provided comparison.
///
/// The callback must describe a sequence sorted around the requested value.
pub fn[A] RingBuffer::binary_search_by(
self : RingBuffer[A],
compare : (A) -> Int,
) -> Result[Int, Int] {
self.deque.binary_search_by(compare)
}
///|
/// Creates a shallow copy with the same fixed capacity and logical order.
pub fn[A] RingBuffer::copy(self : RingBuffer[A]) -> RingBuffer[A] {
let copied = empty_ring_buffer(self.capacity())
for value in self.iter() {
copied.push(value)
}
copied
}
///|
/// Maps every value in logical order into a buffer with the same fixed capacity.
pub fn[A, B] RingBuffer::map(
self : RingBuffer[A],
transform : (A) -> B,
) -> RingBuffer[B] {
let mapped = empty_ring_buffer(self.capacity())
for value in self.iter() {
mapped.push(transform(value))
}
mapped
}
///|
/// Maps every logical index and value into a buffer with the same fixed capacity.
pub fn[A, B] RingBuffer::mapi(
self : RingBuffer[A],
transform : (Int, A) -> B,
) -> RingBuffer[B] {
let mapped = empty_ring_buffer(self.capacity())
self.eachi((index, value) => mapped.push(transform(index, value)))
mapped
}
///|
/// Copies matching values in logical order into a buffer with the same capacity.
pub fn[A] RingBuffer::filter(
self : RingBuffer[A],
predicate : (A) -> Bool raise?,
) -> RingBuffer[A] raise? {
let filtered = empty_ring_buffer(self.capacity())
for value in self.iter() {
if predicate(value) {
filtered.push(value)
}
}
filtered
}
///|
/// Keeps only matching values while preserving their logical order and capacity.
pub fn[A] RingBuffer::retain(
self : RingBuffer[A],
predicate : (A) -> Bool,
) -> Unit {
self.deque.retain(predicate)
}
///|
/// Replaces or removes values while preserving logical order and capacity.
pub fn[A] RingBuffer::retain_map(
self : RingBuffer[A],
transform : (A) -> A?,
) -> Unit {
self.deque.retain_map(transform)
}
///|
/// Joins the current strings from oldest to newest with `separator`.
pub fn RingBuffer::join(
self : RingBuffer[String],
separator : StringView,
) -> String {
self.deque.join(separator)
}
///|
/// Compares ring buffers by their logical value sequence, excluding capacity.
pub impl[A : Eq] Eq for RingBuffer[A] with fn equal(self, other) {
self.deque == other.deque
}
///|
/// Orders ring buffers by length, then compares equal-length logical sequences element by element.
pub impl[A : Compare] Compare for RingBuffer[A] with fn compare(self, other) {
self.deque.compare(other.deque)
}
///|
/// Hashes the logical value sequence without including capacity.
pub impl[A : Hash] Hash for RingBuffer[A] with fn hash_combine(self, hasher) {
Hash::hash_combine(self.deque, hasher)
}
///|
/// Converts the logical value sequence to a JSON array.
pub impl[A : ToJson] ToJson for RingBuffer[A] with fn to_json(self) {
ToJson::to_json(self.deque)
}
///|
/// Formats the logical value sequence for debugging.
pub impl[A : @debug.Debug] @debug.Debug for RingBuffer[A] with fn to_repr(self) {
@debug.Repr::opaque_(
"RingBuffer",
@debug.Repr::array(self.to_array().map(value => @debug.Repr(value))),
)
}