Skip to main content

fbl/
ring_buffer.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use core::mem::MaybeUninit;
8
9/// `RingBuffer` is a statically-allocated, typed ring buffer container.
10/// This container is not thread safe.
11#[repr(C)]
12pub struct RingBuffer<T, const N: usize> {
13    data: [MaybeUninit<T>; N],
14
15    /// The index offset to the oldest element in the buffer.
16    head: u32,
17
18    /// The index offset to the empty slot where the next element should be inserted.
19    tail: u32,
20
21    /// The number of elements currently stored in the buffer.
22    size: u32,
23}
24
25// Compile-time tests to ensure layout compatibility for a specific instance
26zr::static_assert!(core::mem::size_of::<RingBuffer<u8, 10>>() == 24);
27zr::static_assert!(core::mem::align_of::<RingBuffer<u8, 10>>() == 4);
28
29impl<T, const N: usize> RingBuffer<T, N> {
30    const ASSERT_N_POSITIVE: () = assert!(N > 0);
31    const ASSERT_N_FITS_U32: () = assert!(N <= u32::MAX as usize);
32
33    /// Create a new, empty RingBuffer.
34    pub const fn new() -> Self {
35        let _ = Self::ASSERT_N_POSITIVE;
36        let _ = Self::ASSERT_N_FITS_U32;
37        RingBuffer { data: [const { MaybeUninit::uninit() }; N], head: 0, tail: 0, size: 0 }
38    }
39
40    /// Returns the number of elements in the buffer.
41    pub fn size(&self) -> u32 {
42        self.size
43    }
44
45    /// Returns the capacity of the buffer.
46    pub const fn capacity() -> usize {
47        N
48    }
49
50    /// Returns true if the buffer is empty.
51    pub fn is_empty(&self) -> bool {
52        self.size == 0
53    }
54
55    /// Returns true if the buffer is full.
56    pub fn is_full(&self) -> bool {
57        self.size == N as u32
58    }
59
60    /// Returns a reference to the oldest element in the buffer.
61    ///
62    /// # Panics
63    ///
64    /// Panics if the buffer is empty.
65    pub fn front(&self) -> &T {
66        assert!(!self.is_empty(), "Calling front on an empty RingBuffer");
67        // SAFETY: The buffer is not empty, so the element at `head` is initialized.
68        unsafe { self.data[self.head as usize].assume_init_ref() }
69    }
70
71    /// Returns a mutable reference to the oldest element in the buffer.
72    ///
73    /// # Panics
74    ///
75    /// Panics if the buffer is empty.
76    pub fn front_mut(&mut self) -> &mut T {
77        assert!(!self.is_empty(), "Calling front_mut on an empty RingBuffer");
78        // SAFETY: The buffer is not empty, so the element at `head` is initialized.
79        unsafe { self.data[self.head as usize].assume_init_mut() }
80    }
81
82    /// Returns a reference to the newest element in the buffer.
83    ///
84    /// # Panics
85    ///
86    /// Panics if the buffer is empty.
87    pub fn back(&self) -> &T {
88        assert!(!self.is_empty(), "Calling back on an empty RingBuffer");
89        let index = self.previous(self.tail);
90        // SAFETY: The buffer is not empty, so the element at `previous(tail)` is initialized.
91        unsafe { self.data[index as usize].assume_init_ref() }
92    }
93
94    /// Returns a mutable reference to the newest element in the buffer.
95    ///
96    /// # Panics
97    ///
98    /// Panics if the buffer is empty.
99    pub fn back_mut(&mut self) -> &mut T {
100        assert!(!self.is_empty(), "Calling back_mut on an empty RingBuffer");
101        let index = self.previous(self.tail);
102        // SAFETY: The buffer is not empty, so the element at `previous(tail)` is initialized.
103        unsafe { self.data[index as usize].assume_init_mut() }
104    }
105
106    /// Removes the oldest element from the buffer.
107    ///
108    /// # Panics
109    ///
110    /// Panics if the buffer is empty.
111    pub fn pop(&mut self) {
112        assert!(!self.is_empty(), "Calling pop on an empty RingBuffer");
113        // SAFETY: The buffer is not empty, so the element at `head` is initialized.
114        unsafe { self.data[self.head as usize].assume_init_drop() };
115        self.head = self.next(self.head);
116        self.size -= 1;
117    }
118
119    /// Pushes a new element into the buffer.
120    ///
121    /// # Panics
122    ///
123    /// Panics if the buffer is full.
124    pub fn push(&mut self, obj: T) {
125        assert!(!self.is_full(), "Calling push on a full RingBuffer");
126        self.data[self.tail as usize].write(obj);
127        self.tail = self.next(self.tail);
128        self.size += 1;
129    }
130
131    /// Removes all elements from the buffer.
132    pub fn clear(&mut self) {
133        while !self.is_empty() {
134            self.pop();
135        }
136        self.head = 0;
137        self.tail = 0;
138        self.size = 0;
139    }
140
141    fn next(&self, index: u32) -> u32 {
142        if index == (N as u32 - 1) { 0 } else { index + 1 }
143    }
144
145    fn previous(&self, index: u32) -> u32 {
146        if index == 0 { N as u32 - 1 } else { index - 1 }
147    }
148}
149
150impl<T, const N: usize> Drop for RingBuffer<T, N> {
151    fn drop(&mut self) {
152        self.clear();
153    }
154}
155
156impl<T, const N: usize> Default for RingBuffer<T, N> {
157    fn default() -> Self {
158        Self::new()
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165    use core::sync::atomic::{AtomicU32, Ordering};
166
167    #[test]
168    fn pod_push() {
169        const BUFF_SIZE: usize = 10;
170        let mut buffer = RingBuffer::<u8, BUFF_SIZE>::new();
171        assert_eq!(buffer.size(), 0);
172        assert!(buffer.is_empty());
173
174        // Fill the buffer to capacity.
175        for i in 0..BUFF_SIZE {
176            buffer.push(i as u8);
177            assert_eq!(*buffer.front(), 0);
178            assert_eq!(*buffer.back(), i as u8);
179        }
180
181        assert!(buffer.is_full());
182        assert_eq!(*buffer.front(), 0);
183
184        for i in 0..BUFF_SIZE {
185            assert_eq!(*buffer.front(), i as u8);
186            assert_eq!(*buffer.back(), (BUFF_SIZE - 1) as u8);
187            buffer.pop();
188        }
189
190        assert!(buffer.is_empty());
191
192        // Wrap around test.
193        buffer.push(11);
194        assert_eq!(*buffer.front(), 11);
195    }
196
197    #[test]
198    fn default_trait() {
199        let buffer = RingBuffer::<u8, 10>::default();
200        assert_eq!(buffer.size(), 0);
201        assert!(buffer.is_empty());
202    }
203
204    #[test]
205    #[should_panic(expected = "Calling pop on an empty RingBuffer")]
206    fn empty_pop_assert() {
207        let mut buffer = RingBuffer::<u8, 10>::new();
208        buffer.pop();
209    }
210
211    #[test]
212    #[should_panic(expected = "Calling front on an empty RingBuffer")]
213    fn empty_front_assert() {
214        let buffer = RingBuffer::<u8, 10>::new();
215        let _ = buffer.front();
216    }
217
218    #[test]
219    #[should_panic(expected = "Calling back on an empty RingBuffer")]
220    fn empty_back_assert() {
221        let buffer = RingBuffer::<u8, 10>::new();
222        let _ = buffer.back();
223    }
224
225    #[test]
226    #[should_panic(expected = "Calling push on a full RingBuffer")]
227    fn full_push_assert() {
228        let mut buffer = RingBuffer::<u8, 2>::new();
229        buffer.push(1);
230        buffer.push(2);
231        buffer.push(3);
232    }
233
234    #[test]
235    fn construct_destruct_match() {
236        static CONSTRUCT_COUNT: AtomicU32 = AtomicU32::new(0);
237        static DESTRUCT_COUNT: AtomicU32 = AtomicU32::new(0);
238
239        // Reset counts for the test
240        CONSTRUCT_COUNT.store(0, Ordering::Relaxed);
241        DESTRUCT_COUNT.store(0, Ordering::Relaxed);
242
243        struct TestObj;
244
245        impl TestObj {
246            fn new() -> Self {
247                CONSTRUCT_COUNT.fetch_add(1, Ordering::Relaxed);
248                TestObj
249            }
250        }
251
252        impl Drop for TestObj {
253            fn drop(&mut self) {
254                DESTRUCT_COUNT.fetch_add(1, Ordering::Relaxed);
255            }
256        }
257
258        {
259            let mut buffer = RingBuffer::<TestObj, 10>::new();
260
261            buffer.push(TestObj::new());
262            assert_eq!(CONSTRUCT_COUNT.load(Ordering::Relaxed), 1);
263            assert_eq!(DESTRUCT_COUNT.load(Ordering::Relaxed), 0);
264
265            buffer.pop();
266            assert_eq!(CONSTRUCT_COUNT.load(Ordering::Relaxed), 1);
267            assert_eq!(DESTRUCT_COUNT.load(Ordering::Relaxed), 1);
268
269            buffer.push(TestObj::new());
270            buffer.push(TestObj::new());
271            assert_eq!(CONSTRUCT_COUNT.load(Ordering::Relaxed), 3);
272            assert_eq!(DESTRUCT_COUNT.load(Ordering::Relaxed), 1);
273
274            buffer.clear();
275            assert_eq!(CONSTRUCT_COUNT.load(Ordering::Relaxed), 3);
276            assert_eq!(DESTRUCT_COUNT.load(Ordering::Relaxed), 3);
277
278            buffer.push(TestObj::new());
279            buffer.push(TestObj::new());
280            assert_eq!(CONSTRUCT_COUNT.load(Ordering::Relaxed), 5);
281            assert_eq!(DESTRUCT_COUNT.load(Ordering::Relaxed), 3);
282        }
283
284        // Out of scope.
285        assert_eq!(CONSTRUCT_COUNT.load(Ordering::Relaxed), 5);
286        assert_eq!(DESTRUCT_COUNT.load(Ordering::Relaxed), 5);
287    }
288
289    #[test]
290    fn test_ring_buffer_capacity() {
291        assert_eq!(RingBuffer::<u8, 5>::capacity(), 5);
292    }
293
294    #[test]
295    fn test_ring_buffer_mut_accessors() {
296        let mut buffer = RingBuffer::<u8, 5>::new();
297        buffer.push(10);
298        buffer.push(20);
299        *buffer.front_mut() = 15;
300        *buffer.back_mut() = 25;
301        assert_eq!(*buffer.front(), 15);
302        assert_eq!(*buffer.back(), 25);
303    }
304
305    #[test]
306    #[should_panic(expected = "Calling front_mut on an empty RingBuffer")]
307    fn empty_front_mut_assert() {
308        let mut buffer = RingBuffer::<u8, 10>::new();
309        let _ = buffer.front_mut();
310    }
311
312    #[test]
313    #[should_panic(expected = "Calling back_mut on an empty RingBuffer")]
314    fn empty_back_mut_assert() {
315        let mut buffer = RingBuffer::<u8, 10>::new();
316        let _ = buffer.back_mut();
317    }
318}