use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
use core::sync::atomic::{AtomicU8, Ordering};
#[cfg(feature = "debug")]
use std::time::SystemTime;
unsafe impl Send for Slice {}
#[derive(Debug)]
pub struct Slice {
repr: Repr,
#[cfg(feature = "debug")]
mode: u8,
#[cfg(feature = "debug")]
#[allow(dead_code)]
time: SystemTime,
}
#[derive(Debug)]
enum Repr {
Pooled {
memory: SharedState,
ptr: *mut u8,
len: usize,
slot: u8,
},
Heap(Vec<u8>),
}
impl Slice {
pub(crate) fn pooled(
memory: SharedState,
ptr: *mut u8,
len: usize,
slot: u8,
#[cfg(feature = "debug")] mode: u8,
) -> Self {
memory.claim(
slot,
#[cfg(feature = "debug")]
mode,
);
Slice {
repr: Repr::Pooled {
memory,
ptr,
len,
slot,
},
#[cfg(feature = "debug")]
mode,
#[cfg(feature = "debug")]
time: SystemTime::now(),
}
}
pub fn len(&self) -> usize {
match &self.repr {
Repr::Pooled { len, .. } => *len,
Repr::Heap(owned) => owned.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl core::ops::Index<usize> for Slice {
type Output = u8;
fn index(&self, index: usize) -> &Self::Output {
self.as_ref().index(index)
}
}
impl core::ops::IndexMut<usize> for Slice {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
self.as_mut().index_mut(index)
}
}
impl core::ops::Index<core::ops::RangeFrom<usize>> for Slice {
type Output = [u8];
fn index(&self, index: core::ops::RangeFrom<usize>) -> &Self::Output {
self.as_ref().index(index)
}
}
impl core::ops::IndexMut<core::ops::RangeFrom<usize>> for Slice {
fn index_mut(&mut self, index: core::ops::RangeFrom<usize>) -> &mut Self::Output {
self.as_mut().index_mut(index)
}
}
impl core::ops::Index<core::ops::Range<usize>> for Slice {
type Output = [u8];
fn index(&self, index: core::ops::Range<usize>) -> &Self::Output {
self.as_ref().index(index)
}
}
impl core::ops::IndexMut<core::ops::Range<usize>> for Slice {
fn index_mut(&mut self, index: core::ops::Range<usize>) -> &mut Self::Output {
self.as_mut().index_mut(index)
}
}
impl core::ops::Index<core::ops::RangeFull> for Slice {
type Output = [u8];
fn index(&self, index: core::ops::RangeFull) -> &Self::Output {
self.as_ref().index(index)
}
}
impl AsMut<[u8]> for Slice {
#[inline(always)]
fn as_mut(&mut self) -> &mut [u8] {
match &mut self.repr {
Repr::Pooled { ptr, len, .. } => unsafe { core::slice::from_raw_parts_mut(*ptr, *len) },
Repr::Heap(owned) => owned,
}
}
}
impl AsRef<[u8]> for Slice {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
match &self.repr {
Repr::Pooled { ptr, len, .. } => unsafe { core::slice::from_raw_parts(*ptr, *len) },
Repr::Heap(owned) => owned,
}
}
}
impl Clone for Slice {
fn clone(&self) -> Self {
Slice::from(self.as_ref().to_vec())
}
}
impl Drop for Slice {
fn drop(&mut self) {
if let Repr::Pooled { memory, slot, .. } = &self.repr {
memory.release(
*slot,
#[cfg(feature = "debug")]
self.mode,
);
}
}
}
impl From<Vec<u8>> for Slice {
fn from(v: Vec<u8>) -> Self {
Slice {
repr: Repr::Heap(v),
#[cfg(feature = "debug")]
mode: 2,
#[cfg(feature = "debug")]
time: SystemTime::now(),
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct SharedState(Arc<PoolMemory>);
struct PoolMemory {
slots: AtomicU8,
bytes: *mut u8,
capacity: usize,
}
unsafe impl Send for PoolMemory {}
unsafe impl Sync for PoolMemory {}
impl Drop for PoolMemory {
fn drop(&mut self) {
let bytes = core::ptr::slice_from_raw_parts_mut(self.bytes, self.capacity);
drop(unsafe { Box::from_raw(bytes) });
}
}
impl core::fmt::Debug for PoolMemory {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PoolMemory")
.field("slots", &self.slots)
.field("capacity", &self.capacity)
.finish()
}
}
impl SharedState {
pub(crate) fn new(capacity: usize) -> Self {
let bytes = Box::into_raw(vec![0u8; capacity].into_boxed_slice()) as *mut u8;
Self(Arc::new(PoolMemory {
slots: AtomicU8::new(0),
bytes,
capacity,
}))
}
#[inline(always)]
pub(crate) fn bytes(&self) -> *mut u8 {
self.0.bytes
}
#[inline(always)]
pub(crate) fn capacity(&self) -> usize {
self.0.capacity
}
#[inline(always)]
pub(crate) fn load(&self) -> u8 {
self.0.slots.load(Ordering::Acquire)
}
#[inline(always)]
fn mask(position: u8) -> u8 {
match position {
1..=8 => 0b1000_0000 >> (position - 1),
_ => panic!("{}", position),
}
}
#[inline(always)]
pub(crate) fn claim(&self, position: u8, #[cfg(feature = "debug")] mode: u8) {
let mask = Self::mask(position);
let pre = self.0.slots.fetch_or(mask, Ordering::Relaxed);
assert_eq!(pre & mask, 0, "slot {position} is held by a live slice");
#[cfg(feature = "debug")]
println!("CLAIM:: {} {:b} {:b}", mode, pre, pre | mask);
}
#[inline(always)]
pub(crate) fn release(&self, position: u8, #[cfg(feature = "debug")] mode: u8) {
let mask = Self::mask(position);
let pre = self.0.slots.fetch_and(!mask, Ordering::Release);
debug_assert_ne!(pre & mask, 0);
#[cfg(feature = "debug")]
println!("RELEASE:: {} {:b} {:b}", mode, pre, pre & !mask);
}
}