#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
use parking_lot::Mutex;
use std::{
fmt::Debug,
marker::PhantomData,
num::NonZeroU64,
ops::{Deref, DerefMut},
sync::Arc,
};
pub use error::*;
pub use references::*;
pub use sync::SyncStorage;
pub use unsync::UnsyncStorage;
mod entry;
mod error;
mod references;
mod sync;
mod unsync;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct GenerationalBoxId {
data_ptr: *const (),
generation: NonZeroU64,
}
unsafe impl Send for GenerationalBoxId {}
unsafe impl Sync for GenerationalBoxId {}
impl Debug for GenerationalBoxId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{:?}@{:?}", self.data_ptr, self.generation))?;
Ok(())
}
}
pub struct GenerationalBox<T, S: 'static = UnsyncStorage> {
raw: GenerationalPointer<S>,
_marker: PhantomData<T>,
}
impl<T, S: AnyStorage> Debug for GenerationalBox<T, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.raw.fmt(f)
}
}
impl<T, S: Storage<T>> GenerationalBox<T, S> {
#[track_caller]
pub fn leak(value: T, location: &'static std::panic::Location<'static>) -> Self {
let location = S::new(value, location);
Self {
raw: location,
_marker: PhantomData,
}
}
#[track_caller]
pub fn leak_rc(value: T, location: &'static std::panic::Location<'static>) -> Self {
let location = S::new_rc(value, location);
Self {
raw: location,
_marker: PhantomData,
}
}
pub fn raw_ptr(&self) -> *const () {
self.raw.storage.data_ptr()
}
pub fn id(&self) -> GenerationalBoxId {
self.raw.id()
}
#[track_caller]
pub fn try_read(&self) -> Result<S::Ref<'static, T>, BorrowError> {
self.raw.try_read()
}
#[track_caller]
pub fn read(&self) -> S::Ref<'static, T> {
self.try_read().unwrap()
}
#[track_caller]
pub fn try_write(&self) -> Result<S::Mut<'static, T>, BorrowMutError> {
self.raw.try_write()
}
#[track_caller]
pub fn write(&self) -> S::Mut<'static, T> {
self.try_write().unwrap()
}
#[track_caller]
pub fn set(&self, value: T)
where
T: 'static,
{
*self.write() = value;
}
pub fn ptr_eq(&self, other: &Self) -> bool {
self.raw == other.raw
}
pub fn manually_drop(&self)
where
T: 'static,
{
self.raw.recycle();
}
pub fn created_at(&self) -> Option<&'static std::panic::Location<'static>> {
self.raw.location.created_at()
}
#[track_caller]
pub fn leak_reference(&self) -> BorrowResult<GenerationalBox<T, S>> {
Ok(Self {
raw: S::new_reference(self.raw)?,
_marker: std::marker::PhantomData,
})
}
pub fn point_to(&self, other: GenerationalBox<T, S>) -> BorrowResult {
S::change_reference(self.raw, other.raw)
}
}
impl<T, S> Copy for GenerationalBox<T, S> {}
impl<T, S> Clone for GenerationalBox<T, S> {
fn clone(&self) -> Self {
*self
}
}
pub trait Storage<Data = ()>: AnyStorage + 'static {
fn try_read(pointer: GenerationalPointer<Self>) -> BorrowResult<Self::Ref<'static, Data>>;
fn try_write(pointer: GenerationalPointer<Self>) -> BorrowMutResult<Self::Mut<'static, Data>>;
fn new(
value: Data,
caller: &'static std::panic::Location<'static>,
) -> GenerationalPointer<Self>;
fn new_rc(
value: Data,
caller: &'static std::panic::Location<'static>,
) -> GenerationalPointer<Self>;
fn new_reference(inner: GenerationalPointer<Self>) -> BorrowResult<GenerationalPointer<Self>>;
fn change_reference(
pointer: GenerationalPointer<Self>,
rc_pointer: GenerationalPointer<Self>,
) -> BorrowResult;
}
pub trait AnyStorage: Default + 'static {
type Ref<'a, T: ?Sized + 'static>: Deref<Target = T>;
type Mut<'a, T: ?Sized + 'static>: DerefMut<Target = T>;
fn downcast_lifetime_ref<'a: 'b, 'b, T: ?Sized + 'static>(
ref_: Self::Ref<'a, T>,
) -> Self::Ref<'b, T>;
fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
mut_: Self::Mut<'a, T>,
) -> Self::Mut<'b, T>;
fn try_map_mut<T: ?Sized + 'static, U: ?Sized + 'static>(
mut_ref: Self::Mut<'_, T>,
f: impl FnOnce(&mut T) -> Option<&mut U>,
) -> Option<Self::Mut<'_, U>>;
fn map_mut<T: ?Sized + 'static, U: ?Sized + 'static>(
mut_ref: Self::Mut<'_, T>,
f: impl FnOnce(&mut T) -> &mut U,
) -> Self::Mut<'_, U> {
Self::try_map_mut(mut_ref, |v| Some(f(v))).unwrap()
}
fn try_map<T: ?Sized, U: ?Sized + 'static>(
ref_: Self::Ref<'_, T>,
f: impl FnOnce(&T) -> Option<&U>,
) -> Option<Self::Ref<'_, U>>;
fn map<T: ?Sized, U: ?Sized + 'static>(
ref_: Self::Ref<'_, T>,
f: impl FnOnce(&T) -> &U,
) -> Self::Ref<'_, U> {
Self::try_map(ref_, |v| Some(f(v))).unwrap()
}
fn data_ptr(&self) -> *const ();
fn recycle(location: GenerationalPointer<Self>);
fn owner() -> Owner<Self> {
Owner(Arc::new(Mutex::new(OwnerInner {
owned: Default::default(),
})))
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct GenerationalLocation {
generation: NonZeroU64,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at: &'static std::panic::Location<'static>,
}
impl GenerationalLocation {
pub(crate) fn created_at(&self) -> Option<&'static std::panic::Location<'static>> {
#[cfg(debug_assertions)]
{
Some(self.created_at)
}
#[cfg(not(debug_assertions))]
{
None
}
}
}
pub struct GenerationalPointer<S: 'static = UnsyncStorage> {
storage: &'static S,
location: GenerationalLocation,
}
impl<S: AnyStorage + 'static> PartialEq for GenerationalPointer<S> {
fn eq(&self, other: &Self) -> bool {
self.storage.data_ptr() == other.storage.data_ptr()
&& self.location.generation == other.location.generation
}
}
impl<S: AnyStorage + 'static> Debug for GenerationalPointer<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{:?}@{:?}",
self.storage.data_ptr(),
self.location.generation
))
}
}
impl<S: 'static> Clone for GenerationalPointer<S> {
fn clone(&self) -> Self {
*self
}
}
impl<S: 'static> Copy for GenerationalPointer<S> {}
impl<S> GenerationalPointer<S> {
#[track_caller]
fn try_read<T>(self) -> Result<S::Ref<'static, T>, BorrowError>
where
S: Storage<T>,
{
S::try_read(self)
}
#[track_caller]
fn try_write<T>(self) -> Result<S::Mut<'static, T>, BorrowMutError>
where
S: Storage<T>,
{
S::try_write(self)
}
fn recycle(self)
where
S: AnyStorage,
{
S::recycle(self);
}
fn id(&self) -> GenerationalBoxId
where
S: AnyStorage,
{
GenerationalBoxId {
data_ptr: self.storage.data_ptr(),
generation: self.location.generation,
}
}
}
struct OwnerInner<S: AnyStorage + 'static> {
owned: Vec<GenerationalPointer<S>>,
}
impl<S: AnyStorage> Drop for OwnerInner<S> {
fn drop(&mut self) {
for location in self.owned.drain(..) {
location.recycle();
}
}
}
pub struct Owner<S: AnyStorage + 'static = UnsyncStorage>(Arc<Mutex<OwnerInner<S>>>);
impl<S: AnyStorage> Default for Owner<S> {
fn default() -> Self {
S::owner()
}
}
impl<S: AnyStorage> Clone for Owner<S> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<S: AnyStorage> Owner<S> {
#[track_caller]
pub fn insert<T: 'static>(&self, value: T) -> GenerationalBox<T, S>
where
S: Storage<T>,
{
self.insert_with_caller(value, std::panic::Location::caller())
}
#[track_caller]
pub fn insert_rc<T: 'static>(&self, value: T) -> GenerationalBox<T, S>
where
S: Storage<T>,
{
self.insert_rc_with_caller(value, std::panic::Location::caller())
}
pub fn insert_rc_with_caller<T: 'static>(
&self,
value: T,
caller: &'static std::panic::Location<'static>,
) -> GenerationalBox<T, S>
where
S: Storage<T>,
{
let location = S::new_rc(value, caller);
self.0.lock().owned.push(location);
GenerationalBox {
raw: location,
_marker: std::marker::PhantomData,
}
}
pub fn insert_with_caller<T: 'static>(
&self,
value: T,
caller: &'static std::panic::Location<'static>,
) -> GenerationalBox<T, S>
where
S: Storage<T>,
{
let location = S::new(value, caller);
self.0.lock().owned.push(location);
GenerationalBox {
raw: location,
_marker: PhantomData,
}
}
#[track_caller]
pub fn insert_reference<T: 'static>(
&self,
other: GenerationalBox<T, S>,
) -> BorrowResult<GenerationalBox<T, S>>
where
S: Storage<T>,
{
let location = other.leak_reference()?;
self.0.lock().owned.push(location.raw);
Ok(location)
}
}