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}