diff --git a/Cargo.toml b/Cargo.toml index bc16f02..8d3fb62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "orx-split-vec" -version = "3.6.0" +version = "3.7.0" edition = "2021" authors = ["orxfun "] description = "An efficient constant access time vector with dynamic capacity and pinned elements." @@ -11,7 +11,7 @@ categories = ["data-structures", "rust-patterns"] [dependencies] orx-pseudo-default = "1.4" -orx-pinned-vec = "3.6" +orx-pinned-vec = "3.7" [[bench]] name = "serial_access" diff --git a/src/algorithms/in_place_sort.rs b/src/algorithms/in_place_sort.rs index 32f403f..c5efb01 100644 --- a/src/algorithms/in_place_sort.rs +++ b/src/algorithms/in_place_sort.rs @@ -5,7 +5,7 @@ pub fn in_place_sort_by(fragments: &mut [Fragment], mut compare: F) where F: FnMut(&T, &T) -> Ordering, { - if fragments.len() == 0 { + if fragments.is_empty() { return; } @@ -60,9 +60,9 @@ where true => None, false => { let mut best = &fragments[r_best][0]; - for q in (r_best + 1)..fragments.len() { - if let Less = compare(&fragments[q][0], best) { - (best, r_best) = (&fragments[q][0], q); + for (q, fragment) in fragments.iter().enumerate().skip(r_best + 1) { + if let Less = compare(&fragment[0], best) { + (best, r_best) = (&fragment[0], q); } } diff --git a/src/concurrent_pinned_vec.rs b/src/concurrent_pinned_vec.rs index e069bce..0fa17b4 100644 --- a/src/concurrent_pinned_vec.rs +++ b/src/concurrent_pinned_vec.rs @@ -20,7 +20,6 @@ struct FragmentData { pub struct ConcurrentSplitVec { growth: G, data: Vec>, - num_fragments: AtomicUsize, capacity: AtomicUsize, maximum_capacity: usize, max_num_fragments: usize, @@ -30,7 +29,7 @@ pub struct ConcurrentSplitVec { impl Drop for ConcurrentSplitVec { fn drop(&mut self) { let mut take_fragment = |_fragment: Fragment| {}; - unsafe { self.into_fragments(self.pinned_vec_len, &mut take_fragment) }; + unsafe { self.process_into_fragments(self.pinned_vec_len, &mut take_fragment) }; self.zero(); } } @@ -51,7 +50,7 @@ impl ConcurrentSplitVec { } fn layout(len: usize) -> std::alloc::Layout { - std::alloc::Layout::array::(len).unwrap() + std::alloc::Layout::array::(len).expect("len must not overflow") } unsafe fn to_fragment(&self, data: FragmentData) -> Fragment { @@ -59,7 +58,7 @@ impl ConcurrentSplitVec { fragment_from_raw(ptr, data.len, data.capacity) } - unsafe fn into_fragments(&mut self, len: usize, take_fragment: &mut F) + unsafe fn process_into_fragments(&mut self, len: usize, take_fragment: &mut F) where F: FnMut(Fragment), { @@ -116,12 +115,23 @@ impl ConcurrentSplitVec { } fn zero(&mut self) { - self.num_fragments = 0.into(); self.capacity = 0.into(); self.maximum_capacity = 0; self.max_num_fragments = 0; self.pinned_vec_len = 0; } + + fn num_fragments_for_capacity(&self, capacity: usize) -> usize { + match capacity { + 0 => 0, + _ => { + self.growth + .get_fragment_and_inner_indices_unchecked(capacity - 1) + .0 + + 1 + } + } + } } impl From> for ConcurrentSplitVec { @@ -144,7 +154,7 @@ impl From> for ConcurrentSpli total_len += len; maximum_capacity += cap; - data.push(UnsafeCell::new(p as *mut T)); + data.push(UnsafeCell::new(p)); } assert_eq!(total_len, pinned_vec_len); let capacity = maximum_capacity; @@ -159,7 +169,6 @@ impl From> for ConcurrentSpli Self { growth, data, - num_fragments: num_fragments.into(), capacity: capacity.into(), maximum_capacity, max_num_fragments, @@ -174,7 +183,7 @@ impl ConcurrentPinnedVec for ConcurrentSp unsafe fn into_inner(mut self, len: usize) -> Self::P { let mut fragments = Vec::with_capacity(self.max_num_fragments); let mut take_fragment = |fragment| fragments.push(fragment); - self.into_fragments(len, &mut take_fragment); + self.process_into_fragments(len, &mut take_fragment); self.zero(); SplitVec::from_raw_parts(len, fragments, self.growth.clone()) @@ -355,8 +364,7 @@ impl ConcurrentPinnedVec for ConcurrentSp match new_capacity <= capacity { true => Ok(capacity), false => { - let mut f = self.num_fragments.load(Ordering::Relaxed); - + let mut f = self.num_fragments_for_capacity(capacity); let mut current_capacity = capacity; while new_capacity > current_capacity { @@ -369,7 +377,6 @@ impl ConcurrentPinnedVec for ConcurrentSp current_capacity += new_fragment_capacity; } - self.num_fragments.store(f, Ordering::SeqCst); self.capacity.store(current_capacity, Ordering::Release); Ok(current_capacity) @@ -389,7 +396,7 @@ impl ConcurrentPinnedVec for ConcurrentSp match new_capacity <= capacity { true => Ok(capacity), false => { - let mut f = self.num_fragments.load(Ordering::Relaxed); + let mut f = self.num_fragments_for_capacity(capacity); let mut current_capacity = capacity; @@ -408,7 +415,6 @@ impl ConcurrentPinnedVec for ConcurrentSp current_capacity += new_fragment_capacity; } - self.num_fragments.store(f, Ordering::SeqCst); self.capacity.store(current_capacity, Ordering::Release); Ok(current_capacity) @@ -468,13 +474,25 @@ impl ConcurrentPinnedVec for ConcurrentSp self.maximum_capacity } + unsafe fn reserve_maximum_concurrent_capacity_fill_with( + &mut self, + current_len: usize, + new_maximum_capacity: usize, + _fill_with: F, + ) -> usize + where + F: Fn() -> T, + { + self.reserve_maximum_concurrent_capacity(current_len, new_maximum_capacity) + } + unsafe fn set_pinned_vec_len(&mut self, len: usize) { self.pinned_vec_len = len; } unsafe fn clear(&mut self, len: usize) { let mut take_fragment = |_fragment: Fragment| {}; - unsafe { self.into_fragments(len, &mut take_fragment) }; + unsafe { self.process_into_fragments(len, &mut take_fragment) }; self.zero(); let max_num_fragments = self.data.len(); diff --git a/src/fragment/fragment_struct.rs b/src/fragment/fragment_struct.rs index 92ef72d..1849682 100644 --- a/src/fragment/fragment_struct.rs +++ b/src/fragment/fragment_struct.rs @@ -1,5 +1,4 @@ #[derive(Default)] -#[allow(clippy::include)] /// A contagious fragment of the split vector. /// /// Suppose a split vector contains 10 integers from 0 to 9. diff --git a/tests/con_pinned_vec_grow.rs b/tests/con_pinned_vec_grow.rs index dced33f..a80dd92 100644 --- a/tests/con_pinned_vec_grow.rs +++ b/tests/con_pinned_vec_grow.rs @@ -102,3 +102,99 @@ fn con_pin_vec_grow_filled() { test(SplitVec::with_doubling_growth_and_fragments_capacity(32)); test(SplitVec::with_linear_growth_and_fragments_capacity(10, 32)); } + +#[test] +fn reserve() { + fn test(vec: SplitVec) { + let initial_capacity = vec.capacity(); + + let mut con_vec = vec.into_concurrent(); + let max_cap = con_vec.max_capacity(); + + unsafe { con_vec.get_ptr_mut(0).write("first".to_string()) }; + + assert_eq!(con_vec.capacity(), initial_capacity); + + unsafe { con_vec.reserve_maximum_concurrent_capacity(0, max_cap + 1) }; + let new_capacity = con_vec.capacity(); + assert_eq!(new_capacity, initial_capacity); + assert!(con_vec.max_capacity() >= max_cap + 1); + + let vec = unsafe { con_vec.into_inner(1) }; + + assert_eq!(vec.len(), 1); + assert_eq!(vec.capacity(), initial_capacity); + assert_eq!(&vec[0], &"first".to_string()); + } + + test(SplitVec::with_doubling_growth_and_fragments_capacity(16)); + test(SplitVec::with_linear_growth_and_fragments_capacity(10, 32)); +} + +#[test] +fn into_concurrent_fill_with() { + fn test(vec: SplitVec) { + let initial_capacity = vec.capacity(); + let vec2 = vec.clone(); + + let con_vec = vec.into_concurrent_filled_with(|| "x".to_string()); + let vec = unsafe { con_vec.into_inner(initial_capacity) }; + assert_eq!( + vec, + (0..initial_capacity) + .map(|_| "x".to_string()) + .collect::>() + ); + + let mut vec = vec2; + vec.push("y".to_string()); + let con_vec = vec.into_concurrent_filled_with(|| "x".to_string()); + let vec = unsafe { con_vec.into_inner(initial_capacity) }; + assert_eq!(&vec[0], &"y".to_string()); + assert_eq!( + vec.iter().skip(1).cloned().collect::>(), + (1..initial_capacity) + .map(|_| "x".to_string()) + .collect::>() + ); + } + test(SplitVec::with_doubling_growth_and_fragments_capacity(32)); + test(SplitVec::with_linear_growth_and_fragments_capacity(10, 32)); +} + +#[test] +fn reserve_fill_with() { + fn test(vec: SplitVec) { + let initial_capacity = vec.capacity(); + + let mut con_vec = vec.into_concurrent_filled_with(|| "x".to_string()); + let max_cap = con_vec.max_capacity(); + + assert_eq!(con_vec.capacity(), initial_capacity); + + unsafe { + con_vec.reserve_maximum_concurrent_capacity_fill_with( + initial_capacity, + max_cap + 1, + || "y".to_string(), + ) + }; + let new_capacity = con_vec.capacity(); + assert_eq!(new_capacity, initial_capacity); + assert!(con_vec.max_capacity() >= max_cap + 1); + + let vec = unsafe { con_vec.into_inner(initial_capacity) }; + + assert_eq!(vec.len(), initial_capacity); + assert_eq!(vec.capacity(), initial_capacity); + assert_eq!( + vec, + (0..initial_capacity) + .map(|_| "x".to_string()) + .collect::>() + ); + } + + test(SplitVec::with_doubling_growth_and_fragments_capacity(16)); + test(SplitVec::with_linear_growth_and_fragments_capacity(10, 32)); +}