diff --git a/src/iter.rs b/src/iter.rs index 13ed723..95139cb 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -66,6 +66,7 @@ impl ExactSizeIterator for PartialIter{} trait PopulaterIter: private::Sealed + Iterator + FusedIterator + ExactSizeIterator{ fn as_debug(&self) -> &dyn Debug where T: Debug; + fn is_complete(&self) -> bool; } impl private::Sealed for PartialIter{} @@ -85,6 +86,10 @@ impl PopulaterIter for PartialIter where T: Debug { self } + #[inline(always)] + fn is_complete(&self) -> bool { + false + } } impl PopulaterIter for FullIter @@ -94,8 +99,16 @@ impl PopulaterIter for FullIter where T: Debug { self } + #[inline(always)] + fn is_complete(&self) -> bool { + false + } } +/// An iterator over a `Populator<'a, T>`'s items. +/// +/// # Length +/// It is a full iterator of all completed elements, non-inserted elements are ignored. #[derive(Debug)] pub struct IntoIter<'a, T: 'a>(Box + 'a>); @@ -103,9 +116,9 @@ pub struct IntoIter<'a, T: 'a>(Box + 'a>); impl<'a, Iter, T: 'a> From for IntoIter<'a, T> where Iter: private::Sealed + PopulaterIter + 'a { - #[inline(always)] - fn from(v: Iter) -> Self { - Self(Box::new(v)) +#[inline(always)] +fn from(v: Iter) -> Self { +Self(Box::new(v)) } } */ @@ -118,8 +131,28 @@ unsafe fn assume_init_boxed(bx: Box<[MaybeUninit]>) -> Box<[T]> Box::from_raw(raw as *mut [T]) } +#[inline(always)] +pub(crate) unsafe fn assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [T] +{ + //MaybeUninit::slice_assume_init_ref(slice) + &mut (*(slice as *mut [MaybeUninit] as *mut [T]))[..] +} + +#[inline(always)] +pub(crate) unsafe fn assume_init_ref(slice: &[MaybeUninit]) -> &[T] +{ + //MaybeUninit::slice_assume_init_ref(slice) + &(*(slice.as_ref() as *const [MaybeUninit] as *const [T]))[..] +} + impl<'a, T> IntoIter<'a, T> { + /// Does this iterator represent a fully completed `Populator<'a, T>`? + #[inline] + pub fn is_complete(&self) -> bool + { + self.0.as_ref().is_complete() + } pub(super) fn create_from(mut pop: Populator<'a, T>) -> Self { Self(if pop.is_full_exclusive() { diff --git a/src/lib.rs b/src/lib.rs index ae623c3..beb28e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ mod refer; pub use refer::*; pub struct Populator<'a, T: 'a> { values: UnsafeCell]>>, - populates: Box<[AtomicBool]>, // + pub(crate) populates: Box<[AtomicBool]>, // populated: AtomicUsize, // number of populated items _lt: PhantomLifetime<'a>, @@ -159,7 +159,42 @@ impl<'a, T> Populator<'a, T> _lt: PhantomLifetime::new(), } } - + + /// Try to insert `value` at `idx` from an exclusive reference. + /// + /// If `idx` already has a value, then `Err(value)` is returned, otherwise, `value` is inserted into the table and a mutable reference to it is returned. + /// + /// # Exclusive accesses + /// Since this is an `&mut self` receiver, no atomic operations are required since there are no other threads with a reference to the current populator. + pub fn try_insert_exclusive(&mut self, idx: usize, value: T) -> Result<&mut T, T> + { + match self.populates[idx].get_mut() { + &mut true => return Err(value), + none => *none = true, + }; + if cfg!(debug_assertions) { + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let mref = &mut self.values.get_mut()[idx]; + *mref = MaybeUninit::new(value); + unsafe { + mref.assume_init_mut() + } + })) { + Err(p) => { + *self.populates[idx].get_mut() = false; + std::panic::resume_unwind(p) + }, + Ok(mref) => Ok(mref), + } + } else { + let mref = &mut self.values.get_mut()[idx]; + *mref = MaybeUninit::new(value); + Ok(unsafe { + mref.assume_init_mut() + }) + } + } + /// Try to insert `value` at `idx`. /// /// If `idx` 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. @@ -176,7 +211,10 @@ impl<'a, T> Populator<'a, T> *ptr = MaybeUninit::new(value); } })) { - Err(p) => std::panic::resume_unwind(p), + Err(p) => { + self.populates[idx].store(false, atomic::Ordering::SeqCst); // Re-set to unoccupied + std::panic::resume_unwind(p) + }, Ok(_) => (), } } else { @@ -192,7 +230,6 @@ impl<'a, T> Populator<'a, T> Err(value) } } - #[inline(always)] fn bounds_okay(&self, idx: usize) -> bool @@ -224,6 +261,7 @@ impl<'a, T> Populator<'a, T> self.bounds_check(idx); Ref { pop: self, + inserted: &self.populates[idx], idx } } @@ -235,12 +273,29 @@ impl<'a, T> Populator<'a, T> #[inline] pub fn into_ref(self: Arc, idx: usize) -> OwnedRef<'a, T> { + self.bounds_check(idx); OwnedRef { pop: self, idx } } - //TODO: get_excusive -> RefEx + + /// Get an exclusive reference for the item at `idx` whether it exists or not. + /// + /// # Panics + /// If the index is out of bounds + /// + /// # Exclusivity + /// No atomic operations are performed on the returned reference, since as long as it exists, no other reference to this instance can exist. + #[inline] + pub fn get_ref_exclusive(&mut self, idx: usize) -> RefEx<'_, 'a, T> + { + self.bounds_check(idx); + RefEx { + pop: self, + idx, + } + } /// Try to get an exclusive, mutable reference to an item at `idx` if an item exists there. /// @@ -268,7 +323,7 @@ impl<'a, T> Populator<'a, T> /// /// # Panics /// If `idx` already has a value inserted. - #[inline] // Maybe? + #[inline] pub fn insert(&self, idx: usize, value: T) -> usize { #[inline(never)] @@ -283,8 +338,30 @@ impl<'a, T> Populator<'a, T> Err(_) => panic_inserted(idx), } } + + /// Insert `value` into `idx` through an exclusive reference. + /// + /// # Panics + /// If `idx` already has a value inserted. + /// + /// # Exclusivity + /// No atomic operations are required since the `&mut self` receiver guarantees no other references to this instance currently exist. + #[inline] + pub fn insert_exclusive(&mut self, idx: usize, value: T) -> &mut T + { + #[inline(never)] + #[cold] + fn panic_inserted(i: usize) -> ! + { + panic!("There is already a value at {}", i) + } - + match self.try_insert_exclusive(idx, value) { + Ok(v) => v, + Err(_) => panic_inserted(idx), + } + } + #[inline(always)] fn take_all(&mut self) -> (Box<[MaybeUninit]>, Box<[AtomicBool]>) { @@ -299,6 +376,8 @@ impl<'a, T> Populator<'a, T> let inner = self.values.get_mut(); mem::replace(inner, vec![].into_boxed_slice()) } + + /// If all values are populated, then convert it into a boxed slice and return it. pub fn try_complete(mut self) -> Result, Self> @@ -317,6 +396,30 @@ impl<'a, T> Populator<'a, T> } } + /// If all values are populated, returns a slice of all the elements. + /// + /// Performs a single atomic load of the number of currently inserted elements to check for completion + pub fn try_completed_ref(&self) -> Option<&[T]> + { + if self.populated.load(atomic::Ordering::SeqCst) == self.len() { + Some(unsafe { iter::assume_init_ref(&*self.values.get()) }) + } else { + None + } + } + + /// If all values are populated, returns a mutable slice of all the elements. + /// + /// # Exclusivity + /// No atomic operations are required since this exclusive reference to `self` ensures there are no other references to this instance. + pub fn try_completed_mut(&mut self) -> Option<&mut [T]> + { + if *self.populated.get_mut() == self.len() { + Some(unsafe { iter::assume_init_mut(self.values.get_mut()) }) + } else { + None + } + } /// Returns the completed population. /// /// # Panics diff --git a/src/refer.rs b/src/refer.rs index 09b1dce..b254059 100644 --- a/src/refer.rs +++ b/src/refer.rs @@ -8,6 +8,7 @@ pub struct Ref<'re, 'a, T: 'a> 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? + pub(super) inserted: &'re AtomicBool } #[derive(Debug, Clone)] // PartialEq, PartialOrd //XXX: TODO: Should this actually be `Clone`? Ref isn't, because its supposed to be a single reference. But since this is arc'd? @@ -17,6 +18,12 @@ pub struct OwnedRef<'a, T: 'a> // PartialEq, PartialOrd pub(super) idx: usize, } +#[derive(Debug)] +pub struct RefEx<'re, 'a, T: 'a> +{ + pub(super) pop: &'re mut Populator<'a, T>, + pub(super) idx: usize, +} impl<'a, T: 'a> PartialEq for OwnedRef<'a, T> { @@ -78,7 +85,7 @@ impl<'re, 'a, T: 'a> Ref<'re, 'a, T> #[inline] pub fn exists(&self) -> bool { - self.pop.exists(self.idx) + self.inserted.load(atomic::Ordering::SeqCst) } /// Try to insert `value` at the referred slot. /// @@ -166,6 +173,107 @@ impl<'a, T:'a> OwnedRef<'a, T> } } +//RefEx: Exclusive reference, holds &'re mut Populator<'a, T> +impl<'re, 'a, T: 'a> RefEx<'re, 'a ,T> +{ + /// Get a reference to the parent + #[inline] + pub fn parent(&self) -> &Populator<'a, T> + { + &self.pop + } + /// Get a mutable reference to the parent + #[inline] + pub fn parent_mut(&mut self) -> &mut Populator<'a, T> + { + self.pop + } + /// The index that this `RefEx` refers to. + #[inline] + pub fn slot(&self) -> usize + { + self.idx + } + /// Checks if the references item currently exists. + #[inline] + pub fn exists(&mut self) -> bool + { + self.pop.exists_exclusive(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 a reference to the new element is returned + #[inline] + pub fn try_insert(&mut self, value: T) -> Result<&T, T> + { + self.pop.try_insert_exclusive(self.idx, value).map(|x| &*x) + } + /// 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 a mutable reference to the new element is returned. + #[inline] + pub fn try_insert_mut(&mut self, value: T) -> Result<&mut T, T> + { + self.pop.try_insert_exclusive(self.idx, value) + } + /// Insert `value` at the referred slot and returns a reference to the inserted element. + /// + /// # Panics + /// If the slot has a value. + #[inline] + pub fn insert(&mut self, value: T) -> &T + { + &*self.pop.insert_exclusive(self.idx, value) + } + /// Insert `value` at the referred slot and returns a reference to the inserted element. + /// + /// # Panics + /// If the slot has a value. + #[inline] + pub fn insert_mut(&mut self, value: T) -> &mut T + { + self.pop.insert_exclusive(self.idx, value) + } + + /// Consume into the inner populator and slot index + #[inline] + pub fn into_parts(self) -> (&'re mut Populator<'a, T>, usize) + { + (self.pop, self.idx) + } + + /// Consume into the inner populator + #[inline] + pub fn into_inner(self) -> &'re mut Populator<'a, T> + { + self.pop + } +} + +impl<'re, 'a, T: 'a> From<(&'re Populator<'a, T>, usize)> for Ref<'re, 'a, T> +{ + #[inline] + fn from((pop, idx): (&'re Populator<'a, T>, usize)) -> Self + { + Self { + pop, + idx, + inserted: &pop.populates[idx] + } + } +} + +impl<'re, 'a, T: 'a> From> for (&'re Populator<'a, T>, usize) +{ + #[inline] + fn from(from: Ref<'re, 'a, T>) -> Self + { + (from.pop, from.idx) + } +} + + + impl<'a, T: 'a> From<(Arc>, usize)> for OwnedRef<'a, T> { #[inline] @@ -185,4 +293,19 @@ impl<'a, T: 'a> From> for (Arc>, usize) } } -//TODO: RefEx: Exclusive reference, holds &'re mut Populator<'a, T> +impl<'re, 'a, T: 'a> From> for (&'re mut Populator<'a, T>, usize) +{ + #[inline] + fn from(from: RefEx<'re, 'a, T>) -> Self { + (from.pop, from.idx) + } +} + +impl<'re, 'a, T: 'a> From<(&'re mut Populator<'a, T>, usize)> for RefEx<'re, 'a, T> +{ + #[inline] + fn from((pop, idx): (&'re mut Populator<'a, T>, usize)) -> Self + { + Self { pop, idx } + } +}