diff --git a/Cargo.toml b/Cargo.toml index 7908746..bcc5733 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "orx-split-vec" -version = "3.8.0" +version = "3.9.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", "no-std"] [dependencies] orx-pseudo-default = { version = "1.4", default-features = false } -orx-pinned-vec = "3.8" +orx-pinned-vec = "3.9" [[bench]] name = "serial_access" diff --git a/src/common_traits/iterator/iter_ptr.rs b/src/common_traits/iterator/iter_ptr.rs new file mode 100644 index 0000000..934d16b --- /dev/null +++ b/src/common_traits/iterator/iter_ptr.rs @@ -0,0 +1,258 @@ +use crate::{ + pointers::{Ptr, Ptrs}, + Fragment, +}; +use core::iter::FusedIterator; + +#[derive(Copy)] +pub struct IterPtr { + ptrs: Ptrs, + current_f: usize, + current: Ptr, +} + +impl Clone for IterPtr { + fn clone(&self) -> Self { + Self { + ptrs: self.ptrs.clone(), + current_f: self.current_f, + current: self.current.clone(), + } + } +} + +impl<'a, T> From<&'a [Fragment]> for IterPtr { + fn from(value: &'a [Fragment]) -> Self { + let current_f = 0; + let current = match value.get(current_f) { + Some(fragment) => Ptr::from(fragment), + None => Ptr::default(), + }; + let ptrs = Ptrs::from(value); + Self { + ptrs, + current, + current_f, + } + } +} + +impl Iterator for IterPtr { + type Item = *const T; + + fn next(&mut self) -> Option { + match self.current.next() { + Some(x) => Some(x), + None => { + self.current_f += 1; + match unsafe { self.ptrs.get(self.current_f) } { + Some(ptr) => { + self.current = ptr; + self.current.next() + } + None => None, + } + } + } + } +} + +impl FusedIterator for IterPtr {} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::{ + string::{String, ToString}, + vec::Vec, + }; + + #[test] + fn ptr_zero_fragments() { + let fragments: Vec> = Vec::with_capacity(4); + let mut iter = IterPtr::from(fragments.as_slice()); + + assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + } + + #[test] + fn ptr_one_empty_fragment() { + let fragment: Fragment = Vec::with_capacity(2).into(); + let mut fragments: Vec> = Vec::with_capacity(4); + fragments.push(fragment); + + let mut iter = IterPtr::from(fragments.as_slice()); + assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + } + + #[test] + fn ptr_one_non_empty_fragment() { + let mut fragment: Fragment = Vec::with_capacity(3).into(); + fragment.push(0.to_string()); + fragment.push(1.to_string()); + + let mut fragments: Vec> = Vec::with_capacity(4); + fragments.push(fragment); + + let mut iter = IterPtr::from(fragments.as_slice()); + assert_eq!(iter.next().map(|p| unsafe { &*p }), Some(&0.to_string())); + assert_eq!(iter.next().map(|p| unsafe { &*p }), Some(&1.to_string())); + assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + } + + #[test] + fn ptr_many_non_empty_fragments() { + let mut fragments: Vec> = Vec::with_capacity(4); + + let prior = 0; + let n = 4; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 8; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 16; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let mut iter = IterPtr::from(fragments.as_slice()); + let mut prior = 0; + for f in 0..fragments.len() { + for i in 0..fragments[f].len() { + assert_eq!( + iter.next().map(|p| unsafe { &*p }), + Some(&(prior + i).to_string()) + ); + } + prior += fragments[f].len(); + } + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } + + #[test] + fn ptr_many_non_empty_fragments_half_last() { + let mut fragments: Vec> = Vec::with_capacity(4); + + let prior = 0; + let n = 4; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 8; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 16; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..(n / 2) { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let mut iter = IterPtr::from(fragments.as_slice()); + let mut prior = 0; + for f in 0..fragments.len() { + for i in 0..fragments[f].len() { + assert_eq!( + iter.next().map(|p| unsafe { &*p }), + Some(&(prior + i).to_string()) + ); + } + prior += fragments[f].len(); + } + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } + + #[test] + fn ptr_mutate_nodes_while_iterating() { + let mut fragments: Vec> = Vec::with_capacity(4); + + let prior = 0; + let n = 4; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 8; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 16; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..(n / 2) { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let mut iter = IterPtr::from(fragments.as_slice()); + let mut prior = 0; + for f in 0..fragments.len() { + for i in 0..fragments[f].len() { + assert_eq!( + iter.next().map(|p| unsafe { &*p }), + Some(&(prior + i).to_string()) + ); + + if f > 0 { + let first = { + let f = unsafe { &*fragments.as_ptr().add(f - 1) }; + let p = unsafe { f.as_ptr().add(0) } as *mut String; + p + }; + let second = { + let f = unsafe { &*fragments.as_ptr().add(f) }; + let p = unsafe { f.as_ptr().add(i) } as *mut String; + p + }; + unsafe { first.swap(second) }; + } + + // fragments[f][i] = 0.to_string(); => miri error!!! but below is OK + let f = unsafe { &*fragments.as_ptr().add(f) }; + let p = unsafe { f.as_ptr().add(i) } as *mut String; + let mut new_str = 0.to_string(); + let mut_ref = &mut new_str; + let mut_ptr = mut_ref as *mut String; + unsafe { p.swap(mut_ptr) }; + } + prior += fragments[f].len(); + } + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } +} diff --git a/src/common_traits/iterator/iter_ptr_bwd.rs b/src/common_traits/iterator/iter_ptr_bwd.rs new file mode 100644 index 0000000..3f8ad7a --- /dev/null +++ b/src/common_traits/iterator/iter_ptr_bwd.rs @@ -0,0 +1,255 @@ +use crate::{ + pointers::{PtrBackward, Ptrs}, + Fragment, +}; +use core::iter::FusedIterator; + +#[derive(Copy)] +pub struct IterPtrBackward { + ptrs: Ptrs, + current_f: usize, + current: PtrBackward, +} + +impl Clone for IterPtrBackward { + fn clone(&self) -> Self { + Self { + ptrs: self.ptrs.clone(), + current_f: self.current_f, + current: self.current.clone(), + } + } +} + +impl<'a, T> From<&'a [Fragment]> for IterPtrBackward { + fn from(value: &'a [Fragment]) -> Self { + let current_f = value.len().saturating_sub(1); + let current = match value.last() { + Some(fragment) => PtrBackward::from(fragment), + None => PtrBackward::default(), + }; + let ptrs = Ptrs::from(value); + Self { + ptrs, + current, + current_f, + } + } +} + +impl Iterator for IterPtrBackward { + type Item = *const T; + + #[allow(clippy::unwrap_in_result)] + fn next(&mut self) -> Option { + match self.current.next() { + Some(x) => Some(x), + None => match self.current_f { + 0 => None, + x => { + self.current_f = x - 1; + let ptr = unsafe { self.ptrs.get_bwd(self.current_f) }.expect("exists"); + self.current = ptr; + self.current.next() + } + }, + } + } +} + +impl FusedIterator for IterPtrBackward {} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::{ + string::{String, ToString}, + vec::Vec, + }; + + #[test] + fn ptr_bwd_zero_fragments() { + let fragments: Vec> = Vec::with_capacity(4); + let mut iter = IterPtrBackward::from(fragments.as_slice()); + + assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + } + + #[test] + fn ptr_bwd_one_empty_fragment() { + let fragment: Fragment = Vec::with_capacity(2).into(); + let mut fragments: Vec> = Vec::with_capacity(4); + fragments.push(fragment); + + let mut iter = IterPtrBackward::from(fragments.as_slice()); + assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + } + + #[test] + fn ptr_bwd_one_non_empty_fragment() { + let mut fragment: Fragment = Vec::with_capacity(3).into(); + fragment.push(0.to_string()); + fragment.push(1.to_string()); + + let mut fragments: Vec> = Vec::with_capacity(4); + fragments.push(fragment); + + let mut iter = IterPtrBackward::from(fragments.as_slice()); + assert_eq!(iter.next().map(|p| unsafe { &*p }), Some(&1.to_string())); + assert_eq!(iter.next().map(|p| unsafe { &*p }), Some(&0.to_string())); + assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + } + + #[test] + fn ptr_bwd_many_non_empty_fragments() { + let mut fragments: Vec> = Vec::with_capacity(4); + + let prior = 0; + let n = 4; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 8; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 16; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let mut iter = IterPtrBackward::from(fragments.as_slice()); + let mut value = 4 + 8 + 16 - 1; + for f in 0..fragments.len() { + for _ in 0..fragments[f].len() { + assert_eq!( + iter.next().map(|p| unsafe { &*p }), + Some(&value.to_string()) + ); + value -= 1; + } + } + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } + + #[test] + fn ptr_bwd_many_non_empty_fragments_half_last() { + let mut fragments: Vec> = Vec::with_capacity(4); + + let prior = 0; + let n = 4; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 8; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 16; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..(n / 2) { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let mut iter = IterPtrBackward::from(fragments.as_slice()); + let mut value = 4 + 8 + 8 - 1; + for f in 0..fragments.len() { + for _ in 0..fragments[f].len() { + assert_eq!( + iter.next().map(|p| unsafe { &*p }), + Some(&value.to_string()) + ); + value -= 1; + } + } + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } + + #[test] + fn ptr_bwd_mutate_nodes_while_iterating() { + let mut fragments: Vec> = Vec::with_capacity(4); + + let prior = 0; + let n = 4; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 8; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 16; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..(n / 2) { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let mut iter = IterPtrBackward::from(fragments.as_slice()); + let mut value = Some(0.to_string()); + for f in 0..fragments.len() { + for i in 0..fragments[f].len() { + value = iter.next().map(|p| unsafe { &*p }).cloned(); + + if f > 0 { + let first = { + let f = unsafe { &*fragments.as_ptr().add(f - 1) }; + let p = unsafe { f.as_ptr().add(0) } as *mut String; + p + }; + let second = { + let f = unsafe { &*fragments.as_ptr().add(f) }; + let p = unsafe { f.as_ptr().add(i) } as *mut String; + p + }; + unsafe { first.swap(second) }; + } + + // fragments[f][i] = 0.to_string(); => miri error!!! but below is OK + let f = unsafe { &*fragments.as_ptr().add(f) }; + let p = unsafe { f.as_ptr().add(i) } as *mut String; + let mut new_str = 0.to_string(); + let mut_ref = &mut new_str; + let mut_ptr = mut_ref as *mut String; + unsafe { p.swap(mut_ptr) }; + } + } + assert!(value.is_some()); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } +} diff --git a/src/common_traits/iterator/mod.rs b/src/common_traits/iterator/mod.rs index bbaa4ed..2a130a8 100644 --- a/src/common_traits/iterator/mod.rs +++ b/src/common_traits/iterator/mod.rs @@ -4,6 +4,8 @@ pub(crate) mod into_iter; pub(crate) mod iter; pub(crate) mod iter_mut; pub(crate) mod iter_mut_rev; +pub(crate) mod iter_ptr; +pub(crate) mod iter_ptr_bwd; pub(crate) mod iter_rev; mod reductions; diff --git a/src/growth/doubling/doubling_growth.rs b/src/growth/doubling/doubling_growth.rs index 514a4f3..fe518a9 100644 --- a/src/growth/doubling/doubling_growth.rs +++ b/src/growth/doubling/doubling_growth.rs @@ -82,6 +82,19 @@ impl Growth for Doubling { } } + /// ***O(1)*** Returns a pointer to the `index`-th element of the split vector of the `fragments`. + /// + /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments. + /// + /// # Safety + /// + /// 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)] + fn get_ptr(&self, fragments: &[Fragment], index: usize) -> Option<*const T> { + ::get_ptr(self, fragments, index) + } + /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`. /// /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments. @@ -91,7 +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(&self, fragments: &mut [Fragment], index: usize) -> Option<*mut T> { + fn get_ptr_mut(&self, fragments: &mut [Fragment], index: usize) -> Option<*mut T> { ::get_ptr_mut(self, fragments, index) } @@ -106,7 +119,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( + fn get_ptr_mut_and_indices( &self, fragments: &mut [Fragment], index: usize, @@ -225,7 +238,7 @@ impl SplitVec { /// Note that this (only) important for concurrent programs: /// * SplitVec already keeps all elements pinned to their locations; /// * Creating a buffer for storing the meta information is important for keeping the meta information pinned as well. - /// This is relevant and important for concurrent programs. + /// This is relevant and important for concurrent programs. /// /// # Panics /// diff --git a/src/growth/growth_trait.rs b/src/growth/growth_trait.rs index 13d3654..7f1d9f7 100644 --- a/src/growth/growth_trait.rs +++ b/src/growth/growth_trait.rs @@ -53,7 +53,19 @@ pub trait Growth: Clone + PseudoDefault { /// /// This method allows to write to a memory which is greater than the vector's length. /// On the other hand, it will never return a pointer to a memory location that the vector does not own. - unsafe fn get_ptr_mut(&self, fragments: &mut [Fragment], index: usize) -> Option<*mut T> { + fn get_ptr(&self, fragments: &[Fragment], index: usize) -> Option<*const T> { + self.get_ptr_and_indices(fragments, index).map(|x| x.0) + } + + /// ***O(fragments.len())*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`. + /// + /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments. + /// + /// # Safety + /// + /// This method allows to write to a memory which is greater than the vector's length. + /// On the other hand, it will never return a pointer to a memory location that the vector does not own. + fn get_ptr_mut(&self, fragments: &mut [Fragment], index: usize) -> Option<*mut T> { self.get_ptr_mut_and_indices(fragments, index).map(|x| x.0) } @@ -67,7 +79,39 @@ pub trait Growth: Clone + PseudoDefault { /// /// This method allows to write to a memory which is greater than the vector's length. /// On the other hand, it will never return a pointer to a memory location that the vector does not own. - unsafe fn get_ptr_mut_and_indices( + fn get_ptr_and_indices( + &self, + fragments: &[Fragment], + index: usize, + ) -> Option<(*const T, usize, usize)> { + let mut prev_cumulative_capacity = 0; + let mut cumulative_capacity = 0; + for (f, fragment) in fragments.iter().enumerate() { + cumulative_capacity += fragment.capacity(); + if index < cumulative_capacity { + let index_in_fragment = index - prev_cumulative_capacity; + return Some(( + unsafe { fragment.as_ptr().add(index_in_fragment) }, + f, + index_in_fragment, + )); + } + prev_cumulative_capacity = cumulative_capacity; + } + None + } + + /// ***O(fragments.len())*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments` + /// together with the index of the fragment that the element belongs to + /// and index of the element withing the respective fragment. + /// + /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments. + /// + /// # Safety + /// + /// This method allows to write to a memory which is greater than the vector's length. + /// On the other hand, it will never return a pointer to a memory location that the vector does not own. + fn get_ptr_mut_and_indices( &self, fragments: &mut [Fragment], index: usize, @@ -79,7 +123,7 @@ pub trait Growth: Clone + PseudoDefault { if index < cumulative_capacity { let index_in_fragment = index - prev_cumulative_capacity; return Some(( - fragment.as_mut_ptr().add(index_in_fragment), + unsafe { fragment.as_mut_ptr().add(index_in_fragment) }, f, index_in_fragment, )); @@ -177,6 +221,21 @@ pub trait GrowthWithConstantTimeAccess: Growth { /// * and hence, returns the expected fragment and within-fragment indices for any index computed by the constant access time function. fn get_fragment_and_inner_indices_unchecked(&self, element_index: usize) -> (usize, usize); + /// ***O(1)*** Returns a pointer to the `index`-th element of the split vector of the `fragments`. + /// + /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments. + /// + /// # Safety + /// + /// 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. + fn get_ptr(&self, fragments: &[Fragment], index: usize) -> Option<*const T> { + let (f, i) = self.get_fragment_and_inner_indices_unchecked(index); + fragments + .get(f) + .map(|fragment| unsafe { fragment.as_ptr().add(i) }) + } + /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`. /// /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments. @@ -185,11 +244,11 @@ pub trait GrowthWithConstantTimeAccess: Growth { /// /// 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. - unsafe fn get_ptr_mut(&self, fragments: &mut [Fragment], index: usize) -> Option<*mut T> { + fn get_ptr_mut(&self, fragments: &mut [Fragment], index: usize) -> Option<*mut T> { let (f, i) = self.get_fragment_and_inner_indices_unchecked(index); fragments .get_mut(f) - .map(|fragment| fragment.as_mut_ptr().add(i)) + .map(|fragment| unsafe { fragment.as_mut_ptr().add(i) }) } /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments` @@ -202,7 +261,7 @@ pub trait GrowthWithConstantTimeAccess: Growth { /// /// 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. - unsafe fn get_ptr_mut_and_indices( + fn get_ptr_mut_and_indices( &self, fragments: &mut [Fragment], index: usize, @@ -210,7 +269,7 @@ pub trait GrowthWithConstantTimeAccess: Growth { let (f, i) = self.get_fragment_and_inner_indices_unchecked(index); fragments .get_mut(f) - .map(|fragment| (fragment.as_mut_ptr().add(i), f, i)) + .map(|fragment| (unsafe { fragment.as_mut_ptr().add(i) }, f, i)) } /// ***O(1)*** Returns the capacity of the fragment with the given `fragment_index`. diff --git a/src/growth/linear/linear_growth.rs b/src/growth/linear/linear_growth.rs index b86192f..5671b49 100644 --- a/src/growth/linear/linear_growth.rs +++ b/src/growth/linear/linear_growth.rs @@ -83,6 +83,19 @@ impl Growth for Linear { } } + /// ***O(1)*** Returns a pointer to the `index`-th element of the split vector of the `fragments`. + /// + /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments. + /// + /// # Safety + /// + /// 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)] + fn get_ptr(&self, fragments: &[Fragment], index: usize) -> Option<*const T> { + ::get_ptr(self, fragments, index) + } + /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`. /// /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments. @@ -92,7 +105,7 @@ impl Growth for Linear { /// 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(&self, fragments: &mut [Fragment], index: usize) -> Option<*mut T> { + fn get_ptr_mut(&self, fragments: &mut [Fragment], index: usize) -> Option<*mut T> { ::get_ptr_mut(self, fragments, index) } @@ -106,7 +119,7 @@ impl Growth for Linear { /// /// 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. - unsafe fn get_ptr_mut_and_indices( + fn get_ptr_mut_and_indices( &self, fragments: &mut [Fragment], index: usize, @@ -207,7 +220,7 @@ impl SplitVec { /// Note that this (only) important for concurrent programs: /// * SplitVec already keeps all elements pinned to their locations; /// * Creating a buffer for storing the meta information is important for keeping the meta information pinned as well. - /// This is relevant and important for concurrent programs. + /// This is relevant and important for concurrent programs. /// /// # Panics /// diff --git a/src/growth/recursive/append.rs b/src/growth/recursive/append.rs index 8f80e4e..5ca002c 100644 --- a/src/growth/recursive/append.rs +++ b/src/growth/recursive/append.rs @@ -29,5 +29,90 @@ impl SplitVec { self.len += fragment.len(); self.fragments.push(fragment); } + // TODO: does this break internal structure of the vec; be careful on its impact on linked-list + } +} + +#[cfg(test)] +mod tests { + use super::*; + use orx_pinned_vec::PinnedVec; + + #[test] + fn append_full_fragment_when_empty() { + let mut vec = SplitVec::with_recursive_growth(); + assert_eq!(vec.capacity(), 4); + + vec.append(alloc::vec![0, 1, 2]); + assert_eq!(vec.fragments().len(), 2); + assert_eq!(vec.capacity(), 4 + 3); + + vec.push(3); + assert_eq!(vec.fragments().len(), 3); + assert_eq!(vec.capacity(), 4 + 3 + 6); + + assert_eq!(vec, &[0, 1, 2, 3]); + } + + #[test] + fn append_half_fragment_when_empty() { + let mut vec = SplitVec::with_recursive_growth(); + assert_eq!(vec.capacity(), 4); + + let mut append = alloc::vec::Vec::with_capacity(4); + append.extend_from_slice(&[0, 1, 2]); + vec.append(append); + assert_eq!(vec.fragments().len(), 2); + assert_eq!(vec.capacity(), 4 + 4); + + vec.push(3); + assert_eq!(vec.fragments().len(), 2); + assert_eq!(vec.capacity(), 4 + 4); + + vec.push(4); + assert_eq!(vec.fragments().len(), 3); + assert_eq!(vec.capacity(), 4 + 4 + 8); + + assert_eq!(vec, &[0, 1, 2, 3, 4]); + } + + #[test] + fn append_full_fragment_when_non_empty() { + let mut vec = SplitVec::with_recursive_growth(); + vec.push(42); + assert_eq!(vec.capacity(), 4); + + vec.append(alloc::vec![0, 1, 2]); + assert_eq!(vec.fragments().len(), 2); + assert_eq!(vec.capacity(), 4 + 3); + + vec.push(3); + assert_eq!(vec.fragments().len(), 3); + assert_eq!(vec.capacity(), 4 + 3 + 6); + + assert_eq!(vec, &[42, 0, 1, 2, 3]); + } + + #[test] + fn append_half_fragment_when_non_empty() { + let mut vec = SplitVec::with_recursive_growth(); + vec.push(42); + assert_eq!(vec.capacity(), 4); + + let mut append = alloc::vec::Vec::with_capacity(4); + append.extend_from_slice(&[0, 1, 2]); + vec.append(append); + assert_eq!(vec.fragments().len(), 2); + assert_eq!(vec.capacity(), 4 + 4); + + vec.push(3); + assert_eq!(vec.fragments().len(), 2); + assert_eq!(vec.capacity(), 4 + 4); + + vec.push(4); + assert_eq!(vec.fragments().len(), 3); + assert_eq!(vec.capacity(), 4 + 4 + 8); + + assert_eq!(vec, &[42, 0, 1, 2, 3, 4]); } } diff --git a/src/growth/recursive/recursive_growth.rs b/src/growth/recursive/recursive_growth.rs index 0325ec7..5f649b3 100644 --- a/src/growth/recursive/recursive_growth.rs +++ b/src/growth/recursive/recursive_growth.rs @@ -132,8 +132,8 @@ impl SplitVec { /// /// * `Doubling` strategy provides a constant time access by random indices. /// * `Recursive` strategy provides a random access time complexity that is linear in the number of fragments. - /// Note that this is significantly faster than the linear-in-number-of-elements complexity of linked lists; - /// however, significantly slower than the `Doubling` strategy's constant time. + /// Note that this is significantly faster than the linear-in-number-of-elements complexity of linked lists; + /// however, significantly slower than the `Doubling` strategy's constant time. /// /// ## Append /// @@ -141,11 +141,11 @@ impl SplitVec { /// /// `SplitVec::append` method should not be confused with `std::vec::Vec::append` method: /// * The split vector version consumes the vector to be appended. - /// It takes advantage of its split nature and appends the other vector simply by owning its pointer. - /// In other words, the other vector is appended to this vector with no cost and no copies. + /// It takes advantage of its split nature and appends the other vector simply by owning its pointer. + /// In other words, the other vector is appended to this vector with no cost and no copies. /// * The standard vector version mutates the vector to be appended, - /// moving all its element to the first vector leaving the latter empty. - /// This operation is carried out by memory copies. + /// moving all its element to the first vector leaving the latter empty. + /// This operation is carried out by memory copies. /// /// # Examples /// @@ -198,7 +198,7 @@ impl SplitVec { /// Note that this (only) important for concurrent programs: /// * SplitVec already keeps all elements pinned to their locations; /// * Creating a buffer for storing the meta information is important for keeping the meta information pinned as well. - /// This is relevant and important for concurrent programs. + /// This is relevant and important for concurrent programs. /// /// # Panics /// @@ -234,7 +234,7 @@ mod tests { let maybe_fi = growth.get_fragment_and_inner_indices(len, &fragments, index); assert_eq!(maybe_fi, Some((f, i))); - let ptr = unsafe { growth.get_ptr_mut(&mut fragments, index) }.expect("is-some"); + let ptr = growth.get_ptr_mut(&mut fragments, index).expect("is-some"); assert_eq!(unsafe { *ptr }, index); unsafe { *ptr = 10 * index }; @@ -276,8 +276,7 @@ mod tests { assert_eq!(maybe_fi, Some((f, i))); - let ptr = - unsafe { growth.get_ptr_mut(&mut fragments, index) }.expect("is-some"); + let ptr = growth.get_ptr_mut(&mut fragments, index).expect("is-some"); assert_eq!(unsafe { *ptr }, index); unsafe { *ptr = 10 * index }; @@ -322,6 +321,9 @@ mod tests { assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32); vec.append(alloc::vec!['x'; 10]); + assert_eq!(vec.fragments().len(), 2); + assert_eq!(vec.fragments()[1].capacity(), 10); + assert_eq!(vec.fragments()[1].len(), 10); assert_eq!(max_cap(&vec), 4 + 10 + 20 + 40); } @@ -368,6 +370,9 @@ mod tests { #[test] fn required_fragments_len_when_appended() { let mut vec: SplitVec = SplitVec::with_recursive_growth(); + for _ in 0..4 { + vec.push('x') + } vec.append(alloc::vec!['x'; 10]); let num_fragments = |max_cap| { diff --git a/src/lib.rs b/src/lib.rs index b05ea93..ae5c590 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,6 +279,9 @@ )] #![no_std] +#[cfg(test)] +extern crate std; + extern crate alloc; mod algorithms; @@ -289,6 +292,7 @@ mod growth; mod into_concurrent_pinned_vec; mod new_split_vec; mod pinned_vec; +mod pointers; mod range_helpers; mod resize_multiple; mod slice; diff --git a/src/pinned_vec.rs b/src/pinned_vec.rs index 60453cb..382e6bc 100644 --- a/src/pinned_vec.rs +++ b/src/pinned_vec.rs @@ -1,3 +1,5 @@ +use crate::common_traits::iterator::iter_ptr::IterPtr; +use crate::common_traits::iterator::iter_ptr_bwd::IterPtrBackward; use crate::fragment::fragment_struct::set_fragments_len; use crate::range_helpers::{range_end, range_start}; use crate::{algorithms, Fragment, Growth, SplitVec}; @@ -82,6 +84,64 @@ impl PinnedVec for SplitVec { None } + /// Returns the index of the element of the vector that the `element_ptr` points to. + /// This method has *O(f)* time complexity where `f << vec.len()` is the number of fragments. + /// + /// Note that `T: Eq` is not required; reference equality is used. + /// + /// # Safety + /// + /// Since `SplitVec` implements `PinnedVec`, the underlying memory + /// of the vector stays pinned; i.e., is not carried to different memory + /// locations. + /// Therefore, it is possible and safe to compare an element's reference + /// to find its position in the vector. + fn index_of_ptr(&self, element_ptr: *const T) -> Option { + // TODO! # examples in docs + let mut count = 0; + for fragment in &self.fragments { + if let Some(index) = slice::index_of_ptr(&fragment.data, element_ptr) { + return Some(count + index); + } else { + count += fragment.len() + } + } + None + } + + fn push_get_ptr(&mut self, value: T) -> *const T { + self.len += 1; + match self.has_capacity_for_one() { + true => { + let f = self.fragments.len() - 1; + let fragment = &mut self.fragments[f]; + let idx = fragment.len(); + fragment.push(value); + unsafe { fragment.as_ptr().add(idx) } + } + false => { + // + self.add_fragment_with_first_value(value); + let f = self.fragments.len() - 1; + self.fragments[f].as_ptr() + } + } + } + + unsafe fn iter_ptr<'v, 'i>(&'v self) -> impl Iterator + 'i + where + T: 'i, + { + IterPtr::from(self.fragments.as_slice()) + } + + unsafe fn iter_ptr_rev<'v, 'i>(&'v self) -> impl Iterator + 'i + where + T: 'i, + { + IterPtrBackward::from(self.fragments.as_slice()) + } + /// Returns whether or not the `element` with the given reference belongs to the vector. /// This method has *O(f)* time complexity where `f << vec.len()` is the number of fragments. /// @@ -127,7 +187,18 @@ impl PinnedVec for SplitVec { fn contains_reference(&self, element: &T) -> bool { self.fragments .iter() - .any(|fragment| slice::contains_reference(&fragment.data, element)) + .any(|fragment| slice::contains_reference(fragment.as_slice(), element)) + } + + /// Returns whether or not the element with the given pointer belongs to the vector. + /// This method has *O(f)* time complexity where `f << vec.len()` is the number of fragments. + /// + /// Note that `T: Eq` is not required; memory address is used. + /// + fn contains_ptr(&self, element_ptr: *const T) -> bool { + self.fragments + .iter() + .any(|fragment| slice::contains_ptr(fragment.as_slice(), element_ptr)) } /// Returns the total number of elements the split vector can hold without @@ -470,13 +541,13 @@ impl PinnedVec for SplitVec { /// ``` fn push(&mut self, value: T) { self.len += 1; - if self.has_capacity_for_one() { - let last_f = self.fragments.len() - 1; - self.fragments[last_f].push(value); - return; + match self.has_capacity_for_one() { + true => { + let last_f = self.fragments.len() - 1; + self.fragments[last_f].push(value); + } + false => self.add_fragment_with_first_value(value), } - - self.add_fragment_with_first_value(value); } fn remove(&mut self, index: usize) -> T { @@ -506,10 +577,10 @@ impl PinnedVec for SplitVec { fn swap(&mut self, a: usize, b: usize) { let (af, ai) = self .get_fragment_and_inner_indices(a) - .expect("out-of-bounds"); + .expect("first index is out-of-bounds"); let (bf, bi) = self .get_fragment_and_inner_indices(b) - .expect("out-of-bounds"); + .expect("second index out-of-bounds"); if af == bf { self.fragments[af].swap(ai, bi); } else { @@ -692,7 +763,25 @@ impl PinnedVec for SplitVec { } } - /// Returns a mutable reference to the `index`-th element of the vector. + /// Returns a pointer to the `index`-th element of the vector. + /// + /// Returns `None` if `index`-th position does not belong to the vector; i.e., if `index` is out of `capacity`. + /// + /// Time complexity of the method is: + /// * ***O(1)*** when `G: GrowthWithConstantTimeAccess`, + /// * ***O(f)*** for the general case `G: Growth` where `f` is the number of fragments in the split vector. + /// + /// # Safety + /// + /// This method allows to write to a memory which is greater than the 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)] + fn get_ptr(&self, index: usize) -> Option<*const T> { + self.growth_get_ptr(index) + } + + /// Returns a mutable pointer to the `index`-th element of the vector. /// /// Returns `None` if `index`-th position does not belong to the vector; i.e., if `index` is out of `capacity`. /// @@ -706,7 +795,7 @@ impl PinnedVec for SplitVec { /// 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(&mut self, index: usize) -> Option<*mut T> { + fn get_ptr_mut(&mut self, index: usize) -> Option<*mut T> { self.growth_get_ptr_mut(index) } @@ -1213,7 +1302,7 @@ mod tests { } for i in vec.capacity()..(vec.capacity() + 100) { - assert!(unsafe { vec.get_ptr_mut(i) }.is_none()); + assert!(vec.get_ptr_mut(i).is_none()); } } diff --git a/src/pointers/mod.rs b/src/pointers/mod.rs new file mode 100644 index 0000000..a131b9b --- /dev/null +++ b/src/pointers/mod.rs @@ -0,0 +1,7 @@ +mod ptr; +mod ptr_backward; +mod ptrs; + +pub use ptr::Ptr; +pub use ptr_backward::PtrBackward; +pub use ptrs::Ptrs; diff --git a/src/pointers/ptr.rs b/src/pointers/ptr.rs new file mode 100644 index 0000000..236c7f9 --- /dev/null +++ b/src/pointers/ptr.rs @@ -0,0 +1,108 @@ +use crate::Fragment; +use core::iter::FusedIterator; + +#[derive(PartialEq, Eq, Debug, Copy)] +pub struct Ptr { + current: *const T, + stopper: *const T, +} + +impl Default for Ptr { + fn default() -> Self { + Self { + current: core::ptr::null(), + stopper: core::ptr::null(), + } + } +} + +impl Clone for Ptr { + fn clone(&self) -> Self { + Self { + current: self.current, + stopper: self.stopper, + } + } +} + +impl<'a, T> From<&'a Fragment> for Ptr { + fn from(value: &'a Fragment) -> Self { + match value.len() { + 0 => Self::default(), + _ => { + let current = value.as_ptr(); + let stopper = unsafe { current.add(value.len()) }; + Self { current, stopper } + } + } + } +} + +impl Iterator for Ptr { + type Item = *const T; + + fn next(&mut self) -> Option { + let p = self.current; + match p == self.stopper { + false => { + self.current = unsafe { self.current.add(1) }; + Some(p) + } + true => None, + } + } +} + +impl FusedIterator for Ptr {} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::{ + string::{String, ToString}, + vec::Vec, + }; + + #[test] + fn ptr_empty_fragment() { + let fragment: Fragment = Vec::with_capacity(0).into(); + + let mut iter = Ptr::from(&fragment); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } + + #[test] + fn ptr_half_full_fragment() { + let mut fragment: Fragment = Vec::with_capacity(14).into(); + for i in 0..9 { + fragment.push(i.to_string()); + } + + let mut iter = Ptr::from(&fragment); + for i in 0..9 { + let val = iter.next().map(|p| unsafe { &*p }); + assert_eq!(val, Some(&i.to_string())); + } + + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } + + #[test] + fn ptr_full_fragment() { + let mut fragment: Fragment = Vec::with_capacity(14).into(); + for i in 0..14 { + fragment.push(i.to_string()); + } + + let mut iter = Ptr::from(&fragment); + for i in 0..14 { + let val = iter.next().map(|p| unsafe { &*p }); + assert_eq!(val, Some(&i.to_string())); + } + + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } +} diff --git a/src/pointers/ptr_backward.rs b/src/pointers/ptr_backward.rs new file mode 100644 index 0000000..6fc6189 --- /dev/null +++ b/src/pointers/ptr_backward.rs @@ -0,0 +1,104 @@ +use crate::Fragment; +use core::iter::FusedIterator; + +#[derive(PartialEq, Eq, Debug, Copy)] +pub struct PtrBackward { + current: Option<*const T>, + stopper: *const T, +} + +impl Default for PtrBackward { + fn default() -> Self { + Self { + current: None, + stopper: core::ptr::null(), + } + } +} + +impl Clone for PtrBackward { + fn clone(&self) -> Self { + Self { + current: self.current, + stopper: self.stopper, + } + } +} + +impl<'a, T> From<&'a Fragment> for PtrBackward { + fn from(value: &'a Fragment) -> Self { + match value.len() { + 0 => Self::default(), + _ => { + let stopper = value.as_ptr(); + let current = Some(unsafe { value.as_ptr().add(value.len() - 1) }); + Self { current, stopper } + } + } + } +} + +impl Iterator for PtrBackward { + type Item = *const T; + + fn next(&mut self) -> Option { + self.current.inspect(|&p| match p == self.stopper { + false => self.current = Some(unsafe { p.sub(1) }), + true => self.current = None, + }) + } +} + +impl FusedIterator for PtrBackward {} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::{ + string::{String, ToString}, + vec::Vec, + }; + + #[test] + fn ptr_bwd_empty_fragment() { + let fragment: Fragment = Vec::with_capacity(0).into(); + + let mut iter = PtrBackward::from(&fragment); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } + + #[test] + fn ptr_bwd_half_full_fragment() { + let mut fragment: Fragment = Vec::with_capacity(14).into(); + for i in 0..9 { + fragment.push(i.to_string()); + } + + let mut iter = PtrBackward::from(&fragment); + for i in (0..9).rev() { + let val = iter.next().map(|p| unsafe { &*p }); + assert_eq!(val, Some(&i.to_string())); + } + + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } + + #[test] + fn ptr_bwd_full_fragment() { + let mut fragment: Fragment = Vec::with_capacity(14).into(); + for i in 0..14 { + fragment.push(i.to_string()); + } + + let mut iter = PtrBackward::from(&fragment); + for i in (0..14).rev() { + let val = iter.next().map(|p| unsafe { &*p }); + assert_eq!(val, Some(&i.to_string())); + } + + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + } +} diff --git a/src/pointers/ptrs.rs b/src/pointers/ptrs.rs new file mode 100644 index 0000000..061b83f --- /dev/null +++ b/src/pointers/ptrs.rs @@ -0,0 +1,247 @@ +use crate::{ + pointers::{Ptr, PtrBackward}, + Fragment, +}; + +#[derive(Copy)] +pub struct Ptrs { + begin: *const Fragment, + num_fragments: usize, +} + +impl Clone for Ptrs { + fn clone(&self) -> Self { + Self { + begin: self.begin, + num_fragments: self.num_fragments, + } + } +} + +impl<'a, T> From<&'a [Fragment]> for Ptrs { + fn from(value: &'a [Fragment]) -> Self { + match value.len() { + 0 => Self { + begin: core::ptr::null(), + num_fragments: 0, + }, + num_fragments => Self { + begin: value.as_ptr(), + num_fragments, + }, + } + } +} + +impl Ptrs { + pub unsafe fn get(&self, f: usize) -> Option> { + (f < self.num_fragments).then(|| Ptr::from(&*self.begin.add(f))) + } + + pub unsafe fn get_bwd(&self, f: usize) -> Option> { + (f < self.num_fragments).then(|| PtrBackward::from(&*self.begin.add(f))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::{ + string::{String, ToString}, + vec::Vec, + }; + + #[test] + fn ptr_zero_fragments() { + let fragments: Vec> = Vec::with_capacity(4); + let ptrs = Ptrs::from(fragments.as_slice()); + + for i in 0..10 { + assert!(unsafe { ptrs.get(i) }.is_none()); + } + } + + #[test] + fn ptr_one_empty_fragment() { + let fragment: Fragment = Vec::with_capacity(2).into(); + let mut fragments: Vec> = Vec::with_capacity(4); + fragments.push(fragment); + + let ptrs = Ptrs::from(fragments.as_slice()); + let mut ptr = unsafe { ptrs.get(0) }.unwrap(); + assert_eq!(ptr.next(), None); + assert_eq!(ptr.next(), None); + } + + #[test] + fn ptr_one_non_empty_fragment() { + let mut fragment: Fragment = Vec::with_capacity(3).into(); + fragment.push(0.to_string()); + fragment.push(1.to_string()); + + let mut fragments: Vec> = Vec::with_capacity(4); + fragments.push(fragment); + + let ptrs = Ptrs::from(fragments.as_slice()); + let mut ptr = unsafe { ptrs.get(0) }.unwrap(); + for i in 0..2 { + assert_eq!(ptr.next().map(|p| unsafe { &*p }), Some(&i.to_string())); + } + assert_eq!(ptr.next(), None); + assert_eq!(ptr.next(), None); + } + + #[test] + fn ptr_many_non_empty_fragments() { + let mut fragments: Vec> = Vec::with_capacity(4); + + let prior = 0; + let n = 4; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 8; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 16; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let ptrs = Ptrs::from(fragments.as_slice()); + let mut prior = 0; + for f in 0..fragments.len() { + let mut ptr = unsafe { ptrs.get(f) }.unwrap(); + for i in 0..fragments[f].len() { + assert_eq!( + ptr.next().map(|p| unsafe { &*p }), + Some(&(prior + i).to_string()) + ); + } + assert_eq!(ptr.next(), None); + assert_eq!(ptr.next(), None); + prior += fragments[f].len(); + } + } + + #[test] + fn ptr_many_non_empty_fragments_half_last() { + let mut fragments: Vec> = Vec::with_capacity(4); + + let prior = 0; + let n = 4; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 8; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 16; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..(n / 2) { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let ptrs = Ptrs::from(fragments.as_slice()); + let mut prior = 0; + for f in 0..fragments.len() { + let mut ptr = unsafe { ptrs.get(f) }.unwrap(); + for i in 0..fragments[f].len() { + assert_eq!( + ptr.next().map(|p| unsafe { &*p }), + Some(&(prior + i).to_string()) + ); + } + assert_eq!(ptr.next(), None); + assert_eq!(ptr.next(), None); + prior += fragments[f].len(); + } + } + + #[test] + fn ptr_mutate_nodes_while_iterating() { + let mut fragments: Vec> = Vec::with_capacity(4); + + let prior = 0; + let n = 4; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 8; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..n { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let prior = prior + n; + let n = 16; + let mut fragment: Fragment = Vec::with_capacity(n).into(); + for i in 0..(n / 2) { + fragment.push((prior + i).to_string()); + } + fragments.push(fragment); + + let ptrs = Ptrs::from(fragments.as_slice()); + let mut prior = 0; + for f in 0..fragments.len() { + let mut ptr = unsafe { ptrs.get(f) }.unwrap(); + for i in 0..fragments[f].len() { + assert_eq!( + ptr.next().map(|p| unsafe { &*p }), + Some(&(prior + i).to_string()) + ); + + if f > 0 { + let first = { + let f = unsafe { &*fragments.as_ptr().add(f - 1) }; + let p = unsafe { f.as_ptr().add(0) } as *mut String; + p + }; + let second = { + let f = unsafe { &*fragments.as_ptr().add(f) }; + let p = unsafe { f.as_ptr().add(i) } as *mut String; + p + }; + unsafe { first.swap(second) }; + } + + // fragments[f][i] = 0.to_string(); => miri error!!! but below is OK + let f = unsafe { &*fragments.as_ptr().add(f) }; + let p = unsafe { f.as_ptr().add(i) } as *mut String; + let mut new_str = 0.to_string(); + let mut_ref = &mut new_str; + let mut_ptr = mut_ref as *mut String; + unsafe { p.swap(mut_ptr) }; + } + assert_eq!(ptr.next(), None); + assert_eq!(ptr.next(), None); + prior += fragments[f].len(); + } + } +} diff --git a/src/split_vec.rs b/src/split_vec.rs index e7bc279..a3cfda9 100644 --- a/src/split_vec.rs +++ b/src/split_vec.rs @@ -174,7 +174,11 @@ where // helpers + #[inline(always)] pub(crate) fn has_capacity_for_one(&self) -> bool { + // TODO: below line should not fail but it does when clear or truncate is called + // self.fragments[self.fragments.len() - 1].has_capacity_for_one() + self.fragments .last() .map(|f| f.has_capacity_for_one()) @@ -216,7 +220,12 @@ where } #[inline(always)] - pub(crate) unsafe fn growth_get_ptr_mut(&mut self, index: usize) -> Option<*mut T> { + pub(crate) fn growth_get_ptr(&self, index: usize) -> Option<*const T> { + self.growth.get_ptr(&self.fragments, index) + } + + #[inline(always)] + pub(crate) fn growth_get_ptr_mut(&mut self, index: usize) -> Option<*mut T> { self.growth.get_ptr_mut(&mut self.fragments, index) } @@ -295,16 +304,16 @@ mod tests { vec.push(i); } for i in 0..64 { - let p = unsafe { vec.get_ptr_mut(i) }.expect("is-some"); + let p = vec.get_ptr_mut(i).expect("is-some"); assert_eq!(i, unsafe { *p }); } for i in 64..vec.capacity() { - let p = unsafe { vec.get_ptr_mut(i) }; + let p = vec.get_ptr_mut(i); assert!(p.is_some()); } for i in vec.capacity()..(vec.capacity() * 2) { - let p = unsafe { vec.get_ptr_mut(i) }; + let p = vec.get_ptr_mut(i); assert!(p.is_none()); } }