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}