#![allow(clippy::type_complexity)]
use crate::prelude::{
PinnedPineMap, PinnedPineMapEmplace, UnpinnedPineMap, UnpinnedPineMapEmplace,
};
use bumpalo::Bump;
use parking_lot::RwLock;
use std::{
cell::Cell,
collections::BTreeMap,
mem::{self, MaybeUninit},
panic::{self, catch_unwind, AssertUnwindSafe},
pin::Pin,
};
use tap::{Pipe, TapFallible};
use this_is_fine::{prelude::*, Fine};
pub struct PineMap<K: Ord, V> {
contents: RwLock<Cambium<K, V>>,
}
pub struct PressedPineMap<K: Ord, V: ?Sized> {
contents: RwLock<PressedCambium<K, V>>,
}
struct Cambium<K, V> {
addresses: BTreeMap<K, *mut V>,
memory: Bump,
holes: Vec<*mut MaybeUninit<V>>,
}
struct PressedCambium<K, V: ?Sized> {
addresses: BTreeMap<K, *mut V>,
memory: Bump,
}
impl<K: Ord, V> PineMap<K, V> {
#[must_use]
pub fn new() -> Self {
Self {
contents: RwLock::new(Cambium {
addresses: BTreeMap::new(),
memory: Bump::new(),
holes: Vec::new(),
}),
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
contents: RwLock::new(Cambium {
addresses: BTreeMap::new(),
memory: Bump::with_capacity(mem::size_of::<V>() * capacity),
holes: Vec::new(),
}),
}
}
}
impl<K: Ord, V: ?Sized> PressedPineMap<K, V> {
#[must_use]
pub fn new() -> Self {
Self {
contents: RwLock::new(PressedCambium {
addresses: BTreeMap::new(),
memory: Bump::new(),
}),
}
}
#[must_use]
pub fn with_capacity(capacity_bytes: usize) -> Self {
Self {
contents: RwLock::new(PressedCambium {
addresses: BTreeMap::new(),
memory: Bump::with_capacity(capacity_bytes),
}),
}
}
}
impl<K: Ord, V> Default for PineMap<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K: Ord, V: ?Sized> Default for PressedPineMap<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K: Ord, V> UnpinnedPineMap<K, V> for PineMap<K, V> {
fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
Q: Ord + ?Sized,
{
let contents = self.contents.read();
contents.addresses.get(key).map(|value| unsafe { &**value })
}
fn try_insert_with<F: FnOnce(&K) -> Result<V, E>, E>(
&self,
key: K,
value_factory: F,
) -> Result<Fine<&V, (K, F)>, E> {
let value_factory = Cell::new(Some(value_factory));
self.try_emplace_with(key, |key, slot| {
slot.write(value_factory.take().expect("unreachable")(key)?)
.pipe(Ok)
})?
.map_err(|(key, _)| (key, value_factory.take().expect("unreachable")))
.pipe(Ok)
}
fn clear(&mut self) {
let contents = self.contents.get_mut();
contents.holes.clear();
let success = if mem::needs_drop::<V>() {
catch_unwind(AssertUnwindSafe(|| {
drop_all_pinned(mem::take(&mut contents.addresses))
}))
} else {
contents.addresses.clear();
Ok(())
};
contents.memory.reset();
success.unwrap_or_else(|panic| panic::resume_unwind(panic));
}
fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: std::borrow::Borrow<Q>,
Q: Ord + ?Sized,
{
let contents = self.contents.get_mut();
contents
.addresses
.get(key)
.map(|value| unsafe { &mut **value })
}
fn try_insert_with_mut<F: FnOnce(&K) -> Result<V, E>, E>(
&mut self,
key: K,
value_factory: F,
) -> Result<Fine<&mut V, (K, F)>, E> {
let value_factory = Cell::new(Some(value_factory));
self.try_emplace_with_mut(key, |key, slot| {
slot.write(value_factory.take().expect("unreachable")(key)?)
.pipe(Ok)
})?
.map_err(|(key, _)| (key, value_factory.take().expect("unreachable")))
.pipe(Ok)
}
fn remove_pair<Q>(&mut self, key: &Q) -> Option<(K, V)>
where
K: std::borrow::Borrow<Q>,
Q: Ord + ?Sized,
{
let contents = self.contents.get_mut();
let (key, value) = contents.addresses.remove_entry(key)?;
contents.holes.push(value.cast());
Some((key, unsafe { value.read() }))
}
fn remove_key<Q>(&mut self, key: &Q) -> Option<K>
where
K: std::borrow::Borrow<Q>,
Q: Ord + ?Sized,
{
let contents = self.contents.get_mut();
let (key, value) = contents.addresses.remove_entry(key)?;
contents.holes.push(value.cast());
unsafe { value.drop_in_place() };
Some(key)
}
}
impl<K: Ord, V: ?Sized> UnpinnedPineMap<K, V> for PressedPineMap<K, V> {
fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
Q: Ord + ?Sized,
{
let contents = self.contents.read();
contents.addresses.get(key).map(|value| unsafe { &**value })
}
fn try_insert_with<F: FnOnce(&K) -> Result<V, E>, E>(
&self,
key: K,
value_factory: F,
) -> Result<Fine<&V, (K, F)>, E>
where
V: Sized,
{
let value_factory = Cell::new(Some(value_factory));
self.try_emplace_with(key, |key, slot| {
slot.write(value_factory.take().expect("unreachable")(key)?)
.pipe(Ok)
})?
.map_err(|(key, _)| (key, value_factory.take().expect("unreachable")))
.pipe(Ok)
}
fn clear(&mut self) {
let contents = self.contents.get_mut();
let success = catch_unwind(AssertUnwindSafe(|| {
drop_all_pinned(mem::take(&mut contents.addresses))
}));
contents.memory.reset();
success.unwrap_or_else(|panic| panic::resume_unwind(panic));
}
fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: std::borrow::Borrow<Q>,
Q: Ord + ?Sized,
{
let contents = self.contents.get_mut();
contents
.addresses
.get(key)
.map(|value| unsafe { &mut **value })
}
fn try_insert_with_mut<F: FnOnce(&K) -> Result<V, E>, E>(
&mut self,
key: K,
value_factory: F,
) -> Result<Fine<&mut V, (K, F)>, E>
where
V: Sized,
{
let value_factory = Cell::new(Some(value_factory));
self.try_emplace_with_mut(key, |key, slot| {
slot.write(value_factory.take().expect("unreachable")(key)?)
.pipe(Ok)
})?
.map_err(|(key, _)| (key, value_factory.take().expect("unreachable")))
.pipe(Ok)
}
fn remove_pair<Q>(&mut self, key: &Q) -> Option<(K, V)>
where
V: Sized,
K: std::borrow::Borrow<Q>,
Q: Ord + ?Sized,
{
let contents = self.contents.get_mut();
let (key, value) = contents.addresses.remove_entry(key)?;
Some((key, unsafe { value.read() }))
}
fn remove_key<Q>(&mut self, key: &Q) -> Option<K>
where
K: std::borrow::Borrow<Q>,
Q: Ord + ?Sized,
{
let contents = self.contents.get_mut();
let (key, value) = contents.addresses.remove_entry(key)?;
unsafe { value.drop_in_place() };
Some(key)
}
}
impl<K: Ord, V> UnpinnedPineMapEmplace<K, V, V> for PineMap<K, V> {
fn try_emplace_with<
F: for<'a> FnOnce(&K, &'a mut MaybeUninit<V>) -> Result<&'a mut V, E>,
E,
>(
&self,
key: K,
value_factory: F,
) -> Result<Fine<&V, (K, F)>, E> {
let mut contents = self.contents.write();
let Cambium {
addresses,
memory,
holes,
} = &mut *contents;
#[allow(clippy::map_entry)]
if let Some(existing_value) = addresses.get(&key) {
(unsafe { &**existing_value }, Err((key, value_factory)))
} else if let Some(hole) = holes.pop() {
let slot = unsafe { &mut *hole };
let value = value_factory(&key, slot).tap_err(|_| holes.push(hole))?;
addresses.insert(key, value as *mut _);
(&*value, Ok(()))
} else {
let value = value_factory(&key, memory.alloc(MaybeUninit::uninit()))?;
addresses.insert(key, value as *mut _);
(&*value, Ok(()))
}
.map(|value| unsafe { &*(value as *const _) })
.pipe(Ok)
}
fn try_emplace_with_mut<
F: for<'a> FnOnce(&K, &'a mut MaybeUninit<V>) -> Result<&'a mut V, E>,
E,
>(
&mut self,
key: K,
value_factory: F,
) -> Result<Fine<&mut V, (K, F)>, E> {
let Cambium {
addresses,
memory,
holes,
} = self.contents.get_mut();
#[allow(clippy::map_entry)]
if let Some(existing_value) = addresses.get(&key) {
(unsafe { &mut **existing_value }, Err((key, value_factory)))
} else if let Some(hole) = holes.pop() {
let slot = unsafe { &mut *hole };
let value = value_factory(&key, slot).tap_err(|_| holes.push(hole))?;
addresses.insert(key, value as *mut _);
(value, Ok(()))
} else {
let value = value_factory(&key, memory.alloc(MaybeUninit::uninit()))?;
addresses.insert(key, value as *mut _);
(value, Ok(()))
}
.map(|value| unsafe { &mut *(value as *mut _) })
.pipe(Ok)
}
}
impl<K: Ord, V: ?Sized, W> UnpinnedPineMapEmplace<K, V, W> for PressedPineMap<K, V> {
fn try_emplace_with<
F: for<'a> FnOnce(&K, &'a mut MaybeUninit<W>) -> Result<&'a mut V, E>,
E,
>(
&self,
key: K,
value_factory: F,
) -> Result<Fine<&V, (K, F)>, E> {
let mut contents = self.contents.write();
let PressedCambium { addresses, memory } = &mut *contents;
#[allow(clippy::map_entry)]
if let Some(existing_value) = addresses.get(&key) {
(unsafe { &**existing_value }, Err((key, value_factory)))
} else {
let value = value_factory(&key, memory.alloc(MaybeUninit::uninit()))?;
addresses.insert(key, value as *mut _);
(unsafe { &*(value as *const _) }, Ok(()))
}
.pipe(Ok)
}
fn try_emplace_with_mut<
F: for<'a> FnOnce(&K, &'a mut MaybeUninit<W>) -> Result<&'a mut V, E>,
E,
>(
&mut self,
key: K,
value_factory: F,
) -> Result<Fine<&mut V, (K, F)>, E> {
let PressedCambium { addresses, memory } = self.contents.get_mut();
#[allow(clippy::map_entry)]
if let Some(existing_value) = addresses.get(&key) {
(unsafe { &mut **existing_value }, Err((key, value_factory)))
} else {
let value = value_factory(&key, memory.alloc(MaybeUninit::uninit()))?;
addresses.insert(key, value as *mut _);
(unsafe { &mut *(value as *mut _) }, Ok(()))
}
.pipe(Ok)
}
}
unsafe impl<K: Ord, V> PinnedPineMap<K, V> for Pin<PineMap<K, V>> {
type Unpinned = PineMap<K, V>;
}
unsafe impl<K: Ord, V: ?Sized> PinnedPineMap<K, V> for Pin<PressedPineMap<K, V>> {
type Unpinned = PressedPineMap<K, V>;
}
unsafe impl<K: Ord, V> PinnedPineMapEmplace<K, V, V> for Pin<PineMap<K, V>> {}
unsafe impl<K: Ord, V: ?Sized, W> PinnedPineMapEmplace<K, V, W> for Pin<PressedPineMap<K, V>> {}
unsafe impl<K: Ord, V> Send for PineMap<K, V>
where
K: Send,
V: Send,
{
}
unsafe impl<K: Ord, V: ?Sized> Send for PressedPineMap<K, V>
where
K: Send,
V: Send,
{
}
unsafe impl<K: Ord, V> Sync for PineMap<K, V>
where
K: Sync + Send,
V: Sync + Send,
{
}
unsafe impl<K: Ord, V: ?Sized> Sync for PressedPineMap<K, V>
where
K: Sync + Send,
V: Sync + Send,
{
}
impl<K: Ord, V> Drop for PineMap<K, V> {
fn drop(&mut self) {
if !mem::needs_drop::<V>() {
return;
}
let contents = self.contents.get_mut();
drop_all_pinned(mem::take(&mut contents.addresses));
}
}
impl<K: Ord, V: ?Sized> Drop for PressedPineMap<K, V> {
fn drop(&mut self) {
let contents = self.contents.get_mut();
drop_all_pinned(mem::take(&mut contents.addresses));
}
}
fn drop_all_pinned<K, V: ?Sized>(addresses: BTreeMap<K, *mut V>) {
let mut panics = vec![];
for (key, value) in addresses {
catch_unwind(AssertUnwindSafe(|| drop(key))).unwrap_or_else(|panic| panics.push(panic));
catch_unwind(AssertUnwindSafe(|| unsafe { value.drop_in_place() }))
.unwrap_or_else(|panic| panics.push(panic));
}
match panics.len() {
0 => (),
1 => panic::resume_unwind(panics.into_iter().next().expect("unreachable")),
_ => panic::resume_unwind(Box::new(panics)),
}
}