#![allow(clippy::manual_map)]
use std::pin::Pin;
use std::ptr::{self, NonNull};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use crate::shared::Shared;
const MAX_REFCOUNT: usize = (isize::MAX) as usize;
pub(crate) struct Storage<T> {
shared: Arc<Shared>,
tasks: Vec<NonNull<Task<T>>>,
len: usize,
next: usize,
}
enum Entry<T> {
None,
Some(T),
Vacant(usize),
}
#[repr(C)]
pub(crate) struct Task<T> {
header: Header,
entry: Entry<T>,
}
pub(crate) struct Header {
shared: Arc<Shared>,
index: usize,
reference_count: AtomicUsize,
}
impl Header {
fn new(shared: Arc<Shared>, index: usize) -> Self {
Self {
shared,
index,
reference_count: AtomicUsize::new(1),
}
}
#[inline]
pub(crate) fn index(&self) -> usize {
self.index
}
#[inline]
pub(crate) fn shared(&self) -> &Shared {
&self.shared
}
pub(crate) fn increment_ref(&self) {
let count = self.reference_count.fetch_add(1, Ordering::SeqCst);
if count > MAX_REFCOUNT {
std::process::abort();
}
}
pub(crate) unsafe fn decrement_ref(&self) -> bool {
let count = self.reference_count.fetch_sub(1, Ordering::SeqCst);
count == 1
}
pub(crate) unsafe fn deallocate(ptr: NonNull<Header>) {
let drop_task = ptr.as_ref().shared.drop_task;
drop_task(ptr.as_ptr().cast_const().cast());
}
}
impl<T> Storage<T> {
pub(crate) fn new() -> Self {
Self {
shared: Arc::new(Shared::new::<T>()),
tasks: Vec::new(),
len: 0,
next: 0,
}
}
pub(crate) fn shared(&self) -> &Arc<Shared> {
&self.shared
}
pub(crate) fn len(&self) -> usize {
self.len
}
pub(crate) fn is_empty(&self) -> bool {
self.len == 0
}
pub(crate) fn insert(&mut self, val: T) -> usize {
let key = self.next;
self.insert_at(key, val);
key
}
pub(crate) fn get_pin_mut(&mut self, key: usize) -> Option<(NonNull<Header>, Pin<&mut T>)> {
let task = *self.tasks.get(key)?;
unsafe {
match *ptr::addr_of_mut!((*task.as_ptr()).entry) {
Entry::Some(ref mut value) => {
Some((task.cast::<Header>(), Pin::new_unchecked(value)))
}
_ => None,
}
}
}
#[cfg(test)]
pub(crate) fn get(&self, key: usize) -> Option<&T> {
let task = *self.tasks.get(key)?;
unsafe {
match *ptr::addr_of!((*task.as_ptr()).entry) {
Entry::Some(ref value) => Some(value),
_ => None,
}
}
}
pub(crate) fn get_mut(&mut self, key: usize) -> Option<&mut T>
where
T: Unpin,
{
let task = *self.tasks.get(key)?;
unsafe {
match *ptr::addr_of_mut!((*task.as_ptr()).entry) {
Entry::Some(ref mut value) => Some(value),
_ => None,
}
}
}
pub(crate) fn remove(&mut self, key: usize) -> bool {
let task = match self.tasks.get(key) {
Some(entry) => *entry,
None => return false,
};
if !unsafe { make_slot_vacant(task, self.next) } {
return false;
}
self.len -= 1;
self.next = key;
true
}
pub(crate) fn clear(&mut self) {
unsafe {
for &task in &self.tasks {
make_slot_vacant(task, 0);
if task.as_ref().header.decrement_ref() {
_ = Box::from_raw(task.as_ptr());
}
}
self.tasks.set_len(0);
}
}
fn insert_at(&mut self, key: usize, value: T) {
if key >= self.tasks.len() {
self.tasks.reserve(key - self.tasks.len() + 1);
for index in self.tasks.len()..=key {
self.tasks.push(NonNull::from(Box::leak(Box::new(Task {
header: Header::new(self.shared.clone(), index),
entry: Entry::None,
}))));
}
}
let task = *self.tasks.get(key).unwrap();
unsafe {
self.next = match ptr::addr_of_mut!((*task.as_ptr()).entry).replace(Entry::Some(value))
{
Entry::None => key + 1,
Entry::Vacant(next) => next,
_ => unreachable!(),
};
self.len += 1;
}
}
}
unsafe fn make_slot_vacant<T>(task: NonNull<Task<T>>, next: usize) -> bool {
let entry = unsafe { &mut *ptr::addr_of_mut!((*task.as_ptr()).entry) };
if !matches!(entry, Entry::Some(_)) {
return false;
}
*entry = Entry::Vacant(next);
true
}
impl<T> Default for Storage<T> {
fn default() -> Self {
Self::new()
}
}
unsafe impl<T> Send for Storage<T> where T: Send {}
unsafe impl<T> Sync for Storage<T> where T: Sync {}
impl<T> Drop for Storage<T> {
fn drop(&mut self) {
self.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn assert_send_sync() {
fn assert_send<T: Send>(_: &T) {}
fn assert_sync<T: Sync>(_: &T) {}
let slab = Storage::<i32>::new();
assert_send(&slab);
assert_sync(&slab);
let _slab = Storage::<std::cell::Cell<i32>>::new();
let _slab = Storage::<std::rc::Rc<i32>>::new();
}
#[test]
fn test_basic() {
let mut slab = Storage::new();
assert!(!slab.remove(0));
let index = slab.insert(42);
assert!(slab.remove(index));
assert!(!slab.remove(index));
}
#[test]
fn test_new() {
let mut slab = Storage::new();
assert!(!slab.remove(0));
let index = slab.insert(42);
assert!(slab.remove(index));
assert!(!slab.remove(index));
}
#[test]
fn test_len() {
let mut slab = Storage::new();
assert_eq!(0, slab.len());
assert_eq!(0, slab.insert(42));
assert_eq!(1, slab.len());
}
#[test]
fn test_is_empty() {
let mut slab = Storage::new();
assert!(slab.is_empty());
assert_eq!(0, slab.insert(42));
assert!(!slab.is_empty());
}
#[test]
fn test_get() {
let mut slab = Storage::new();
let key = slab.insert(42);
assert_eq!(Some(&42), slab.get(key));
}
#[test]
fn test_get_mut() {
let mut slab = Storage::new();
let key = slab.insert(42);
*slab.get_mut(key).unwrap() = 43;
assert_eq!(Some(&43), slab.get(key));
}
#[test]
fn test_remove() {
let mut slab = Storage::new();
assert!(!slab.remove(0));
let index = slab.insert(42);
assert!(slab.remove(index));
assert!(!slab.remove(index));
}
#[test]
fn test_clear() {
let mut slab = Storage::new();
assert_eq!(0, slab.insert(42));
slab.clear();
assert!(slab.get(0).is_none());
}
}