orx_split_vec/
split_vec.rs

1use crate::{fragment::fragment_struct::Fragment, Doubling, Growth};
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());
61        Self {
62            len,
63            fragments,
64            growth,
65        }
66    }
67
68    // get
69    /// Growth strategy of the split vector.
70    ///
71    /// Note that allocated data of split vector is pinned and allocated in fragments.
72    /// Therefore, growth does not require copying data.
73    ///
74    /// The growth strategy determines the capacity of each fragment
75    /// that will be added to the split vector when needed.
76    ///
77    /// Furthermore, it has an impact on index-access to the elements.
78    /// See below for the complexities:
79    ///
80    /// * `Linear` (`SplitVec::with_linear_growth`) -> O(1)
81    /// * `Doubling` (`SplitVec::with_doubling_growth`) -> O(1)
82    /// * `Recursive` (`SplitVec::with_recursive_growth`) -> O(f) where f is the number of fragments; and O(1) append time complexity
83    pub fn growth(&self) -> &G {
84        &self.growth
85    }
86
87    /// Returns a mutable reference to the vector of fragments.
88    ///
89    /// # Safety
90    ///
91    /// Fragments of the split vector maintain the following structure:
92    /// * the fragments vector is never empty, it has at least one fragment;
93    /// * all fragments have a positive capacity;
94    ///     * capacity of fragment f is equal to `self.growth.get_capacity(f)`.
95    /// * if there exist F fragments in the vector:
96    ///     * none of the fragments with indices `0..F-2` has capacity; i.e., len==capacity,
97    ///     * the last fragment at position `F-1` might or might not have capacity.
98    ///
99    /// Breaking this structure invalidates the `SplitVec` struct,
100    /// and its methods lead to UB.
101    pub unsafe fn fragments_mut(&mut self) -> &mut Vec<Fragment<T>> {
102        &mut self.fragments
103    }
104
105    /// Returns the fragments of the split vector.
106    ///
107    /// The fragments of the split vector satisfy the following structure:
108    /// * the fragments vector is never empty, it has at least one fragment;
109    /// * all fragments have a positive capacity;
110    ///     * capacity of fragment f is equal to `self.growth.get_capacity(f)`.
111    /// * if there exist F fragments in the vector:
112    ///     * none of the fragments with indices `0..F-2` has capacity; i.e., len==capacity,
113    ///     * the last fragment at position `F-1` might or might not have capacity.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use orx_split_vec::*;
119    ///
120    /// let mut vec = SplitVec::with_linear_growth(2);
121    ///
122    /// for i in 0..6 {
123    ///     vec.push(i);
124    /// }
125    ///
126    /// assert_eq!(2, vec.fragments().len());
127    /// assert_eq!(&[0, 1, 2, 3], vec.fragments()[0].as_slice());
128    /// assert_eq!(&[4, 5], vec.fragments()[1].as_slice());
129    ///
130    /// ```
131    pub fn fragments(&self) -> &[Fragment<T>] {
132        &self.fragments
133    }
134
135    /// Maximum capacity that can safely be reached by the vector in a concurrent program.
136    /// This value is often related with the capacity of the container holding meta information about allocations.
137    /// Note that the split vector can naturally grow beyond this number, this bound is only relevant when the vector is `Sync`ed among threads.
138    pub fn maximum_concurrent_capacity(&self) -> usize {
139        self.growth()
140            .maximum_concurrent_capacity(&self.fragments, self.fragments.capacity())
141    }
142
143    /// Makes sure that the split vector can safely reach the given `maximum_capacity` in a concurrent program.
144    /// * returns Ok of the new maximum capacity if the vector succeeds to reserve.
145    /// * returns the corresponding error message otherwise.
146    ///
147    /// Note that this method does not allocate the `maximum_capacity`, it only ensures that the concurrent growth to this capacity is safe.
148    /// In order to achieve this, it might need to extend allocation of the fragments collection.
149    /// However, note that by definition number of fragments is insignificant in a split vector.
150    pub fn concurrent_reserve(&mut self, maximum_capacity: usize) -> Result<usize, String> {
151        let required_num_fragments = self
152            .growth
153            .required_fragments_len(&self.fragments, maximum_capacity)?;
154
155        let additional_fragments = match required_num_fragments > self.fragments.capacity() {
156            true => required_num_fragments - self.fragments.capacity(),
157            false => 0,
158        };
159
160        if additional_fragments > 0 {
161            let prior_fragments_capacity = self.fragments.capacity();
162            let num_fragments = self.fragments.len();
163
164            unsafe { self.fragments.set_len(prior_fragments_capacity) };
165
166            self.fragments.reserve(additional_fragments);
167
168            #[allow(clippy::uninit_vec)]
169            unsafe {
170                self.fragments.set_len(num_fragments)
171            };
172        }
173
174        Ok(self.maximum_concurrent_capacity())
175    }
176
177    /// Returns the fragment index and the index within fragment of the item with the given `index`;
178    /// None if the index is out of bounds.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// use orx_split_vec::*;
184    ///
185    /// let mut vec = SplitVec::with_linear_growth(2);
186    ///
187    /// for i in 0..6 {
188    ///     vec.push(i);
189    /// }
190    ///
191    /// assert_eq!(&[0, 1, 2, 3], vec.fragments()[0].as_slice());
192    /// assert_eq!(&[4, 5], vec.fragments()[1].as_slice());
193    ///
194    /// // first fragment
195    /// assert_eq!(Some((0, 0)), vec.get_fragment_and_inner_indices(0));
196    /// assert_eq!(Some((0, 1)), vec.get_fragment_and_inner_indices(1));
197    /// assert_eq!(Some((0, 2)), vec.get_fragment_and_inner_indices(2));
198    /// assert_eq!(Some((0, 3)), vec.get_fragment_and_inner_indices(3));
199    ///
200    /// // second fragment
201    /// assert_eq!(Some((1, 0)), vec.get_fragment_and_inner_indices(4));
202    /// assert_eq!(Some((1, 1)), vec.get_fragment_and_inner_indices(5));
203    ///
204    /// // out of bounds
205    /// assert_eq!(None, vec.get_fragment_and_inner_indices(6));
206    /// ```
207    #[inline(always)]
208    pub fn get_fragment_and_inner_indices(&self, index: usize) -> Option<(usize, usize)> {
209        self.growth
210            .get_fragment_and_inner_indices(self.len, &self.fragments, index)
211    }
212
213    // helpers
214
215    #[inline(always)]
216    pub(crate) fn has_capacity_for_one(&self) -> bool {
217        // TODO: below line should not fail but it does when clear or truncate is called
218        // self.fragments[self.fragments.len() - 1].has_capacity_for_one()
219
220        self.fragments
221            .last()
222            .map(|f| f.has_capacity_for_one())
223            .unwrap_or(false)
224    }
225
226    /// Adds a new fragment to fragments of the split vector; returns the capacity of the new fragment.
227    #[inline(always)]
228    pub(crate) fn add_fragment(&mut self) -> usize {
229        self.add_fragment_get_fragment_capacity(false)
230    }
231
232    /// Adds a new fragment and return the capacity of the added (now last) fragment.
233    fn add_fragment_get_fragment_capacity(&mut self, zeroed: bool) -> usize {
234        let new_fragment_capacity = self.growth.new_fragment_capacity(&self.fragments);
235
236        let mut new_fragment = Fragment::new(new_fragment_capacity);
237        if zeroed {
238            // SAFETY: new_fragment empty with len=0, zeroed elements will not be read with safe api
239            unsafe { new_fragment.zero() };
240        }
241
242        self.fragments.push(new_fragment);
243
244        new_fragment_capacity
245    }
246
247    pub(crate) fn add_fragment_with_first_value(&mut self, first_value: T) {
248        let capacity = self.growth.new_fragment_capacity(&self.fragments);
249        let new_fragment = Fragment::new_with_first_value(capacity, first_value);
250        self.fragments.push(new_fragment);
251    }
252
253    pub(crate) fn drop_last_empty_fragment(&mut self) {
254        let drop_empty_last_fragment = self.fragments.last().map(|f| f.is_empty()).unwrap_or(false);
255        if drop_empty_last_fragment {
256            _ = self.fragments.pop();
257        }
258    }
259
260    #[inline(always)]
261    pub(crate) fn growth_get_ptr(&self, index: usize) -> Option<*const T> {
262        self.growth.get_ptr(&self.fragments, index)
263    }
264
265    #[inline(always)]
266    pub(crate) fn growth_get_ptr_mut(&mut self, index: usize) -> Option<*mut T> {
267        self.growth.get_ptr_mut(&mut self.fragments, index)
268    }
269
270    /// Makes sure that the split vector can safely reach the given `maximum_capacity` in a concurrent program.
271    ///
272    /// Returns new maximum capacity.
273    ///
274    /// Note that this method does not allocate the `maximum_capacity`, it only ensures that the concurrent growth to this capacity is safe.
275    /// In order to achieve this, it might need to extend allocation of the fragments collection.
276    /// However, note that by definition number of fragments is insignificant in a split vector.
277    ///
278    /// # Panics
279    ///
280    /// Panics if the vector fails to reserve the requested capacity.
281    pub fn reserve_maximum_concurrent_capacity(&mut self, new_maximum_capacity: usize) -> usize {
282        let current_max = self.maximum_concurrent_capacity();
283        match current_max < new_maximum_capacity {
284            true => {
285                self.concurrent_reserve(new_maximum_capacity)
286                    .expect("Failed to reserve maximum capacity");
287                self.maximum_concurrent_capacity()
288            }
289            false => self.maximum_concurrent_capacity(),
290        }
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use crate::growth::growth_trait::GrowthWithConstantTimeAccess;
297    use crate::test_all_growth_types;
298    use crate::*;
299    use alloc::vec;
300
301    #[test]
302    fn fragments() {
303        fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
304            for i in 0..42 {
305                vec.push(i);
306            }
307
308            let mut combined = vec![];
309            let mut combined_mut = vec![];
310            for fra in vec.fragments() {
311                combined.extend_from_slice(fra);
312            }
313            for fra in unsafe { vec.fragments_mut() } {
314                combined_mut.extend_from_slice(fra);
315            }
316
317            for i in 0..42 {
318                assert_eq!(i, vec[i]);
319                assert_eq!(i, combined[i]);
320                assert_eq!(i, combined_mut[i]);
321            }
322        }
323        test_all_growth_types!(test);
324    }
325
326    #[test]
327    fn get_fragment_and_inner_indices() {
328        fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
329            for i in 0..432 {
330                vec.push(i);
331                assert_eq!(None, vec.get_fragment_and_inner_indices(i + 1));
332            }
333
334            for i in 0..432 {
335                let (f, ii) = vec.get_fragment_and_inner_indices(i).expect("is-some");
336                assert_eq!(vec[i], vec.fragments[f][ii]);
337            }
338        }
339        test_all_growth_types!(test);
340    }
341
342    #[test]
343    fn get_ptr_mut() {
344        fn test<G: GrowthWithConstantTimeAccess>(mut vec: SplitVec<usize, G>) {
345            for i in 0..65 {
346                vec.push(i);
347            }
348            for i in 0..64 {
349                let p = vec.get_ptr_mut(i).expect("is-some");
350                assert_eq!(i, unsafe { *p });
351            }
352            for i in 64..vec.capacity() {
353                let p = vec.get_ptr_mut(i);
354                assert!(p.is_some());
355            }
356
357            for i in vec.capacity()..(vec.capacity() * 2) {
358                let p = vec.get_ptr_mut(i);
359                assert!(p.is_none());
360            }
361        }
362
363        test(SplitVec::with_doubling_growth());
364        test(SplitVec::with_linear_growth(6));
365    }
366
367    #[test]
368    fn add_fragment() {
369        fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
370            for _ in 0..10 {
371                let expected_new_fragment_cap = vec.growth.new_fragment_capacity(&vec.fragments);
372                let new_fragment_cap = vec.add_fragment();
373                assert_eq!(expected_new_fragment_cap, new_fragment_cap);
374            }
375
376            vec.clear();
377
378            let mut expected_capacity = vec.capacity();
379            for _ in 0..2 {
380                let expected_new_fragment_cap = vec.growth.new_fragment_capacity(&vec.fragments);
381                expected_capacity += expected_new_fragment_cap;
382                vec.add_fragment();
383            }
384
385            assert_eq!(expected_capacity, vec.capacity());
386        }
387
388        test_all_growth_types!(test);
389    }
390}