orx_split_vec/
split_vec.rs

1use crate::{Doubling, Growth, fragment::fragment_struct::Fragment};
2use alloc::string::String;
3use alloc::vec::Vec;
4
5/// A split vector consisting of a vector of fragments.
6///
7/// A fragment is a contiguous memory storing elements of the vector.
8/// Therefore, SplitVec is not one large contiguous memory fragment;
9/// it is rather a sequence of contiguous fragments.
10///
11/// Different [`Growth`] strategies define the size of the fragments:
12/// * [`Doubling`] (similarly [`Recursive`]) strategy keeps doubling the capacity
13///   of fragments. Therefore, for sequential iteration, its amortized time
14///   complexity is equal to one large contiguous fragment.
15///   Furthermore, it allows for constant time random access.
16/// * [`Linear`], on the other hand, keeps creating fragments of equal
17///   sizes. It is then the caller's choice to decide on the level of
18///   fragmentation. Linear growth strategy also allows for constant time random
19///   access.
20/// * It is also possible to define a custom growth strategy where the implementation
21///   decides on the size of each next fragment to be allocated. Please see the
22///   [`Growth`] trait documentation for details.
23///
24///
25/// # Features
26///
27/// SplitVec behaves pretty much like a standard vector. However, since it implements [`PinnedVec`],
28/// it can be used as the vector storage that requires pinned elements.
29/// For instance, we cannot use a standard vector as the backing storage of a
30/// [`LinkedList`](https://crates.io/crates/orx-linked-list), [`ImpVec`](https://crates.io/crates/orx-imp-vec)
31/// or [`ConcurrentVec`](https://crates.io/crates/orx-concurrent-vec), while we can use SplitVec due to
32/// its pinned elements guarantee.
33///
34/// A split vec has the following features:
35///
36/// * Flexible in growth strategies; custom strategies can be defined.
37/// * Growth does not cause memory copies.
38/// * Capacity of an already created fragment is never changed.
39/// * Memory location of an item added to the split vector will never change unless
40///   either of `remove`, `pop`, `insert`, `clear` or `truncate` mutation methods are
41///   called.
42///
43/// [`Recursive`]: crate::Recursive
44/// [`Linear`]: crate::Linear
45/// [`PinnedVec`]: orx_pinned_vec::PinnedVec
46pub struct SplitVec<T, G = Doubling>
47where
48    G: Growth,
49{
50    pub(crate) len: usize,
51    pub(crate) fragments: Vec<Fragment<T>>,
52    pub(crate) growth: G,
53}
54
55impl<T, G> SplitVec<T, G>
56where
57    G: Growth,
58{
59    pub(crate) fn from_raw_parts(len: usize, fragments: Vec<Fragment<T>>, growth: G) -> Self {
60        debug_assert_eq!(len, fragments.iter().map(|x| x.len()).sum::<usize>());
61        Self {
62            len,
63            fragments,
64            growth,
65        }
66    }
67
68    // get
69
70    /// Growth strategy of the split vector.
71    ///
72    /// Note that allocated data of split vector is pinned and allocated in fragments.
73    /// Therefore, growth does not require copying data.
74    ///
75    /// The growth strategy determines the capacity of each fragment
76    /// that will be added to the split vector when needed.
77    ///
78    /// Furthermore, it has an impact on index-access to the elements.
79    /// See below for the complexities:
80    ///
81    /// * `Linear` (`SplitVec::with_linear_growth`) -> O(1)
82    /// * `Doubling` (`SplitVec::with_doubling_growth`) -> O(1)
83    /// * `Recursive` (`SplitVec::with_recursive_growth`) -> O(f) where f is the number of fragments; and O(1) append time complexity
84    pub fn growth(&self) -> &G {
85        &self.growth
86    }
87
88    /// Returns a mutable reference to the vector of fragments.
89    ///
90    /// # Safety
91    ///
92    /// Fragments of the split vector maintain the following structure:
93    /// * the fragments vector is never empty, it has at least one fragment;
94    /// * all fragments have a positive capacity;
95    ///     * capacity of fragment f is equal to `self.growth.get_capacity(f)`.
96    /// * if there exist F fragments in the vector:
97    ///     * none of the fragments with indices `0..F-2` has capacity; i.e., len==capacity,
98    ///     * the last fragment at position `F-1` might or might not have capacity.
99    ///
100    /// Breaking this structure invalidates the `SplitVec` struct,
101    /// and its methods lead to UB.
102    pub unsafe fn fragments_mut(&mut self) -> &mut Vec<Fragment<T>> {
103        &mut self.fragments
104    }
105
106    /// Returns the fragments of the split vector.
107    ///
108    /// The fragments of the split vector satisfy the following structure:
109    /// * the fragments vector is never empty, it has at least one fragment;
110    /// * all fragments have a positive capacity;
111    ///     * capacity of fragment f is equal to `self.growth.get_capacity(f)`.
112    /// * if there exist F fragments in the vector:
113    ///     * none of the fragments with indices `0..F-2` has capacity; i.e., len==capacity,
114    ///     * the last fragment at position `F-1` might or might not have capacity.
115    ///
116    /// # Examples
117    ///
118    /// ```
119    /// use orx_split_vec::*;
120    ///
121    /// let mut vec = SplitVec::with_linear_growth(2);
122    ///
123    /// for i in 0..6 {
124    ///     vec.push(i);
125    /// }
126    ///
127    /// assert_eq!(2, vec.fragments().len());
128    /// assert_eq!(&[0, 1, 2, 3], vec.fragments()[0].as_slice());
129    /// assert_eq!(&[4, 5], vec.fragments()[1].as_slice());
130    ///
131    /// ```
132    pub fn fragments(&self) -> &[Fragment<T>] {
133        &self.fragments
134    }
135
136    /// Maximum capacity that can safely be reached by the vector in a concurrent program.
137    /// This value is often related with the capacity of the container holding meta information about allocations.
138    /// Note that the split vector can naturally grow beyond this number, this bound is only relevant when the vector is `Sync`ed among threads.
139    pub fn maximum_concurrent_capacity(&self) -> usize {
140        self.growth()
141            .maximum_concurrent_capacity(&self.fragments, self.fragments.capacity())
142    }
143
144    /// Makes sure that the split vector can safely reach the given `maximum_capacity` in a concurrent program.
145    /// * returns Ok of the new maximum capacity if the vector succeeds to reserve.
146    /// * returns the corresponding error message otherwise.
147    ///
148    /// Note that this method does not allocate the `maximum_capacity`, it only ensures that the concurrent growth to this capacity is safe.
149    /// In order to achieve this, it might need to extend allocation of the fragments collection.
150    /// However, note that by definition number of fragments is insignificant in a split vector.
151    pub fn concurrent_reserve(&mut self, maximum_capacity: usize) -> Result<usize, String> {
152        let required_num_fragments = self
153            .growth
154            .required_fragments_len(&self.fragments, maximum_capacity)?;
155
156        let additional_fragments = match required_num_fragments > self.fragments.capacity() {
157            true => required_num_fragments - self.fragments.capacity(),
158            false => 0,
159        };
160
161        if additional_fragments > 0 {
162            let prior_fragments_capacity = self.fragments.capacity();
163            let num_fragments = self.fragments.len();
164
165            unsafe { self.fragments.set_len(prior_fragments_capacity) };
166
167            self.fragments.reserve(additional_fragments);
168
169            #[allow(clippy::uninit_vec)]
170            unsafe {
171                self.fragments.set_len(num_fragments)
172            };
173        }
174
175        Ok(self.maximum_concurrent_capacity())
176    }
177
178    /// Returns the fragment index and the index within fragment of the item with the given `index`;
179    /// None if the index is out of bounds.
180    ///
181    /// # Examples
182    ///
183    /// ```
184    /// use orx_split_vec::*;
185    ///
186    /// let mut vec = SplitVec::with_linear_growth(2);
187    ///
188    /// for i in 0..6 {
189    ///     vec.push(i);
190    /// }
191    ///
192    /// assert_eq!(&[0, 1, 2, 3], vec.fragments()[0].as_slice());
193    /// assert_eq!(&[4, 5], vec.fragments()[1].as_slice());
194    ///
195    /// // first fragment
196    /// assert_eq!(Some((0, 0)), vec.get_fragment_and_inner_indices(0));
197    /// assert_eq!(Some((0, 1)), vec.get_fragment_and_inner_indices(1));
198    /// assert_eq!(Some((0, 2)), vec.get_fragment_and_inner_indices(2));
199    /// assert_eq!(Some((0, 3)), vec.get_fragment_and_inner_indices(3));
200    ///
201    /// // second fragment
202    /// assert_eq!(Some((1, 0)), vec.get_fragment_and_inner_indices(4));
203    /// assert_eq!(Some((1, 1)), vec.get_fragment_and_inner_indices(5));
204    ///
205    /// // out of bounds
206    /// assert_eq!(None, vec.get_fragment_and_inner_indices(6));
207    /// ```
208    #[inline(always)]
209    pub fn get_fragment_and_inner_indices(&self, index: usize) -> Option<(usize, usize)> {
210        self.growth
211            .get_fragment_and_inner_indices(self.len, &self.fragments, index)
212    }
213
214    // helpers
215
216    #[inline(always)]
217    pub(crate) fn has_capacity_for_one(&self) -> bool {
218        // TODO: below line should not fail but it does when clear or truncate is called
219        // self.fragments[self.fragments.len() - 1].has_capacity_for_one()
220
221        self.fragments
222            .last()
223            .map(|f| f.has_capacity_for_one())
224            .unwrap_or(false)
225    }
226
227    /// Adds a new fragment to fragments of the split vector; returns the capacity of the new fragment.
228    #[inline(always)]
229    pub(crate) fn add_fragment(&mut self) -> usize {
230        self.add_fragment_get_fragment_capacity(false)
231    }
232
233    /// Adds a new fragment and return the capacity of the added (now last) fragment.
234    fn add_fragment_get_fragment_capacity(&mut self, zeroed: bool) -> usize {
235        let new_fragment_capacity = self.growth.new_fragment_capacity(&self.fragments);
236
237        let mut new_fragment = Fragment::new(new_fragment_capacity);
238        if zeroed {
239            // SAFETY: new_fragment empty with len=0, zeroed elements will not be read with safe api
240            unsafe { new_fragment.zero() };
241        }
242
243        self.fragments.push(new_fragment);
244
245        new_fragment_capacity
246    }
247
248    pub(crate) fn add_fragment_with_first_value(&mut self, first_value: T) {
249        let capacity = self.growth.new_fragment_capacity(&self.fragments);
250        let new_fragment = Fragment::new_with_first_value(capacity, first_value);
251        self.fragments.push(new_fragment);
252    }
253
254    pub(crate) fn drop_last_empty_fragment(&mut self) {
255        let drop_empty_last_fragment = self.fragments.last().map(|f| f.is_empty()).unwrap_or(false);
256        if drop_empty_last_fragment {
257            _ = self.fragments.pop();
258        }
259    }
260
261    #[inline(always)]
262    pub(crate) fn growth_get_ptr(&self, index: usize) -> Option<*const T> {
263        self.growth.get_ptr(&self.fragments, index)
264    }
265
266    #[inline(always)]
267    pub(crate) fn growth_get_ptr_mut(&mut self, index: usize) -> Option<*mut T> {
268        self.growth.get_ptr_mut(&mut self.fragments, index)
269    }
270
271    /// Makes sure that the split vector can safely reach the given `maximum_capacity` in a concurrent program.
272    ///
273    /// Returns new maximum capacity.
274    ///
275    /// Note that this method does not allocate the `maximum_capacity`, it only ensures that the concurrent growth to this capacity is safe.
276    /// In order to achieve this, it might need to extend allocation of the fragments collection.
277    /// However, note that by definition number of fragments is insignificant in a split vector.
278    ///
279    /// # Panics
280    ///
281    /// Panics if the vector fails to reserve the requested capacity.
282    pub fn reserve_maximum_concurrent_capacity(&mut self, new_maximum_capacity: usize) -> usize {
283        let current_max = self.maximum_concurrent_capacity();
284        match current_max < new_maximum_capacity {
285            true => {
286                self.concurrent_reserve(new_maximum_capacity)
287                    .expect("Failed to reserve maximum capacity");
288                self.maximum_concurrent_capacity()
289            }
290            false => self.maximum_concurrent_capacity(),
291        }
292    }
293}
294
295#[cfg(test)]
296mod tests {
297    use crate::growth::growth_trait::GrowthWithConstantTimeAccess;
298    use crate::test_all_growth_types;
299    use crate::*;
300    use alloc::vec;
301
302    #[test]
303    fn fragments() {
304        fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
305            for i in 0..42 {
306                vec.push(i);
307            }
308
309            let mut combined = vec![];
310            let mut combined_mut = vec![];
311            for fra in vec.fragments() {
312                combined.extend_from_slice(fra);
313            }
314            for fra in unsafe { vec.fragments_mut() } {
315                combined_mut.extend_from_slice(fra);
316            }
317
318            for i in 0..42 {
319                assert_eq!(i, vec[i]);
320                assert_eq!(i, combined[i]);
321                assert_eq!(i, combined_mut[i]);
322            }
323        }
324        test_all_growth_types!(test);
325    }
326
327    #[test]
328    fn get_fragment_and_inner_indices() {
329        #[cfg(not(miri))]
330        const LEN: usize = 432;
331        #[cfg(miri)]
332        const LEN: usize = 57;
333
334        fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
335            for i in 0..LEN {
336                vec.push(i);
337                assert_eq!(None, vec.get_fragment_and_inner_indices(i + 1));
338            }
339
340            for i in 0..LEN {
341                let (f, ii) = vec.get_fragment_and_inner_indices(i).expect("is-some");
342                assert_eq!(vec[i], vec.fragments[f][ii]);
343            }
344        }
345        test_all_growth_types!(test);
346    }
347
348    #[test]
349    fn get_ptr_mut() {
350        fn test<G: GrowthWithConstantTimeAccess>(mut vec: SplitVec<usize, G>) {
351            for i in 0..65 {
352                vec.push(i);
353            }
354            for i in 0..64 {
355                let p = vec.get_ptr_mut(i).expect("is-some");
356                assert_eq!(i, unsafe { *p });
357            }
358            for i in 64..vec.capacity() {
359                let p = vec.get_ptr_mut(i);
360                assert!(p.is_some());
361            }
362
363            for i in vec.capacity()..(vec.capacity() * 2) {
364                let p = vec.get_ptr_mut(i);
365                assert!(p.is_none());
366            }
367        }
368
369        test(SplitVec::with_doubling_growth());
370        test(SplitVec::with_linear_growth(6));
371    }
372
373    #[test]
374    fn add_fragment() {
375        fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
376            for _ in 0..10 {
377                let expected_new_fragment_cap = vec.growth.new_fragment_capacity(&vec.fragments);
378                let new_fragment_cap = vec.add_fragment();
379                assert_eq!(expected_new_fragment_cap, new_fragment_cap);
380            }
381
382            vec.clear();
383
384            let mut expected_capacity = vec.capacity();
385            for _ in 0..2 {
386                let expected_new_fragment_cap = vec.growth.new_fragment_capacity(&vec.fragments);
387                expected_capacity += expected_new_fragment_cap;
388                vec.add_fragment();
389            }
390
391            assert_eq!(expected_capacity, vec.capacity());
392        }
393
394        test_all_growth_types!(test);
395    }
396}