From f536ba2d48bc4446580578f0a9e4fad12b63b97b Mon Sep 17 00:00:00 2001 From: "Ugur Arikan (DHL Data & Analytics)" Date: Tue, 23 Jul 2024 23:38:45 +0200 Subject: [PATCH] support-for-concurrency # Support for Concurrency In version 2, PinnedVec grew with new methods to support concurrent data structures. However, this caused problems since these exposed methods were often unsafe, and further, they were not directly useful for the pinned vector consumers except for concurrent data structures wrapping a pinned vector. Furthermore, they are alien to a regular vector interface that we are used to using. In version 3, a second trait called `ConcurrentPinnedVec` is defined. All useful methods related with concurrent programming are moved to this trait. This trait has an associated type defining the underlying pinned vector type. It can be turned into the pinned vector. Finally, `IntoConcurrentPinnedVec` trait is defined. A pinned vector implementing this trait can be turned into a `ConcurrentPinnedVec`. As explained above, it can be converted back to the pinned vector. This bi-directional transformation allows to wrap pinned vector to have concurrent support, and unwrap whenever concurrency is not required anymore. An important advantage of this approach is that it allowed to clean up the `PinnedVec` api from unsafe and alien concurrency related methods. # Also Tests using clock are revised so that currently pinned vector tests are **miri safe**. `PseudoDefault` is required for all pinned vectors. Note that a `FixedVec` cannot implement a `Default`, but as any type, it can implement a pseudo-default, which is also required for concurrent wrappers. --- Cargo.toml | 5 +- README.md | 11 +- src/common_traits/iterator/iter_con.rs | 69 ++++ src/common_traits/iterator/iter_fragment.rs | 35 ++ src/common_traits/iterator/mod.rs | 2 + src/concurrent_pinned_vec.rs | 360 ++++++++++++++++++++ src/fragment/fragment_struct.rs | 65 ++++ src/growth/doubling/doubling_growth.rs | 114 ++----- src/growth/growth_trait.rs | 21 +- src/growth/linear/linear_growth.rs | 112 +----- src/growth/recursive/recursive_growth.rs | 102 +----- src/into_concurrent_pinned_vec.rs | 10 + src/lib.rs | 19 +- src/new_split_vec/into.rs | 14 +- src/new_split_vec/new.rs | 18 +- src/pinned_vec.rs | 313 ++--------------- src/prelude.rs | 6 +- src/split_vec.rs | 85 ++--- tests/grow_and_initialize.rs | 33 -- 19 files changed, 704 insertions(+), 690 deletions(-) create mode 100644 src/common_traits/iterator/iter_con.rs create mode 100644 src/common_traits/iterator/iter_fragment.rs create mode 100644 src/concurrent_pinned_vec.rs create mode 100644 src/into_concurrent_pinned_vec.rs delete mode 100644 tests/grow_and_initialize.rs 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)); - } -}