Skip to main content

circular_buffer/
heap.rs

1// Copyright © 2026 Andrea Corbellini and contributors
2// SPDX-License-Identifier: BSD-3-Clause
3
4//! Heap-allocated, variable-size circular buffer.
5
6use crate::CircularBuffer;
7use crate::Inner;
8use crate::Iter;
9use crate::IterMut;
10use crate::add_mod;
11use ::alloc::alloc;
12use ::alloc::alloc::Layout;
13use ::alloc::alloc::LayoutError;
14use core::borrow::Borrow;
15use core::borrow::BorrowMut;
16use core::mem;
17use core::mem::MaybeUninit;
18use core::ops::Deref;
19use core::ops::DerefMut;
20use core::ops::Index;
21use core::ops::IndexMut;
22use core::ptr;
23
24#[cfg(all(not(feature = "std"), feature = "alloc"))]
25use ::alloc::boxed::Box;
26
27pub use crate::iter::heap::IntoIter;
28
29/// A heap-allocated, variable-size circular buffer.
30///
31/// A `HeapCircularBuffer` can be allocated at runtime with an arbitrary capacity, similar to a
32/// [`Vec`]. The capacity of the buffer can be adjusted using [`resize()`](Self::resize).
33///
34/// See the [module-level documentation](crate) for more details and examples.
35#[repr(transparent)]
36pub struct HeapCircularBuffer<T> {
37    inner: Box<Inner<[MaybeUninit<T>]>>,
38}
39
40impl<T> HeapCircularBuffer<T> {
41    #[inline]
42    fn layout_for(capacity: usize) -> Layout {
43        Self::try_layout_for(capacity).expect("capacity overflow")
44    }
45
46    #[inline]
47    fn try_layout_for(capacity: usize) -> Result<Layout, LayoutError> {
48        Ok(Layout::new::<Inner<()>>()
49            .extend(Layout::array::<T>(capacity)?)?
50            .0
51            .pad_to_align())
52    }
53
54    #[inline]
55    const fn wide_inner_ptr(raw_ptr: *mut u8, capacity: usize) -> *mut Inner<[MaybeUninit<T>]> {
56        // `raw_ptr` is a thin-pointer `*mut u8`. We need to convert it to a wide-pointer. We use
57        // `slice_from_raw_parts_mut()` for that, which will return a `*mut [u8]`. Here we rely on
58        // the implicit assumption that a wide-pointer for `[u8]` is the same as a wide-pointer for
59        // `[T]`.
60        //
61        // TODO: Switch to `ptr::from_raw_parts_mut()` once it's stabilized.
62        ptr::slice_from_raw_parts_mut(raw_ptr, capacity) as *mut Inner<[MaybeUninit<T>]>
63    }
64
65    /// Returns an empty `HeapCircularBuffer` with the specified capacity.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use circular_buffer::HeapCircularBuffer;
71    /// let buf = HeapCircularBuffer::<u32>::with_capacity(16);
72    /// assert_eq!(buf.capacity(), 16);
73    /// assert_eq!(buf.len(), 0);
74    /// assert_eq!(buf, []);
75    /// ```
76    #[must_use]
77    pub fn with_capacity(capacity: usize) -> Self {
78        let layout = Self::layout_for(capacity);
79        debug_assert!(layout.size() > 0);
80
81        // SAFETY: `layout` has a non-zero size: even if `capacity` is 0, there are two `usize`
82        // fields (`start` and `size`).
83        let ptr = unsafe { alloc::alloc(layout) };
84        if ptr.is_null() {
85            alloc::handle_alloc_error(layout);
86        }
87
88        // Convert `ptr` from a `*mut u8` to a `*mut Inner<[MaybeUninit<T>]>`
89        let ptr = Self::wide_inner_ptr(ptr, capacity);
90
91        // SAFETY: At this point, `ptr` should point to a memory location that is:
92        // - valid for reads and writes;
93        // - is properly aligned and sized for `Inner<[MaybeUninit<T>]>` where the slice has the
94        //   specified `capacity`.
95        let inner = unsafe {
96            ptr::addr_of_mut!((*ptr).start).write(0);
97            ptr::addr_of_mut!((*ptr).size).write(0);
98            Box::from_raw(ptr)
99        };
100
101        debug_assert_eq!(layout, Layout::for_value(&*inner));
102        debug_assert_eq!(inner.items.len(), capacity);
103
104        Self { inner }
105    }
106
107    /// Changes the capacity of the buffer, without changing its elements.
108    ///
109    /// # Panics
110    ///
111    /// If `new_capacity` is lower than the number of elements in the buffer.
112    ///
113    /// # Examples
114    ///
115    /// Increasing capacity:
116    ///
117    /// ```
118    /// use circular_buffer::HeapCircularBuffer;
119    ///
120    /// let mut buf = HeapCircularBuffer::<char>::with_capacity(3);
121    /// buf.push_back('a');
122    /// buf.push_back('b');
123    /// buf.push_back('c');
124    /// buf.push_back('d');
125    /// assert_eq!(buf, ['b', 'c', 'd']);
126    ///
127    /// buf.resize(5);
128    /// buf.push_back('e');
129    /// buf.push_back('f');
130    /// buf.push_back('g');
131    /// assert_eq!(buf, ['c', 'd', 'e', 'f', 'g']);
132    /// ```
133    ///
134    /// Decreasing capacity is fine as long as the new capacity leaves enough room for existing
135    /// elements:
136    ///
137    /// ```
138    /// use circular_buffer::HeapCircularBuffer;
139    ///
140    /// let mut buf = HeapCircularBuffer::<char>::with_capacity(3);
141    /// assert_eq!(buf.capacity(), 3);
142    /// buf.push_back('a');
143    /// buf.push_back('b');
144    /// assert_eq!(buf, ['a', 'b']);
145    ///
146    /// buf.resize(2);
147    /// assert_eq!(buf.capacity(), 2);
148    /// assert_eq!(buf, ['a', 'b']);
149    /// ```
150    ///
151    /// Decreasing capacity panics if the buffer is too large:
152    ///
153    /// ```should_panic
154    /// use circular_buffer::HeapCircularBuffer;
155    ///
156    /// let mut buf = HeapCircularBuffer::<char>::with_capacity(3);
157    /// buf.push_back('a');
158    /// buf.push_back('b');
159    /// buf.push_back('c');
160    ///
161    /// buf.resize(2); // panics
162    /// ```
163    pub fn resize(&mut self, new_capacity: usize) {
164        if new_capacity == self.capacity() {
165            // Nothing to do.
166            return;
167        }
168
169        assert!(
170            new_capacity >= self.inner.size,
171            "new capacity is lower than the length of the buffer"
172        );
173
174        // Ensure that the elements of the buffer are not "wrapping around" the boundary of the
175        // memory slice, because that boundary is going to change after the capacity is adjusted.
176        self.make_contiguous();
177
178        if self.capacity() > 0 {
179            // Now that the buffer is contiguous, the elements may still be over the new boundary.
180            // We need to check for that condition, and, if necessary, shift the elements back so
181            // that they fit within the new boundary.
182            let size = self.inner.size;
183            let start = self.inner.start;
184            let end = add_mod(start, size, self.capacity());
185            debug_assert!(
186                start <= end,
187                "start index should precede end index after a call to `make_contiguous()`"
188            );
189
190            if end >= new_capacity {
191                // The elements exist outside of the new boundary. We need to shift them back.
192                let new_start_ptr = self.inner.items.as_mut_ptr();
193                // SAFETY: The resulting pointer is within the same allocated object
194                // (`self.inner.items`).
195                let old_start_ptr = unsafe { new_start_ptr.add(start) };
196                // SAFETY: Both the source and destination pointers are valid, properly aligned, and
197                // they belong to the same object (`self.inner.items`). Because we're changing
198                // `start`, the source items will not be accessible, so effectively we're moving
199                // them.
200                unsafe { old_start_ptr.copy_to(new_start_ptr, size) };
201                self.inner.start = 0;
202            }
203        }
204
205        let old_layout = Layout::for_value(&*self.inner);
206        let new_layout = Self::layout_for(new_capacity);
207        debug_assert!(new_layout.size() > 0);
208
209        // SAFETY: `read()` is called on a valid memory location that comes from a valid reference.
210        // The `Box` is copied, but its copy in `self` is not accessed again, and is later
211        // overwritten.
212        let old_ptr = Box::into_raw(unsafe { ptr::addr_of!(self.inner).read() }) as *mut u8;
213
214        // SAFETY:
215        // - `old_ptr` was allocated via the global allocator;
216        // - `old_layout` is the layout for this object;
217        // - `new_layout.size()` is greater than 0 (even if `new_capacity` is 0) because there are
218        //   two `usize` fields in `Inner`;
219        // - `new_layout.size()` does not overflow `isize` after rounding, because it comes from a
220        //   `Layout` object, which already provides such guarantees.
221        let new_ptr = unsafe { alloc::realloc(old_ptr, old_layout, new_layout.size()) };
222        if new_ptr.is_null() {
223            alloc::handle_alloc_error(new_layout);
224        }
225
226        let new_ptr = Self::wide_inner_ptr(new_ptr, new_capacity);
227
228        // SAFETY: `new_ptr` is valid for reads and writes, is properly aligned, and sized.
229        let inner = unsafe { Box::from_raw(new_ptr) };
230
231        debug_assert_eq!(new_layout, Layout::for_value(&*inner));
232        debug_assert_eq!(inner.items.len(), new_capacity);
233
234        // SAFETY: `write()` is called on a valid memory location that comes from a valid reference.
235        unsafe { ptr::addr_of_mut!(self.inner).write(inner) };
236    }
237
238    /// Returns a reference to this buffer.
239    #[inline]
240    #[must_use]
241    pub const fn as_circular_buffer(&self) -> &CircularBuffer<T> {
242        // Transmute the inner pointer to a `CircularBuffer<T>`.
243        //
244        // SAFETY: `CircularBuffer` uses `repr(transparent)`, therefore it has the same layout and
245        // representation as `Inner<[MaybeUninit<T>]>`.
246        unsafe { mem::transmute(&*self.inner) }
247    }
248
249    /// Returns a mutable reference to this buffer.
250    #[inline]
251    #[must_use]
252    pub const fn as_mut_circular_buffer(&mut self) -> &mut CircularBuffer<T> {
253        // Transmute the inner pointer to a `CircularBuffer<T>`.
254        //
255        // SAFETY: `CircularBuffer` uses `repr(transparent)`, therefore it has the same layout and
256        // representation as `Inner<[MaybeUninit<T>]>`.
257        unsafe { mem::transmute(&mut *self.inner) }
258    }
259
260    /// Consumes and leaks the buffer, returning a mutable reference to the contents as a
261    /// [`CircularBuffer`].
262    ///
263    /// Note that the type `T` must outlive the chosen lifetime `'a`. If the type has only static
264    /// references, or none at all, then this may be chosen to be `'static`.
265    ///
266    /// This function is mainly useful for data that lives for the remainder of the program’s life.
267    /// Dropping the returned reference will cause a memory leak.
268    ///
269    /// # Examples
270    ///
271    /// ```
272    /// use circular_buffer::CircularBuffer;
273    /// use circular_buffer::HeapCircularBuffer;
274    ///
275    /// let mut buf = HeapCircularBuffer::<u32>::with_capacity(5);
276    /// buf.extend([1, 2, 3]);
277    ///
278    /// let static_ref: &'static mut CircularBuffer<u32> = buf.leak();
279    /// assert_eq!(static_ref, [1, 2, 3]);
280    ///
281    /// # // Miri will flag this test as having a memory leak (which is correct: a memory leak is
282    /// # // precisely what this test intends to trigger). The following code ensures that
283    /// # // destructors are run, so that Miri does not complain.
284    /// # let _ = unsafe { Box::from_raw(static_ref) };
285    /// ```
286    #[inline]
287    #[must_use]
288    pub fn leak<'a>(self) -> &'a mut CircularBuffer<T>
289    where
290        T: 'a,
291    {
292        // Wrap `self` into a `MaybeUninit` to ensure that destructors are not run.
293        let buf = MaybeUninit::new(self);
294        let buf = buf.as_ptr();
295
296        // SAFETY: `read()` is called on a valid memory location that comes from a valid reference.
297        // The `Box` is copied, which is not generally supported, but the call to `mem::forget()`
298        // ensures that the `Box` does not exist in two places.
299        let ptr = Box::into_raw(unsafe { ptr::addr_of!((*buf).inner).read() });
300
301        // SAFETY: This is a valid pointer (because it comes from a `Box`), and we have exclusive
302        // ownership of it (because we have erased all traces of the `Box`).
303        let inner: &'a mut Inner<[MaybeUninit<T>]> = unsafe { &mut *ptr };
304
305        // SAFETY: `CircularBuffer<T>` uses `repr(transparent)`, therefore it has the same layout
306        // and representation as `Inner<[MaybeUninit<T>]>`.
307        unsafe { mem::transmute::<&mut Inner<[MaybeUninit<T>]>, &'a mut CircularBuffer<T>>(inner) }
308    }
309
310    /// Converts the `HeapCircularBuffer<T>` into a `Box<CircularBuffer<T>>`.
311    ///
312    /// # Examples
313    ///
314    /// ```
315    /// use circular_buffer::HeapCircularBuffer;
316    ///
317    /// let mut buf = HeapCircularBuffer::<u32>::with_capacity(5);
318    /// buf.extend([1, 2, 3]);
319    ///
320    /// let boxed_buf = buf.into_boxed_circular_buffer();
321    /// assert_eq!(&*boxed_buf, [1, 2, 3]);
322    /// ```
323    #[inline]
324    #[must_use]
325    pub fn into_boxed_circular_buffer(self) -> Box<CircularBuffer<T>> {
326        // SAFETY: The pointer is valid because it comes from a reference, and we have exclusive
327        // ownership of the memory that is pointed to.
328        unsafe { Box::from_raw(self.leak()) }
329    }
330}
331
332impl<T> Deref for HeapCircularBuffer<T> {
333    type Target = CircularBuffer<T>;
334
335    #[inline]
336    fn deref(&self) -> &Self::Target {
337        self.as_circular_buffer()
338    }
339}
340
341impl<T> DerefMut for HeapCircularBuffer<T> {
342    #[inline]
343    fn deref_mut(&mut self) -> &mut Self::Target {
344        self.as_mut_circular_buffer()
345    }
346}
347
348impl<T> Borrow<CircularBuffer<T>> for HeapCircularBuffer<T> {
349    #[inline]
350    fn borrow(&self) -> &CircularBuffer<T> {
351        self.as_circular_buffer()
352    }
353}
354
355impl<T> BorrowMut<CircularBuffer<T>> for HeapCircularBuffer<T> {
356    #[inline]
357    fn borrow_mut(&mut self) -> &mut CircularBuffer<T> {
358        self.as_mut_circular_buffer()
359    }
360}
361
362impl<T> AsRef<CircularBuffer<T>> for HeapCircularBuffer<T> {
363    #[inline]
364    fn as_ref(&self) -> &CircularBuffer<T> {
365        self.as_circular_buffer()
366    }
367}
368
369impl<T> AsMut<CircularBuffer<T>> for HeapCircularBuffer<T> {
370    #[inline]
371    fn as_mut(&mut self) -> &mut CircularBuffer<T> {
372        self.as_mut_circular_buffer()
373    }
374}
375
376impl<T> Index<usize> for HeapCircularBuffer<T> {
377    type Output = T;
378
379    #[inline]
380    fn index(&self, index: usize) -> &Self::Output {
381        self.deref().index(index)
382    }
383}
384
385impl<T> IndexMut<usize> for HeapCircularBuffer<T> {
386    #[inline]
387    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
388        self.deref_mut().index_mut(index)
389    }
390}
391
392impl<T> IntoIterator for HeapCircularBuffer<T> {
393    type Item = T;
394    type IntoIter = IntoIter<T>;
395
396    #[inline]
397    fn into_iter(self) -> Self::IntoIter {
398        IntoIter::new(self)
399    }
400}
401
402impl<'a, T> IntoIterator for &'a HeapCircularBuffer<T> {
403    type Item = &'a T;
404    type IntoIter = Iter<'a, T>;
405
406    #[inline]
407    fn into_iter(self) -> Self::IntoIter {
408        Iter::new(self)
409    }
410}
411
412impl<'a, T> IntoIterator for &'a mut HeapCircularBuffer<T> {
413    type Item = &'a mut T;
414    type IntoIter = IterMut<'a, T>;
415
416    #[inline]
417    fn into_iter(self) -> Self::IntoIter {
418        IterMut::new(self)
419    }
420}
421
422impl<T> Clone for HeapCircularBuffer<T>
423where
424    T: Clone,
425{
426    fn clone(&self) -> Self {
427        let (front, back) = self.as_slices();
428        let mut clone = Self::with_capacity(self.capacity());
429        clone.extend_from_slice(front);
430        clone.extend_from_slice(back);
431        clone
432    }
433
434    fn clone_from(&mut self, other: &Self) {
435        let (front, back) = other.as_slices();
436        self.clear();
437        self.resize(other.capacity());
438        self.extend_from_slice(front);
439        self.extend_from_slice(back);
440    }
441}
442
443impl<T> Drop for HeapCircularBuffer<T> {
444    #[inline]
445    fn drop(&mut self) {
446        // `clear()` will make sure that every element is dropped in a safe way
447        self.clear();
448    }
449}