use std::{mem, num, slice};
use crate::{
buffer,
buffer::{
bytes::window,
pool::owners,
storage,
write::{self, Sink, consume, consume::Consume},
},
mem::rc,
};
#[derive(Default)]
pub struct Snapshot<const MAX_CAPACITY: usize> {
state: Option<window::State<(), MAX_CAPACITY>>,
tail: u32,
}
impl<const MAX_CAPACITY: usize> Snapshot<MAX_CAPACITY> {
pub const fn new() -> Self {
Self {
state: None,
tail: 0,
}
}
pub fn try_with(capacity: num::NonZero<usize>) -> Result<Self, storage::AllocError> {
Self::try_copy(&[], capacity)
}
pub fn try_copy(
src: &[u8],
capacity: num::NonZero<usize>,
) -> Result<Self, storage::AllocError> {
let state = window::Fresh::try_copy(src, capacity)?;
Ok(Self {
state: Some(state),
tail: 0,
})
}
pub fn spare(&mut self) -> write::Spare<'_> {
let Some(state) = &mut self.state else {
return unsafe { write::Spare::from_parts(&mut [], &mut self.tail) };
};
let pointer = state.buf.data().cast_mut().cast::<mem::MaybeUninit<u8>>();
let start = state.head as usize;
let len = (state.cap.get() - state.head) as usize;
let spare = unsafe { slice::from_raw_parts_mut(pointer.add(start), len) };
unsafe { write::Spare::from_parts(spare, &mut state.head) }
}
pub fn into_shared(self) -> Option<storage::Shared> {
if self.is_empty() {
return None;
}
Some(self.freeze())
}
pub fn freeze(self) -> storage::Shared {
let Some(state) = self.state else {
return storage::Shared::new();
};
let len = state.head - self.tail;
storage::Shared::from(unsafe { state.buf.into_range(self.tail, len) })
}
pub fn try_snapshot(&self) -> Result<Option<storage::Shared>, rc::Error> {
use buffer::raw::Retain as _;
let Some(state) = &self.state else {
return Ok(None);
};
if state.head == self.tail {
return Ok(None);
}
let len = state.head - self.tail;
Ok(Some(storage::Shared::from(unsafe {
state.buf.try_retain()?.into_range(self.tail, len)
})))
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
self.state
.as_ref()
.map_or(0, |state| (state.head - self.tail) as usize)
}
pub fn clear(&mut self) {
if let Some(state) = &self.state {
self.tail = state.head;
}
}
pub fn try_as_mut(&mut self) -> Result<&mut [u8], storage::AllocError> {
let Some(state) = &mut self.state else {
return Ok(&mut []);
};
window::Edit::new(state, &mut self.tail).try_as_mut()
}
}
impl<const MAX_CAPACITY: usize> Consume for Snapshot<MAX_CAPACITY> {
fn remaining(&self) -> usize {
self.len()
}
fn try_consume(&mut self, amount: usize) -> Result<(), consume::Error> {
consume::Error::check(amount, self.len())?;
self.tail += amount as u32;
Ok(())
}
fn consume_max(&mut self, max: usize) -> usize {
let amount = max.min(self.len());
self.tail += amount as u32;
amount
}
}
impl<const MAX_CAPACITY: usize> AsRef<[u8]> for Snapshot<MAX_CAPACITY> {
fn as_ref(&self) -> &[u8] {
self.state.as_ref().map_or(&[], |state| {
unsafe { state.as_slice().get_unchecked(self.tail as usize..) }
})
}
}
impl<const MAX_CAPACITY: usize> Sink for Snapshot<MAX_CAPACITY> {
type Error = storage::AllocError;
fn write_slice(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
if bytes.is_empty() {
return Ok(());
}
let state = match &mut self.state {
Some(state) => state,
slot @ None => {
let capacity = num::NonZero::<usize>::MIN.saturating_add(bytes.len() - 1);
*slot = Some(window::Fresh::try_copy(bytes, capacity)?);
return Ok(());
}
};
window::Edit::new(state, &mut self.tail).try_extend(bytes)
}
fn write_slices<const N: usize>(&mut self, slices: [&[u8]; N]) -> Result<(), Self::Error> {
let source = owners::Measured::try_slices(slices, MAX_CAPACITY - self.len())
.map_err(storage::AllocError::Capacity)?;
match &mut self.state {
Some(state) => window::Edit::new(state, &mut self.tail).try_slices(source),
slot @ None => {
*slot = window::Fresh::try_slices(source)?;
Ok(())
}
}
}
}
const _: () = assert!(size_of::<Snapshot<1>>() == 3 * size_of::<usize>());
unsafe impl<const MAX_CAPACITY: usize> buffer::raw::Source for Snapshot<MAX_CAPACITY> {}