use crate::buffer_pool::{InnerMemory, PoolFront, PoolMode, POOL_CAPACITY};
#[derive(Debug)]
pub struct PoolBack {
back_start: usize,
len: usize,
}
impl PoolBack {
pub fn new() -> Self {
Self {
back_start: 0,
len: 0,
}
}
#[inline(always)]
pub fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub fn set_len_from_inner_memory(&mut self, len: usize) {
let len = len - self.back_start;
#[cfg(feature = "fuzz")]
assert!(len + self.back_start <= POOL_CAPACITY);
self.len = len;
}
#[inline(always)]
pub fn back_start(&self) -> usize {
self.back_start
}
#[inline(always)]
pub fn reset(&mut self) {
self.back_start = 0;
self.len = 0;
}
#[inline(always)]
pub fn try_clear_tail_unchecked(
&mut self,
memory: &mut InnerMemory,
shared_state: u8,
len: usize,
) -> bool {
let trailing_0 = shared_state.trailing_zeros();
let element_in_back = POOL_CAPACITY - self.back_start;
let element_to_drop = usize::min(element_in_back, shared_state.trailing_zeros() as usize);
let already_dropped = POOL_CAPACITY - (self.back_start + self.len);
match (
trailing_0,
element_in_back,
element_to_drop < already_dropped,
) {
(0, _, _) | (_, 0, _) | (_, _, true) => false,
_ => {
let element_to_drop = element_to_drop - already_dropped;
let back_len = self.len - element_to_drop;
#[cfg(feature = "fuzz")]
assert!(
!(back_len + self.back_start > POOL_CAPACITY
|| back_len + element_to_drop + already_dropped + self.back_start
!= POOL_CAPACITY)
);
let raw_offset = match back_len {
0 => memory.front_end(shared_state, self.back_start),
_ => memory.raw_offset_from_len(back_len + self.back_start),
};
let cleared = memory.try_change_len(back_len + self.back_start, raw_offset, len);
if cleared {
self.len = back_len;
}
cleared
}
}
}
#[inline(always)]
pub fn tail_is_clearable(&self, shared_state: u8) -> bool {
let element_in_back = POOL_CAPACITY - self.back_start;
let element_to_drop = usize::min(element_in_back, shared_state.trailing_zeros() as usize);
element_to_drop <= self.len && self.back_start + self.len >= POOL_CAPACITY
}
#[inline(always)]
fn try_clear_head(
&mut self,
shared_state: u8,
memory: &mut InnerMemory,
) -> Result<(), PoolMode> {
let front_slots = shared_state.leading_zeros() as usize;
if front_slots >= 1 && memory.raw_len < memory.slots[front_slots].0 {
if front_slots > self.back_start {
self.len -= front_slots - self.back_start;
self.back_start = front_slots;
}
let pool_front = PoolFront::new(memory.get_front_capacity(front_slots), front_slots);
Err(PoolMode::Front(pool_front))
} else {
Err(PoolMode::Alloc)
}
}
#[inline(always)]
pub fn clear_unchecked(
&mut self,
memory: &mut InnerMemory,
shared_state: u8,
len: usize,
) -> Result<(), PoolMode> {
let tail_cleared = self.try_clear_tail_unchecked(memory, shared_state, len);
if tail_cleared {
return Ok(());
};
self.try_clear_head(shared_state, memory)
}
#[inline(always)]
pub fn reserve(
&mut self,
len: usize,
memory: &mut InnerMemory,
shared_state: u8,
) -> Result<*mut u8, PoolMode> {
let pool_has_byte_capacity = memory.has_tail_capacity(len);
let pool_has_slice_capacity = (self.len + self.back_start) < POOL_CAPACITY;
if pool_has_byte_capacity && pool_has_slice_capacity {
#[cfg(feature = "fuzz")]
assert!(self.len + self.back_start < POOL_CAPACITY);
return Ok(memory.reserve_raw(len));
}
if !self.tail_is_clearable(shared_state) {
return Err(PoolMode::Alloc);
}
match self.clear_unchecked(memory, shared_state, len) {
Ok(_) => {
let pool_has_byte_capacity = memory.has_tail_capacity(len);
let pool_has_slice_capacity = (self.len + self.back_start) < POOL_CAPACITY;
if pool_has_byte_capacity && pool_has_slice_capacity {
#[cfg(feature = "fuzz")]
assert!(self.len + self.back_start < POOL_CAPACITY);
Ok(memory.reserve_raw(len))
} else {
Err(PoolMode::Alloc)
}
}
Err(pool_mode) => Err(pool_mode),
}
}
}