use std::{num::NonZero, ops::ControlFlow};
use crate::{
buffer::{
self,
segments::{Many, Nonempty},
storage,
},
mem::Rejected,
};
#[must_use = "step or release the retained copy"]
pub struct Flatten<T: buffer::raw::Segment> {
pub(in crate::buffer) source: Nonempty<T>,
pub(in crate::buffer) offset: usize,
pub(in crate::buffer) output: storage::Owned,
}
impl<T: buffer::raw::Segment> Flatten<T> {
pub fn try_new(source: Many<T>) -> Result<Self, Rejected<Many<T>, storage::AllocError>> {
let output = match storage::Owned::try_with(source.len().get()) {
Ok(output) => output,
Err(error) => return Err(Rejected::new(source, error)),
};
Ok(Self {
source: Nonempty::Many(source),
offset: 0,
output,
})
}
pub fn step(mut self, mut budget: NonZero<usize>) -> ControlFlow<storage::Shared, Self> {
loop {
let copied = buffer::raw::Copy::copy((&mut self, budget));
let left = budget.get() - copied.get();
if self.offset < self.source.front().as_ref().len() {
return ControlFlow::Continue(self);
}
self.source = match self.source {
Nonempty::One(_) => return ControlFlow::Break(self.output.freeze()),
Nonempty::Many(source) => source.take_front().1,
};
self.offset = 0;
let Some(left) = NonZero::new(left) else {
return ControlFlow::Continue(self);
};
budget = left;
}
}
}
unsafe impl<T: buffer::raw::Segment> buffer::raw::Copy for (&mut Flatten<T>, NonZero<usize>) {
type Output = NonZero<usize>;
fn parts(&mut self) -> (impl buffer::raw::SliceExt, *mut u8, usize) {
let source = self.0.source.front().as_ref();
let amount = self.1.get().min(source.len() - self.0.offset);
self.1 = unsafe { NonZero::new_unchecked(amount) };
let source = unsafe { source.get_unchecked(self.0.offset..self.0.offset + amount) };
let output = &mut *self.0.output;
let pointer = buffer::raw::Exclusive::as_mut_ptr(output);
let start = *buffer::raw::Exclusive::len_mut(output) as usize;
(source, pointer.cast(), start)
}
unsafe fn publish(self) -> NonZero<usize> {
let amount = self.1.get();
*buffer::raw::Exclusive::len_mut(&mut *self.0.output) += amount as u32;
self.0.offset += amount;
self.1
}
}