use std::{alloc, marker, mem, ptr, slice};
use crate::buffer::{
self, pool,
pool::state,
sealed::{self, core},
write,
};
pub struct Lease<
S: state::State = state::Uninitialized,
C: pool::Capacity = pool::RuntimeCapacity,
O: pool::Ownership = pool::Owned,
> {
core: ptr::NonNull<core::Core>,
index: u32,
len: u32,
marker: marker::PhantomData<(S, C, O, *mut ())>,
}
impl<S: state::State, C: pool::Capacity, O: pool::Ownership> Lease<S, C, O> {
pub(super) fn new(core: ptr::NonNull<core::Core>, index: u32) -> Self {
Self {
core,
index,
len: 0,
marker: marker::PhantomData,
}
}
pub fn len(&self) -> usize {
self.len as usize
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(core::Core::data(self.core, self.index), self.len()) }
}
pub fn capacity(&self) -> usize {
unsafe { self.core.as_ref() }.capacity as usize
}
pub fn truncate(&mut self, len: usize) {
if len < self.len() {
self.len = len as u32;
}
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(core::Core::data(self.core, self.index), self.len()) }
}
}
impl<S: state::State, C: pool::Capacity> Lease<S, C, pool::Owned> {
pub fn freeze(self) -> sealed::Frozen {
let this = mem::ManuallyDrop::new(self);
sealed::Frozen::new(this.core, this.index, this.len)
}
}
impl<'pool, S: state::State, C: pool::Capacity> Lease<S, C, pool::Borrowed<'pool>> {
pub fn freeze(self) -> sealed::Frozen<pool::Borrowed<'pool>> {
let this = mem::ManuallyDrop::new(self);
sealed::Frozen::new(this.core, this.index, this.len)
}
}
impl<C: pool::Capacity, O: pool::Ownership> Lease<state::Uninitialized, C, O> {
pub fn try_push(&mut self, byte: u8) -> Result<(), buffer::CapacityError> {
let written = self.len();
let capacity = self.capacity();
if written == capacity {
return Err(buffer::CapacityError::new(
written.saturating_add(1),
capacity,
));
}
unsafe {
core::Core::data(self.core, self.index)
.add(written)
.cast::<mem::MaybeUninit<u8>>()
.write(mem::MaybeUninit::new(byte));
}
self.len += 1;
Ok(())
}
pub fn try_extend(&mut self, src: &[u8]) -> Result<(), buffer::CapacityError> {
let start = self.len();
let capacity = self.capacity();
let end = start
.checked_add(src.len())
.ok_or_else(|| buffer::CapacityError::new(usize::MAX, capacity))?;
if end > capacity {
return Err(buffer::CapacityError::new(end, capacity));
}
unsafe {
ptr::copy_nonoverlapping(
src.as_ptr(),
core::Core::data(self.core, self.index).add(start),
src.len(),
)
};
self.len = end as u32;
Ok(())
}
pub fn try_extend_from_slices<const N: usize>(
&mut self,
slices: [&[u8]; N],
) -> Result<(), buffer::CapacityError> {
let start = self.len();
let end = buffer::checked_append_len(start, self.capacity(), &slices)?;
let mut offset = start;
for src in slices {
unsafe {
ptr::copy_nonoverlapping(
src.as_ptr(),
core::Core::data(self.core, self.index).add(offset),
src.len(),
)
};
offset += src.len();
}
self.len = end as u32;
Ok(())
}
pub fn spare_writer(&mut self) -> write::SpareWriter<'_> {
let capacity = unsafe { self.core.as_ref() }.capacity as usize;
let written = self.len as usize;
let spare = unsafe {
slice::from_raw_parts_mut(
core::Core::data(self.core, self.index).add(written).cast(),
capacity - written,
)
};
write::SpareWriter::new(spare, &mut self.len)
}
}
impl<C: pool::Capacity, O: pool::Ownership> Lease<state::Initialized, C, O> {
pub fn spare_mut(&mut self) -> &mut [u8] {
let len = self.len();
let remaining = self.capacity() - len;
let bytes = unsafe {
slice::from_raw_parts_mut(core::Core::data(self.core, self.index), self.capacity())
};
&mut bytes[len..len + remaining]
}
pub fn try_advance(&mut self, additional: usize) -> Result<(), buffer::CapacityError> {
let len = self.len();
let capacity = self.capacity();
let attempted = len
.checked_add(additional)
.ok_or_else(|| buffer::CapacityError::new(usize::MAX, capacity))?;
if attempted > capacity {
return Err(buffer::CapacityError::new(attempted, capacity));
}
self.len = attempted as u32;
Ok(())
}
}
impl<S: state::State, C: pool::Capacity, O: pool::Ownership> Drop for Lease<S, C, O> {
fn drop(&mut self) {
let core = unsafe { self.core.as_ref() };
let slot = unsafe { &*core::Core::slot(self.core, self.index) };
if !slot.refs.release() {
return;
}
slot.refs.deactivate();
slot.next.set(core.free.get());
core.free.set(self.index);
core.free_len.set(core.free_len.get() + 1);
if O::RETAINS_CORE && core.refs.release() {
let layout = unsafe {
alloc::Layout::from_size_align_unchecked(
core.allocation_size,
align_of::<core::Core>(),
)
};
unsafe { alloc::dealloc(self.core.as_ptr().cast(), layout) };
}
}
}
impl<S: state::State, C: pool::Capacity, O: pool::Ownership> buffer::PrefixLength
for Lease<S, C, O>
{
fn prefix_len(&self) -> usize {
self.len as usize
}
}