use core::{
alloc::Layout,
convert::{identity, Infallible},
marker::PhantomData,
mem::{needs_drop, size_of, ManuallyDrop, MaybeUninit},
ptr::{self, NonNull},
};
#[cfg(all(feature = "nightly",feature = "alloc"))]
use alloc::alloc::Global;
#[cfg(all(not(feature = "nightly"),feature = "alloc"))]
use allocator_api2::alloc::Global;
use crate::{
api::BlinkAllocator,
cold,
drop_list::{DropItem, DropList},
in_place,
};
#[cfg(not(no_global_oom_handling))]
use crate::ResultExt;
#[cfg(feature = "alloc")]
use crate::local::BlinkAlloc;
#[cfg(not(no_global_oom_handling))]
use crate::oom::{handle_alloc_error, size_overflow};
type EmplaceType<T, E> = Result<T, ManuallyDrop<E>>;
type EmplaceSlot<T, E> = MaybeUninit<EmplaceType<T, E>>;
pub trait CoerceFromMut<'a, T: ?Sized> {
fn coerce(t: &'a mut T) -> Self;
}
impl<'a, T: ?Sized> CoerceFromMut<'a, T> for &'a mut T {
#[inline(always)]
fn coerce(t: &'a mut T) -> Self {
t
}
}
impl<'a, T: ?Sized> CoerceFromMut<'a, T> for &'a T {
#[inline(always)]
fn coerce(t: &'a mut T) -> Self {
t
}
}
pub trait IteratorExt: Iterator {
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
fn collect_to_blink<A: BlinkAllocator>(self, blink: &mut Blink<A>) -> &mut [Self::Item]
where
Self: Sized,
Self::Item: 'static,
{
blink.emplace().from_iter(self)
}
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
fn collect_to_blink_shared<A: BlinkAllocator>(self, blink: &mut Blink<A>) -> &[Self::Item]
where
Self: Sized,
{
blink.emplace_shared().from_iter(self)
}
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
fn collect_to_blink_no_drop<A: BlinkAllocator>(self, blink: &mut Blink<A>) -> &mut [Self::Item]
where
Self: Sized,
{
blink.emplace_no_drop().from_iter(self)
}
#[inline(always)]
#[allow(clippy::type_complexity)]
fn try_collect_to_blink<A: BlinkAllocator>(
self,
blink: &mut Blink<A>,
) -> Result<&mut [Self::Item], (&mut [Self::Item], Option<Self::Item>)>
where
Self: Sized,
Self::Item: 'static,
{
blink.emplace().try_from_iter(self)
}
#[inline(always)]
#[allow(clippy::type_complexity)]
fn try_collect_to_blink_shared<A: BlinkAllocator>(
self,
blink: &mut Blink<A>,
) -> Result<&[Self::Item], (&[Self::Item], Option<Self::Item>)>
where
Self: Sized,
{
blink.emplace_shared().try_from_iter(self)
}
#[inline(always)]
#[allow(clippy::type_complexity)]
fn try_collect_to_blink_no_drop<A: BlinkAllocator>(
self,
blink: &mut Blink<A>,
) -> Result<&mut [Self::Item], (&mut [Self::Item], Option<Self::Item>)>
where
Self: Sized,
{
blink.emplace_no_drop().try_from_iter(self)
}
}
impl<I> IteratorExt for I where I: Iterator {}
switch_alloc_default! {
pub struct Blink<A = +BlinkAlloc<Global>> {
drop_list: DropList,
alloc: A,
}
}
impl<A> Drop for Blink<A> {
#[inline(always)]
fn drop(&mut self) {
self.drop_all();
}
}
unsafe impl<A> Send for Blink<A> where A: Send {}
impl<A> Default for Blink<A>
where
A: Default,
{
fn default() -> Self {
Blink::new_in(Default::default())
}
}
#[cfg(feature = "alloc")]
impl Blink<BlinkAlloc<Global>> {
#[inline(always)]
pub const fn new() -> Self {
Blink::new_in(BlinkAlloc::new())
}
#[inline(always)]
pub const fn with_chunk_size(capacity: usize) -> Self {
Blink::new_in(BlinkAlloc::with_chunk_size(capacity))
}
}
impl<A> Blink<A> {
#[inline(always)]
pub const fn new_in(alloc: A) -> Self {
Blink {
drop_list: DropList::new(),
alloc,
}
}
#[inline(always)]
pub fn allocator(&self) -> &A {
&self.alloc
}
#[inline(always)]
pub fn drop_all(&mut self) {
self.drop_list.reset();
}
}
impl<A> Blink<A>
where
A: BlinkAllocator,
{
#[inline(always)]
pub fn reset(&mut self) {
self.drop_list.reset();
self.alloc.reset();
}
#[inline]
unsafe fn _try_copy_slice<'a, T, E>(
&'a self,
slice: &[T],
alloc_err: impl FnOnce(Layout) -> E,
) -> Result<&'a mut [T], E>
where
T: Copy,
{
let layout = Layout::for_value(slice);
let Ok(ptr) = self.alloc.allocate(layout) else {
return Err(alloc_err(layout));
};
let ptr = ptr.as_ptr().cast();
core::ptr::copy_nonoverlapping(slice.as_ptr(), ptr, slice.len());
Ok(core::slice::from_raw_parts_mut(ptr, slice.len()))
}
unsafe fn _try_emplace_drop<'a, T, I, G: 'a, E>(
&'a self,
init: I,
f: impl FnOnce(&mut EmplaceSlot<T, G>, I),
err: impl FnOnce(G) -> E,
alloc_err: impl FnOnce(I, Layout) -> E,
) -> Result<&'a mut T, E> {
let layout = Layout::new::<DropItem<Result<T, ManuallyDrop<E>>>>();
let Ok(ptr) = self.alloc.allocate(layout) else {
return Err(alloc_err(init, layout));
};
let item = unsafe { DropItem::init_value(ptr.cast(), init, f) };
if item.value.is_ok() {
match self.drop_list.add(item) {
Ok(value) => return Ok(value),
_ => unreachable!(),
}
}
match &mut item.value {
Err(g) => {
let err = err(unsafe { ManuallyDrop::take(g) });
self.alloc.deallocate(ptr.cast(), layout);
Err(err)
}
_ => unreachable!(),
}
}
unsafe fn _try_emplace_no_drop<'a, T, I, G: 'a, E>(
&self,
init: I,
f: impl FnOnce(&mut EmplaceSlot<T, G>, I),
err: impl FnOnce(G) -> E,
alloc_err: impl FnOnce(I, Layout) -> E,
) -> Result<&'a mut T, E> {
let layout = Layout::new::<T>();
let Ok(ptr) = self.alloc.allocate(layout) else {
return Err(alloc_err(init, layout));
};
let uninit = &mut *ptr.as_ptr().cast();
f(uninit, init);
match uninit.assume_init_mut() {
Ok(value) => Ok(value),
Err(g) => {
let err = err(unsafe { ManuallyDrop::take(g) });
self.alloc.deallocate(ptr.cast(), layout);
Err(err)
}
}
}
#[inline(always)]
unsafe fn _try_emplace<'a, T, I, G: 'a, E>(
&'a self,
init: I,
f: impl FnOnce(&mut EmplaceSlot<T, G>, I),
no_drop: bool,
err: impl FnOnce(G) -> E,
alloc_err: impl FnOnce(I, Layout) -> E,
) -> Result<&'a mut T, E> {
if !needs_drop::<T>() || no_drop {
self._try_emplace_no_drop(init, f, err, alloc_err)
} else {
self._try_emplace_drop(init, f, err, alloc_err)
}
}
unsafe fn _try_emplace_drop_from_iter<'a, T: 'a, I, E>(
&'a self,
mut iter: I,
err: impl FnOnce(&'a mut [T], Option<T>, Option<Layout>) -> E,
) -> Result<&'a mut [T], E>
where
I: Iterator<Item = T>,
{
if size_of::<T>() == 0 {
let item_layout = Layout::new::<DropItem<[T; 0]>>();
let Ok(ptr) = self.alloc.allocate(item_layout) else {
return Err(err(&mut [], None, Some(item_layout)));
};
let count = saturating_drain_iter(iter);
let (item, slice) = DropItem::init_slice(ptr.cast(), count);
self.drop_list.add(item);
return Ok(slice);
}
struct Guard<'a, T: 'a, A: BlinkAllocator> {
ptr: Option<NonNull<DropItem<[T; 0]>>>,
count: usize,
cap: usize,
layout: Layout,
alloc: &'a A,
drop_list: &'a DropList,
}
impl<'a, T, A> Drop for Guard<'a, T, A>
where
A: BlinkAllocator,
{
#[inline(always)]
fn drop(&mut self) {
self.flush();
}
}
impl<'a, T, A> Guard<'a, T, A>
where
A: BlinkAllocator,
{
#[inline(always)]
fn flush(&mut self) -> &'a mut [T] {
match self.ptr.take() {
Some(ptr) if self.count != 0 => {
let (item, slice) = unsafe { DropItem::init_slice(ptr, self.count) };
unsafe {
self.drop_list.add(item);
}
slice
}
Some(ptr) => unsafe {
self.alloc.deallocate(ptr.cast(), self.layout);
&mut []
},
None => &mut [],
}
}
#[inline(always)]
fn fill(
&mut self,
size_hint: usize,
one_more_elem: &mut Option<T>,
iter: &mut impl Iterator<Item = T>,
) -> Result<(), Option<Layout>> {
let Ok(array_layout) = Layout::array::<T>(size_hint) else {
return Err(None);
};
let item_layout = Layout::new::<DropItem<[T; 0]>>();
let Ok((full_layout, array_offset)) = item_layout.extend(array_layout) else {
return Err(None);
};
debug_assert_eq!(array_offset, size_of::<DropItem<[T; 0]>>());
let res = match self.ptr {
None => self.alloc.allocate(full_layout),
Some(ptr) => unsafe { self.alloc.grow(ptr.cast(), self.layout, full_layout) },
};
let Ok(ptr) = res else {
return Err(Some(full_layout));
};
self.layout = full_layout;
let item_ptr = ptr.cast();
self.ptr = Some(item_ptr);
let len = ptr.len();
if len > full_layout.size() {
self.cap = (len - size_of::<DropItem<[T; 0]>>()) / size_of::<T>()
} else {
debug_assert_eq!(len, full_layout.size());
self.cap = size_hint;
};
let array_ptr = unsafe { item_ptr.as_ptr().add(1).cast::<T>() };
if let Some(one_more_elem) = one_more_elem.take() {
debug_assert!(self.count < self.cap);
unsafe {
ptr::write(array_ptr.add(self.count), one_more_elem);
}
self.count += 1;
}
for idx in self.count..self.cap {
if Layout::new::<Option<T>>() == Layout::new::<T>() {
let elem = unsafe {
in_place(array_ptr.add(idx).cast(), &mut *iter, Iterator::next)
};
if elem.is_none() {
break;
}
} else {
match iter.next() {
None => break,
Some(elem) => unsafe { ptr::write(array_ptr.add(idx), elem) },
}
}
self.count = idx + 1;
}
Ok(())
}
}
let mut guard = Guard {
ptr: None,
count: 0,
cap: 0,
layout: Layout::new::<()>(),
alloc: &self.alloc,
drop_list: &self.drop_list,
};
let (lower, _) = iter.size_hint();
if lower != 0 {
if let Err(layout) = guard.fill(lower.max(FASTER_START), &mut None, &mut iter) {
return Err(err(guard.flush(), None, layout));
}
}
let mut one_more = iter.next();
if one_more.is_none() {
return Ok(guard.flush());
};
cold();
loop {
let (lower, upper) = iter.size_hint();
let Some(size_hint) = size_hint_and_one(lower, upper, guard.count.max(FASTER_START))
else {
return Err(err(guard.flush(), one_more, None));
};
if let Err(layout) = guard.fill(size_hint, &mut one_more, &mut iter) {
return Err(err(guard.flush(), one_more, layout));
}
one_more = iter.next();
if one_more.is_none() {
return Ok(guard.flush());
};
}
}
unsafe fn _try_emplace_no_drop_from_iter<'a, T: 'a, I, E>(
&'a self,
mut iter: I,
err: impl FnOnce(&'a mut [T], Option<T>, Option<Layout>) -> E,
) -> Result<&'a mut [T], E>
where
I: Iterator<Item = T>,
{
if size_of::<T>() == 0 {
let count = saturating_drain_iter(iter);
let ptr = NonNull::<T>::dangling();
let slice = core::slice::from_raw_parts_mut(ptr.as_ptr(), count);
return Ok(slice);
}
struct Guard<'a, T: 'a, A: BlinkAllocator> {
ptr: Option<NonNull<T>>,
count: usize,
cap: usize,
layout: Layout,
alloc: &'a A,
}
impl<'a, T, A> Guard<'a, T, A>
where
A: BlinkAllocator,
{
#[inline(always)]
fn flush(&mut self) -> &'a mut [T] {
match self.ptr.take() {
Some(ptr) if self.count != 0 => {
unsafe { &mut *core::slice::from_raw_parts_mut(ptr.as_ptr(), self.count) }
}
Some(ptr) => {
unsafe { self.alloc.deallocate(ptr.cast(), self.layout) };
&mut []
}
None => &mut [],
}
}
#[inline(always)]
fn fill(
&mut self,
size_hint: usize,
one_more_elem: &mut Option<T>,
iter: &mut impl Iterator<Item = T>,
) -> Result<(), Option<Layout>> {
let Ok(full_layout) = Layout::array::<T>(size_hint) else {
return Err(None);
};
let res = match self.ptr {
None => self.alloc.allocate(full_layout),
Some(ptr) => unsafe { self.alloc.grow(ptr.cast(), self.layout, full_layout) },
};
let Ok(ptr) = res else {
return Err(Some(full_layout));
};
self.layout = full_layout;
self.ptr = Some(ptr.cast());
let len = ptr.len();
if len > full_layout.size() {
self.cap = len / size_of::<T>()
} else {
debug_assert_eq!(len, full_layout.size());
self.cap = size_hint;
};
let array_ptr = ptr.as_ptr().cast::<T>();
if let Some(one_more_elem) = one_more_elem.take() {
debug_assert!(self.count < self.cap);
unsafe {
ptr::write(array_ptr.add(self.count), one_more_elem);
}
self.count += 1;
}
for idx in self.count..size_hint {
if Layout::new::<Option<T>>() == Layout::new::<T>() {
let elem = unsafe {
in_place(array_ptr.add(idx).cast(), &mut *iter, Iterator::next)
};
if elem.is_none() {
break;
}
} else {
match iter.next() {
None => break,
Some(elem) => unsafe { ptr::write(array_ptr.add(idx), elem) },
}
}
self.count = idx + 1;
}
Ok(())
}
}
let mut guard = Guard {
ptr: None,
count: 0,
cap: 0,
layout: Layout::new::<T>(),
alloc: &self.alloc,
};
let (lower, _) = iter.size_hint();
if lower != 0 {
if let Err(layout) = guard.fill(lower.max(FASTER_START), &mut None, &mut iter) {
return Err(err(guard.flush(), None, layout));
}
}
let mut one_more = iter.next();
if one_more.is_none() {
return Ok(guard.flush());
};
cold();
loop {
let (lower, upper) = iter.size_hint();
let Some(size_hint) = size_hint_and_one(lower, upper, guard.count.max(FASTER_START))
else {
return Err(err(guard.flush(), one_more, None));
};
if let Err(layout) = guard.fill(size_hint, &mut one_more, &mut iter) {
return Err(err(guard.flush(), one_more, layout));
}
one_more = iter.next();
if one_more.is_none() {
return Ok(guard.flush());
};
}
}
#[inline(always)]
unsafe fn _try_emplace_from_iter<'a, T: 'a, I, E>(
&'a self,
iter: I,
no_drop: bool,
err: impl FnOnce(&'a mut [T], Option<T>, Option<Layout>) -> E,
) -> Result<&'a mut [T], E>
where
I: IntoIterator<Item = T>,
{
if !needs_drop::<T>() || no_drop {
self._try_emplace_no_drop_from_iter(iter.into_iter(), err)
} else {
self._try_emplace_drop_from_iter(iter.into_iter(), err)
}
}
}
pub struct Emplace<'a, A, T, R = &'a mut T, S = &'a mut [T]> {
blink: &'a Blink<A>,
no_drop: bool,
marker: PhantomData<fn(T) -> (R, S)>,
}
impl<'a, A, T, R, S> Emplace<'a, A, T, R, S>
where
A: BlinkAllocator,
T: 'a,
R: CoerceFromMut<'a, T>,
S: CoerceFromMut<'a, [T]>,
{
#[inline(always)]
pub fn try_value(&self, value: T) -> Result<R, T> {
unsafe {
self.blink._try_emplace(
value,
|slot, value| {
slot.write(Ok::<_, ManuallyDrop<Infallible>>(value));
},
self.no_drop,
|never| match never {},
|init, _| init,
)
}
.map(R::coerce)
}
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
pub fn value(&self, value: T) -> R {
R::coerce(
unsafe {
self.blink._try_emplace(
value,
|slot, value| {
slot.write(Ok::<_, ManuallyDrop<Infallible>>(value));
},
self.no_drop,
identity,
|_, layout| handle_alloc_error(layout),
)
}
.safe_ok(),
)
}
#[inline(always)]
pub fn try_with<F>(&self, f: F) -> Result<R, F>
where
F: FnOnce() -> T,
{
unsafe {
self.blink._try_emplace(
f,
|slot, f| {
slot.write(Ok::<_, ManuallyDrop<Infallible>>(f()));
},
self.no_drop,
never,
|f, _| f,
)
}
.map(R::coerce)
}
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
pub fn with<F>(&self, f: F) -> R
where
F: FnOnce() -> T,
{
R::coerce(
unsafe {
self.blink._try_emplace(
f,
|slot, f| {
slot.write(Ok::<_, ManuallyDrop<Infallible>>(f()));
},
self.no_drop,
never,
|_, layout| handle_alloc_error(layout),
)
}
.safe_ok(),
)
}
#[inline(always)]
pub fn try_with_fallible<F, E>(&self, f: F) -> Result<R, Result<E, F>>
where
F: FnOnce() -> Result<T, E>,
E: 'a,
{
unsafe {
self.blink._try_emplace(
f,
|slot, f| {
slot.write(f().map_err(ManuallyDrop::new));
},
self.no_drop,
|err| Ok(err),
|f, _| Err(f),
)
}
.map(R::coerce)
}
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
pub fn with_fallible<F, E>(&self, f: F) -> Result<R, E>
where
F: FnOnce() -> Result<T, E>,
E: 'a,
{
unsafe {
self.blink._try_emplace(
f,
|slot, f| {
slot.write(f().map_err(ManuallyDrop::new));
},
self.no_drop,
identity,
|_, layout| handle_alloc_error(layout),
)
}
.map(R::coerce)
}
#[inline(always)]
pub fn try_from_iter<I>(&self, iter: I) -> Result<S, (S, Option<T>)>
where
I: IntoIterator<Item = T>,
{
unsafe {
self.blink
._try_emplace_from_iter(iter, self.no_drop, |slice: &'a mut [T], value, _| {
(S::coerce(slice), value)
})
}
.map(S::coerce)
}
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
pub fn from_iter<I>(&self, iter: I) -> S
where
I: Iterator<Item = T>,
{
S::coerce(
unsafe {
self.blink
._try_emplace_from_iter(iter, self.no_drop, |_, _, layout| match layout {
Some(layout) => handle_alloc_error(layout),
None => size_overflow(),
})
}
.safe_ok(),
)
}
}
impl<A> Blink<A>
where
A: BlinkAllocator,
{
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub fn put<T: 'static>(&self, value: T) -> &mut T {
unsafe {
self._try_emplace(
value,
|slot, value| {
slot.write(Ok::<_, ManuallyDrop<Infallible>>(value));
},
false,
identity,
|_, layout| handle_alloc_error(layout),
)
}
.safe_ok()
}
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub fn put_no_drop<T>(&self, value: T) -> &mut T {
unsafe {
self._try_emplace(
value,
|slot, value| {
slot.write(Ok::<_, ManuallyDrop<Infallible>>(value));
},
true,
identity,
|_, layout| handle_alloc_error(layout),
)
}
.safe_ok()
}
#[inline(always)]
pub fn try_uninit<T>(&self) -> Option<&mut MaybeUninit<T>> {
let layout = Layout::new::<T>();
let ptr = self.alloc.allocate(layout).ok()?;
Some(unsafe { &mut *ptr.as_ptr().cast() })
}
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub fn uninit<T>(&self) -> &mut MaybeUninit<T> {
let layout = Layout::new::<T>();
let ptr = self
.alloc
.allocate(layout)
.unwrap_or_else(|_| handle_alloc_error(layout));
unsafe { &mut *ptr.as_ptr().cast() }
}
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub fn copy_slice<T>(&self, slice: &[T]) -> &mut [T]
where
T: Copy,
{
let result = unsafe { self._try_copy_slice(slice, handle_alloc_error) };
match result {
Ok(slice) => slice,
Err(never) => never,
}
}
#[inline(always)]
pub fn try_copy_slice<T>(&self, slice: &[T]) -> Option<&mut [T]>
where
T: Copy,
{
unsafe { self._try_copy_slice(slice, |_| ()) }.ok()
}
#[cfg(not(no_global_oom_handling))]
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub fn copy_str(&self, string: &str) -> &mut str {
let result = unsafe { self._try_copy_slice(string.as_bytes(), handle_alloc_error) };
match result {
Ok(slice) => unsafe { core::str::from_utf8_unchecked_mut(slice) },
Err(never) => never,
}
}
#[inline(always)]
pub fn try_copy_str(&self, string: &str) -> Option<&mut str> {
unsafe { self._try_copy_slice(string.as_bytes(), |_| ()) }
.ok()
.map(|bytes| unsafe { core::str::from_utf8_unchecked_mut(bytes) })
}
#[inline(always)]
pub fn emplace<T: 'static>(&self) -> Emplace<'_, A, T> {
Emplace {
blink: self,
no_drop: false,
marker: PhantomData,
}
}
#[inline(always)]
pub fn emplace_no_drop<T>(&self) -> Emplace<'_, A, T> {
Emplace {
blink: self,
no_drop: true,
marker: PhantomData,
}
}
#[inline(always)]
pub fn emplace_shared<T>(&self) -> Emplace<'_, A, T, &T, &[T]> {
Emplace {
blink: self,
no_drop: true,
marker: PhantomData,
}
}
#[inline(always)]
pub unsafe fn emplace_unchecked<T>(&self) -> Emplace<'_, A, T> {
Emplace {
blink: self,
no_drop: false,
marker: PhantomData,
}
}
}
pub struct SendBlink<A> {
blink: Blink<A>,
}
impl<A> SendBlink<A>
where
A: BlinkAllocator,
{
#[inline(always)]
pub fn new(mut blink: Blink<A>) -> Self {
blink.reset();
SendBlink { blink }
}
#[inline(always)]
pub fn into_inner(self) -> Blink<A> {
self.blink
}
}
#[inline(always)]
fn never<T>(never: Infallible) -> T {
match never {}
}
const FASTER_START: usize = 8;
#[inline]
fn size_hint_and_one(lower: usize, upper: Option<usize>, count: usize) -> Option<usize> {
let upper = upper.map_or(count, |upper| upper.min(count));
let size_hint = lower.max(upper);
let size_hint = size_hint.checked_add(1)?;
count.checked_add(size_hint)
}
#[inline]
fn saturating_drain_iter<T>(mut iter: impl Iterator<Item = T>) -> usize {
let mut drained = 0;
loop {
let (lower, _) = iter.size_hint();
if lower == 0 {
match iter.next() {
None => return drained,
Some(_) => drained += 1,
}
continue;
}
let lower = lower.min(usize::MAX - drained);
match iter.nth(lower - 1) {
None => {
return drained;
}
Some(_) => {
drained += lower;
}
}
if drained == usize::MAX {
return usize::MAX;
}
}
}
#[test]
fn test_iter_drain() {
assert_eq!(5, saturating_drain_iter(0..5));
assert_eq!(usize::MAX, saturating_drain_iter(0..usize::MAX));
assert_eq!(usize::MAX, saturating_drain_iter(core::iter::repeat(1)));
}