use core::fmt::Debug;
use alloc::vec::Vec;
use crate::prelude::*;
#[derive(Clone)]
pub struct IntoConsumer<T>(Vec<T>, usize);
impl<T> From<IntoConsumer<T>> for Vec<T> {
fn from(value: IntoConsumer<T>) -> Self {
let (mut v, len) = (value.0, value.1);
v.truncate(len);
v
}
}
impl<T> Debug for IntoConsumer<T>
where
T: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("IntoConsumer")
.field(&&self.0[..self.1])
.finish()
}
}
impl<T> IntoConsumer<T> {
pub fn as_slice(&self) -> &[T] {
&self.0[..self.1]
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.0[..self.1]
}
pub fn prepare_slots(&mut self, amount: usize)
where
T: Default,
{
let old_len = self.0.len();
self.0.resize_with(old_len + amount, Default::default);
}
}
impl<T> Consumer for IntoConsumer<T> {
type Item = T;
type Final = ();
type Error = Infallible;
async fn consume(&mut self, val: Either<Self::Item, Self::Final>) -> Result<(), Self::Error> {
match val {
Left(item) => {
if self.0.len() == self.1 {
self.0.push(item);
} else {
debug_assert!(self.0.len() > self.1);
self.0[self.1] = item;
}
self.1 += 1;
Ok(())
}
Right(()) => Ok(()),
}
}
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<T: Default> BulkConsumer for IntoConsumer<T> {
async fn expose_slots_gracefully<F, R>(&mut self, f: F) -> Result<R, (F, Self::Error)>
where
F: AsyncFnOnce(&mut [Self::Item]) -> (usize, R),
{
let len = self.0.len() - self.1;
if len == 0 {
let new_len = self.1 * 2 + 1;
self.0.resize_with(new_len, Default::default);
}
let (amount, ret) = f(&mut self.0[self.1..]).await;
self.1 += amount;
Ok(ret)
}
}
impl<T> crate::IntoConsumer for Vec<T> {
type Item = T;
type Final = ();
type Error = Infallible;
type IntoConsumer = IntoConsumer<T>;
fn into_consumer(self) -> Self::IntoConsumer {
let len = self.len();
IntoConsumer(self, len)
}
}
pub struct IntoConsumerMut<'a, T> {
inner: &'a mut Vec<T>,
items: usize,
bulk_initialised: usize,
}
impl<'a, T> Debug for IntoConsumerMut<'a, T>
where
T: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("IntoConsumerMut")
.field(&&self.inner[..self.items])
.finish()
}
}
impl<'a, T> Drop for IntoConsumerMut<'a, T> {
fn drop(&mut self) {
unsafe {
self.inner.set_len(self.bulk_initialised.max(self.items));
}
self.inner.truncate(self.items);
}
}
impl<'a, T> Consumer for IntoConsumerMut<'a, T> {
type Item = T;
type Final = ();
type Error = Infallible;
async fn consume(&mut self, val: Either<Self::Item, Self::Final>) -> Result<(), Self::Error> {
match val {
Left(item) => {
if self.inner.len() == self.items {
self.inner.push(item);
} else {
debug_assert!(self.inner.len() > self.items);
self.inner[self.items] = item;
}
self.items += 1;
Ok(())
}
Right(()) => Ok(()),
}
}
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'a, T: Default> BulkConsumer for IntoConsumerMut<'a, T> {
async fn expose_slots_gracefully<F, R>(&mut self, f: F) -> Result<R, (F, Self::Error)>
where
F: AsyncFnOnce(&mut [Self::Item]) -> (usize, R),
{
let len = self.inner.len() - self.items;
let resize = len == 0;
if resize {
self.bulk_initialised = self.items * 2 + 1;
self.inner
.resize_with(self.bulk_initialised, Default::default);
}
let (amount, ret) = f(&mut self.inner[self.items..]).await;
self.items += amount;
if resize {
unsafe {
self.inner.set_len(self.items);
}
}
Ok(ret)
}
}
impl<'a, T> crate::IntoConsumer for &'a mut Vec<T> {
type Item = T;
type Final = ();
type Error = Infallible;
type IntoConsumer = IntoConsumerMut<'a, T>;
fn into_consumer(self) -> Self::IntoConsumer {
let len = self.len();
IntoConsumerMut {
inner: self,
items: len,
bulk_initialised: len,
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use alloc::vec;
use core::mem::forget;
#[test]
fn forget_ref_mut_matches_drop_ref_mut() {
let mut v1 = vec![0, 1, 2];
let mut v2 = v1.clone();
let mut dropped = (&mut v1).into_consumer();
let mut forgotten = (&mut v2).into_consumer();
let new_items = vec![17; 17];
pollster::block_on(async {
assert_eq!(
dropped.bulk_consume_full_slice(&new_items[..]).await,
Ok(())
);
assert_eq!(
forgotten.bulk_consume_full_slice(&new_items[..]).await,
Ok(())
);
});
drop(dropped);
forget(forgotten);
assert_eq!(v1, v2);
}
}