#![no_std]
#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
#![feature(generic_associated_types)]
use core::{fmt, marker::PhantomData, ptr::NonNull};
#[doc(hidden)]
pub use core::mem::transmute;
use hv_guarded_borrow::{
NonBlockingGuardedBorrow, NonBlockingGuardedBorrowMut, NonBlockingGuardedMutBorrowMut,
};
use hv_cell::{ArcCell, ArcRef, ArcRefMut, AtomicRef, AtomicRefMut};
#[macro_export]
macro_rules! impl_stretched_methods {
() => {
unsafe fn lengthen(this: Self::Parameterized<'_>) -> Self {
$crate::transmute(this)
}
unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a> {
$crate::transmute(this)
}
unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a> {
$crate::transmute(this)
}
unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a> {
$crate::transmute(this)
}
};
}
pub mod external;
pub trait Stretchable<'a>: 'a {
#[rustfmt::skip]
type Stretched: Stretched<Parameterized<'a> = Self>;
}
pub unsafe trait Stretched: 'static + Sized {
type Parameterized<'a>: Stretchable<'a, Stretched = Self>
where
Self: 'a;
unsafe fn lengthen(this: Self::Parameterized<'_>) -> Self;
unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a>;
unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a>;
unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a>;
}
pub struct ElasticGuard<'a, T: Stretchable<'a>> {
slot: ArcCell<Option<T::Stretched>>,
_phantom: PhantomData<fn(&'a ())>,
}
impl<'a, T: Stretchable<'a>> fmt::Debug for ElasticGuard<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ElasticGuard {{ .. }}")
}
}
impl<'a, T: Stretchable<'a>> ElasticGuard<'a, T> {
pub fn take(self) -> T {
let stretched = self
.slot
.as_inner()
.borrow_mut()
.take()
.expect("empty slot!");
let shortened = unsafe { <T::Stretched>::shorten(stretched) };
core::mem::forget(self);
shortened
}
}
impl<'a, T: Stretchable<'a>> Drop for ElasticGuard<'a, T> {
fn drop(&mut self) {
if let Some(stretched) = self.slot.as_inner().borrow_mut().take() {
drop(unsafe { <T::Stretched>::shorten(stretched) });
}
}
}
#[derive(Debug)]
pub struct Elastic<T: Stretched> {
slot: ArcCell<Option<T>>,
}
impl<T: Stretched> Clone for Elastic<T> {
fn clone(&self) -> Self {
Self {
slot: self.slot.clone(),
}
}
}
impl<T: Stretched> Default for Elastic<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Stretched> Elastic<T> {
pub fn new() -> Self {
Self {
slot: Default::default(),
}
}
#[track_caller]
pub fn borrow(&self) -> Option<AtomicRef<T::Parameterized<'_>>> {
AtomicRef::filter_map(self.slot.as_inner().borrow(), Option::as_ref)
.map(|arm| AtomicRef::map(arm, |t| unsafe { T::shorten_ref(t) }))
}
#[track_caller]
pub fn borrow_mut(&self) -> Option<AtomicRefMut<T::Parameterized<'_>>> {
AtomicRefMut::filter_map(self.slot.as_inner().borrow_mut(), Option::as_mut)
.map(|arm| AtomicRefMut::map(arm, |t| unsafe { T::shorten_mut(t) }))
}
#[track_caller]
pub fn borrow_arc<'b, U: 'b, F>(&'b self, f: F) -> Option<ArcRef<U, Option<T>>>
where
F: for<'a> FnOnce(&'a T::Parameterized<'a>) -> &'a U,
{
ArcRef::filter_map(self.slot.borrow(), Option::as_ref)
.map(|arc| ArcRef::map(arc, |t| f(unsafe { T::shorten_ref(t) })))
}
#[track_caller]
pub fn borrow_arc_mut<'b, U: 'b, F>(&'b mut self, f: F) -> Option<ArcRefMut<U, Option<T>>>
where
F: for<'a> FnOnce(&'a mut T::Parameterized<'a>) -> &'a mut U,
{
ArcRefMut::filter_map(self.slot.borrow_mut(), Option::as_mut)
.map(|arc| ArcRefMut::map(arc, |t| f(unsafe { T::shorten_mut(t) })))
}
#[track_caller]
pub unsafe fn loan<'a>(
&self,
t: T::Parameterized<'a>,
) -> ElasticGuard<'a, T::Parameterized<'a>> {
let mut slot = self.slot.as_inner().borrow_mut();
assert!(
slot.is_none(),
"Elastic is already in the middle of a loan!"
);
let stretched = T::lengthen(t);
*slot = Some(stretched);
ElasticGuard {
slot: self.slot.clone(),
_phantom: PhantomData,
}
}
}
impl<T: Stretched, U: ?Sized> NonBlockingGuardedBorrow<U> for Elastic<T>
where
for<'a> T::Parameterized<'a>: core::borrow::Borrow<U>,
{
type Guard<'a>
where
U: 'a,
= AtomicRef<'a, U>;
type BorrowError<'a>
where
U: 'a,
= ();
fn try_nonblocking_guarded_borrow(&self) -> Result<Self::Guard<'_>, Self::BorrowError<'_>> {
self.borrow()
.ok_or(())
.map(|guard| AtomicRef::map(guard, |t| core::borrow::Borrow::borrow(t)))
}
}
impl<T: Stretched, U: ?Sized> NonBlockingGuardedBorrowMut<U> for Elastic<T>
where
for<'a> T::Parameterized<'a>: core::borrow::BorrowMut<U>,
{
type GuardMut<'a>
where
U: 'a,
= AtomicRefMut<'a, U>;
type BorrowMutError<'a>
where
U: 'a,
= ();
fn try_nonblocking_guarded_borrow_mut(
&self,
) -> Result<Self::GuardMut<'_>, Self::BorrowMutError<'_>> {
self.borrow_mut()
.ok_or(())
.map(|guard| AtomicRefMut::map(guard, |t| core::borrow::BorrowMut::borrow_mut(t)))
}
}
impl<T: Stretched, U: ?Sized> NonBlockingGuardedMutBorrowMut<U> for Elastic<T>
where
for<'a> T::Parameterized<'a>: core::borrow::BorrowMut<U>,
{
type MutGuardMut<'a>
where
U: 'a,
= AtomicRefMut<'a, U>;
type MutBorrowMutError<'a>
where
U: 'a,
= ();
fn try_nonblocking_guarded_mut_borrow_mut(
&mut self,
) -> Result<Self::MutGuardMut<'_>, Self::MutBorrowMutError<'_>> {
self.borrow_mut()
.ok_or(())
.map(|guard| AtomicRefMut::map(guard, |t| core::borrow::BorrowMut::borrow_mut(t)))
}
}
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct StretchedRef<T: ?Sized>(*const T);
unsafe impl<T: Sync> Send for StretchedRef<T> {}
unsafe impl<T: Sync> Sync for StretchedRef<T> {}
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct StretchedMut<T: ?Sized>(NonNull<T>);
unsafe impl<T: Send> Send for StretchedMut<T> {}
unsafe impl<T: Sync> Sync for StretchedMut<T> {}
unsafe impl<T: 'static> Stretched for StretchedRef<T> {
type Parameterized<'a> = &'a T;
unsafe fn lengthen(this: Self::Parameterized<'_>) -> Self {
core::mem::transmute(this)
}
unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a> {
&*this.0.cast()
}
unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a> {
core::mem::transmute(this)
}
unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a> {
core::mem::transmute(this)
}
}
impl<'a, T: 'static> Stretchable<'a> for &'a T {
type Stretched = StretchedRef<T>;
}
unsafe impl<T: 'static> Stretched for StretchedMut<T> {
type Parameterized<'a> = &'a mut T;
unsafe fn lengthen(this: Self::Parameterized<'_>) -> Self {
core::mem::transmute(this)
}
unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a> {
this.0.cast().as_mut()
}
unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a> {
core::mem::transmute(this)
}
unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a> {
core::mem::transmute(this)
}
}
impl<'a, T: 'static> Stretchable<'a> for &'a mut T {
type Stretched = StretchedMut<T>;
}
macro_rules! impl_tuple {
($($letter:ident),*) => {
unsafe impl<$($letter: Stretched,)*> Stretched for ($($letter,)*) {
type Parameterized<'a> = ($(<$letter as Stretched>::Parameterized<'a>,)*);
#[allow(non_snake_case, clippy::unused_unit)]
unsafe fn lengthen(this: ($(<$letter as Stretched>::Parameterized<'_>,)*)) -> Self {
let ($($letter,)*) = this;
($($letter::lengthen($letter),)*)
}
#[allow(non_snake_case, clippy::unused_unit)]
unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a> {
let ($($letter,)*) = this;
($($letter::shorten($letter),)*)
}
unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a> {
core::mem::transmute(this)
}
unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a> {
core::mem::transmute(this)
}
}
impl<'a, $($letter: Stretchable<'a>,)*> Stretchable<'a> for ($($letter,)*) {
type Stretched = ($(<$letter as Stretchable<'a>>::Stretched,)*);
}
};
}
macro_rules! russian_tuples {
($m: ident, $ty: tt) => {
$m!{}
$m!{$ty}
};
($m: ident, $ty: tt, $($tt: tt),*) => {
russian_tuples!{$m, $($tt),*}
$m!{$ty, $($tt),*}
};
}
russian_tuples!(impl_tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
unsafe impl<T: Stretched> Stretched for Option<T> {
type Parameterized<'a> = Option<T::Parameterized<'a>>;
unsafe fn lengthen(this: Self::Parameterized<'_>) -> Self {
this.map(|t| unsafe { T::lengthen(t) })
}
unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a> {
this.map(|t| unsafe { T::shorten(t) })
}
unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a> {
core::mem::transmute(this)
}
unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a> {
core::mem::transmute(this)
}
}
impl<'a, T: Stretchable<'a>> Stretchable<'a> for Option<T> {
type Stretched = Option<T::Stretched>;
}