Skip to main content

circular_buffer/
drain.rs

1// Copyright © 2023-2026 Andrea Corbellini and contributors
2// SPDX-License-Identifier: BSD-3-Clause
3
4use crate::CircularBuffer;
5use crate::add_mod;
6use crate::iter::Iter;
7use crate::iter::translate_range_bounds;
8use core::fmt;
9use core::iter::FusedIterator;
10use core::marker::PhantomData;
11use core::ops::Range;
12use core::ops::RangeBounds;
13use core::ptr;
14use core::ptr::NonNull;
15
16/// A draining [iterator](core::iter::Iterator) that removes and returns elements from a
17/// [`CircularBuffer`].
18///
19/// This struct is created by [`CircularBuffer::drain()`]. See its documentation for more details.
20pub struct Drain<'a, T> {
21    /// This is a pointer and not a reference (`&'a mut CircularBuffer`) because using a reference
22    /// would make `Drain` an invariant over `CircularBuffer`, but instead we want `Drain` to be
23    /// covariant over `CircularBuffer`.
24    ///
25    /// The reason why `Drain` needs to be covariant is that, semantically,
26    /// `CircularBuffer::drain()` should be equivalent to popping all the drained elements from the
27    /// buffer, storing them into a vector, and returning an iterable over the vector.
28    /// Equivalently, `Drain` owns the drained elements, so it would be unnecessarily restrictive to
29    /// make this type invariant over `CircularBuffer`.
30    buf: NonNull<CircularBuffer<T>>,
31    /// A backup of the size of the buffer. Necessary because `buf.inner.size` is set to 0 during
32    /// the lifetime of the `Drain` and is restored only during drop.
33    buf_size: usize,
34    /// The range that was requested to drain. Necessary to properly rearrange the buffer memory
35    /// during drop.
36    range: Range<usize>,
37    /// An iterator over the indexes of the elements to return from the draining iterator.
38    /// Initially, `range` and `iter` are set to the same `Range`, but as the draining iterator is
39    /// used (via `Iterator::next()`, or similar), `iter` is mutated, while `range` is preserved.
40    iter: Range<usize>,
41    /// Necessary to bind the lifetime of `CircularBuffer` to `Drain`. Note that this is an `&`
42    /// reference, and not a mutable `&mut` reference: this is to make `Drain` covariant over
43    /// `CircularBuffer`.
44    phantom: PhantomData<&'a T>,
45}
46
47impl<'a, T> Drain<'a, T> {
48    pub(crate) fn over_range<R>(buf: &'a mut CircularBuffer<T>, range: R) -> Self
49    where
50        R: RangeBounds<usize>,
51    {
52        let (start, end) = translate_range_bounds(buf, range);
53
54        // Iterating over a `Drain` returns items from the buffer, but does not actually remove the
55        // item from the buffer right away. Because of that, forgetting a `Drain` (via
56        // `mem::forget`) can potentially leave the `CircularBuffer` in an unsafe state: the same
57        // item may have been returned from the `Drain` iterator, and be part of the
58        // `CircularBuffer` at the same time, which would be unsafe for non-`Copy` types.
59        //
60        // To avoid getting into this unsafe state, the size of the buffer is set to 0 while the
61        // `Drain` is alive, and it's restored when the `Drain` is dropped. Forgetting a `Drain`
62        // will therefore forget all the items in the buffer (even the ones that were not drained).
63        // This ensures maximum safety while keeping the implementation simple and performant
64        // enough.
65        let buf_size = buf.inner.size;
66        buf.inner.size = 0;
67
68        let buf = NonNull::from(buf);
69
70        Self {
71            buf,
72            buf_size,
73            range: start..end,
74            iter: start..end,
75            phantom: PhantomData,
76        }
77    }
78
79    /// Reads an element from the `CircularBuffer`.
80    ///
81    /// # Safety
82    ///
83    /// The `index` must point to an initialized element of the buffer. After this method is used,
84    /// the element at `index` must be considered as uninitialized memory and therefore the `index`
85    /// must not be reused.
86    unsafe fn read(&self, index: usize) -> T {
87        // SAFETY: the pointer is valid for the whole lifetime of `self`. Also, while `self` exists,
88        // it is not possible to mutate the underlying buffer because `Drain` holds a phantom shared
89        // reference to the buffer.
90        let buf = unsafe { self.buf.as_ref() };
91
92        debug_assert!(
93            index < buf.capacity() && index < self.buf_size,
94            "index out-of-bounds for buffer"
95        );
96        debug_assert!(
97            index >= self.range.start && index < self.range.end,
98            "index out-of-bounds for drain range"
99        );
100        debug_assert!(
101            index < self.iter.start || index >= self.iter.end,
102            "attempt to read an item that may be returned by the iterator"
103        );
104
105        let index = add_mod(buf.inner.start, index, buf.capacity());
106        // SAFETY: upheld by the caller
107        unsafe { ptr::read(buf.inner.items[index].assume_init_ref()) }
108    }
109
110    fn as_slices(&self) -> (&[T], &[T]) {
111        // SAFETY: the pointer is valid for the whole lifetime of `self`. Also, while `self` exists,
112        // it is not possible to mutate the underlying buffer because `Drain` holds a phantom shared
113        // reference to the buffer.
114        let buf = unsafe { self.buf.as_ref() };
115
116        if buf.capacity() == 0 || self.buf_size == 0 || self.iter.is_empty() {
117            return (&[][..], &[][..]);
118        }
119
120        debug_assert!(buf.inner.start < buf.capacity(), "start out-of-bounds");
121        debug_assert!(self.buf_size <= buf.capacity(), "size out-of-bounds");
122
123        let start = add_mod(buf.inner.start, self.iter.start, buf.capacity());
124        let end = add_mod(buf.inner.start, self.iter.end, buf.capacity());
125
126        let (right, left) = if start < end {
127            (&buf.inner.items[start..end], &[][..])
128        } else {
129            let (left, right) = buf.inner.items.split_at(end);
130            let right = &right[start - end..];
131            (right, left)
132        };
133
134        // SAFETY: The elements in these slices are guaranteed to be initialized
135        unsafe { (right.assume_init_ref(), left.assume_init_ref()) }
136    }
137
138    fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) {
139        // SAFETY: the pointer is valid for the whole lifetime of `self`. Also, while `self` exists,
140        // it is not possible to mutate the underlying buffer because `Drain` holds a phantom shared
141        // reference to the buffer.
142        let buf = unsafe { self.buf.as_mut() };
143
144        if buf.capacity() == 0 || self.buf_size == 0 || self.iter.is_empty() {
145            return (&mut [][..], &mut [][..]);
146        }
147
148        debug_assert!(buf.inner.start < buf.capacity(), "start out-of-bounds");
149        debug_assert!(self.buf_size <= buf.capacity(), "size out-of-bounds");
150
151        let start = add_mod(buf.inner.start, self.iter.start, buf.capacity());
152        let end = add_mod(buf.inner.start, self.iter.end, buf.capacity());
153
154        let (right, left) = if start < end {
155            (&mut buf.inner.items[start..end], &mut [][..])
156        } else {
157            let (left, right) = buf.inner.items.split_at_mut(end);
158            let right = &mut right[start - end..];
159            (right, left)
160        };
161
162        // SAFETY: The elements in these slices are guaranteed to be initialized
163        unsafe { (right.assume_init_mut(), left.assume_init_mut()) }
164    }
165}
166
167impl<T> Iterator for Drain<'_, T> {
168    type Item = T;
169
170    #[inline]
171    fn next(&mut self) -> Option<Self::Item> {
172        // SAFETY: the element at the index is guaranteed to be initialized
173        self.iter.next().map(|index| unsafe { self.read(index) })
174    }
175
176    #[inline]
177    fn size_hint(&self) -> (usize, Option<usize>) {
178        self.iter.size_hint()
179    }
180}
181
182impl<T> ExactSizeIterator for Drain<'_, T> {
183    #[inline]
184    fn len(&self) -> usize {
185        self.iter.len()
186    }
187}
188
189impl<T> FusedIterator for Drain<'_, T> {}
190
191impl<T> DoubleEndedIterator for Drain<'_, T> {
192    fn next_back(&mut self) -> Option<Self::Item> {
193        // SAFETY: the element at the index is guaranteed to be initialized
194        self.iter
195            .next_back()
196            .map(|index| unsafe { self.read(index) })
197    }
198}
199
200impl<T> Drop for Drain<'_, T> {
201    fn drop(&mut self) {
202        if self.buf_size == 0 {
203            // Nothing to do
204            return;
205        }
206
207        // Drop the items that were not consumed
208        struct Dropper<'a, T>(&'a mut [T]);
209
210        impl<T> Drop for Dropper<'_, T> {
211            fn drop(&mut self) {
212                // SAFETY: the slice is guaranteed to be valid for read and writes as the `Drain`
213                // holds a mutable reference to the `CircularBuffer` that contains the data
214                // referenced by the slices.
215                unsafe {
216                    ptr::drop_in_place(self.0);
217                }
218            }
219        }
220
221        let (right, left) = self.as_mut_slices();
222
223        let right = Dropper(right);
224        let left = Dropper(left);
225
226        drop(right);
227        drop(left);
228
229        // The drain has left a "hole" of items in the `CircularBuffer` that either got moved out
230        // during iteration, or got dropped earlier. There are 3 possible scenarios for the state
231        // of the `CircularBuffer` at this point:
232        //
233        // 1. The "hole" is at the front of the buffer:
234        //    | hole | remaining items |
235        //
236        // 2. The "hole" is at the back of the buffer:
237        //    | remaining items | hole |
238        //
239        // 3. The "hole" is in the middle of the buffer:
240        //    | remaining items | hole | remaining items |
241        //
242        // Scenario #1 and #2 can be handled by adjusting the start offset and length of the
243        // buffer. Scenario #3 requires moving the remaining items into the "hole" to fill the gap.
244        //
245        // Filling the hole for scenario #3 requires at most a 3-steps. The worst case looks like
246        // this:
247        //
248        //     | back items [part 2/2] | front items | hole | back items [part 1/2] |
249        //                             ^
250        //                             ` start
251        //
252        // The first step to do is to move `back items [part 1/2]` into `hole`, so that the
253        // `CircularBuffer` looks like this:
254        //
255        //     | back items [part 2/2] | front items | back items [part 1/2] | hole |
256        //                             ^
257        //                             ` start
258        //
259        // Then a portion of `back items [part 2/2]` can be copied into the new `hole`. Note that
260        // `back items [part 2/2]` may not fit into `hole`, and so it may be necessary to split it
261        // in two chunks:
262        //
263        //     | hole | back items [part 3/3] | front items | back items [part 1/3] | back items [part 2/3] |
264        //                                    ^
265        //                                    ` start
266        //
267        // Finally the last chunk `back items [part 3/3]` can be moved into that `hole`:
268        //
269        //     | back items [part 3/3] | hole | front items | back items [part 1/3] | back items [part 2/3] |
270        //                                    ^
271        //                                    ` start
272        //
273        // A similar strategy could be applied to move the front items into the hole instead of the
274        // back items. Ideally the implementation should decide whether to move the front items or
275        // the back items depending on which one results in fewer data to be moved; however for now
276        // only the implementation always moves the back items.
277
278        // TODO: optimize for the case where the hole is in the front or the back
279        // TODO: optimize for the case where there are fewer items to move from the front
280
281        // SAFETY: `buf` is a valid pointer because `Drain` holds a mutable reference to it.
282        let buf = unsafe { self.buf.as_mut() };
283        let mut remaining = self.buf_size - self.range.end;
284
285        let items = CircularSlicePtr::new(&mut buf.inner.items).add(buf.inner.start);
286        let mut hole = items.add(self.range.start);
287        let mut backfill = items.add(self.range.end);
288
289        // This loop should run at most 3 times as explained above
290        while remaining > 0 {
291            let copy_len = hole
292                .available_len()
293                .min(backfill.available_len())
294                .min(remaining);
295            // SAFETY: both pointers are properly aligned, and are valid for read and writes.
296            unsafe { ptr::copy(backfill.as_ptr(), hole.as_mut_ptr(), copy_len) };
297
298            hole = hole.add(copy_len);
299            backfill = backfill.add(copy_len);
300            remaining -= copy_len;
301        }
302
303        // Now that the buffer memory contains valid items, the size can be restored
304        buf.inner.size = self.buf_size - self.range.len();
305    }
306}
307
308impl<T> fmt::Debug for Drain<'_, T>
309where
310    T: fmt::Debug,
311{
312    #[inline]
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        let (right, left) = self.as_slices();
315        let it = Iter { right, left };
316        it.fmt(f)
317    }
318}
319
320#[derive(Debug)]
321struct CircularSlicePtr<'a, T> {
322    slice_start: *mut T,
323    slice_len: usize,
324    offset: usize,
325    phantom: PhantomData<&'a T>,
326}
327
328impl<'a, T> CircularSlicePtr<'a, T> {
329    const fn new(slice: &'a mut [T]) -> Self {
330        Self {
331            slice_start: slice as *mut [T] as *mut T,
332            slice_len: slice.len(),
333            offset: 0,
334            phantom: PhantomData,
335        }
336    }
337
338    fn as_ptr(&self) -> *const T {
339        debug_assert!(self.offset < self.slice_len);
340        // SAFETY: `slice_start` is a valid pointer because it was obtained from a reference that
341        // is still alive; `offset` is within the bounds of the slice, so the resulting pointer is
342        // also valid.
343        unsafe { self.slice_start.add(self.offset) }
344    }
345
346    fn as_mut_ptr(&self) -> *mut T {
347        debug_assert!(self.offset < self.slice_len);
348        // SAFETY: `slice_start` is a valid pointer because it was obtained from a reference that
349        // is still alive; `offset` is within the bounds of the slice, so the resulting pointer is
350        // also valid.
351        unsafe { self.slice_start.add(self.offset) }
352    }
353
354    fn available_len(&self) -> usize {
355        debug_assert!(self.offset < self.slice_len);
356        self.slice_len - self.offset
357    }
358
359    fn add(mut self, increment: usize) -> Self {
360        debug_assert!(self.offset < self.slice_len);
361        debug_assert!(increment <= self.slice_len);
362        self.offset = add_mod(self.offset, increment, self.slice_len);
363        self
364    }
365}
366
367// Need to manually implement `Copy` because `#[derive(Copy)]` requires `T` to implement `Copy`.
368// Also need to manually implement `Clone` because `Copy` requires `Clone`.
369
370impl<T> Copy for CircularSlicePtr<'_, T> {}
371
372impl<T> Clone for CircularSlicePtr<'_, T> {
373    fn clone(&self) -> Self {
374        *self
375    }
376}