diff --git a/Cargo.toml b/Cargo.toml index 5fab3cf..9b5b85c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "orx-split-vec" -version = "2.14.0" +version = "3.0.0" edition = "2021" authors = ["orxfun "] description = "An efficient constant access time vector with dynamic capacity and pinned elements." @@ -10,7 +10,8 @@ keywords = ["vec", "array", "split", "fragments", "pinned"] categories = ["data-structures", "rust-patterns"] [dependencies] -orx-pinned-vec = "2.12" +orx-pseudo-default = "1.0" +orx-pinned-vec = "3.0" [[bench]] name = "serial_access" diff --git a/README.md b/README.md index 8110930..afe441e 100644 --- a/README.md +++ b/README.md @@ -103,12 +103,19 @@ use orx_split_vec::*; #[derive(Clone)] struct MyCustomGrowth; + impl Growth for MyCustomGrowth { - fn new_fragment_capacity(&self, fragments: &[Fragment]) -> usize { - fragments.last().map(|f| f.capacity() + 1).unwrap_or(4) + fn new_fragment_capacity_from(&self, fragment_capacities: impl ExactSizeIterator) -> usize { + fragment_capacities.last().map(|f| f + 1).unwrap_or(4) } } +impl PseudoDefault for MyCustomGrowth { + fn pseudo_default() -> Self { + MyCustomGrowth + } +} + // set the growth explicitly let vec: SplitVec = SplitVec::with_linear_growth(4); let vec: SplitVec = SplitVec::with_doubling_growth(); diff --git a/src/common_traits/iterator/iter_con.rs b/src/common_traits/iterator/iter_con.rs new file mode 100644 index 0000000..0de61a2 --- /dev/null +++ b/src/common_traits/iterator/iter_con.rs @@ -0,0 +1,69 @@ +use super::iter_fragment::FragmentIter; +use crate::fragment::fragment_struct::Fragment; +use std::iter::FusedIterator; + +/// Iterator over the `SplitVec`. +/// +/// This struct is created by `SplitVec::iter()` method. +#[derive(Debug, Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct IterCon<'a, T> { + num_fragments: usize, + last_fragment_len: usize, + fragments: &'a [Fragment], + inner: FragmentIter<'a, T>, + f: usize, +} + +impl<'a, T> IterCon<'a, T> { + pub(crate) fn new(fragments: &'a [Fragment], last_fragment_len: usize) -> Self { + assert!(fragments.len() > 0); + + let num_fragments = fragments.len(); + + let first_fragment_len = match num_fragments { + 0 => 0, + 1 => last_fragment_len, + _ => fragments[0].capacity(), + }; + let inner = FragmentIter::new(&fragments[0], first_fragment_len); + + Self { + num_fragments, + last_fragment_len, + fragments, + inner, + f: 0, + } + } + + fn next_fragment(&mut self) -> Option<&'a T> { + match self.f + 1 < self.num_fragments { + true => { + self.f += 1; + let fragment_len = match self.f == self.num_fragments - 1 { + false => self.fragments[self.f].capacity(), + true => self.last_fragment_len, + }; + self.inner = FragmentIter::new(&self.fragments[self.f], fragment_len); + self.next() + } + false => None, + } + } +} + +impl<'a, T> Iterator for IterCon<'a, T> { + type Item = &'a T; + + #[inline(always)] + fn next(&mut self) -> Option { + let next_element = self.inner.next(); + match next_element.is_some() { + true => next_element, + false => self.next_fragment(), + } + } +} + +impl FusedIterator for IterCon<'_, T> {} diff --git a/src/common_traits/iterator/iter_fragment.rs b/src/common_traits/iterator/iter_fragment.rs new file mode 100644 index 0000000..145a61f --- /dev/null +++ b/src/common_traits/iterator/iter_fragment.rs @@ -0,0 +1,35 @@ +use crate::Fragment; +use std::marker::PhantomData; + +#[derive(Debug, Clone)] +pub(crate) struct FragmentIter<'a, T> { + fragment: &'a [T], + i: usize, + phantom: PhantomData<&'a ()>, +} + +impl<'a, T> FragmentIter<'a, T> { + pub(crate) fn new(fragment: &'a Fragment, len: usize) -> Self { + let fragment = unsafe { std::slice::from_raw_parts(fragment.as_ptr(), len) }; + Self { + fragment, + i: 0, + phantom: PhantomData, + } + } +} + +impl<'a, T: 'a> Iterator for FragmentIter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + match self.i < self.fragment.len() { + true => { + let element = unsafe { self.fragment.get_unchecked(self.i) }; + self.i += 1; + Some(element) + } + false => None, + } + } +} diff --git a/src/common_traits/iterator/mod.rs b/src/common_traits/iterator/mod.rs index bbaa4ed..bf41ce9 100644 --- a/src/common_traits/iterator/mod.rs +++ b/src/common_traits/iterator/mod.rs @@ -2,6 +2,8 @@ mod eq; mod from_iter; pub(crate) mod into_iter; pub(crate) mod iter; +pub(crate) mod iter_con; +mod iter_fragment; pub(crate) mod iter_mut; pub(crate) mod iter_mut_rev; pub(crate) mod iter_rev; diff --git a/src/concurrent_pinned_vec.rs b/src/concurrent_pinned_vec.rs new file mode 100644 index 0000000..5420056 --- /dev/null +++ b/src/concurrent_pinned_vec.rs @@ -0,0 +1,360 @@ +use crate::{ + common_traits::iterator::iter_con::IterCon, + fragment::fragment_struct::{ + maximum_concurrent_capacity, num_fragments_for_capacity, set_fragments_len, + }, + range_helpers::{range_end, range_start}, + Doubling, Fragment, Growth, GrowthWithConstantTimeAccess, SplitVec, +}; +use orx_pinned_vec::{ConcurrentPinnedVec, PinnedVec}; +use std::{ + ops::RangeBounds, + sync::atomic::{AtomicUsize, Ordering}, +}; + +/// Concurrent wrapper ([`orx_pinned_vec::ConcurrentPinnedVec`]) for the `SplitVec`. +pub struct ConcurrentSplitVec { + capacity: AtomicUsize, + maximum_capacity: usize, + num_fragments: AtomicUsize, + growth: G, + fragments: Vec>, + ptr_fragments: *mut Fragment, + fragment_pointers: Vec<*const T>, + ptr_fragments_pointers: *const *const T, +} + +impl Drop for ConcurrentSplitVec { + fn drop(&mut self) { + unsafe { self.fragments.set_len(self.num_fragments()) }; + } +} + +impl ConcurrentSplitVec { + fn num_fragments(&self) -> usize { + self.num_fragments.load(Ordering::Relaxed) + } + + fn fragments(&self) -> &[Fragment] { + let len = self.num_fragments(); + self.fragments_for(len) + } + + fn fragments_for(&self, num_fragments: usize) -> &[Fragment] { + unsafe { std::slice::from_raw_parts(self.ptr_fragments, num_fragments) } + } + + fn push_fragment(&self, fragment: Fragment, fragment_index: usize) { + let p = unsafe { self.fragment_pointers.as_ptr().add(fragment_index) }; + let p = p as *mut *const T; + unsafe { p.write(fragment.as_ptr()) }; + unsafe { self.ptr_fragments.add(fragment_index).write(fragment) }; + } + + fn fragment_element_ptr_mut(&self, f: usize, i: usize) -> *mut T { + let p = unsafe { self.ptr_fragments_pointers.add(f).read() }; + let p = unsafe { p.add(i) }; + return p as *mut T; + } + + fn fragment_element_ptr(&self, f: usize, i: usize) -> *const T { + let p = unsafe { self.ptr_fragments_pointers.add(f).read() }; + return unsafe { p.add(i) }; + } +} + +fn get_pointers( + fragments: &[Fragment], + fragments_capacity: usize, +) -> (Vec<*const T>, *const *const T) { + let first = fragments[0].as_ptr(); + let mut fragment_pointers = vec![first; fragments_capacity]; + for (f, fragment) in fragments.iter().enumerate() { + fragment_pointers[f] = fragment.as_ptr(); + } + let ptr_fragments_pointers = fragment_pointers.as_ptr(); + + (fragment_pointers, ptr_fragments_pointers) +} + +impl From> for ConcurrentSplitVec { + fn from(value: SplitVec) -> Self { + let (mut fragments, growth) = (value.fragments, value.growth); + + let data = data(&mut fragments, &growth); + + Self { + capacity: data.capacity.into(), + maximum_capacity: data.maximum_capacity, + num_fragments: data.num_fragments.into(), + growth, + fragments, + ptr_fragments: data.ptr_fragments, + fragment_pointers: data.fragment_pointers, + ptr_fragments_pointers: data.ptr_fragments_pointers, + } + } +} + +impl ConcurrentPinnedVec for ConcurrentSplitVec { + type P = SplitVec; + + unsafe fn into_inner(mut self, len: usize) -> Self::P { + self.fragments.set_len(self.num_fragments()); + + let mut fragments = vec![]; + std::mem::swap(&mut fragments, &mut self.fragments); + set_fragments_len(&mut fragments, len); + + self.num_fragments.store(0, Ordering::Relaxed); + self.capacity.store(0, Ordering::Relaxed); + + let growth = self.growth.clone(); + + // let (mut fragments, growth) = (self.fragments, self.growth.clone()); + SplitVec::from_raw_parts(len, fragments, growth) + } + + fn capacity(&self) -> usize { + self.capacity.load(Ordering::SeqCst) + } + + fn max_capacity(&self) -> usize { + self.maximum_capacity + } + + fn grow_to(&self, new_capacity: usize) -> Result { + let capacity = self.capacity(); + match new_capacity <= capacity { + true => Ok(capacity), + false => { + let mut num_fragments = self.num_fragments(); + + let mut current_capacity = capacity; + + while new_capacity > current_capacity { + let new_fragment_capacity = self + .growth + .new_fragment_capacity(self.fragments_for(num_fragments)); + let new_fragment = Fragment::new(new_fragment_capacity); + + self.push_fragment(new_fragment, num_fragments); + + num_fragments += 1; + current_capacity += new_fragment_capacity; + } + + self.num_fragments.store(num_fragments, Ordering::SeqCst); + self.capacity.store(current_capacity, Ordering::SeqCst); + + Ok(current_capacity) + } + } + } + + unsafe fn slices_mut>( + &self, + range: R, + ) -> >::SliceMutIter<'_> { + use std::slice::from_raw_parts_mut; + + let fragments = self.fragments(); + let fragment_and_inner_indices = + |i| self.growth.get_fragment_and_inner_indices_unchecked(i); + + let a = range_start(&range); + let b = range_end(&range, self.capacity()); + + match b.saturating_sub(a) { + 0 => vec![], + _ => { + let (sf, si) = fragment_and_inner_indices(a); + let (ef, ei) = fragment_and_inner_indices(b - 1); + + match sf == ef { + true => { + let p = self.fragment_element_ptr_mut(sf, si); + let slice = from_raw_parts_mut(p, ei - si + 1); + vec![slice] + } + false => { + let mut vec = Vec::with_capacity(ef - sf + 1); + + let slice_len = fragments[sf].capacity() - si; + let ptr_s = self.fragment_element_ptr_mut(sf, si); + vec.push(from_raw_parts_mut(ptr_s, slice_len)); + + for f in sf + 1..ef { + let slice_len = fragments[f].capacity(); + let ptr_s = self.fragment_element_ptr_mut(f, 0); + vec.push(from_raw_parts_mut(ptr_s, slice_len)); + } + + let slice_len = ei + 1; + let ptr_s = self.fragment_element_ptr_mut(ef, 0); + vec.push(from_raw_parts_mut(ptr_s, slice_len)); + + vec + } + } + } + } + } + + unsafe fn iter<'a>(&'a self, len: usize) -> impl Iterator + 'a + where + T: 'a, + { + let fragments = self.fragments(); + + match len { + 0 => IterCon::new(&fragments[0..1], 0), + _ => { + let mut num_fragments = 0; + let mut count = 0; + + for fragment in fragments.iter() { + num_fragments += 1; + let capacity = fragment.capacity(); + let new_count = count + capacity; + + match new_count >= len { + true => { + let last_fragment_len = capacity - (new_count - len); + return IterCon::new(&fragments[0..num_fragments], last_fragment_len); + } + false => count = new_count, + } + } + + let last_fragment_len = fragments[fragments.len() - 1].capacity(); + IterCon::new(fragments, last_fragment_len) + } + } + } + + unsafe fn iter_mut<'a>(&'a mut self, len: usize) -> impl Iterator + 'a + where + T: 'a, + { + self.fragments.set_len(self.num_fragments()); + set_fragments_len(&mut self.fragments, len); + let iter = crate::IterMut::new(&mut self.fragments); + iter.take(len) + } + + unsafe fn set_pinned_vec_len(&mut self, len: usize) { + self.fragments.set_len(self.num_fragments()); + set_fragments_len(&mut self.fragments, len); + } + + unsafe fn get(&self, index: usize) -> Option<&T> { + match index < self.capacity() { + true => { + let (f, i) = self.growth.get_fragment_and_inner_indices_unchecked(index); + Some(&*self.fragment_element_ptr(f, i)) + } + false => None, + } + } + + unsafe fn get_mut(&mut self, index: usize) -> Option<&mut T> { + match index < self.capacity() { + true => { + let (f, i) = self.growth.get_fragment_and_inner_indices_unchecked(index); + Some(&mut *self.fragment_element_ptr_mut(f, i)) + } + false => None, + } + } + + unsafe fn get_ptr_mut(&self, index: usize) -> *mut T { + assert!(index < self.capacity()); + let (f, i) = self.growth.get_fragment_and_inner_indices_unchecked(index); + self.fragment_element_ptr(f, i) as *mut T + } + + unsafe fn reserve_maximum_concurrent_capacity( + &mut self, + current_len: usize, + new_maximum_capacity: usize, + ) -> usize { + self.fragments.set_len(self.num_fragments()); + set_fragments_len(&mut self.fragments, current_len); + + if self.maximum_capacity < new_maximum_capacity { + let (num_required_fragments, target_capacity) = + num_fragments_for_capacity(&self.fragments, &self.growth, new_maximum_capacity); + + assert!(target_capacity >= new_maximum_capacity); + + let num_additional_fragments = + num_required_fragments.saturating_sub(self.fragments.len()); + self.fragments.reserve(num_additional_fragments); + + self.maximum_capacity = target_capacity; + self.ptr_fragments = self.fragments.as_mut_ptr(); + + let (fragment_pointers, ptr_fragments_pointers) = + get_pointers(&self.fragments, self.fragments.capacity()); + self.fragment_pointers = fragment_pointers; + self.ptr_fragments_pointers = ptr_fragments_pointers; + + self.fragments.set_len(self.fragments.capacity()); + } + + self.maximum_capacity + } + + unsafe fn clear(&mut self, prior_len: usize) { + self.fragments.set_len(self.num_fragments()); + self.set_pinned_vec_len(prior_len); + + if !self.fragments.is_empty() { + self.fragments.truncate(1); + self.fragments[0].clear(); + } + + let data = data(&mut self.fragments, &self.growth); + + self.capacity = data.capacity.into(); + self.maximum_capacity = data.maximum_capacity; + self.num_fragments = data.num_fragments.into(); + self.ptr_fragments = data.ptr_fragments; + self.fragment_pointers = data.fragment_pointers; + self.ptr_fragments_pointers = data.ptr_fragments_pointers; + } +} + +fn data(fragments: &mut Vec>, growth: &G) -> Data { + let num_fragments = fragments.len(); + + let capacity = fragments.iter().map(|x| x.capacity()).sum::(); + + let maximum_capacity = maximum_concurrent_capacity(&fragments, fragments.capacity(), growth); + + let ptr_fragments = fragments.as_mut_ptr(); + + let (fragment_pointers, ptr_fragments_pointers) = + get_pointers(&fragments, fragments.capacity()); + + unsafe { fragments.set_len(fragments.capacity()) }; + + Data { + capacity, + maximum_capacity, + num_fragments, + ptr_fragments, + fragment_pointers, + ptr_fragments_pointers, + } +} + +struct Data { + capacity: usize, + maximum_capacity: usize, + num_fragments: usize, + ptr_fragments: *mut Fragment, + fragment_pointers: Vec<*const T>, + ptr_fragments_pointers: *const *const T, +} diff --git a/src/fragment/fragment_struct.rs b/src/fragment/fragment_struct.rs index fe8125c..e3b3ce5 100644 --- a/src/fragment/fragment_struct.rs +++ b/src/fragment/fragment_struct.rs @@ -1,3 +1,5 @@ +use crate::Growth; + #[derive(Default, Clone)] /// A contagious fragment of the split vector. /// @@ -75,6 +77,69 @@ impl Fragment { } } +pub(crate) unsafe fn set_fragments_len(fragments: &mut [Fragment], len: usize) { + let mut remaining = len; + + for fragment in fragments { + let capacity = fragment.capacity(); + + match remaining <= capacity { + true => { + fragment.set_len(remaining); + remaining = 0; + } + false => { + fragment.set_len(capacity); + remaining -= capacity; + } + } + } +} + +pub(crate) fn maximum_concurrent_capacity( + fragments: &[Fragment], + fragments_capacity: usize, + growth: &G, +) -> usize { + assert!(fragments_capacity >= fragments.len()); + + match fragments_capacity == fragments.len() { + true => fragments.iter().map(|x| x.capacity()).sum(), + false => { + let mut capacities: Vec<_> = fragments.iter().map(|x| x.capacity()).collect(); + for _ in fragments.len()..fragments_capacity { + let new_capacity = growth.new_fragment_capacity_from(capacities.iter().copied()); + capacities.push(new_capacity); + } + capacities.iter().sum() + } + } +} + +pub(crate) fn num_fragments_for_capacity( + fragments: &[Fragment], + growth: &G, + required_capacity: usize, +) -> (usize, usize) { + let current_capacity: usize = fragments.iter().map(|x| x.capacity()).sum(); + + match current_capacity >= required_capacity { + true => (fragments.len(), current_capacity), + false => { + let mut num_fragments = fragments.len(); + let mut capacities: Vec<_> = fragments.iter().map(|x| x.capacity()).collect(); + let mut capacity = current_capacity; + while capacity < required_capacity { + let new_capacity = growth.new_fragment_capacity_from(capacities.iter().copied()); + capacities.push(new_capacity); + capacity += new_capacity; + num_fragments += 1; + } + (num_fragments, capacity) + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/growth/doubling/doubling_growth.rs b/src/growth/doubling/doubling_growth.rs index 6349245..b9cefab 100644 --- a/src/growth/doubling/doubling_growth.rs +++ b/src/growth/doubling/doubling_growth.rs @@ -1,6 +1,7 @@ use super::constants::*; use crate::growth::growth_trait::{Growth, GrowthWithConstantTimeAccess}; use crate::{Fragment, SplitVec}; +use orx_pseudo_default::PseudoDefault; /// Strategy which allows creates a fragment with double the capacity /// of the prior fragment every time the split vector needs to expand. @@ -52,9 +53,19 @@ use crate::{Fragment, SplitVec}; #[derive(Debug, Default, Clone, PartialEq)] pub struct Doubling; +impl PseudoDefault for Doubling { + fn pseudo_default() -> Self { + Default::default() + } +} + impl Growth for Doubling { - fn new_fragment_capacity(&self, fragments: &[Fragment]) -> usize { - fragments.last().map(|f| f.capacity() * 2).unwrap_or(4) + #[inline(always)] + fn new_fragment_capacity_from( + &self, + fragment_capacities: impl ExactSizeIterator, + ) -> usize { + fragment_capacities.last().map(|x| x * 2).unwrap_or(4) } #[inline(always)] @@ -64,13 +75,9 @@ impl Growth for Doubling { _fragments: &[Fragment], element_index: usize, ) -> Option<(usize, usize)> { - if element_index < vec_len { - let element_index_offset = element_index + FIRST_FRAGMENT_CAPACITY; - let leading_zeros = usize::leading_zeros(element_index_offset) as usize; - let f = OFFSET_FRAGMENT_IDX - leading_zeros; - Some((f, element_index - CUMULATIVE_CAPACITIES[f])) - } else { - None + match element_index < vec_len { + true => Some(self.get_fragment_and_inner_indices_unchecked(element_index)), + false => None, } } @@ -97,6 +104,7 @@ impl Growth for Doubling { /// /// This method allows to write to a memory which is greater than the split vector's length. /// On the other hand, it will never return a pointer to a memory location that the vector does not own. + #[inline(always)] unsafe fn get_ptr_mut_and_indices( &self, fragments: &mut [Fragment], @@ -141,6 +149,7 @@ impl Growth for Doubling { } impl GrowthWithConstantTimeAccess for Doubling { + #[inline(always)] fn get_fragment_and_inner_indices_unchecked(&self, element_index: usize) -> (usize, usize) { let element_index_offset = element_index + FIRST_FRAGMENT_CAPACITY; let leading_zeros = usize::leading_zeros(element_index_offset) as usize; @@ -227,8 +236,7 @@ impl SplitVec { #[cfg(test)] mod tests { use super::*; - use orx_pinned_vec::{PinnedVec, PinnedVecGrowthError}; - use test_case::test_matrix; + use orx_pinned_vec::PinnedVec; #[test] fn get_fragment_and_inner_indices() { @@ -307,23 +315,6 @@ mod tests { assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512); } - #[test_matrix([true, false])] - fn with_doubling_growth(zero_memory: bool) { - let mut vec: SplitVec = SplitVec::with_doubling_growth(); - - assert_eq!(4, vec.fragments.capacity()); - - for _ in 0..100_000 { - vec.push('x'); - } - - assert!(vec.fragments.capacity() > 4); - - let mut vec: SplitVec = SplitVec::with_doubling_growth(); - let result = unsafe { vec.grow_to(100_000, zero_memory) }; - assert!(result.expect("must-be-ok") >= 100_000); - } - #[test] fn with_doubling_growth_and_fragments_capacity_normal_growth() { let mut vec: SplitVec = SplitVec::with_doubling_growth_and_fragments_capacity(1); @@ -337,73 +328,6 @@ mod tests { assert!(vec.fragments.capacity() > 4); } - #[test_matrix([true, false])] - fn with_doubling_growth_and_fragments_capacity_concurrent_grow_never(zero_memory: bool) { - let mut vec: SplitVec = SplitVec::with_doubling_growth_and_fragments_capacity(1); - - assert!(!vec.can_concurrently_add_fragment()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - } - - #[test_matrix([true, false])] - fn with_doubling_growth_and_fragments_capacity_concurrent_grow_once(zero_memory: bool) { - let mut vec: SplitVec = SplitVec::with_doubling_growth_and_fragments_capacity(2); - - assert!(vec.can_concurrently_add_fragment()); - - let next_capacity = vec.capacity() + vec.growth().new_fragment_capacity(vec.fragments()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!(result, Ok(next_capacity)); - - assert!(!vec.can_concurrently_add_fragment()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - } - - #[test_matrix([true, false])] - fn with_doubling_growth_and_fragments_capacity_concurrent_grow_twice(zero_memory: bool) { - // when possible - let mut vec: SplitVec = SplitVec::with_doubling_growth_and_fragments_capacity(3); - - assert!(vec.can_concurrently_add_fragment()); - - let fragment_2_capacity = vec.growth().new_fragment_capacity(vec.fragments()); - let fragment_3_capacity = fragment_2_capacity * 2; - let new_capacity = vec.capacity() + fragment_2_capacity + fragment_3_capacity; - - let result = unsafe { vec.concurrently_grow_to(new_capacity - 1, zero_memory) }; - assert_eq!(result, Ok(new_capacity)); - - assert!(!vec.can_concurrently_add_fragment()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - - // when not possible - let mut vec: SplitVec = SplitVec::with_doubling_growth_and_fragments_capacity(2); - - assert!(vec.can_concurrently_add_fragment()); // although we can add one fragment - - let result = unsafe { vec.concurrently_grow_to(new_capacity - 1, zero_memory) }; // we cannot add two - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - } - #[test] #[should_panic] fn with_doubling_growth_and_fragments_capacity_zero() { diff --git a/src/growth/growth_trait.rs b/src/growth/growth_trait.rs index da4ed4a..e26bbde 100644 --- a/src/growth/growth_trait.rs +++ b/src/growth/growth_trait.rs @@ -1,10 +1,27 @@ use crate::Fragment; +use orx_pseudo_default::PseudoDefault; /// Growth strategy of a split vector. -pub trait Growth: Clone { +pub trait Growth: Clone + PseudoDefault { + /// Given that the split vector has no fragments yet, + /// returns the capacity of the first fragment. + fn first_fragment_capacity(&self) -> usize { + self.new_fragment_capacity_from([].into_iter()) + } + /// Given that the split vector contains the given `fragments`, /// returns the capacity of the next fragment. - fn new_fragment_capacity(&self, fragments: &[Fragment]) -> usize; + #[inline(always)] + fn new_fragment_capacity(&self, fragments: &[Fragment]) -> usize { + self.new_fragment_capacity_from(fragments.iter().map(|x| x.capacity())) + } + + /// Given that the split vector contains fragments with the given `fragment_capacities`, + /// returns the capacity of the next fragment. + fn new_fragment_capacity_from( + &self, + fragment_capacities: impl ExactSizeIterator, + ) -> usize; /// ***O(fragments.len())*** Returns the location of the element with the given `element_index` on the split vector as a tuple of (fragment-index, index-within-fragment). /// diff --git a/src/growth/linear/linear_growth.rs b/src/growth/linear/linear_growth.rs index 3c83ed6..d3ccab5 100644 --- a/src/growth/linear/linear_growth.rs +++ b/src/growth/linear/linear_growth.rs @@ -1,6 +1,7 @@ use crate::growth::growth_trait::{Growth, GrowthWithConstantTimeAccess}; use crate::growth::linear::constants::FIXED_CAPACITIES; use crate::{Fragment, SplitVec}; +use orx_pseudo_default::PseudoDefault; /// Strategy which allows the split vector to grow linearly. /// @@ -41,6 +42,7 @@ pub struct Linear { constant_fragment_capacity_exponent: usize, constant_fragment_capacity: usize, } + impl Linear { pub(crate) fn new(constant_fragment_capacity_exponent: usize) -> Self { let constant_fragment_capacity = FIXED_CAPACITIES[constant_fragment_capacity_exponent]; @@ -51,8 +53,18 @@ impl Linear { } } +impl PseudoDefault for Linear { + fn pseudo_default() -> Self { + Self::new(1) + } +} + impl Growth for Linear { - fn new_fragment_capacity(&self, _fragments: &[Fragment]) -> usize { + #[inline(always)] + fn new_fragment_capacity_from( + &self, + _fragment_capacities: impl ExactSizeIterator, + ) -> usize { self.constant_fragment_capacity } @@ -63,12 +75,9 @@ impl Growth for Linear { _fragments: &[Fragment], element_index: usize, ) -> Option<(usize, usize)> { - if element_index < vec_len { - let f = element_index >> self.constant_fragment_capacity_exponent; - let i = element_index % self.constant_fragment_capacity; - Some((f, i)) - } else { - None + match element_index < vec_len { + true => Some(self.get_fragment_and_inner_indices_unchecked(element_index)), + false => None, } } @@ -127,6 +136,7 @@ impl Growth for Linear { } impl GrowthWithConstantTimeAccess for Linear { + #[inline(always)] fn get_fragment_and_inner_indices_unchecked(&self, element_index: usize) -> (usize, usize) { let f = element_index >> self.constant_fragment_capacity_exponent; let i = element_index % self.constant_fragment_capacity; @@ -211,8 +221,7 @@ impl SplitVec { #[cfg(test)] mod tests { use super::*; - use orx_pinned_vec::{PinnedVec, PinnedVecGrowthError}; - use test_case::test_matrix; + use orx_pinned_vec::PinnedVec; #[test] fn get_fragment_and_inner_indices() { @@ -291,24 +300,6 @@ mod tests { assert_eq!(max_cap(&vec), 8 * 2usize.pow(5)); } - #[test_matrix([true, false])] - fn with_linear_growth(zero_memory: bool) { - let mut vec: SplitVec = SplitVec::with_linear_growth(10); - - assert_eq!(4, vec.fragments.capacity()); - - for _ in 0..100_000 { - vec.push('x'); - } - - assert!(vec.fragments.capacity() > 4); - - let mut vec: SplitVec = SplitVec::with_linear_growth(10); - let result = unsafe { vec.grow_to(100_000, zero_memory) }; - assert!(result.is_ok()); - assert!(result.expect("is-ok") >= 100_000); - } - #[test] fn with_linear_growth_and_fragments_capacity_normal_growth() { let mut vec: SplitVec = SplitVec::with_linear_growth_and_fragments_capacity(10, 1); @@ -322,73 +313,6 @@ mod tests { assert!(vec.fragments.capacity() > 4); } - #[test_matrix([true, false])] - fn with_linear_growth_and_fragments_capacity_concurrent_grow_never(zero_memory: bool) { - let mut vec: SplitVec = SplitVec::with_linear_growth_and_fragments_capacity(10, 1); - - assert!(!vec.can_concurrently_add_fragment()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - } - - #[test_matrix([true, false])] - fn with_linear_growth_and_fragments_capacity_concurrent_grow_once(zero_memory: bool) { - let mut vec: SplitVec = SplitVec::with_linear_growth_and_fragments_capacity(10, 2); - - assert!(vec.can_concurrently_add_fragment()); - - let next_capacity = vec.capacity() + vec.growth().new_fragment_capacity(vec.fragments()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!(result, Ok(next_capacity)); - - assert!(!vec.can_concurrently_add_fragment()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - } - - #[test_matrix([true, false])] - fn with_linear_growth_and_fragments_capacity_concurrent_grow_twice(zero_memory: bool) { - // when possible - let mut vec: SplitVec = SplitVec::with_linear_growth_and_fragments_capacity(10, 3); - - assert!(vec.can_concurrently_add_fragment()); - - let fragment_2_capacity = vec.growth().new_fragment_capacity(vec.fragments()); - let fragment_3_capacity = fragment_2_capacity; - let new_capacity = vec.capacity() + fragment_2_capacity + fragment_3_capacity; - - let result = unsafe { vec.concurrently_grow_to(new_capacity - 1, zero_memory) }; - assert_eq!(result, Ok(new_capacity)); - - assert!(!vec.can_concurrently_add_fragment()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - - // when not possible - let mut vec: SplitVec = SplitVec::with_linear_growth_and_fragments_capacity(10, 2); - - assert!(vec.can_concurrently_add_fragment()); // although we can add one fragment - - let result = unsafe { vec.concurrently_grow_to(new_capacity - 1, zero_memory) }; // we cannot add two - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - } - #[test] #[should_panic] fn with_linear_growth_and_fragments_capacity_zero() { diff --git a/src/growth/recursive/recursive_growth.rs b/src/growth/recursive/recursive_growth.rs index b3df80a..f5e5432 100644 --- a/src/growth/recursive/recursive_growth.rs +++ b/src/growth/recursive/recursive_growth.rs @@ -1,4 +1,5 @@ use crate::{Doubling, Fragment, Growth, SplitVec}; +use orx_pseudo_default::PseudoDefault; /// Equivalent to [`Doubling`] strategy except for the following: /// @@ -41,10 +42,19 @@ use crate::{Doubling, Fragment, Growth, SplitVec}; #[derive(Debug, Default, Clone, PartialEq)] pub struct Recursive; +impl PseudoDefault for Recursive { + fn pseudo_default() -> Self { + Default::default() + } +} + impl Growth for Recursive { #[inline(always)] - fn new_fragment_capacity(&self, fragments: &[Fragment]) -> usize { - Doubling.new_fragment_capacity(fragments) + fn new_fragment_capacity_from( + &self, + fragment_capacities: impl ExactSizeIterator, + ) -> usize { + Doubling.new_fragment_capacity_from(fragment_capacities) } fn maximum_concurrent_capacity( @@ -200,8 +210,7 @@ impl SplitVec { #[cfg(test)] mod tests { use super::*; - use orx_pinned_vec::{PinnedVec, PinnedVecGrowthError}; - use test_case::test_matrix; + use orx_pinned_vec::PinnedVec; #[test] fn get_fragment_and_inner_indices() { @@ -315,24 +324,6 @@ mod tests { assert_eq!(max_cap(&vec), 4 + 10 + 20 + 40); } - #[test_matrix([true, false])] - fn with_recursive_growth(zero_memory: bool) { - let mut vec: SplitVec = SplitVec::with_recursive_growth(); - - assert_eq!(4, vec.fragments.capacity()); - - for _ in 0..100_000 { - vec.push('x'); - } - - assert!(vec.fragments.capacity() > 4); - - let mut vec: SplitVec = SplitVec::with_recursive_growth(); - let result = unsafe { vec.grow_to(100_000, zero_memory) }; - assert!(result.is_ok()); - assert!(result.expect("is-ok") >= 100_000); - } - #[test] fn with_recursive_growth_and_fragments_capacity_normal_growth() { let mut vec: SplitVec = SplitVec::with_recursive_growth_and_fragments_capacity(1); @@ -346,73 +337,6 @@ mod tests { assert!(vec.fragments.capacity() > 4); } - #[test_matrix([true, false])] - fn with_recursive_growth_and_fragments_capacity_concurrent_grow_never(zero_memory: bool) { - let mut vec: SplitVec = SplitVec::with_recursive_growth_and_fragments_capacity(1); - - assert!(!vec.can_concurrently_add_fragment()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - } - - #[test_matrix([true, false])] - fn with_recursive_growth_and_fragments_capacity_concurrent_grow_once(zero_memory: bool) { - let mut vec: SplitVec = SplitVec::with_recursive_growth_and_fragments_capacity(2); - - assert!(vec.can_concurrently_add_fragment()); - - let next_capacity = vec.capacity() + vec.growth().new_fragment_capacity(vec.fragments()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!(result, Ok(next_capacity)); - - assert!(!vec.can_concurrently_add_fragment()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - } - - #[test_matrix([true, false])] - fn with_recursive_growth_and_fragments_capacity_concurrent_grow_twice(zero_memory: bool) { - // when possible - let mut vec: SplitVec = SplitVec::with_recursive_growth_and_fragments_capacity(3); - - assert!(vec.can_concurrently_add_fragment()); - - let fragment_2_capacity = vec.growth().new_fragment_capacity(vec.fragments()); - let fragment_3_capacity = fragment_2_capacity * 2; - let new_capacity = vec.capacity() + fragment_2_capacity + fragment_3_capacity; - - let result = unsafe { vec.concurrently_grow_to(new_capacity - 1, zero_memory) }; - assert_eq!(result, Ok(new_capacity)); - - assert!(!vec.can_concurrently_add_fragment()); - - let result = unsafe { vec.concurrently_grow_to(vec.capacity() + 1, zero_memory) }; - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - - // when not possible - let mut vec: SplitVec = SplitVec::with_recursive_growth_and_fragments_capacity(2); - - assert!(vec.can_concurrently_add_fragment()); // although we can add one fragment - - let result = unsafe { vec.concurrently_grow_to(new_capacity - 1, zero_memory) }; // we cannot add two - assert_eq!( - result, - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned) - ); - } - #[test] #[should_panic] fn with_recursive_growth_and_fragments_capacity_zero() { diff --git a/src/into_concurrent_pinned_vec.rs b/src/into_concurrent_pinned_vec.rs new file mode 100644 index 0000000..adcb263 --- /dev/null +++ b/src/into_concurrent_pinned_vec.rs @@ -0,0 +1,10 @@ +use crate::{ConcurrentSplitVec, GrowthWithConstantTimeAccess, SplitVec}; +use orx_pinned_vec::IntoConcurrentPinnedVec; + +impl IntoConcurrentPinnedVec for SplitVec { + type ConPinnedVec = ConcurrentSplitVec; + + fn into_concurrent(self) -> Self::ConPinnedVec { + self.into() + } +} diff --git a/src/lib.rs b/src/lib.rs index b5631c5..38bd371 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,12 +103,19 @@ //! //! #[derive(Clone)] //! struct MyCustomGrowth; +//! //! impl Growth for MyCustomGrowth { -//! fn new_fragment_capacity(&self, fragments: &[Fragment]) -> usize { -//! fragments.last().map(|f| f.capacity() + 1).unwrap_or(4) +//! fn new_fragment_capacity_from(&self, fragment_capacities: impl ExactSizeIterator) -> usize { +//! fragment_capacities.last().map(|f| f + 1).unwrap_or(4) //! } //! } //! +//! impl PseudoDefault for MyCustomGrowth { +//! fn pseudo_default() -> Self { +//! MyCustomGrowth +//! } +//! } +//! //! // set the growth explicitly //! let vec: SplitVec = SplitVec::with_linear_growth(4); //! let vec: SplitVec = SplitVec::with_doubling_growth(); @@ -273,8 +280,10 @@ mod algorithms; mod common_traits; +mod concurrent_pinned_vec; mod fragment; mod growth; +mod into_concurrent_pinned_vec; mod new_split_vec; mod pinned_vec; mod range_helpers; @@ -290,6 +299,7 @@ pub mod prelude; pub use common_traits::iterator::{ into_iter::IntoIter, iter::Iter, iter_mut::IterMut, iter_mut_rev::IterMutRev, iter_rev::IterRev, }; +pub use concurrent_pinned_vec::ConcurrentSplitVec; pub use fragment::fragment_struct::Fragment; pub use fragment::into_fragments::IntoFragments; pub use growth::{ @@ -298,6 +308,9 @@ pub use growth::{ linear::Linear, recursive::Recursive, }; -pub use orx_pinned_vec::PinnedVec; +pub use orx_pinned_vec::{ + ConcurrentPinnedVec, IntoConcurrentPinnedVec, PinnedVec, PinnedVecGrowthError, +}; +pub use orx_pseudo_default::PseudoDefault; pub use slice::SplitVecSlice; pub use split_vec::SplitVec; diff --git a/src/new_split_vec/into.rs b/src/new_split_vec/into.rs index a1625af..f665061 100644 --- a/src/new_split_vec/into.rs +++ b/src/new_split_vec/into.rs @@ -36,14 +36,12 @@ where /// ``` fn from(mut value: SplitVec) -> Self { if value.fragments().len() == 1 { - let mut fragments = value.fragments; - let fragment = &mut fragments[0].data; - let vec = unsafe { - Vec::from_raw_parts(fragment.as_mut_ptr(), fragment.len(), fragment.capacity()) - }; - - std::mem::forget(fragments); - vec + value + .fragments + .into_iter() + .map(|x| x.data) + .next() + .expect("There exists exactly one fragment") } else { let mut vec = Vec::with_capacity(value.len()); vec.reserve(value.len()); diff --git a/src/new_split_vec/new.rs b/src/new_split_vec/new.rs index 9ac2f37..73d7916 100644 --- a/src/new_split_vec/new.rs +++ b/src/new_split_vec/new.rs @@ -35,16 +35,24 @@ where /// /// #[derive(Clone)] /// pub struct DoubleEverySecondFragment(usize); // any custom growth strategy + /// + /// impl PseudoDefault for DoubleEverySecondFragment { + /// fn pseudo_default() -> Self { + /// DoubleEverySecondFragment(1) + /// } + /// } + /// /// impl Growth for DoubleEverySecondFragment { - /// fn new_fragment_capacity(&self, fragments: &[Fragment]) -> usize { - /// fragments + /// fn new_fragment_capacity_from(&self, fragment_capacities: impl ExactSizeIterator) -> usize { + /// let num_fragments = fragment_capacities.len(); + /// fragment_capacities /// .last() /// .map(|f| { - /// let do_double = fragments.len() % 2 == 0; + /// let do_double = num_fragments % 2 == 0; /// if do_double { - /// f.capacity() * 2 + /// f * 2 /// } else { - /// f.capacity() + /// f /// } /// }) /// .unwrap_or(self.0) diff --git a/src/pinned_vec.rs b/src/pinned_vec.rs index 32b3987..143cfa3 100644 --- a/src/pinned_vec.rs +++ b/src/pinned_vec.rs @@ -1,14 +1,22 @@ +use crate::fragment::fragment_struct::set_fragments_len; use crate::range_helpers::{range_end, range_start}; -use crate::{algorithms, Growth, SplitVec}; +use crate::{algorithms, Fragment, Growth, SplitVec}; use orx_pinned_vec::utils::slice; -use orx_pinned_vec::{CapacityState, PinnedVec, PinnedVecGrowthError}; +use orx_pinned_vec::{CapacityState, PinnedVec}; +use orx_pseudo_default::PseudoDefault; use std::cmp::Ordering; use std::ops::RangeBounds; -impl PinnedVec for SplitVec -where - G: Growth, -{ +impl PseudoDefault for SplitVec { + fn pseudo_default() -> Self { + let growth = G::pseudo_default(); + let capacity = growth.first_fragment_capacity(); + let fragments = vec![Fragment::new(capacity)]; + Self::from_raw_parts(0, fragments, growth) + } +} + +impl PinnedVec for SplitVec { type Iter<'a> = crate::common_traits::iterator::iter::Iter<'a, T> where T: 'a, Self: 'a; type IterMut<'a> = crate::common_traits::iterator::iter_mut::IterMut<'a, T> where T: 'a, Self: 'a; type IterRev<'a> = crate::common_traits::iterator::iter_rev::IterRev<'a, T> where T: 'a, Self: 'a; @@ -701,23 +709,8 @@ where } unsafe fn set_len(&mut self, new_len: usize) { + set_fragments_len(&mut self.fragments, new_len); self.len = new_len; - - let mut remaining = new_len; - - for fragment in &mut self.fragments { - let capacity = fragment.capacity(); - if remaining <= capacity { - if fragment.len() != remaining { - unsafe { fragment.set_len(remaining) }; - } - } else { - if fragment.len() != capacity { - unsafe { fragment.set_len(capacity) }; - } - remaining -= capacity; - } - } } fn binary_search_by(&self, f: F) -> Result @@ -726,112 +719,6 @@ where { algorithms::binary_search::binary_search_by(&self.fragments, f) } - - fn try_grow(&mut self) -> Result { - if self.len() < self.capacity() { - Err(PinnedVecGrowthError::CanOnlyGrowWhenVecIsAtCapacity) - } else { - self.add_fragment(); - Ok(self.capacity()) - } - } - - #[allow(clippy::unwrap_in_result)] - unsafe fn grow_to( - &mut self, - new_capacity: usize, - zero_memory: bool, - ) -> Result { - let capacity = self.capacity(); - match new_capacity.cmp(&capacity) { - Ordering::Less | Ordering::Equal => Ok(capacity), - Ordering::Greater => { - let mut current_capacity = capacity; - while new_capacity > current_capacity { - let new_fragment_capacity = match zero_memory { - true => self.add_zeroed_fragment(), - false => self.add_fragment(), - }; - current_capacity += new_fragment_capacity; - } - - debug_assert_eq!(current_capacity, self.capacity()); - Ok(current_capacity) - } - } - } - - fn grow_and_initialize( - &mut self, - new_min_len: usize, - f: F, - ) -> Result - where - F: Fn() -> T, - Self: Sized, - { - let (prior_len, capacity) = (self.len(), self.capacity()); - if prior_len < capacity { - let lf = self.fragments.len() - 1; - let last_fragment = &mut self.fragments[lf]; - debug_assert_eq!( - capacity - prior_len, - last_fragment.capacity() - last_fragment.len() - ); - for _ in prior_len..capacity { - last_fragment.push(f()); - } - - unsafe { self.set_len(capacity) }; - } - - let mut capacity = self.capacity(); - while capacity < new_min_len { - capacity += self.add_filled_fragment(&f); - } - - Ok(capacity) - } - - unsafe fn concurrently_grow_to( - &mut self, - new_capacity: usize, - zero_memory: bool, - ) -> Result { - if new_capacity <= self.capacity() { - Ok(self.capacity()) - } else { - let mut current_capacity = self.capacity(); - while new_capacity > current_capacity { - if !self.can_concurrently_add_fragment() { - return Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned); - } - - let new_fragment_capacity = match zero_memory { - true => self.add_zeroed_fragment(), - false => self.add_fragment(), - }; - - current_capacity += new_fragment_capacity; - } - debug_assert_eq!(current_capacity, self.capacity()); - Ok(current_capacity) - } - } - - fn try_reserve_maximum_concurrent_capacity( - &mut self, - new_maximum_capacity: usize, - ) -> Result { - let current_max = self.maximum_concurrent_capacity(); - match current_max.cmp(&new_maximum_capacity) { - Ordering::Less => { - self.concurrent_reserve(new_maximum_capacity)?; - Ok(self.maximum_concurrent_capacity()) - } - _ => Ok(self.maximum_concurrent_capacity()), - } - } } #[cfg(test)] @@ -840,6 +727,7 @@ mod tests { use crate::test_all_growth_types; use crate::*; use orx_pinned_vec::*; + use orx_pseudo_default::PseudoDefault; #[test] fn pinned_vec_tests() { @@ -1303,169 +1191,14 @@ mod tests { } #[test] - fn try_grow() { - fn test(mut vec: SplitVec) { - fn grow_one_fragment(vec: &mut SplitVec) { - let old_len = vec.len(); - let old_capacity = vec.capacity(); - assert!(old_len < old_capacity); - - for i in old_len..old_capacity { - assert_eq!( - Err(PinnedVecGrowthError::CanOnlyGrowWhenVecIsAtCapacity), - vec.try_grow() - ); - vec.push(i); - } - assert_eq!(vec.capacity(), old_capacity); - - let result = vec.try_grow(); - assert!(result.is_ok()); - let new_capacity = result.expect("is-ok"); - assert!(new_capacity > old_capacity); - } + fn pseudo_default() { + let vec = SplitVec::::pseudo_default(); + assert_eq!(vec.len(), 0); - for _ in 0..5 { - grow_one_fragment(&mut vec); - } + let vec = SplitVec::::pseudo_default(); + assert_eq!(vec.len(), 0); - assert_eq!(5 + 1, vec.fragments().len()); - } - test_all_growth_types!(test); - } - - #[test] - fn grow_to_under_capacity() { - fn test(mut vec: SplitVec) { - for _ in 0..10 { - assert_eq!(Ok(vec.capacity()), unsafe { vec.grow_to(0, false) }); - assert_eq!(Ok(vec.capacity()), unsafe { - vec.grow_to(vec.capacity() - 1, true) - }); - assert_eq!(Ok(vec.capacity()), unsafe { - vec.grow_to(vec.capacity(), false) - }); - } - } - test_all_growth_types!(test); - } - - #[test] - fn grow_to() { - fn test(mut vec: SplitVec) { - for _ in 0..10 { - let expected_capacity = - vec.capacity() + vec.growth().new_fragment_capacity(vec.fragments()); - let expected_num_fragments = vec.fragments().len() + 1; - - assert_eq!(Ok(expected_capacity), unsafe { - vec.grow_to(vec.capacity() + 1, true) - }); - - assert_eq!(vec.fragments().len(), expected_num_fragments); - assert_eq!(vec.capacity(), expected_capacity); - } - - vec.clear(); - - for _ in 0..10 { - let prev_num_fragments = vec.fragments().len(); - - let new_capacity = - unsafe { vec.grow_to(vec.capacity() + 1000, false) }.expect("is-okay"); - - assert!(vec.fragments().len() >= prev_num_fragments); - assert_eq!(vec.capacity(), new_capacity); - } - } - - test_all_growth_types!(test); - } - - #[test] - fn grow_to_zeroed() { - fn test(mut vec: SplitVec) { - let zero_memory = true; - - for i in 0..(64 * 1025) { - vec.push(i); - } - - let initial_capacity = vec.capacity(); - let new_capacity = - unsafe { vec.grow_to(initial_capacity + 1, zero_memory) }.expect("must be okay"); - assert!(new_capacity > initial_capacity); - - for i in initial_capacity..new_capacity { - let ptr = unsafe { vec.get_ptr_mut(i) }.expect("must be in bounds"); - let value = unsafe { *ptr }; - assert_eq!(value, unsafe { std::mem::zeroed() }); - } - } - - test_all_growth_types!(test); - } - - #[test] - fn concurrently_grow_to() { - fn test_succeed(mut vec: SplitVec) { - let max_con_cap = vec.capacity_state().maximum_concurrent_capacity(); - - assert_eq!(Ok(max_con_cap), unsafe { - vec.concurrently_grow_to(max_con_cap, true) - }); - } - - fn test_fail(mut vec: SplitVec) { - let max_con_cap = vec.capacity_state().maximum_concurrent_capacity(); - - assert_eq!( - Err(PinnedVecGrowthError::FailedToGrowWhileKeepingElementsPinned), - unsafe { vec.concurrently_grow_to(max_con_cap + 1, false) } - ); - } - - test_all_growth_types!(test_succeed); - test_all_growth_types!(test_fail); - } - - #[test] - fn concurrently_grow_to_zeroed() { - fn test(mut vec: SplitVec) { - let zero_memory = true; - - for i in 0..1025 { - vec.push(i); - } - - let initial_capacity = vec.capacity(); - let new_capacity = - unsafe { vec.concurrently_grow_to(initial_capacity + 1, zero_memory) } - .expect("must be okay"); - assert!(new_capacity > initial_capacity); - - for i in initial_capacity..new_capacity { - let ptr = unsafe { vec.get_ptr_mut(i) }.expect("must be in bounds"); - let value = unsafe { *ptr }; - assert_eq!(value, unsafe { std::mem::zeroed() }); - } - } - - test_all_growth_types!(test); - } - - #[test] - fn try_reserve_maximum_concurrent_capacity() { - fn test(mut vec: SplitVec) { - let current_max = vec.maximum_concurrent_capacity(); - let target_max = current_max * 2; - - let result = vec.try_reserve_maximum_concurrent_capacity(target_max); - - assert_eq!(result, Ok(vec.maximum_concurrent_capacity())); - assert!(vec.maximum_concurrent_capacity() >= target_max); - } - - test_all_growth_types!(test); + let vec = SplitVec::::pseudo_default(); + assert_eq!(vec.len(), 0); } } diff --git a/src/prelude.rs b/src/prelude.rs index 593604f..882241c 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,5 +1,3 @@ -pub use orx_pinned_vec::{CapacityState, PinnedVec, PinnedVecGrowthError}; - pub use crate::common_traits::iterator::iter::Iter; pub use crate::fragment::fragment_struct::Fragment; pub use crate::fragment::into_fragments::IntoFragments; @@ -11,3 +9,7 @@ pub use crate::growth::{ }; pub use crate::slice::SplitVecSlice; pub use crate::split_vec::SplitVec; +pub use orx_pinned_vec::{ + ConcurrentPinnedVec, IntoConcurrentPinnedVec, PinnedVec, PinnedVecGrowthError, +}; +pub use orx_pseudo_default::PseudoDefault; diff --git a/src/split_vec.rs b/src/split_vec.rs index 3592959..578ae2f 100644 --- a/src/split_vec.rs +++ b/src/split_vec.rs @@ -1,5 +1,3 @@ -use orx_pinned_vec::PinnedVec; - use crate::{fragment::fragment_struct::Fragment, Doubling, Growth}; /// A split vector; i.e., a vector of fragments, with the following features: @@ -14,7 +12,7 @@ where { pub(crate) len: usize, pub(crate) fragments: Vec>, - growth: G, + pub(crate) growth: G, } impl SplitVec @@ -117,10 +115,9 @@ where .growth .required_fragments_len(&self.fragments, maximum_capacity)?; - let additional_fragments = if required_num_fragments > self.fragments.capacity() { - required_num_fragments - self.fragments.capacity() - } else { - 0 + let additional_fragments = match required_num_fragments > self.fragments.capacity() { + true => required_num_fragments - self.fragments.capacity(), + false => 0, }; if additional_fragments > 0 { @@ -188,27 +185,6 @@ where self.add_fragment_get_fragment_capacity(false) } - /// Adds a new zeroed-fragment to fragments of the split vector; returns the capacity of the new fragment. - #[inline(always)] - pub(crate) fn add_zeroed_fragment(&mut self) -> usize { - self.add_fragment_get_fragment_capacity(true) - } - - pub(crate) fn add_filled_fragment T>(&mut self, f: F) -> usize { - assert_eq!(self.len(), self.capacity()); - - let new_fragment_capacity = self.growth.new_fragment_capacity(&self.fragments); - let new_len = self.len() + new_fragment_capacity; - self.fragments - .push(Fragment::new_filled(new_fragment_capacity, f)); - unsafe { self.set_len(new_len) }; - - debug_assert_eq!(self.len(), new_len); - debug_assert_eq!(self.capacity(), new_len); - - new_fragment_capacity - } - /// Adds a new fragment and return the capacity of the added (now last) fragment. fn add_fragment_get_fragment_capacity(&mut self, zeroed: bool) -> usize { let new_fragment_capacity = self.growth.new_fragment_capacity(&self.fragments); @@ -242,12 +218,23 @@ where self.growth.get_ptr_mut(&mut self.fragments, index) } - /// Returns `true` only if it is concurrently safe to add a new fragment to the split vector. + /// Makes sure that the split vector can safely reach the given `maximum_capacity` in a concurrent program. /// - /// It is only concurrently safe if the internal `fragments` collection does not move already pushed fragments in memory. - /// Note that if the fragments are stored as a vector, an increase in capacity might lead to this concurrency problem. - pub(crate) fn can_concurrently_add_fragment(&self) -> bool { - self.fragments.capacity() > self.fragments.len() + /// Returns new maximum capacity. + /// + /// Note that this method does not allocate the `maximum_capacity`, it only ensures that the concurrent growth to this capacity is safe. + /// In order to achieve this, it might need to extend allocation of the fragments collection. + /// However, note that by definition number of fragments is insignificant in a split vector. + pub fn reserve_maximum_concurrent_capacity(&mut self, new_maximum_capacity: usize) -> usize { + let current_max = self.maximum_concurrent_capacity(); + match current_max < new_maximum_capacity { + true => { + self.concurrent_reserve(new_maximum_capacity) + .expect("Failed to reserve maximum capacity"); + self.maximum_concurrent_capacity() + } + false => self.maximum_concurrent_capacity(), + } } } @@ -334,14 +321,6 @@ mod tests { vec.clear(); - for _ in 0..10 { - let expected_new_fragment_cap = vec.growth.new_fragment_capacity(&vec.fragments); - let new_fragment_cap = vec.add_zeroed_fragment(); - assert_eq!(expected_new_fragment_cap, new_fragment_cap); - } - - vec.clear(); - let mut expected_capacity = vec.capacity(); for _ in 0..2 { let expected_new_fragment_cap = vec.growth.new_fragment_capacity(&vec.fragments); @@ -354,28 +333,4 @@ mod tests { test_all_growth_types!(test); } - - #[test] - fn concurrent_reserve() { - fn test(mut vec: SplitVec) { - for zero_memory in [false, true] { - vec.clear(); - - let current_max = vec.capacity_state().maximum_concurrent_capacity(); - let target_max = current_max * 2 + 1; - - let result = vec.concurrent_reserve(target_max); - assert_eq!(result, Ok(vec.maximum_concurrent_capacity())); - assert!(vec.maximum_concurrent_capacity() >= current_max); - - match unsafe { vec.concurrently_grow_to(target_max, zero_memory) } { - #[allow(clippy::panic)] - Err(_) => panic!("failed to reserve"), - Ok(new_capacity) => assert!(new_capacity >= target_max), - } - } - } - - test_all_growth_types!(test); - } } diff --git a/tests/grow_and_initialize.rs b/tests/grow_and_initialize.rs deleted file mode 100644 index b2fbec9..0000000 --- a/tests/grow_and_initialize.rs +++ /dev/null @@ -1,33 +0,0 @@ -use orx_split_vec::prelude::*; - -#[test] -fn grow_and_initialize() { - let mut vec = SplitVec::new(); - - for i in 0..2044 { - vec.push(i); - } - let capacity_before = vec.capacity(); - - let result = vec.grow_and_initialize(0, || 42); - assert_eq!(result, Ok(vec.capacity())); - assert_eq!(vec.capacity(), capacity_before); - assert_eq!(vec.len(), vec.capacity()); - for i in 0..2044 { - assert_eq!(vec.get(i), Some(&i)); - } - for i in 2044..vec.len() { - assert_eq!(vec.get(i), Some(&42)); - } - - let result = vec.grow_and_initialize(10000, || 42); - assert_eq!(result, Ok(vec.capacity())); - assert_eq!(vec.len(), vec.capacity()); - assert!(vec.capacity() >= 10000); - for i in 0..2044 { - assert_eq!(vec.get(i), Some(&i)); - } - for i in 2044..vec.len() { - assert_eq!(vec.get(i), Some(&42)); - } -}