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
119impl<T> SplitVec<T, Recursive> {
120 /// Strategy which allows to create a fragment with double the capacity
121 /// of the prior fragment every time the split vector needs to expand.
122 ///
123 /// Notice that this is similar to the `Doubling` growth strategy.
124 /// However, `Recursive` and `Doubling` strategies have the two following important differences in terms of performance:
125 ///
126 /// * Random access by indices is much faster with `Doubling`.
127 /// * Recursive strategy enables copy-free `append` method which merges another vector to this vector in constant time.
128 ///
129 /// All other operations are expected to have similar complexity.
130 ///
131 /// ## Random Access
132 ///
133 /// * `Doubling` strategy provides a constant time access by random indices.
134 /// * `Recursive` strategy provides a random access time complexity that is linear in the number of fragments.
135 /// Note that this is significantly faster than the linear-in-number-of-elements complexity of linked lists;
136 /// however, significantly slower than the `Doubling` strategy's constant time.
137 ///
138 /// ## Append
139 ///
140 /// * `Recursive` strategy provides `append` operation which allows merging two vectors in constant time without copies.
141 ///
142 /// `SplitVec::append` method should not be confused with `std::vec::Vec::append` method:
143 /// * The split vector version consumes the vector to be appended.
144 /// It takes advantage of its split nature and appends the other vector simply by owning its pointer.
145 /// In other words, the other vector is appended to this vector with no cost and no copies.
146 /// * The standard vector version mutates the vector to be appended,
147 /// moving all its element to the first vector leaving the latter empty.
148 /// This operation is carried out by memory copies.
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// use orx_split_vec::*;
154 ///
155 /// // SplitVec<usize, Doubling>
156 /// let mut vec = SplitVec::with_recursive_growth();
157 ///
158 /// assert_eq!(1, vec.fragments().len());
159 /// assert_eq!(Some(4), vec.fragments().first().map(|f| f.capacity()));
160 /// assert_eq!(Some(0), vec.fragments().first().map(|f| f.len()));
161 ///
162 /// // fill the first 5 fragments
163 /// let expected_fragment_capacities = vec![4, 8, 16, 32];
164 /// let num_items: usize = expected_fragment_capacities.iter().sum();
165 /// for i in 0..num_items {
166 /// vec.push(i);
167 /// }
168 ///
169 /// assert_eq!(
170 /// expected_fragment_capacities,
171 /// vec.fragments()
172 /// .iter()
173 /// .map(|f| f.capacity())
174 /// .collect::<Vec<_>>()
175 /// );
176 /// assert_eq!(
177 /// expected_fragment_capacities,
178 /// vec.fragments().iter().map(|f| f.len()).collect::<Vec<_>>()
179 /// );
180 ///
181 /// // create the 6-th fragment doubling the capacity
182 /// vec.push(42);
183 /// assert_eq!(
184 /// vec.fragments().len(),
185 /// expected_fragment_capacities.len() + 1
186 /// );
187 ///
188 /// assert_eq!(vec.fragments().last().map(|f| f.capacity()), Some(32 * 2));
189 /// assert_eq!(vec.fragments().last().map(|f| f.len()), Some(1));
190 /// ```
191 pub fn with_recursive_growth() -> Self {
192 SplitVec::with_doubling_growth().into()
193 }
194
195 /// Creates a new split vector with `Recursive` growth and initial `fragments_capacity`.
196 ///
197 /// This method differs from [`SplitVec::with_recursive_growth`] only by the pre-allocation of fragments collection.
198 /// Note that this (only) important for concurrent programs:
199 /// * SplitVec already keeps all elements pinned to their locations;
200 /// * Creating a buffer for storing the meta information is important for keeping the meta information pinned as well.
201 /// This is relevant and important for concurrent programs.
202 ///
203 /// # Panics
204 ///
205 /// Panics if `fragments_capacity == 0`.
206 pub fn with_recursive_growth_and_fragments_capacity(fragments_capacity: usize) -> Self {
207 SplitVec::with_doubling_growth_and_fragments_capacity(fragments_capacity).into()
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use alloc::vec::Vec;
215 use orx_pinned_vec::PinnedVec;
216
217 #[test]
218 fn get_fragment_and_inner_indices() {
219 let growth = Recursive;
220
221 let vecs = alloc::vec![
222 alloc::vec![0, 1, 2, 3],
223 alloc::vec![4, 5],
224 alloc::vec![6, 7, 8],
225 alloc::vec![9],
226 alloc::vec![10, 11, 12, 13, 14],
227 ];
228 let mut fragments: Vec<Fragment<_>> = vecs.clone().into_iter().map(|x| x.into()).collect();
229 let len = fragments.iter().map(|x| x.len()).sum();
230
231 let mut index = 0;
232 for (f, vec) in vecs.iter().enumerate() {
233 for (i, _) in vec.iter().enumerate() {
234 let maybe_fi = growth.get_fragment_and_inner_indices(len, &fragments, index);
235 assert_eq!(maybe_fi, Some((f, i)));
236
237 let ptr = growth.get_ptr_mut(&mut fragments, index).expect("is-some");
238 assert_eq!(unsafe { *ptr }, index);
239
240 unsafe { *ptr = 10 * index };
241 assert_eq!(unsafe { *ptr }, 10 * index);
242
243 index += 1;
244 }
245 }
246 }
247
248 #[test]
249 fn get_fragment_and_inner_indices_exhaustive() {
250 let growth = Recursive;
251
252 let mut fragments: Vec<Fragment<_>> = alloc::vec![];
253
254 let lengths = [30, 1, 7, 3, 79, 147, 530];
255 let mut index = 0;
256 for _ in 0..10 {
257 for &len in &lengths {
258 let mut vec = Vec::with_capacity(len);
259 for _ in 0..len {
260 vec.push(index);
261 index += 1;
262 }
263 fragments.push(vec.into());
264 }
265 }
266
267 let total_len = fragments.iter().map(|x| x.len()).sum();
268
269 let mut index = 0;
270 let mut f = 0;
271 for _ in 0..10 {
272 for &len in &lengths {
273 for i in 0..len {
274 let maybe_fi =
275 growth.get_fragment_and_inner_indices(total_len, &fragments, index);
276
277 assert_eq!(maybe_fi, Some((f, i)));
278
279 let ptr = growth.get_ptr_mut(&mut fragments, index).expect("is-some");
280 assert_eq!(unsafe { *ptr }, index);
281
282 unsafe { *ptr = 10 * index };
283 assert_eq!(unsafe { *ptr }, 10 * index);
284
285 index += 1;
286 }
287 f += 1;
288 }
289 }
290 }
291
292 #[test]
293 fn maximum_concurrent_capacity() {
294 fn max_cap<T>(vec: &SplitVec<T, Recursive>) -> usize {
295 vec.growth()
296 .maximum_concurrent_capacity(vec.fragments(), vec.fragments.capacity())
297 }
298
299 let mut vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
300 assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32);
301
302 let until = max_cap(&vec);
303 for _ in 0..until {
304 vec.push('x');
305 assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32);
306 }
307
308 // fragments allocate beyond max_cap
309 vec.push('x');
310 assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512);
311 }
312
313 #[test]
314 fn maximum_concurrent_capacity_when_appended() {
315 fn max_cap<T>(vec: &SplitVec<T, Recursive>) -> usize {
316 vec.growth()
317 .maximum_concurrent_capacity(vec.fragments(), vec.fragments.capacity())
318 }
319
320 let mut vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
321 assert_eq!(max_cap(&vec), 4 + 8 + 16 + 32);
322
323 vec.append(alloc::vec!['x'; 10]);
324 assert_eq!(vec.fragments().len(), 2);
325 assert_eq!(vec.fragments()[1].capacity(), 10);
326 assert_eq!(vec.fragments()[1].len(), 10);
327
328 assert_eq!(max_cap(&vec), 4 + 10 + 20 + 40);
329 }
330
331 #[test]
332 fn with_recursive_growth_and_fragments_capacity_normal_growth() {
333 let mut vec: SplitVec<char, _> = SplitVec::with_recursive_growth_and_fragments_capacity(1);
334
335 assert_eq!(1, vec.fragments.capacity());
336
337 for _ in 0..100_000 {
338 vec.push('x');
339 }
340
341 assert!(vec.fragments.capacity() > 4);
342 }
343
344 #[test]
345 #[should_panic]
346 fn with_recursive_growth_and_fragments_capacity_zero() {
347 let _: SplitVec<char, _> = SplitVec::with_recursive_growth_and_fragments_capacity(0);
348 }
349
350 #[test]
351 fn required_fragments_len() {
352 let vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
353 let num_fragments = |max_cap| {
354 vec.growth()
355 .required_fragments_len(vec.fragments(), max_cap)
356 };
357
358 // 4 - 12 - 28 - 60 - 124
359 assert_eq!(num_fragments(0), Ok(1));
360 assert_eq!(num_fragments(1), Ok(1));
361 assert_eq!(num_fragments(4), Ok(1));
362 assert_eq!(num_fragments(5), Ok(2));
363 assert_eq!(num_fragments(12), Ok(2));
364 assert_eq!(num_fragments(13), Ok(3));
365 assert_eq!(num_fragments(36), Ok(4));
366 assert_eq!(num_fragments(67), Ok(5));
367 assert_eq!(num_fragments(136), Ok(6));
368 }
369
370 #[test]
371 fn required_fragments_len_when_appended() {
372 let mut vec: SplitVec<char, Recursive> = SplitVec::with_recursive_growth();
373 for _ in 0..4 {
374 vec.push('x')
375 }
376 vec.append(alloc::vec!['x'; 10]);
377
378 let num_fragments = |max_cap| {
379 vec.growth()
380 .required_fragments_len(vec.fragments(), max_cap)
381 };
382
383 // 4 - 10 - 20 - 40 - 80
384 // 4 - 14 - 34 - 74 - 154
385 assert_eq!(num_fragments(0), Ok(2));
386 assert_eq!(num_fragments(1), Ok(2));
387 assert_eq!(num_fragments(14), Ok(2));
388 assert_eq!(num_fragments(15), Ok(3));
389 assert_eq!(num_fragments(21), Ok(3));
390 assert_eq!(num_fragments(34), Ok(3));
391 assert_eq!(num_fragments(35), Ok(4));
392 assert_eq!(num_fragments(74), Ok(4));
393 assert_eq!(num_fragments(75), Ok(5));
394 assert_eq!(num_fragments(154), Ok(5));
395 assert_eq!(num_fragments(155), Ok(6));
396 }
397}