use core::ops::{Bound, RangeBounds};
use core::ptr::{self, NonNull};
use core::{fmt, slice};
use allocator_api2::alloc::Allocator;
use super::Vec;
impl<'a, T, A: Allocator + Clone> Vec<'a, T, A> {
pub fn drain<R: RangeBounds<usize>>(&mut self, range: R) -> Drain<'_, 'a, T, A> {
let len = self.len;
let start = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.checked_add(1).expect("excluded drain start bound overflowed usize"),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n.checked_add(1).expect("included drain end bound overflowed usize"),
Bound::Excluded(&n) => n,
Bound::Unbounded => len,
};
assert!(start <= end, "drain range start exceeds end");
assert!(end <= len, "drain range end out of bounds");
self.len = start;
Drain {
vec: NonNull::from(self),
drain_start: start,
drain_end: end,
front: start,
back: end,
original_len: len,
_marker: core::marker::PhantomData,
}
}
}
pub struct Drain<'d, 'a, T, A: Allocator + Clone> {
vec: NonNull<Vec<'a, T, A>>,
drain_start: usize,
drain_end: usize,
front: usize,
back: usize,
original_len: usize,
_marker: core::marker::PhantomData<&'d mut Vec<'a, T, A>>,
}
impl<T: fmt::Debug, A: Allocator + Clone> fmt::Debug for Drain<'_, '_, T, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let remaining = unsafe {
let vec = self.vec.as_ref();
slice::from_raw_parts(vec.data.as_ptr().add(self.front), self.back - self.front)
};
f.debug_struct("Drain").field("remaining", &remaining).finish()
}
}
impl<T, A: Allocator + Clone> Iterator for Drain<'_, '_, T, A> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.front == self.back {
return None;
}
let value = unsafe { self.vec.as_ref().data.as_ptr().add(self.front).read() };
self.front = self
.front
.checked_add(1)
.expect("front < back ensures increment stays within the drained range");
Some(value)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.back - self.front;
(len, Some(len))
}
}
impl<T, A: Allocator + Clone> DoubleEndedIterator for Drain<'_, '_, T, A> {
fn next_back(&mut self) -> Option<T> {
if self.front == self.back {
return None;
}
self.back -= 1;
Some(unsafe { self.vec.as_ref().data.as_ptr().add(self.back).read() })
}
}
impl<T, A: Allocator + Clone> ExactSizeIterator for Drain<'_, '_, T, A> {}
impl<T, A: Allocator + Clone> core::iter::FusedIterator for Drain<'_, '_, T, A> {}
impl<T, A: Allocator + Clone> Drop for Drain<'_, '_, T, A> {
#[cfg_attr(test, mutants::skip)]
fn drop(&mut self) {
struct TailFix<'x, 'd, 'a, T, A: Allocator + Clone> {
d: &'x mut Drain<'d, 'a, T, A>,
}
impl<T, A: Allocator + Clone> Drop for TailFix<'_, '_, '_, T, A> {
#[cfg_attr(test, mutants::skip)]
fn drop(&mut self) {
let vec = unsafe { self.d.vec.as_mut() };
let tail_len = self.d.original_len - self.d.drain_end;
if tail_len > 0 {
unsafe {
ptr::copy(
vec.data.as_ptr().add(self.d.drain_end),
vec.data.as_ptr().add(self.d.drain_start),
tail_len,
);
}
}
vec.len = self.d.drain_start + tail_len;
}
}
let g = TailFix { d: self };
let remaining = g.d.back - g.d.front;
if remaining > 0 {
unsafe {
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(
g.d.vec.as_ref().data.as_ptr().add(g.d.front),
remaining,
));
}
}
}
}