use crate::{
cohesive_map::CohesiveMapExt,
shunting_map::{Direction, ShuntingMapExt},
Index,
};
use alloc::{borrow::ToOwned, vec::Vec};
use core::{
borrow::Borrow,
convert::Infallible,
iter,
mem::{self, MaybeUninit},
pin::Pin,
ptr::{drop_in_place, NonNull},
};
use higher_order_closure::higher_order_closure;
use scopeguard::ScopeGuard;
use tap::{Pipe, Tap};
use typed_arena::Arena;
pub struct OwnedProjection<K, V> {
live: Index<K, V>,
stale: Index<K, V>,
storage: Arena<V>,
dropped: Vec<NonNull<V>>,
pub(crate) pinning: bool,
}
impl<K, V> Default for OwnedProjection<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K, V> Drop for OwnedProjection<K, V> {
fn drop(&mut self) {
struct Waste;
impl<T> Extend<T> for Waste {
fn extend<I: IntoIterator<Item = T>>(&mut self, _: I) {}
}
drop_stale(&mut self.stale, &mut Waste, self.pinning);
drop_stale(&mut self.live, &mut Waste, self.pinning);
}
}
fn drop_stale<K, V>(stale: &mut Index<K, V>, dropped: &mut impl Extend<NonNull<V>>, pinning: bool) {
#[cfg(feature = "std")]
let mut panic = None;
let guard = pinning.then(|| scopeguard::guard((), |()| {
panic!("`Pin<OwnedProjection>`: A key or value drop implementation panicked. The `drop`-guarantee cannot be upheld without the `\"std\"` feature.")
}));
for (k, v) in stale.drain(..) {
match v {
None => (),
Some(v) => unsafe {
#[cfg(feature = "std")]
{
use std::panic::{catch_unwind, AssertUnwindSafe};
let k_ = catch_unwind(AssertUnwindSafe(|| drop(k.assume_init()))).err();
let v_ = catch_unwind(AssertUnwindSafe(|| drop_in_place(v.as_ptr()))).err();
panic = panic.or(k_).or(v_);
}
#[cfg(not(feature = "std"))]
{
drop(k.assume_init());
drop_in_place(v.as_ptr());
}
dropped.extend(iter::once(v))
},
}
}
guard.map(ScopeGuard::into_inner);
#[cfg(feature = "std")]
if let Some(panic) = panic {
std::panic::resume_unwind(panic)
}
}
impl<K, V> OwnedProjection<K, V> {
#[must_use]
pub fn new() -> Self {
Self {
live: Vec::new(),
stale: Vec::new(),
storage: Arena::new(),
dropped: Vec::new(),
pinning: false,
}
}
pub const fn pin(mut self) -> Pin<Self> {
self.pinning = true;
unsafe { mem::transmute(self) }
}
pub fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: ?Sized + Eq,
{
self.live.iter().find_map(|(k, v)| match v {
Some(v) if key == unsafe { k.assume_init_ref() }.borrow() => {
Some(unsafe { v.as_ref() })
}
_ => None,
})
}
pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: ?Sized + Eq,
{
self.live.iter_mut().find_map(|(k, v)| match v {
Some(v) if key == unsafe { k.assume_init_ref() }.borrow() => {
Some(unsafe { v.as_mut() })
}
_ => None,
})
}
pub fn reproject_try_by_keyed_try_with_keyed<'a: 'b, 'b, T, Q, S, F, I, E>(
&'a mut self,
items_selectors_factories: I,
) -> impl 'b + Iterator<Item = Result<(T, &'a mut V), E>>
where
K: Borrow<Q>,
T: 'b,
Q: ?Sized + Eq + ToOwned<Owned = K>,
S: 'b + FnOnce(&mut T) -> Result<&Q, E>,
F: 'b + FnOnce(&mut T, &K) -> Result<V, E>,
I: 'b + IntoIterator<Item = (T, S, F)>,
E: 'b,
{
let pinning = self.pinning;
drop_stale(&mut self.stale, &mut self.dropped, pinning);
mem::swap(&mut self.live, &mut self.stale);
let mut stale_and_dropped = scopeguard::guard(
(&mut self.stale, &mut self.dropped),
move |(stale, dropped)| drop_stale(stale, dropped, pinning),
);
let live = &mut self.live;
let values = &self.storage;
items_selectors_factories.into_iter().shunting_map(
move |(mut item, selector, factory), direction| {
let (stale, dropped) = &mut *stale_and_dropped;
let key = selector(&mut item)?;
let mut stale = stale.iter_mut();
let predicate = |(k, v): &mut (MaybeUninit<K>, _)| match v {
Some(_) if &*key == unsafe { k.assume_init_ref() }.borrow() => v
.take()
.tap(|v| live.push((MaybeUninit::new(unsafe { k.as_ptr().read() }), *v)))
.map(|mut v| unsafe { v.as_mut() })
.map(Ok),
_ => None,
};
let v = match direction {
Direction::Forwards => stale.find_map(predicate),
Direction::Backwards => stale.rev().find_map(predicate),
};
if let Some(v) = v {
v.map(|v| (item, v))
} else {
let k = key.to_owned();
let v = if let Some(mut v) = dropped.pop() {
unsafe {
v.as_ptr().write(factory(&mut item, &k)?);
v.as_mut()
}
} else {
values.alloc(factory(&mut item, &k)?)
};
live.push((MaybeUninit::new(k), NonNull::new(v)));
Ok((item, v))
}
},
)
}
pub fn reproject_try_by_try_with<'a: 'b, 'b, T, Q, S, F, I, E>(
&'a mut self,
items: I,
mut selector: S,
mut factory: F,
) -> impl 'b + Iterator<Item = Result<(T, &'a mut V), E>>
where
K: Borrow<Q>,
T: 'b,
Q: 'b + ?Sized + Eq + ToOwned<Owned = K>,
S: 'b + FnMut(&mut T) -> Result<&Q, E>,
F: 'b + FnMut(&mut T, &K) -> Result<V, E>,
I: 'b + IntoIterator<Item = T>,
E: 'b,
{
self.reproject_try_by_keyed_try_with_keyed(items.into_iter().cohesive_map(move |item| {
let mut selector = NonNull::from(&mut selector);
let mut factory = NonNull::from(&mut factory);
(
item,
higher_order_closure! {
#![with<T, Q: ?Sized, E>]
move |item: &mut T| -> Result<&Q, E> {
(unsafe { selector.as_mut() })(item)
}
},
move |item: &mut T, k: &K| unsafe { factory.as_mut() }(item, k)?.pipe(Ok),
)
}))
}
pub fn reproject_by_keyed_with_keyed<'a: 'b, 'b, T, Q, S, F, I>(
&'a mut self,
items_selectors_factories: I,
) -> impl 'b + Iterator<Item = (T, &'a mut V)>
where
K: Borrow<Q>,
T: 'b,
Q: 'b + ?Sized + Eq + ToOwned<Owned = K>,
S: 'b + FnOnce(&mut T) -> &Q,
F: 'b + FnOnce(&mut T, &K) -> V,
I: 'b + IntoIterator<Item = (T, S, F)>,
{
self.reproject_try_by_keyed_try_with_keyed(items_selectors_factories.into_iter().map(
|(key, selector, factory)| {
(
key,
higher_order_closure! {
#![with<T, Q: ?Sized>]
|item: &mut T| -> Result<&Q, Infallible> {
Ok(selector(item))
}
},
|item: &mut T, k: &K| Ok(factory(item, k)),
)
},
))
.map(unwrap_infallible)
}
pub fn reproject_by_with<'a: 'b, 'b, T, Q, S, F, I>(
&'a mut self,
items: I,
mut selector: S,
mut factory: F,
) -> impl 'b + Iterator<Item = (T, &'a mut V)>
where
K: Borrow<Q>,
T: 'b,
Q: 'b + ?Sized + Eq + ToOwned<Owned = K>,
S: 'b + FnMut(&mut T) -> &Q,
F: 'b + FnMut(&mut T, &K) -> V,
I: 'b + IntoIterator<Item = T>,
{
self.reproject_try_by_try_with(
items,
move |item| Ok(selector(item)),
move |item, k| Ok(factory(item, k)),
)
.map(unwrap_infallible)
}
}
fn unwrap_infallible<T>(infallible: Result<T, Infallible>) -> T {
infallible.unwrap()
}