Skip to main content

ph_eventing/
ring.rs

1//! Fixed-size, stack-allocated ring buffer — no heap, no alloc, no atomics.
2//!
3//! [`RingBuf`] is a single-owner (`&mut self`) ring that overwrites the
4//! oldest element when full. It requires `T: Copy + Default` and is ideal
5//! for sample windows, local event logs, and anywhere a simple circular
6//! buffer is needed without cross-thread sharing.
7//!
8//! For a lock-free SPSC ring with sequence tracking, see [`crate::SeqRing`].
9//! For a lock-free SPSC ring with backpressure, see [`crate::EventBuf`].
10//!
11//! # Example
12//! ```
13//! use ph_eventing::RingBuf;
14//!
15//! let mut r = RingBuf::<u32, 4>::new();
16//! r.push(10);
17//! r.push(20);
18//! assert_eq!(r.latest(), Some(20));
19//! assert_eq!(r.get(0), Some(10)); // oldest
20//! ```
21
22/// A ring buffer of `N` elements stored entirely on the stack.
23///
24/// Once full, new pushes overwrite the oldest entry. Iteration with
25/// [`iter()`](RingBuf::iter) yields elements from oldest to newest.
26pub struct RingBuf<T: Copy + Default, const N: usize> {
27    buf: [T; N],
28    /// Write cursor — always points to the *next* slot to write.
29    head: usize,
30    /// Number of elements currently stored (≤ N).
31    len: usize,
32}
33
34impl<T: Copy + Default, const N: usize> RingBuf<T, N> {
35    /// Create a new, empty ring buffer with every slot default-initialised.
36    ///
37    /// # Panics
38    /// Panics if `N == 0`.
39    pub fn new() -> Self {
40        assert!(N > 0, "RingBuf capacity N must be > 0");
41        Self {
42            buf: [T::default(); N],
43            head: 0,
44            len: 0,
45        }
46    }
47
48    /// Append a value, overwriting the oldest entry once the ring is full.
49    ///
50    /// This never fails and never blocks; if losing the oldest entry is not
51    /// acceptable, use [`crate::EventBuf`], whose `push` reports when full.
52    pub fn push(&mut self, val: T) {
53        self.buf[self.head] = val;
54        self.head = (self.head + 1) % N;
55        if self.len < N {
56            self.len += 1;
57        }
58    }
59
60    /// Number of elements currently stored, always in `0..=N`.
61    pub fn len(&self) -> usize {
62        self.len
63    }
64
65    /// Returns `true` if the ring holds no elements.
66    pub fn is_empty(&self) -> bool {
67        self.len == 0
68    }
69
70    /// Returns `true` if the ring is at capacity, so the next
71    /// [`push`](Self::push) will overwrite the oldest entry.
72    pub fn is_full(&self) -> bool {
73        self.len == N
74    }
75
76    /// Number of elements the ring can hold.
77    #[inline]
78    pub const fn capacity(&self) -> usize {
79        N
80    }
81
82    /// Drop every element, resetting the ring to empty.
83    ///
84    /// The backing array is left as-is; only the cursors are reset, so this is
85    /// O(1) and does not touch the stored values.
86    pub fn clear(&mut self) {
87        self.head = 0;
88        self.len = 0;
89    }
90
91    /// Read the `i`-th element (0 = oldest).
92    pub fn get(&self, i: usize) -> Option<T> {
93        if i >= self.len {
94            return None;
95        }
96        let idx = if self.len < N { i } else { (self.head + i) % N };
97        Some(self.buf[idx])
98    }
99
100    /// Most recently pushed element.
101    pub fn latest(&self) -> Option<T> {
102        if self.len == 0 {
103            return None;
104        }
105        let idx = if self.head == 0 { N - 1 } else { self.head - 1 };
106        Some(self.buf[idx])
107    }
108
109    /// Iterate over elements oldest→newest.
110    pub fn iter(&self) -> RingIter<'_, T, N> {
111        RingIter { ring: self, pos: 0 }
112    }
113}
114
115impl<T: Copy + Default, const N: usize> Default for RingBuf<T, N> {
116    fn default() -> Self {
117        Self::new()
118    }
119}
120
121impl<T: Copy + Default, const N: usize> core::fmt::Debug for RingBuf<T, N> {
122    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123        f.debug_struct("RingBuf")
124            .field("len", &self.len)
125            .field("capacity", &N)
126            .finish()
127    }
128}
129
130/// Iterator over [`RingBuf`] elements from oldest to newest.
131pub struct RingIter<'a, T: Copy + Default, const N: usize> {
132    ring: &'a RingBuf<T, N>,
133    pos: usize,
134}
135
136impl<'a, T: Copy + Default, const N: usize> Iterator for RingIter<'a, T, N> {
137    type Item = T;
138
139    fn next(&mut self) -> Option<T> {
140        let val = self.ring.get(self.pos)?;
141        self.pos += 1;
142        Some(val)
143    }
144
145    fn size_hint(&self) -> (usize, Option<usize>) {
146        let remaining = self.ring.len().saturating_sub(self.pos);
147        (remaining, Some(remaining))
148    }
149}
150
151impl<T: Copy + Default, const N: usize> ExactSizeIterator for RingIter<'_, T, N> {}
152
153impl<T: Copy + Default, const N: usize> core::fmt::Debug for RingIter<'_, T, N> {
154    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
155        f.debug_struct("RingIter")
156            .field("remaining", &(self.ring.len().saturating_sub(self.pos)))
157            .finish()
158    }
159}
160
161impl<'a, T: Copy + Default, const N: usize> IntoIterator for &'a RingBuf<T, N> {
162    type Item = T;
163    type IntoIter = RingIter<'a, T, N>;
164
165    fn into_iter(self) -> RingIter<'a, T, N> {
166        self.iter()
167    }
168}
169
170impl<T: Copy + Default, const N: usize> crate::traits::Sink<T> for RingBuf<T, N> {
171    type Error = core::convert::Infallible;
172
173    #[inline]
174    fn try_push(&mut self, val: T) -> Result<(), core::convert::Infallible> {
175        self.push(val);
176        Ok(())
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn new_ring_is_empty() {
186        let r = RingBuf::<u32, 4>::new();
187        assert!(r.is_empty());
188        assert!(!r.is_full());
189        assert_eq!(r.len(), 0);
190        assert_eq!(r.latest(), None);
191        assert_eq!(r.get(0), None);
192    }
193
194    #[test]
195    fn push_and_get() {
196        let mut r = RingBuf::<u32, 4>::new();
197        r.push(10);
198        r.push(20);
199        r.push(30);
200        assert_eq!(r.len(), 3);
201        assert_eq!(r.get(0), Some(10));
202        assert_eq!(r.get(1), Some(20));
203        assert_eq!(r.get(2), Some(30));
204        assert_eq!(r.get(3), None);
205        assert_eq!(r.latest(), Some(30));
206    }
207
208    #[test]
209    fn overwrite_oldest_when_full() {
210        let mut r = RingBuf::<u32, 3>::new();
211        r.push(1);
212        r.push(2);
213        r.push(3);
214        assert!(r.is_full());
215
216        r.push(4); // overwrites 1
217        assert_eq!(r.len(), 3);
218        assert_eq!(r.get(0), Some(2));
219        assert_eq!(r.get(1), Some(3));
220        assert_eq!(r.get(2), Some(4));
221        assert_eq!(r.latest(), Some(4));
222    }
223
224    #[test]
225    fn clear_resets_state() {
226        let mut r = RingBuf::<u32, 4>::new();
227        r.push(1);
228        r.push(2);
229        r.clear();
230        assert!(r.is_empty());
231        assert_eq!(r.len(), 0);
232        assert_eq!(r.latest(), None);
233    }
234
235    #[test]
236    fn iter_oldest_to_newest() {
237        let mut r = RingBuf::<u32, 4>::new();
238        for i in 1..=6 {
239            r.push(i);
240        }
241        // capacity 4, pushed 6 → oldest is 3
242        let v: std::vec::Vec<u32> = r.iter().collect();
243        assert_eq!(v, [3, 4, 5, 6]);
244    }
245
246    #[test]
247    fn iter_exact_size() {
248        let mut r = RingBuf::<u32, 4>::new();
249        r.push(1);
250        r.push(2);
251        let it = r.iter();
252        assert_eq!(it.len(), 2);
253    }
254
255    #[test]
256    fn default_is_new() {
257        let r: RingBuf<u8, 8> = RingBuf::default();
258        assert!(r.is_empty());
259    }
260
261    #[test]
262    #[should_panic(expected = "RingBuf capacity N must be > 0")]
263    fn zero_capacity_panics() {
264        let _ = RingBuf::<u32, 0>::new();
265    }
266
267    #[test]
268    fn capacity_returns_n() {
269        let r = RingBuf::<u32, 8>::new();
270        assert_eq!(r.capacity(), 8);
271    }
272
273    #[test]
274    fn into_iter_for_ref() {
275        let mut r = RingBuf::<u32, 4>::new();
276        r.push(1);
277        r.push(2);
278        r.push(3);
279        let v: std::vec::Vec<u32> = (&r).into_iter().collect();
280        assert_eq!(v, [1, 2, 3]);
281    }
282}