use alloc::vec::Vec;
#[cfg(test)]
use crate::buffer::TestBufferFromMemory;
use crate::{
buffer::BufferFromSystemMemory,
slice::{SharedState, Slice},
Buffer,
};
#[cfg(feature = "debug")]
use std::time::SystemTime;
mod pool_back;
pub use pool_back::PoolBack;
#[cfg(test)]
mod test;
pub const POOL_CAPACITY: usize = 8;
#[derive(Debug)]
pub struct PoolFront {
back_start: usize,
byte_capacity: usize,
len: usize,
}
impl PoolFront {
#[inline(always)]
fn new(byte_capacity: usize, back_start: usize) -> Self {
Self {
byte_capacity,
back_start,
len: 0,
}
}
#[inline(always)]
fn try_clear_tail(&mut self, memory: &mut InnerMemory, mut shared_state: u8) -> bool {
#[cfg(feature = "fuzz")]
assert!(self.len > 0);
shared_state >>= POOL_CAPACITY - self.len;
let element_to_drop = shared_state.trailing_zeros();
match element_to_drop {
0 => false,
8 => {
self.len = 0;
memory.len = self.len;
let raw_offset = memory.raw_offset();
memory.move_raw_at_offset(raw_offset);
true
}
_ => {
#[cfg(feature = "fuzz")]
assert!(
element_to_drop <= self.back_start as u32 && element_to_drop <= self.len as u32
);
self.len -= element_to_drop as usize;
memory.len = self.len;
let raw_offset = memory.raw_offset();
memory.move_raw_at_offset(raw_offset);
true
}
}
}
#[inline(always)]
fn clear(
&mut self,
memory: &mut InnerMemory,
shared_state: u8,
len: usize,
) -> Result<(), PoolMode> {
if self.len > 0 && self.try_clear_tail(memory, shared_state) {
if memory.has_capacity_until_offset(len, self.byte_capacity) {
Ok(())
} else {
Err(PoolMode::Back)
}
} else {
Err(PoolMode::Back)
}
}
#[inline(always)]
fn reserve(
&mut self,
len: usize,
memory: &mut InnerMemory,
shared_state: u8,
) -> Result<*mut u8, PoolMode> {
let pool_has_slice_capacity = self.len < self.back_start;
let pool_has_head_capacity = memory.has_capacity_until_offset(len, self.byte_capacity);
if pool_has_slice_capacity && pool_has_head_capacity {
return Ok(memory.reserve_raw(len));
};
self.clear(memory, shared_state, len)
.map(|_| memory.reserve_raw(len))
}
}
#[derive(Debug)]
pub enum PoolMode {
Back,
Front(PoolFront),
Alloc,
}
pub struct InnerMemory {
memory: SharedState,
pub(crate) raw_offset: usize,
pub(crate) raw_len: usize,
slots: [(usize, usize); POOL_CAPACITY],
len: usize,
}
impl core::fmt::Debug for InnerMemory {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("InnerMemory")
.field("memory", &self.memory)
.field("raw_offset", &self.raw_offset)
.field("raw_len", &self.raw_len)
.field("slots", &self.slots)
.field("len", &self.len)
.finish()
}
}
impl InnerMemory {
fn new(capacity: usize) -> Self {
Self {
memory: SharedState::new(capacity),
raw_offset: 0,
raw_len: 0,
slots: [(0_usize, 0_usize); POOL_CAPACITY],
len: 0,
}
}
#[inline(always)]
fn capacity(&self) -> usize {
self.memory.capacity()
}
#[inline(always)]
fn in_bounds(&self, offset: usize, len: usize) -> bool {
offset
.checked_add(len)
.is_some_and(|end| end <= self.capacity())
}
#[inline(always)]
fn raw_data(&self) -> &[u8] {
assert!(self.in_bounds(self.raw_offset, self.raw_len));
unsafe {
core::slice::from_raw_parts(self.memory.bytes().add(self.raw_offset), self.raw_len)
}
}
#[inline(always)]
fn raw_data_mut(&mut self) -> &mut [u8] {
assert!(self.in_bounds(self.raw_offset, self.raw_len));
unsafe {
core::slice::from_raw_parts_mut(self.memory.bytes().add(self.raw_offset), self.raw_len)
}
}
#[inline(always)]
fn copy_within(&mut self, src: usize, len: usize, dst: usize) {
assert!(self.in_bounds(src, len) && self.in_bounds(dst, len));
let bytes = self.memory.bytes();
unsafe { core::ptr::copy(bytes.add(src), bytes.add(dst), len) }
}
#[inline(always)]
fn reset(&mut self) {
self.raw_len = 0;
self.raw_offset = 0;
self.slots = [(0_usize, 0_usize); POOL_CAPACITY];
}
#[inline(always)]
fn reset_raw(&mut self) {
self.raw_len = 0;
self.raw_offset = 0;
}
#[inline(always)]
fn get_front_capacity(&self, back_start: usize) -> usize {
#[cfg(feature = "fuzz")]
assert!(
(1..POOL_CAPACITY).contains(&back_start)
&& self.slots[back_start].0 != 0_usize
&& self.slots[back_start].1 != 0_usize
&& self.slots[back_start].0 + self.slots[back_start].1 <= self.capacity()
);
self.slots[back_start].0
}
#[inline(always)]
fn raw_offset(&self) -> usize {
self.raw_offset_from_len(self.len)
}
#[inline(always)]
fn raw_offset_from_len(&self, len: usize) -> usize {
match len {
0 => 0,
_ => {
let index = len - 1;
#[cfg(feature = "fuzz")]
assert!(
index < POOL_CAPACITY
&& self.slots[index].1 != 0_usize
&& self.slots[index].0 + self.slots[index].1 <= self.capacity()
);
let (index, len) = self.slots[index];
index + len
}
}
}
#[inline(always)]
fn move_raw_at_front(&mut self) {
self.move_raw_at_offset(0);
}
#[inline(always)]
fn front_end(&self, shared_state: u8, back_start: usize) -> usize {
if back_start == 0 {
return 0;
}
match shared_state >> (POOL_CAPACITY - back_start) {
0 => 0,
front => {
let highest = back_start - 1 - front.trailing_zeros() as usize;
let (offset, len) = self.slots[highest];
offset + len
}
}
}
#[inline(always)]
fn try_change_len(&mut self, slot_len: usize, raw_offset: usize, raw_len: usize) -> bool {
let end = raw_offset + self.raw_len;
if self.in_bounds(end, raw_len) {
self.len = slot_len;
self.move_raw_at_offset(raw_offset);
true
} else {
false
}
}
#[inline(always)]
fn move_raw_at_offset(&mut self, offset: usize) {
match self.raw_len {
0 => self.raw_offset = offset,
_ => {
self.copy_within(self.raw_offset, self.raw_len, offset);
self.raw_offset = offset;
}
}
}
#[inline(never)]
fn prepend_raw_data(&mut self, raw_data: &[u8]) {
assert!(self.in_bounds(0, raw_data.len()));
let dest = self.memory.bytes();
unsafe { core::ptr::copy_nonoverlapping(raw_data.as_ptr(), dest, raw_data.len()) };
self.raw_offset = 0;
self.raw_len = raw_data.len();
}
#[inline(never)]
fn copy_into_buffer(&mut self, buffer: &mut impl Buffer) {
buffer
.reserve(self.raw_len)
.copy_from_slice(self.raw_data());
buffer.commit(self.raw_len);
}
#[inline(always)]
fn has_tail_capacity(&self, len: usize) -> bool {
self.in_bounds(self.raw_offset + self.raw_len, len)
}
#[inline(always)]
fn has_capacity_until_offset(&self, len: usize, offset: usize) -> bool {
(self.raw_offset + self.raw_len)
.checked_add(len)
.is_some_and(|end| end <= offset)
}
#[inline(always)]
fn reserve_raw(&mut self, len: usize) -> *mut u8 {
let writable_offset = self.raw_offset + self.raw_len;
assert!(self.in_bounds(writable_offset, len));
unsafe { self.memory.bytes().add(writable_offset) }
}
#[inline(always)]
fn commit_raw(&mut self, len: usize) {
assert!(self.in_bounds(self.raw_offset + self.raw_len, len));
self.raw_len += len;
}
#[inline(always)]
fn get_data_owned(&mut self, #[cfg(feature = "debug")] mode: u8) -> Slice {
if self.raw_len == 0 {
return Slice::from(Vec::new());
}
let offset = unsafe { self.memory.bytes().add(self.raw_offset) };
self.slots[self.len] = (self.raw_offset, self.raw_len);
self.len += 1;
let slice = Slice::pooled(
self.memory.clone(),
offset,
self.raw_len,
self.len as u8,
#[cfg(feature = "debug")]
mode,
);
self.raw_offset += self.raw_len;
self.raw_len = 0;
slice
}
}
#[derive(Debug)]
pub struct BufferPool<T: Buffer> {
pool_back: PoolBack,
mode: PoolMode,
inner_memory: InnerMemory,
system_memory: T,
reserved: usize,
}
impl BufferPool<BufferFromSystemMemory> {
pub fn new(capacity: usize) -> Self {
Self {
pool_back: PoolBack::new(),
mode: PoolMode::Back,
inner_memory: InnerMemory::new(capacity),
system_memory: BufferFromSystemMemory::default(),
reserved: 0,
}
}
}
#[cfg(test)]
impl BufferPool<TestBufferFromMemory> {
pub fn new_fail_system_memory(capacity: usize) -> Self {
Self {
pool_back: PoolBack::new(),
mode: PoolMode::Back,
inner_memory: InnerMemory::new(capacity),
system_memory: TestBufferFromMemory(Vec::new()),
reserved: 0,
}
}
}
impl<T: Buffer> BufferPool<T> {
pub fn is_front_mode(&self) -> bool {
matches!(self.mode, PoolMode::Front(_))
}
pub fn is_back_mode(&self) -> bool {
matches!(self.mode, PoolMode::Back)
}
pub fn is_alloc_mode(&self) -> bool {
matches!(self.mode, PoolMode::Alloc)
}
#[inline(always)]
fn reset(&mut self) {
#[cfg(feature = "debug")]
println!("RESET");
self.inner_memory.len = 0;
match self.mode {
PoolMode::Back => {
self.inner_memory.move_raw_at_front();
self.pool_back.reset();
}
PoolMode::Front(_) => {
self.inner_memory.move_raw_at_front();
self.mode = PoolMode::Back;
self.pool_back.reset();
}
PoolMode::Alloc => {
if self.system_memory.len() < self.inner_memory.capacity() {
let raw_len = self.system_memory.len();
if raw_len > 0 {
self.inner_memory
.prepend_raw_data(self.system_memory.frame());
self.system_memory.get_data_owned();
} else {
self.inner_memory.reset();
}
self.mode = PoolMode::Back;
self.pool_back.reset();
}
}
}
}
#[inline(never)]
fn reserve_from_system_memory(
&mut self,
len: usize,
shared_state: u8,
without_check: bool,
) -> &mut [u8] {
if self.system_memory.len() > 0
|| without_check
|| self.pool_back.len() == 0
|| !self.pool_back.tail_is_clearable(shared_state)
{
self.system_memory.reserve(len)
} else {
#[cfg(feature = "fuzz")]
assert!(self.inner_memory.raw_len == 0 && self.inner_memory.raw_offset == 0);
match self
.pool_back
.clear_unchecked(&mut self.inner_memory, shared_state, len)
{
Ok(_) => {
self.change_mode(PoolMode::Back, len, shared_state);
self.reserve_(len, shared_state, false)
}
Err(PoolMode::Front(f)) => {
self.change_mode(PoolMode::Front(f), len, shared_state);
self.reserve_(len, shared_state, false)
}
Err(PoolMode::Alloc) => {
self.inner_memory.reset_raw();
self.system_memory.reserve(len)
}
Err(PoolMode::Back) => unreachable!("clearing the back never asks for the back"),
}
}
}
#[inline(never)]
fn get_data_owned_from_system_memory(&mut self) -> Slice {
self.system_memory.get_data_owned().into()
}
#[inline(always)]
fn change_mode(&mut self, mode: PoolMode, len: usize, shared_state: u8) {
match (&mut self.mode, &mode) {
(PoolMode::Back, PoolMode::Alloc) => {
#[cfg(feature = "debug")]
println!(
"BACK => ALLOC {} {:?}",
self.pool_back.len(),
SystemTime::now()
);
self.inner_memory.copy_into_buffer(&mut self.system_memory);
self.inner_memory.reset_raw();
self.mode = mode;
}
(PoolMode::Back, PoolMode::Front(_)) => {
#[cfg(feature = "fuzz")]
assert!(shared_state.leading_zeros() > 0);
#[cfg(feature = "debug")]
println!("BACK => FRONT");
self.inner_memory.len = 0;
self.inner_memory.move_raw_at_front();
self.mode = mode;
}
(PoolMode::Front(_), PoolMode::Back) => {
#[cfg(feature = "debug")]
println!("FRONT => BACK");
if !self.pool_back.tail_is_clearable(shared_state) {
self.inner_memory.copy_into_buffer(&mut self.system_memory);
self.inner_memory.reset_raw();
self.mode = PoolMode::Alloc;
} else if self.pool_back.try_clear_tail_unchecked(
&mut self.inner_memory,
shared_state,
len,
) {
self.mode = mode;
} else {
#[cfg(feature = "debug")]
println!("ALLOC 2");
self.inner_memory.copy_into_buffer(&mut self.system_memory);
self.inner_memory.reset_raw();
self.mode = PoolMode::Alloc;
}
}
(PoolMode::Alloc, PoolMode::Back) => {
#[cfg(feature = "debug")]
println!("ALLOC => BACK {:?}", SystemTime::now());
self.inner_memory.len = self.pool_back.len() + self.pool_back.back_start();
self.mode = mode;
}
(PoolMode::Alloc, PoolMode::Front(_)) => {
#[cfg(feature = "fuzz")]
assert!(shared_state.leading_zeros() > 0);
#[cfg(feature = "debug")]
println!("ALLOC => FRONT {:?} {:b}", SystemTime::now(), shared_state);
self.inner_memory.reset_raw();
self.inner_memory.len = 0;
self.mode = mode;
}
(PoolMode::Front(_), PoolMode::Alloc) => {
unreachable!("front mode only ever gives way to the back")
}
(PoolMode::Back, PoolMode::Back)
| (PoolMode::Front(_), PoolMode::Front(_))
| (PoolMode::Alloc, PoolMode::Alloc) => {
unreachable!("a mode change always leaves the current mode")
}
}
}
#[inline(always)]
fn reserve_(&mut self, len: usize, shared_state: u8, without_check: bool) -> &mut [u8] {
let writable = match &mut self.mode {
PoolMode::Back => self
.pool_back
.reserve(len, &mut self.inner_memory, shared_state),
PoolMode::Front(front) => front.reserve(len, &mut self.inner_memory, shared_state),
PoolMode::Alloc => {
return self.reserve_from_system_memory(len, shared_state, without_check)
}
};
match writable {
Ok(offset) => {
let writable: &mut [u8];
unsafe {
writable = core::slice::from_raw_parts_mut(offset, len);
}
writable
}
Err(mode) => {
self.change_mode(mode, len, shared_state);
let without_check = self.is_alloc_mode();
self.reserve_(len, shared_state, without_check)
}
}
}
}
impl<T: Buffer> Buffer for BufferPool<T> {
type Slice = Slice;
#[inline(always)]
fn reserve(&mut self, len: usize) -> &mut [u8] {
let shared_state = self.inner_memory.memory.load();
let alloc_mode_is_done = self.is_alloc_mode() && self.system_memory.len() == 0;
if shared_state == 0 && (self.pool_back.len() != 0 || alloc_mode_is_done) {
self.reset();
}
self.reserved = len;
self.reserve_(len, shared_state, false)
}
#[inline(always)]
fn commit(&mut self, len: usize) {
assert!(len <= self.reserved, "commit exceeds the last reservation");
self.reserved = 0;
match self.mode {
PoolMode::Alloc => self.system_memory.commit(len),
_ => self.inner_memory.commit_raw(len),
}
}
#[inline(always)]
fn get_data_owned(&mut self) -> Self::Slice {
self.reserved = 0;
#[cfg(feature = "debug")]
let mode: u8 = match self.mode {
PoolMode::Back => 0,
PoolMode::Front(_) => 1,
PoolMode::Alloc => 8,
};
match &mut self.mode {
PoolMode::Back => {
#[cfg(feature = "debug")]
println!(
"{} {} {}",
self.inner_memory.raw_offset, self.inner_memory.raw_len, self.inner_memory.len
);
let res = self.inner_memory.get_data_owned(
#[cfg(feature = "debug")]
mode,
);
self.pool_back
.set_len_from_inner_memory(self.inner_memory.len);
#[cfg(feature = "debug")]
{
println!(
"{} {} {}",
self.inner_memory.raw_offset,
self.inner_memory.raw_len,
self.inner_memory.len
);
println!("GET DATA BACK {:?}", self.inner_memory.slots);
}
res
}
PoolMode::Front(f) => {
let res = self.inner_memory.get_data_owned(
#[cfg(feature = "debug")]
mode,
);
f.len = self.inner_memory.len;
#[cfg(feature = "debug")]
println!("GET DATA FRONT {:?}", self.inner_memory.slots);
res
}
PoolMode::Alloc => self.get_data_owned_from_system_memory(),
}
}
fn frame(&self) -> &[u8] {
match self.mode {
PoolMode::Alloc => self.system_memory.frame(),
_ => self.inner_memory.raw_data(),
}
}
fn frame_mut(&mut self) -> &mut [u8] {
match self.mode {
PoolMode::Alloc => self.system_memory.frame_mut(),
_ => self.inner_memory.raw_data_mut(),
}
}
fn len(&self) -> usize {
match self.mode {
PoolMode::Alloc => self.system_memory.len(),
_ => self.inner_memory.raw_len,
}
}
fn truncate(&mut self, len: usize) {
self.reserved = 0;
match self.mode {
PoolMode::Alloc => Buffer::truncate(&mut self.system_memory, len),
_ => self.inner_memory.raw_len = self.inner_memory.raw_len.min(len),
}
}
}
#[cfg(test)]
impl<T: Buffer> BufferPool<T> {
pub(crate) fn live_slots(&self) -> u8 {
self.inner_memory.memory.load()
}
}