Added basic (kinda janky) IntoIter

Fortune for parapop's current commit: Curse − 凶
master
Avril 2 years ago
parent 0443248162
commit 49dd362b5b
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -0,0 +1,148 @@
//! Iterators for `Populator`
use super::*;
use std::iter::{
self,
DoubleEndedIterator,
FusedIterator,
};
use std::fmt::{self, Debug};
#[derive(Debug, Clone)]
struct FullIter<T>(std::vec::IntoIter<T>);
#[derive(Debug)]
struct PartialIter<T>(std::vec::IntoIter<MaybeUninit<T>>,
std::vec::IntoIter<AtomicBool>, usize);
impl<T> Iterator for FullIter<T>
{
type Item = T;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<T> PartialIter<T> {
#[inline(always)]
fn next_raw(&mut self) -> Option<(MaybeUninit<T>, bool)>
{
let a = self.0.next()?;
let b = if cfg!(debug_assertions) { self.1.next()? } else { unsafe { self.1.next().unwrap_unchecked() } };
Some((a, b.into_inner()))
}
}
impl<T> Iterator for PartialIter<T>
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
Some(loop {
match self.next_raw()? {
(value, true) => break unsafe { value.assume_init() },
_ => (),
}
})
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.2, Some(self.2))
}
}
impl<T> FusedIterator for FullIter<T>{}
impl<T> ExactSizeIterator for FullIter<T>{}
impl<T> FusedIterator for PartialIter<T>{}
impl<T> ExactSizeIterator for PartialIter<T>{}
trait PopulaterIter<T>: private::Sealed + Iterator<Item=T> + FusedIterator + ExactSizeIterator{
fn as_debug(&self) -> &dyn Debug
where T: Debug;
}
impl<T> private::Sealed for PartialIter<T>{}
impl<T> private::Sealed for FullIter<T>{}
impl<'a, T: 'a + Debug> Debug for dyn PopulaterIter<T> + 'a {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result
{
self.as_debug().fmt(fmt)
}
}
impl<T> PopulaterIter<T> for PartialIter<T>
{
#[inline(always)]
fn as_debug(&self) -> &dyn Debug
where T: Debug {
self
}
}
impl<T> PopulaterIter<T> for FullIter<T>
{
#[inline(always)]
fn as_debug(&self) -> &dyn Debug
where T: Debug {
self
}
}
#[derive(Debug)]
pub struct IntoIter<'a, T: 'a>(Box<dyn PopulaterIter<T> + 'a>);
/*
impl<'a, Iter, T: 'a> From<Iter> for IntoIter<'a, T>
where Iter: private::Sealed + PopulaterIter<T> + 'a
{
#[inline(always)]
fn from(v: Iter) -> Self {
Self(Box::new(v))
}
}
*/
#[inline(always)]
unsafe fn assume_init_boxed<T>(bx: Box<[MaybeUninit<T>]>) -> Box<[T]>
{
let raw = Box::into_raw(bx);
Box::from_raw(raw as *mut [T])
}
impl<'a, T> IntoIter<'a, T>
{
pub(super) fn create_from(mut pop: Populator<T>) -> Self
{
Self(if pop.is_full_exclusive() {
let values = pop.take_values();
Box::new(FullIter(Vec::from(unsafe { assume_init_boxed(values) }).into_iter()))
} else {
let (values, populates) = pop.take_all();
Box::new(PartialIter(Vec::from(values).into_iter(),
Vec::from(populates).into_iter(),
*pop.populated.get_mut()))
})
}
}
impl<'a, T> Iterator for IntoIter<'a, T>
{
type Item = T;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<'a, T> FusedIterator for IntoIter<'a, T>{}
impl<'a, T> ExactSizeIterator for IntoIter<'a, T>{}

@ -10,27 +10,69 @@ use std::mem::{self, MaybeUninit};
use std::cell::UnsafeCell;
use std::ops::Drop;
pub mod iter;
use iter::*;
mod private
{
pub(crate) trait Sealed{}
}
/// A parallel, atomic populator of items
pub struct Populator<T>
{
values: Box<[UnsafeCell<MaybeUninit<T>>]>,
values: UnsafeCell<Box<[MaybeUninit<T>]>>,
populates: Box<[AtomicBool]>, //
populated: AtomicUsize, // number of populated items
}
impl<T> Populator<T>
{
#[inline(always)]
fn values_mut(&mut self) -> &mut [MaybeUninit<T>]
{
self.values.get_mut()
}
#[inline(always)]
fn values_ref(&self) -> &[MaybeUninit<T>]
{
let ptr = self.values.get() as *const Box<[_]>;
unsafe {
&(*ptr)[..]
}
}
#[inline(always)]
fn get_mut_ptr(&self, idx: usize) -> *mut MaybeUninit<T>
{
let ptr = self.values.get();
unsafe {
&mut (*ptr)[idx] as *mut _
}
}
}
impl<T> Drop for Populator<T>
{
fn drop(&mut self)
{
if mem::needs_drop::<T>() {
if self.populated() == self.values.len() {
let len = self.values_ref().len();
if *self.populated.get_mut() == len {
// Fully populated, drop whole slice in place
unsafe {
std::ptr::drop_in_place(self.values.as_mut() as *mut [UnsafeCell<MaybeUninit<T>>] as *mut [T])
std::ptr::drop_in_place( self.values_mut() as *mut [MaybeUninit<T>] as *mut [T])
}
} else if self.values.len() > 0 { // If values is 0, then that means `[try_]complete()` has been called.
} else if len > 0 { // If values is 0, then that means `[try_]complete()` has been called.
// Partially populated, drop individual parts
for value in self.values.iter_mut().zip(self.populates.iter().map(|x| x.load(atomic::Ordering::Acquire))).filter_map(|(v, prod)| prod.then(move || v.get_mut().as_mut_ptr()))
for value in self.values.get_mut().iter_mut()
.zip(self.populates.iter()
.map(|x| x.load(atomic::Ordering::Acquire)))
.filter_map(|(v, prod)|
prod.then(move ||
v.as_mut_ptr()))
{
unsafe {
std::ptr::drop_in_place(value)
@ -47,6 +89,7 @@ unsafe impl<T> Sync for Populator<T>{} // Populator is always sync
impl<T> Populator<T>
{
/// How many items are populated
pub fn populated(&self) -> usize
{
@ -55,23 +98,24 @@ impl<T> Populator<T>
/// Is the populator full?
pub fn is_full(&self) -> bool
{
self.populated() == self.values.len()
self.populated() == self.len()
}
/// Number of items held by the populator
#[inline]
pub fn len(&self) -> usize
{
self.values.len()
self.values_ref().len()
}
/// Create a new, empty populator with this size
pub fn new(size: usize) -> Self
{
Self {
// SAFETY: MaybeUninit is not Copy, so instead we allocate the space for uninitialised memory and then .set_len().
values: unsafe {
values: UnsafeCell::new(unsafe {
let mut uninit = Vec::with_capacity(size);
uninit.set_len(size);
uninit
}.into_boxed_slice(),
}.into_boxed_slice()),
populates: std::iter::repeat_with(|| false.into()).take(size).collect(),
populated: 0usize.into(),
}
@ -88,7 +132,7 @@ impl<T> Populator<T>
if cfg!(debug_assertions) {
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let ptr = self.values[idx].get();
let ptr = self.get_mut_ptr(idx); //self.values[idx].get();
unsafe {
*ptr = MaybeUninit::new(value);
}
@ -100,7 +144,7 @@ impl<T> Populator<T>
// SAFETY: This operation will never panic, since `values` and `populates` are always the same size
// SAFETY: We have already ensured that `values[idx]` does not contain a value.
unsafe {
*self.values[idx].get() = MaybeUninit::new(value);
*self.get_mut_ptr(idx) = MaybeUninit::new(value);
}
}
// Value is inserted, increment `populated`
@ -129,11 +173,36 @@ impl<T> Populator<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<T>]>, Box<[AtomicBool]>)
{
let inner = self.values.get_mut();
(mem::replace(inner, vec![].into_boxed_slice()),
mem::replace(&mut self.populates, vec![].into_boxed_slice()))
}
#[inline(always)]
fn take_values(&mut self) -> Box<[MaybeUninit<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<Box<[T]>, Self>
{
if *self.populated.get_mut() == self.values.len() {
let ptr = Box::into_raw(std::mem::replace(&mut self.values, vec![].into_boxed_slice()));
if *self.populated.get_mut() == self.len() {
//let ptr = Box::into_raw(std::mem::replace(&mut self.values, UnsafeCell::new(vec![].into_boxed_slice())).into_inner());
let ptr = {
let inner = self.values.get_mut();
Box::into_raw(mem::replace(inner, vec![].into_boxed_slice()))
};
Ok(unsafe {
Box::from_raw(ptr as *mut [T])
})
@ -162,6 +231,17 @@ impl<T> Populator<T>
}
}
impl<T: 'static> IntoIterator for Populator<T> // FUCK why do we need to make this 'static???? fuck this... dyn dispatch in rust is so jank. why can't we use 'a!!!
{
type Item = T;
type IntoIter = iter::IntoIter<'static, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
iter::IntoIter::create_from(self)
}
}
#[cfg(test)]
mod tests {
#[test]

Loading…
Cancel
Save