diff --git a/src/ext.rs b/src/ext.rs new file mode 100644 index 0000000..b9f7b5e --- /dev/null +++ b/src/ext.rs @@ -0,0 +1,28 @@ +//! Helper functions & types +use super::*; + +/// Like PhantomData but for a lifetime. Essentially PhantomData &'a () +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Copy)] +#[repr(transparent)] +pub struct PhantomLifetime<'a>(std::marker::PhantomData<&'a ()>); + +impl<'a> PhantomLifetime<'a> +{ + #[inline(always)] + pub const fn new() -> Self { Self (std::marker::PhantomData) } +} + +unsafe impl<'a> Send for PhantomLifetime<'a>{} +unsafe impl<'a> Sync for PhantomLifetime<'a>{} +impl<'a> private::Sealed for PhantomLifetime<'a>{} + +#[inline(always)] +pub unsafe fn address_eq_overlap<'t, 'u, T, U>(a: &'t T, b: &'u U) -> bool +{ + std::ptr::eq(a as *const _, b as *const _ as *const T) +} +#[inline(always)] +pub fn address_eq<'a, 'b, T: ?Sized>(a: &'a T, b: &'b T) -> bool +{ + std::ptr::eq(a as *const _, b as *const _) +} diff --git a/src/lib.rs b/src/lib.rs index 9b3aad5..818baa6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -//#![cfg_attr(all(nightly, feature="nightly"), feature(never_type))] - #![allow(dead_code)] use std::sync::atomic::{ @@ -12,34 +10,16 @@ use std::mem::{self, MaybeUninit}; use std::cell::UnsafeCell; use std::ops::Drop; -pub mod iter; - -/* XXX: We don't need this. We can just use `()` -#[cfg(all(nightly, feature="nightly"))] -type Void = !; -#[cfg(not(all(nightly, feature="nightly")))] -type Void = std::convert::Infallible; -*/ - -/// Like PhantomData but for a lifetime. Essentially PhantomData &'a () -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Copy)] -#[repr(transparent)] -struct PhantomLifetime<'a>(std::marker::PhantomData<&'a ()>); - -impl<'a> PhantomLifetime<'a> -{ - #[inline(always)] - pub const fn new() -> Self { Self (std::marker::PhantomData) } -} - -unsafe impl<'a> Send for PhantomLifetime<'a>{} -unsafe impl<'a> Sync for PhantomLifetime<'a>{} - mod private { pub(crate) trait Sealed{} } +mod ext; use ext::*; + +pub mod iter; +mod refer; pub use refer::*; + /// A parallel, atomic populator of items #[derive(Debug)] pub struct Populator<'a, T: 'a> @@ -51,60 +31,6 @@ pub struct Populator<'a, T: 'a> _lt: PhantomLifetime<'a>, } -#[derive(Debug)] // PartialEq, PartialOrd -pub struct Ref<'re, 'a, T: 'a> -{ - pop: &'re Populator<'a, T>, - idx: usize, - //TODO: Maybe add inserted bool, or state representing if this Ref has made a change to the populator. The value will be loaded on creation of the Ref, and will be used as a cached version of `completes[idx].load()` - //TODO: OR: Hold a reference to the actual AtomicBool at `idx` itself? -} - -#[inline(always)] -unsafe fn address_eq_overlap<'t, 'u, T, U>(a: &'t T, b: &'u U) -> bool -{ - std::ptr::eq(a as *const _, b as *const _ as *const T) -} -#[inline(always)] -fn address_eq<'a, 'b, T: ?Sized>(a: &'a T, b: &'b T) -> bool -{ - std::ptr::eq(a as *const _, b as *const _) -} - -impl<'re, 'a, T: 'a> PartialEq for Ref<'re, 'a, T> -{ - #[inline] - fn eq(&self, other: &Self) -> bool - { - address_eq(self.pop, other.pop) && self.idx == other.idx - } -} - -impl<'re, 'a, T: 'a> PartialOrd for Ref<'re, 'a, T> -{ - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - if address_eq(self.pop, other.pop) { - self.idx.partial_cmp(&other.idx) - } else { - None - } - } -} - -impl<'re, 'a, T: 'a> Ref<'re, 'a, T> -{ - /// Checks if the references item currently exists. - #[inline] - pub fn exists(&self) -> bool - { - self.pop.exists(self.idx) - } - //TODO: Rest, including insertions, etc. -} - -//TODO: RefEx: Exclusive reference, holds &'ref mut Populator<'a, T> - impl<'a, T: 'a> Populator<'a, T> { #[inline(always)] @@ -197,6 +123,12 @@ impl<'a, T> Populator<'a, T> { self.populated.load(atomic::Ordering::Acquire) } + + /// Faster fullness check for when this instance has no other references + #[inline] + pub fn is_full_exclusive(&mut self) -> bool { + *self.populated.get_mut() == self.len() + } /// Is the populator full? #[inline] pub fn is_full(&self) -> bool @@ -207,16 +139,7 @@ impl<'a, T> Populator<'a, T> #[inline] pub fn len(&self) -> usize { - self.values_ref().len() - } - - /// Number of items held by the populator - /// - /// A faster access than normal `len()`, since this is an exclusive reference - #[inline] - pub fn len_exclusive(&mut self) -> usize - { - self.values.get_mut().len() + self.populates.len() } /// Create a new, empty populator with this size @@ -273,11 +196,21 @@ impl<'a, T> Populator<'a, T> #[inline] pub fn get_ref(&self, idx: usize) -> Ref<'_, 'a, T> { + #[inline(never)] + #[cold] + fn _panic_oob(idx: usize, len: usize) -> ! + { + panic!("Cannot reference slot at index {} (length is {})", idx,len) + } + if idx >= self.populates.len() { + _panic_oob(idx, self.populates.len()) + } Ref { pop: self, idx } } + //TODO: get_into(Arc) -> OwnedRef //TODO: get_excusive -> RefEx /// Try to get an exclusive, mutable reference to an item at `idx` if an item exists there. @@ -322,11 +255,6 @@ impl<'a, T> Populator<'a, T> } } - /// Faster fullness check for when this instance has no other references - #[inline] - pub fn is_full_exclusive(&mut self) -> bool { - *self.populated.get_mut() == self.len() - } #[inline(always)] fn take_all(&mut self) -> (Box<[MaybeUninit]>, Box<[AtomicBool]>) diff --git a/src/refer.rs b/src/refer.rs new file mode 100644 index 0000000..df91100 --- /dev/null +++ b/src/refer.rs @@ -0,0 +1,70 @@ +//! Single-index references. +use super::*; + +#[derive(Debug)] // PartialEq, PartialOrd +pub struct Ref<'re, 'a, T: 'a> +{ + pub(super) pop: &'re Populator<'a, T>, + pub(super) idx: usize, + //TODO: Maybe add inserted bool, or state representing if this Ref has made a change to the populator. The value will be loaded on creation of the Ref, and will be used as a cached version of `completes[idx].load()` + //TODO: OR: Hold a reference to the actual AtomicBool at `idx` itself? +} + + +impl<'re, 'a, T: 'a> PartialEq for Ref<'re, 'a, T> +{ + #[inline] + fn eq(&self, other: &Self) -> bool + { + address_eq(self.pop, other.pop) && self.idx == other.idx + } +} + +impl<'re, 'a, T: 'a> PartialOrd for Ref<'re, 'a, T> +{ + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + if address_eq(self.pop, other.pop) { + self.idx.partial_cmp(&other.idx) + } else { + None + } + } +} + +impl<'re, 'a, T: 'a> Ref<'re, 'a, T> +{ + /// The index that this `Ref` refers to. + #[inline] + pub fn slot(&self) -> usize + { + self.idx + } + /// Checks if the references item currently exists. + #[inline] + pub fn exists(&self) -> bool + { + self.pop.exists(self.idx) + } + /// Try to insert `value` at the referred slot. + /// + /// If the slot already has a value, then `Err(value)` is returned, otherwise, `value` is inserted into the table and the number of items now populated is returned. + #[inline] + pub fn try_insert(&self, value: T) -> Result + { + self.pop.try_insert(self.idx, value) + } + /// Insert `value` at the referred slot. + /// + /// # Panics + /// If the slot has a value. + #[inline] + pub fn insert(&self, value: T) -> usize + { + self.pop.insert(self.idx, value) + } + +} + +//TODO: OwnedRef: From Arc receiver of Arc> +//TODO: RefEx: Exclusive reference, holds &'ref mut Populator<'a, T>