///|
/// A fixed-size circular buffer: pushing past the capacity overwrites the oldest
/// sample. What a service keeps its last N events, timings or failures in.
pub struct Ring[T] {
slots : Array[T?]
capacity : Int32
mut start : Int32
mut size : Int32
}
///|
/// A ring holding at most `capacity` items.
///
/// # Parameters
/// - `capacity` : how many items it keeps; `0` is raised to `1`, because a ring of
/// nothing could never hold a sample
pub fn[T] Ring::new(capacity : Int32) -> Ring[T] {
let capacity = if capacity < 1 { 1 } else { capacity }
{ slots: Array::make(capacity, None), capacity, start: 0, size: 0, }
}
///|
/// Append a sample, overwriting the oldest one when the ring is full.
pub fn[T] Ring::push(self : Ring[T], item : T) -> Unit {
if self.size < self.capacity {
let at = (self.start + self.size) % self.capacity
self.slots[at] = Some(item)
self.size = self.size + 1
} else {
self.slots[self.start] = Some(item)
self.start = (self.start + 1) % self.capacity
}
}
///|
/// The sample `i` places from the oldest, or `None` past the end.
pub fn[T] Ring::get(self : Ring[T], i : Int32) -> T? {
guard i >= 0 && i < self.size else { None }
self.slots[(self.start + i) % self.capacity]
}
///|
/// The newest sample.
pub fn[T] Ring::last(self : Ring[T]) -> T? {
if self.size == 0 {
None
} else {
self.get(self.size - 1)
}
}
///|
/// How many samples are held.
pub fn[T] Ring::len(self : Ring[T]) -> Int32 {
self.size
}
///|
/// The capacity it was built with.
pub fn[T] Ring::capacity(self : Ring[T]) -> Int32 {
self.capacity
}
///|
/// The samples, oldest first.
pub fn[T] Ring::to_array(self : Ring[T]) -> Array[T] {
let out : Array[T] = []
for i = 0; i < self.size; i = i + 1 {
if self.get(i) is Some(item) {
out.push(item)
}
}
out
}