Skip to main content

circular_buffer/
fixed.rs

1// Copyright © 2023-2026 Andrea Corbellini and contributors
2// SPDX-License-Identifier: BSD-3-Clause
3
4//! Fixed-size circular buffer.
5
6use crate::CircularBuffer;
7use crate::Inner;
8use crate::Iter;
9use crate::IterMut;
10use core::borrow::Borrow;
11use core::borrow::BorrowMut;
12use core::mem;
13use core::mem::MaybeUninit;
14use core::ops::Deref;
15use core::ops::DerefMut;
16use core::ops::Index;
17use core::ops::IndexMut;
18use core::ptr;
19
20#[cfg(all(not(feature = "std"), feature = "alloc"))]
21use alloc::boxed::Box;
22
23pub use crate::iter::fixed::IntoIter;
24
25/// A fixed-size circular buffer.
26///
27/// A `FixedCircularBuffer` has a fixed capacity that is specified at compile-time, similar to an
28/// [`array`]. It may live on the stack or be initialized in `const` contexts.
29///
30/// Wrap the `FixedCircularBuffer` in a [`Box`](std::boxed) using [`FixedCircularBuffer::boxed()`]
31/// if you need the struct to be heap-allocated. Consider using
32/// [`HeapCircularBuffer`](crate::HeapCircularBuffer) if the capacity cannot be specified at
33/// compile-time.
34///
35/// See the [module-level documentation](crate) for more details and examples.
36#[repr(transparent)]
37pub struct FixedCircularBuffer<T, const N: usize> {
38    inner: Inner<[MaybeUninit<T>; N]>,
39}
40
41impl<T, const N: usize> FixedCircularBuffer<T, N> {
42    /// Returns an empty `FixedCircularBuffer`.
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// use circular_buffer::FixedCircularBuffer;
48    /// let buf = FixedCircularBuffer::<u32, 16>::new();
49    /// assert_eq!(buf.capacity(), 16);
50    /// assert_eq!(buf.len(), 0);
51    /// assert_eq!(buf, []);
52    /// ```
53    #[inline]
54    #[must_use]
55    pub const fn new() -> Self {
56        Self {
57            inner: Inner {
58                size: 0,
59                start: 0,
60                items: [const { MaybeUninit::uninit() }; N],
61            },
62        }
63    }
64
65    /// Returns an empty heap-allocated `FixedCircularBuffer`.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use circular_buffer::FixedCircularBuffer;
71    /// let buf = FixedCircularBuffer::<f64, 1024>::boxed();
72    /// assert_eq!(buf.len(), 0);
73    /// ```
74    #[must_use]
75    #[cfg(feature = "alloc")]
76    pub fn boxed() -> Box<Self> {
77        let mut uninit: Box<MaybeUninit<Self>> = Box::new_uninit();
78        let ptr = uninit.as_mut_ptr();
79
80        // SAFETY: the pointer contains enough memory to contain `Self` and `addr_of_mut` ensures
81        // that the address written to is properly aligned.
82        unsafe {
83            core::ptr::addr_of_mut!((*ptr).inner.size).write(0);
84            core::ptr::addr_of_mut!((*ptr).inner.start).write(0);
85        }
86
87        // SAFETY: `size` and `start` have been properly initialized to 0; `items` does not need to
88        // be initialized if `size` is 0
89        unsafe { uninit.assume_init() }
90    }
91
92    /// Returns a reference to this buffer.
93    #[inline]
94    #[must_use]
95    pub const fn as_circular_buffer(&self) -> &CircularBuffer<T> {
96        // Mutate `&self.inner` from a thin-pointer of type `Inner<[X; N]>` to a fat-pointer of type
97        // `Inner<[X]>`.
98        let inner_unsized: &Inner<[MaybeUninit<T>]> = &self.inner;
99        // Transmute the fat-pointer to a `CircularBuffer<T>`.
100        //
101        // SAFETY: `CircularBuffer` uses `repr(transparent)`, therefore it has the same layout and
102        // representation as `Inner<[MaybeUninit<T>]>`.
103        unsafe { mem::transmute(inner_unsized) }
104    }
105
106    /// Returns a mutable reference to this buffer.
107    #[inline]
108    #[must_use]
109    pub const fn as_mut_circular_buffer(&mut self) -> &mut CircularBuffer<T> {
110        // Mutate `&mut self.inner` from a thin-pointer of type `Inner<[X; N]>` to a fat-pointer of
111        // type `Inner<[X]>`.
112        let inner_unsized: &mut Inner<[MaybeUninit<T>]> = &mut self.inner;
113        // Transmute the fat-pointer to a `CircularBuffer<T>`.
114        //
115        // SAFETY: `CircularBuffer` uses `repr(transparent)`, therefore it has the same layout and
116        // representation as `Inner<[MaybeUninit<T>]>`.
117        unsafe { mem::transmute(inner_unsized) }
118    }
119}
120
121impl<T, const N: usize> Default for FixedCircularBuffer<T, N> {
122    #[inline]
123    fn default() -> Self {
124        Self::new()
125    }
126}
127
128impl<const N: usize, const M: usize, T> From<[T; M]> for FixedCircularBuffer<T, N> {
129    fn from(mut arr: [T; M]) -> Self {
130        let size = if N >= M { M } else { N };
131        let mut elems = [const { MaybeUninit::uninit() }; N];
132        let (discard, copy) = arr.split_at_mut(M - size);
133
134        debug_assert_eq!(copy.len(), size);
135
136        let elems_ptr = elems.as_mut_ptr();
137        let discard_ptr = discard as *mut [T];
138        let copy_ptr = copy.as_ptr() as *const MaybeUninit<T>;
139
140        // SAFETY:
141        // - `copy_ptr` points to a memory location that contains exactly `size` elements.
142        // - `elems_ptr` points to a memory location that contains exactly `N` elements, and `N` is
143        //   greater than or equal to `size`.
144        unsafe {
145            copy_ptr.copy_to_nonoverlapping(elems_ptr, size);
146        }
147
148        // Now that some elements have been moved, ensure that no destructors are implicitly run.
149        mem::forget(arr);
150
151        // Prevent destructors from running on those elements that we've taken ownership of; only
152        // destroy the elements that were discarded.
153        //
154        // SAFETY: All elements in `arr` are initialized; the call to `forget()` earlier ensures
155        // that destructors are not run twice, even if a panic occurs in a `Drop` implementation.
156        unsafe {
157            ptr::drop_in_place(discard_ptr);
158        }
159
160        Self {
161            inner: Inner {
162                size,
163                start: 0,
164                items: elems,
165            },
166        }
167    }
168}
169
170impl<T, const N: usize> FromIterator<T> for FixedCircularBuffer<T, N> {
171    fn from_iter<I>(iter: I) -> Self
172    where
173        I: IntoIterator<Item = T>,
174    {
175        // TODO Optimize
176        let mut buf = Self::new();
177        iter.into_iter().for_each(|item| {
178            buf.push_back(item);
179        });
180        buf
181    }
182}
183
184impl<T, const N: usize> Deref for FixedCircularBuffer<T, N> {
185    type Target = CircularBuffer<T>;
186
187    #[inline]
188    fn deref(&self) -> &Self::Target {
189        self.as_circular_buffer()
190    }
191}
192
193impl<T, const N: usize> DerefMut for FixedCircularBuffer<T, N> {
194    #[inline]
195    fn deref_mut(&mut self) -> &mut Self::Target {
196        self.as_mut_circular_buffer()
197    }
198}
199
200impl<T, const N: usize> Borrow<CircularBuffer<T>> for FixedCircularBuffer<T, N> {
201    #[inline]
202    fn borrow(&self) -> &CircularBuffer<T> {
203        self.as_circular_buffer()
204    }
205}
206
207impl<T, const N: usize> BorrowMut<CircularBuffer<T>> for FixedCircularBuffer<T, N> {
208    #[inline]
209    fn borrow_mut(&mut self) -> &mut CircularBuffer<T> {
210        self.as_mut_circular_buffer()
211    }
212}
213
214impl<T, const N: usize> AsRef<CircularBuffer<T>> for FixedCircularBuffer<T, N> {
215    #[inline]
216    fn as_ref(&self) -> &CircularBuffer<T> {
217        self.as_circular_buffer()
218    }
219}
220
221impl<T, const N: usize> AsMut<CircularBuffer<T>> for FixedCircularBuffer<T, N> {
222    #[inline]
223    fn as_mut(&mut self) -> &mut CircularBuffer<T> {
224        self.as_mut_circular_buffer()
225    }
226}
227
228impl<T, const N: usize> Extend<T> for FixedCircularBuffer<T, N> {
229    fn extend<I>(&mut self, iter: I)
230    where
231        I: IntoIterator<Item = T>,
232    {
233        self.deref_mut().extend(iter);
234    }
235}
236
237impl<'a, T, const N: usize> Extend<&'a T> for FixedCircularBuffer<T, N>
238where
239    T: Copy,
240{
241    fn extend<I>(&mut self, iter: I)
242    where
243        I: IntoIterator<Item = &'a T>,
244    {
245        self.deref_mut().extend(iter);
246    }
247}
248
249impl<T, const N: usize> Index<usize> for FixedCircularBuffer<T, N> {
250    type Output = T;
251
252    #[inline]
253    fn index(&self, index: usize) -> &Self::Output {
254        self.deref().index(index)
255    }
256}
257
258impl<T, const N: usize> IndexMut<usize> for FixedCircularBuffer<T, N> {
259    #[inline]
260    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
261        self.deref_mut().index_mut(index)
262    }
263}
264
265impl<T, const N: usize> IntoIterator for FixedCircularBuffer<T, N> {
266    type Item = T;
267    type IntoIter = IntoIter<T, N>;
268
269    #[inline]
270    fn into_iter(self) -> Self::IntoIter {
271        IntoIter::new(self)
272    }
273}
274
275impl<'a, T, const N: usize> IntoIterator for &'a FixedCircularBuffer<T, N> {
276    type Item = &'a T;
277    type IntoIter = Iter<'a, T>;
278
279    #[inline]
280    fn into_iter(self) -> Self::IntoIter {
281        Iter::new(self)
282    }
283}
284
285impl<'a, T, const N: usize> IntoIterator for &'a mut FixedCircularBuffer<T, N> {
286    type Item = &'a mut T;
287    type IntoIter = IterMut<'a, T>;
288
289    #[inline]
290    fn into_iter(self) -> Self::IntoIter {
291        IterMut::new(self)
292    }
293}
294
295impl<T, const N: usize> Clone for FixedCircularBuffer<T, N>
296where
297    T: Clone,
298{
299    fn clone(&self) -> Self {
300        let (front, back) = self.as_slices();
301        let mut buf = Self::new();
302        buf.extend_from_slice(front);
303        buf.extend_from_slice(back);
304        buf
305    }
306
307    fn clone_from(&mut self, other: &Self) {
308        let (front, back) = other.as_slices();
309        self.clear();
310        self.extend_from_slice(front);
311        self.extend_from_slice(back);
312    }
313}
314
315impl<T, const N: usize> Drop for FixedCircularBuffer<T, N> {
316    #[inline]
317    fn drop(&mut self) {
318        // `clear()` will make sure that every element is dropped in a safe way
319        self.clear();
320    }
321}