Skip to main content

orx_split_vec/growth/recursive/
recursive_growth.rs

1use crate::{Doubling, Fragment, Growth, SplitVec};
2use alloc::string::String;
3use orx_pseudo_default::PseudoDefault;
4
5/// Equivalent to [`Doubling`] strategy except for the following:
6///
7/// * enables zero-cost (no-ops) `append` operation:
8///   * we can append standard vectors, vectors of vectors, split vectors, etc., any data that implements `IntoFragments` trait,
9///   * by simply accepting it as a whole fragment,
10///   * according to benchmarks documented in the crate definition:
11///     * `SplitVec<_, Recursive>` is infinitely faster than other growth strategies or standard vector :)
12///     * since its time complexity is independent of size of the data to be appended.
13/// * at the expense of providing slower random-access performance:
14///   * random access time complexity of `Doubling` strategy is constant time;
15///   * that of `Recursive` strategy is linear in the number of fragments;
16///   * according to benchmarks documented in the crate definition:
17///     * `SplitVec<_, Doubling>` or standard vector are around 4 to 7 times faster than `SplitVec<_, Recursive>`,
18///     * and 1.5 times faster when the elements get very large (16 x `u64`).
19///
20/// Note that other operations such as serial access are equivalent to `Doubling` strategy.
21///
22/// # Examples
23///
24/// ```
25/// use orx_split_vec::*;
26///
27/// // SplitVec<usize, Recursive>
28/// let mut vec = SplitVec::with_recursive_growth();
29///
30/// vec.push('a');
31/// assert_eq!(vec, &['a']);
32///
33/// vec.append(vec!['b', 'c']);
34/// assert_eq!(vec, &['a', 'b', 'c']);
35///
36/// vec.append(vec![vec!['d'], vec!['e', 'f']]);
37/// assert_eq!(vec, &['a', 'b', 'c', 'd', 'e', 'f']);
38///
39/// let other_split_vec: SplitVec<_> = vec!['g', 'h'].into();
40/// vec.append(other_split_vec);
41/// assert_eq!(vec, &['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']);
42/// ```
43#[derive(Debug, Default, Clone, PartialEq)]
44pub struct Recursive;
45
46impl PseudoDefault for Recursive {
47    fn pseudo_default() -> Self {
48        Default::default()
49    }
50}
51
52impl Growth for Recursive {
53    #[inline(always)]
54    fn new_fragment_capacity_from(
55        &self,
56        fragment_capacities: impl ExactSizeIterator<Item = usize>,
57    ) -> usize {
58        Doubling.new_fragment_capacity_from(fragment_capacities)
59    }
60
61    fn maximum_concurrent_capacity<T>(
62        &self,
63        fragments: &[Fragment<T>],
64        fragments_capacity: usize,
65    ) -> usize {
66        assert!(fragments_capacity >= fragments.len());
67
68        let current_capacity = fragments.iter().map(|x| x.capacity()).sum();
69        let mut last_capacity = fragments.last().map(|x| x.capacity()).unwrap_or(2);
70
71        let mut total_capacity = current_capacity;
72
73        for _ in fragments.len()..fragments_capacity {
74            last_capacity *= 2;
75            total_capacity += last_capacity;
76        }
77
78        total_capacity
79    }
80
81    fn required_fragments_len<T>(
82        &self,
83        fragments: &[Fragment<T>],
84        maximum_capacity: usize,
85    ) -> Result<usize, String> {
86        fn overflown_err() -> String {
87            alloc::format!(
88                "Maximum cumulative capacity that can be reached by the Recursive strategy is {}.",
89                usize::MAX
90            )
91        }
92
93        let current_capacity: usize = fragments.iter().map(|x| x.capacity()).sum();
94        let mut last_capacity = fragments.last().map(|x| x.capacity()).unwrap_or(2);
95
96        let mut total_capacity = current_capacity;
97        let mut f = fragments.len();
98
99        while total_capacity < maximum_capacity {
100            let (new_last_capacity, overflown) = last_capacity.overflowing_mul(2);
101            if overflown {
102                return Err(overflown_err());
103            }
104            last_capacity = new_last_capacity;
105
106            let (new_total_capacity, overflown) = total_capacity.overflowing_add(last_capacity);
107            if overflown {
108                return Err(overflown_err());
109            }
110
111            total_capacity = new_total_capacity;
112            f += 1;
113        }
114
115        Ok(f)
116    }
117
118    fn maximum_concurrent_capacity_bound<T>(
119        &self,
120        fragments: &[Fragment<T>],
121        fragments_capacity: usize,
122    ) -> usize {
123        Doubling.maximum_concurrent_capacity_bound(fragments, fragments_capacity)
124    }
125}
126
127impl<T> SplitVec<T, Recursive> {
128    /// Strategy which allows to create a fragment with double the capacity
129    /// of the prior fragment every time the split vector needs to expand.
130    ///
131    /// Notice that this is similar to the `Doubling` growth strategy.
132    /// However, `Recursive` and `Doubling` strategies have the two following important differences in terms of performance:
133    ///
134    /// * Random access by indices is much faster with `Doubling`.
135    /// * Recursive strategy enables copy-free `append` method which merges another vector to this vector in constant time.
136    ///
137    /// All other operations are expected to have similar complexity.
138    ///
139    /// ## Random Access
140    ///
141    /// * `Doubling` strategy provides a constant time access by random indices.
142    /// * `Recursive` strategy provides a random access time complexity that is linear in the number of fragments.
143    ///   Note that this is significantly faster than the linear-in-number-of-elements complexity of linked lists;
144    ///   however, significantly slower than the `Doubling` strategy's constant time.
145    ///
146    /// ## Append
147    ///
148    /// * `Recursive` strategy provides `append` operation which allows merging two vectors in constant time without copies.
149    ///
150    /// `SplitVec::append` method should not be confused with `std::vec::Vec::append` method:
151    /// * The split vector version consumes the vector to be appended.
152    ///   It takes advantage of its split nature and appends the other vector simply by owning its pointer.
153    ///   In other words, the other vector is appended to this vector with no cost and no copies.
154    /// * The standard vector version mutates the vector to be appended,
155    ///   moving all its element to the first vector leaving the latter empty.
156    ///   This operation is carried out by memory copies.
157    ///
158    /// # Examples
159    ///
160    /// ```
161    /// use orx_split_vec::*;
162    ///
163    /// // SplitVec<usize, Doubling>
164    /// let mut vec = SplitVec::with_recursive_growth();
165    ///
166    /// assert_eq!(1, vec.fragments().len());
167    /// assert_eq!(Some(4), vec.fragments().first().map(|f| f.capacity()));
168    /// assert_eq!(Some(0), vec.fragments().first().map(|f| f.len()));
169    ///
170    /// // fill the first 5 fragments
171    /// let expected_fragment_capacities = vec![4, 8, 16, 32];
172    /// let num_items: usize = expected_fragment_capacities.iter().sum();
173    /// for i in 0..num_items {
174    ///     vec.push(i);
175    /// }
176    ///
177    /// assert_eq!(
178    ///     expected_fragment_capacities,
179    ///     vec.fragments()
180    ///     .iter()
181    ///     .map(|f| f.capacity())
182    ///     .collect::<Vec<_>>()
183    /// );
184    /// assert_eq!(
185    ///     expected_fragment_capacities,
186    ///     vec.fragments().iter().map(|f| f.len()).collect::<Vec<_>>()
187    /// );
188    ///
189    /// // create the 6-th fragment doubling the capacity
190    /// vec.push(42);
191    /// assert_eq!(
192    ///     vec.fragments().len(),
193    ///     expected_fragment_capacities.len() + 1
194    /// );
195    ///
196    /// assert_eq!(vec.fragments().last().map(|f| f.capacity()), Some(32 * 2));
197    /// assert_eq!(vec.fragments().last().map(|f| f.len()), Some(1));
198    /// ```
199    pub fn with_recursive_growth() -> Self {
200        SplitVec::with_doubling_growth().into()
201    }
202
203    /// Creates a new split vector with `Recursive` growth and initial `fragments_capacity`.
204    ///
205    /// This method differs from [`SplitVec::with_recursive_growth`] only by the pre-allocation of fragments collection.
206    /// Note that this (only) important for concurrent programs:
207    /// * SplitVec already keeps all elements pinned to their locations;
208    /// * Creating a buffer for storing the meta information is important for keeping the meta information pinned as well.
209    ///   This is relevant and important for concurrent programs.
210    ///
211    /// # Panics
212    ///
213    /// Panics if `fragments_capacity == 0`.
214    pub fn with_recursive_growth_and_fragments_capacity(fragments_capacity: usize) -> Self {
215        SplitVec::with_doubling_growth_and_fragments_capacity(fragments_capacity).into()
216    }
217
218    /// Creates a new split vector with `Recursive` growth and maximum concurrent capacity which depends
219    /// on the pointer size of the target architecture.
220    ///
221    /// This method differs from [`SplitVec::with_recursive_growth`] only by the pre-allocation of fragments collection,
222    /// which never contains more elements than 33.
223    pub fn with_recursive_growth_and_max_concurrent_capacity() -> Self {
224        SplitVec::with_doubling_growth_and_max_concurrent_capacity().into()
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231    use crate::growth::doubling::constants::{CAPACITIES_LEN, FIRST_FRAGMENT_CAPACITY};
232    use alloc::vec::Vec;
233    use orx_pinned_vec::PinnedVec;
234
235    #[test]
236    fn get_fragment_and_inner_indices() {
237        let growth = Recursive;
238
239        let vecs = alloc::vec![
240            alloc::vec![0, 1, 2, 3],
241            alloc::vec![4, 5],
242            alloc::vec![6, 7, 8],
243            alloc::vec![9],
244            alloc::vec![10, 11, 12, 13, 14],
245        ];
246        let mut fragments: Vec<Fragment<_>> = vecs
247            .clone()
248            .into_iter()
249            .map(|x| Fragment::new(x.capacity(), x))
250            .collect();
251        let len = fragments.iter().map(|x| x.len()).sum();
252
253        let mut index = 0;
254        for (f, vec) in vecs.iter().enumerate() {
255            for (i, _) in vec.iter().enumerate() {
256                let maybe_fi = growth.get_fragment_and_inner_indices(len, &fragments, index);
257                assert_eq!(maybe_fi, Some((f, i)));
258
259                let ptr = growth.get_ptr_mut(&mut fragments, index).expect("is-some");
260                assert_eq!(unsafe { *ptr }, index);
261
262                unsafe { *ptr = 10 * index };
263                assert_eq!(unsafe { *ptr }, 10 * index);
264
265                index += 1;
266            }
267        }
268    }
269
270    #[test]
271    fn get_fragment_and_inner_indices_exhaustive() {
272        let growth = Recursive;
273
274        let mut fragments: Vec<Fragment<_>> = alloc::vec![];
275
276        #[cfg(not(miri))]
277        let lengths = [30, 1, 7, 3, 79, 147, 530];
278        #[cfg(miri)]
279        let lengths = [1, 7, 3, 30];
280
281        let mut index = 0;
282        for _ in 0..10 {
283            for &len in &lengths {
284                let mut vec = Vec::with_capacity(len);
285                for _ in 0..len {
286                    vec.push(index);
287                    index += 1;
288                }
289                let fragment = Fragment::new(len, vec);
290                fragments.push(fragment);
291            }
292        }
293
294        let total_len = fragments.iter().map(|x| x.len()).sum();
295
296        let mut index = 0;
297        let mut f = 0;
298        for _ in 0..10 {
299            for &len in &lengths {
300                for i in 0..len {
301                    let maybe_fi =
302                        growth.get_fragment_and_inner_indices(total_len, &fragments, index);
303
304                    assert_eq!(maybe_fi, Some((f, i)));
305
306                    let ptr = growth.get_ptr_mut(&mut fragments, index).expect("is-some");
307                    assert_eq!(unsafe { *ptr }, index);
308
309                    unsafe { *ptr = 10 * index };
310                    assert_eq!(unsafe { *ptr }, 10 * index);
311
312                    index += 1;
313                }
314                f += 1;
315            }
316        }
317    }
318
319    #[test]
320    fn maximum_concurrent_capacity() {
321        fn max_cap<T>(vec: &SplitVec<T, Recursive>) -> usize {
322            vec.growth()
323                .maximum_concurrent_capacity(vec.fragments(), vec.fragments.capacity())
324        }
325
326        let mut vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
327        assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32);
328
329        let until = max_cap(&vec);
330        for _ in 0..until {
331            vec.push('x');
332            assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32);
333        }
334
335        // fragments allocate beyond max_cap
336        vec.push('x');
337        assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512);
338    }
339
340    #[test]
341    fn maximum_concurrent_capacity_when_appended() {
342        fn max_cap<T>(vec: &SplitVec<T, Recursive>) -> usize {
343            vec.growth()
344                .maximum_concurrent_capacity(vec.fragments(), vec.fragments.capacity())
345        }
346
347        let mut vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
348        assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32);
349
350        vec.append(alloc::vec!['x'; 10]);
351        assert_eq!(vec.fragments().len(), 2);
352        assert_eq!(vec.fragments()[1].capacity(), 10);
353        assert_eq!(vec.fragments()[1].len(), 10);
354
355        assert_eq!(max_cap(&vec), 4 + 10 + 20 + 40);
356    }
357
358    #[test]
359    fn with_recursive_growth_and_fragments_capacity_normal_growth() {
360        let mut vec: SplitVec<char, _> = SplitVec::with_recursive_growth_and_fragments_capacity(1);
361
362        assert_eq!(1, vec.fragments.capacity());
363
364        #[cfg(not(miri))]
365        let n = 100_000;
366        #[cfg(miri)]
367        let n = 55;
368
369        for _ in 0..n {
370            vec.push('x');
371        }
372
373        #[cfg(not(miri))]
374        assert!(vec.fragments.capacity() > 4);
375    }
376
377    #[test]
378    #[should_panic]
379    fn with_recursive_growth_and_fragments_capacity_zero() {
380        let _: SplitVec<char, _> = SplitVec::with_recursive_growth_and_fragments_capacity(0);
381    }
382
383    #[test]
384    #[should_panic]
385    fn with_recursive_growth_and_fragments_capacity_too_large_fragments_capacity() {
386        let vec: SplitVec<char, _> = SplitVec::with_recursive_growth_and_fragments_capacity(1000);
387        assert_eq!(
388            vec.maximum_concurrent_capacity(),
389            (1 << (CAPACITIES_LEN + 2)) - FIRST_FRAGMENT_CAPACITY
390        );
391    }
392
393    #[test]
394    fn with_recursive_growth_and_max_concurrent_capacity() {
395        let vec: SplitVec<char, _> = SplitVec::with_recursive_growth_and_max_concurrent_capacity();
396        assert_eq!(
397            vec.maximum_concurrent_capacity(),
398            (1 << (CAPACITIES_LEN + 2)) - FIRST_FRAGMENT_CAPACITY
399        );
400    }
401
402    #[test]
403    fn required_fragments_len() {
404        let vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
405        let num_fragments = |max_cap| {
406            vec.growth()
407                .required_fragments_len(vec.fragments(), max_cap)
408        };
409
410        // 4 - 12 - 28 - 60 - 124
411        assert_eq!(num_fragments(0), Ok(1));
412        assert_eq!(num_fragments(1), Ok(1));
413        assert_eq!(num_fragments(4), Ok(1));
414        assert_eq!(num_fragments(5), Ok(2));
415        assert_eq!(num_fragments(12), Ok(2));
416        assert_eq!(num_fragments(13), Ok(3));
417        assert_eq!(num_fragments(36), Ok(4));
418        assert_eq!(num_fragments(67), Ok(5));
419        assert_eq!(num_fragments(136), Ok(6));
420    }
421
422    #[test]
423    fn required_fragments_len_when_appended() {
424        let mut vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
425        for _ in 0..4 {
426            vec.push('x')
427        }
428        vec.append(alloc::vec!['x'; 10]);
429
430        let num_fragments = |max_cap| {
431            vec.growth()
432                .required_fragments_len(vec.fragments(), max_cap)
433        };
434
435        // 4 - 10 - 20 - 40 - 80
436        // 4 - 14 - 34 - 74 - 154
437        assert_eq!(num_fragments(0), Ok(2));
438        assert_eq!(num_fragments(1), Ok(2));
439        assert_eq!(num_fragments(14), Ok(2));
440        assert_eq!(num_fragments(15), Ok(3));
441        assert_eq!(num_fragments(21), Ok(3));
442        assert_eq!(num_fragments(34), Ok(3));
443        assert_eq!(num_fragments(35), Ok(4));
444        assert_eq!(num_fragments(74), Ok(4));
445        assert_eq!(num_fragments(75), Ok(5));
446        assert_eq!(num_fragments(154), Ok(5));
447        assert_eq!(num_fragments(155), Ok(6));
448    }
449}