orx_split_vec/growth/linear/
linear_growth.rs

1use crate::growth::growth_trait::{Growth, GrowthWithConstantTimeAccess};
2use crate::growth::linear::constants::FIXED_CAPACITIES;
3use crate::{Fragment, SplitVec};
4use alloc::string::String;
5use orx_pseudo_default::PseudoDefault;
6
7/// Strategy which allows the split vector to grow linearly.
8///
9/// In other words, each new fragment will have equal capacity,
10/// which is equal to the capacity of the first fragment.
11///
12/// # Examples
13///
14/// ```
15/// use orx_split_vec::*;
16///
17/// // SplitVec<usize, Linear>
18/// let mut vec = SplitVec::with_linear_growth(4);
19///
20/// assert_eq!(1, vec.fragments().len());
21/// assert_eq!(Some(16), vec.fragments().first().map(|f| f.capacity()));
22/// assert_eq!(Some(0), vec.fragments().first().map(|f| f.len()));
23///
24/// // push 160 elements
25/// for i in 0..10 * 16 {
26///     vec.push(i);
27/// }
28///
29/// assert_eq!(10, vec.fragments().len());
30/// for fragment in vec.fragments() {
31///     assert_eq!(16, fragment.len());
32///     assert_eq!(16, fragment.capacity());
33/// }
34///
35/// // push the 161-st element
36/// vec.push(42);
37/// assert_eq!(11, vec.fragments().len());
38/// assert_eq!(Some(16), vec.fragments().last().map(|f| f.capacity()));
39/// assert_eq!(Some(1), vec.fragments().last().map(|f| f.len()));
40/// ```
41#[derive(Debug, Clone, PartialEq)]
42pub struct Linear {
43    constant_fragment_capacity_exponent: usize,
44    constant_fragment_capacity: usize,
45}
46
47impl Linear {
48    /// Creates a linear growth where each fragment will have a capacity of `2 ^ constant_fragment_capacity_exponent`.
49    pub fn new(constant_fragment_capacity_exponent: usize) -> Self {
50        let constant_fragment_capacity = FIXED_CAPACITIES[constant_fragment_capacity_exponent];
51        Self {
52            constant_fragment_capacity_exponent,
53            constant_fragment_capacity,
54        }
55    }
56}
57
58impl PseudoDefault for Linear {
59    fn pseudo_default() -> Self {
60        Self::new(1)
61    }
62}
63
64impl Growth for Linear {
65    #[inline(always)]
66    fn new_fragment_capacity_from(
67        &self,
68        _fragment_capacities: impl ExactSizeIterator<Item = usize>,
69    ) -> usize {
70        self.constant_fragment_capacity
71    }
72
73    #[inline(always)]
74    fn get_fragment_and_inner_indices<T>(
75        &self,
76        vec_len: usize,
77        _fragments: &[Fragment<T>],
78        element_index: usize,
79    ) -> Option<(usize, usize)> {
80        match element_index < vec_len {
81            true => Some(self.get_fragment_and_inner_indices_unchecked(element_index)),
82            false => None,
83        }
84    }
85
86    /// ***O(1)*** Returns a pointer to the `index`-th element of the split vector of the `fragments`.
87    ///
88    /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
89    ///
90    /// # Safety
91    ///
92    /// This method allows to write to a memory which is greater than the split vector's length.
93    /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
94    #[inline(always)]
95    fn get_ptr<T>(&self, fragments: &[Fragment<T>], index: usize) -> Option<*const T> {
96        <Self as GrowthWithConstantTimeAccess>::get_ptr(self, fragments, index)
97    }
98
99    /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`.
100    ///
101    /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
102    ///
103    /// # Safety
104    ///
105    /// This method allows to write to a memory which is greater than the split vector's length.
106    /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
107    #[inline(always)]
108    fn get_ptr_mut<T>(&self, fragments: &mut [Fragment<T>], index: usize) -> Option<*mut T> {
109        <Self as GrowthWithConstantTimeAccess>::get_ptr_mut(self, fragments, index)
110    }
111
112    /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`
113    /// together with the index of the fragment that the element belongs to
114    /// and index of the element withing the respective fragment.
115    ///
116    /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
117    ///
118    /// # Safety
119    ///
120    /// This method allows to write to a memory which is greater than the split vector's length.
121    /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
122    fn get_ptr_mut_and_indices<T>(
123        &self,
124        fragments: &mut [Fragment<T>],
125        index: usize,
126    ) -> Option<(*mut T, usize, usize)> {
127        <Self as GrowthWithConstantTimeAccess>::get_ptr_mut_and_indices(self, fragments, index)
128    }
129
130    fn maximum_concurrent_capacity<T>(
131        &self,
132        fragments: &[Fragment<T>],
133        fragments_capacity: usize,
134    ) -> usize {
135        assert!(fragments_capacity >= fragments.len());
136
137        fragments_capacity * self.constant_fragment_capacity
138    }
139
140    fn required_fragments_len<T>(
141        &self,
142        _: &[Fragment<T>],
143        maximum_capacity: usize,
144    ) -> Result<usize, String> {
145        let num_full_fragments = maximum_capacity / self.constant_fragment_capacity;
146        let remainder = maximum_capacity % self.constant_fragment_capacity;
147        let additional_fragment = if remainder > 0 { 1 } else { 0 };
148
149        Ok(num_full_fragments + additional_fragment)
150    }
151}
152
153impl GrowthWithConstantTimeAccess for Linear {
154    #[inline(always)]
155    fn get_fragment_and_inner_indices_unchecked(&self, element_index: usize) -> (usize, usize) {
156        let f = element_index >> self.constant_fragment_capacity_exponent;
157        let i = element_index % self.constant_fragment_capacity;
158        (f, i)
159    }
160
161    fn fragment_capacity_of(&self, _: usize) -> usize {
162        self.constant_fragment_capacity
163    }
164}
165
166impl<T> SplitVec<T, Linear> {
167    /// Creates a split vector with linear growth where each fragment will have a capacity of `2 ^ constant_fragment_capacity_exponent`.
168    ///
169    /// Assuming it is the common case compared to empty vector scenarios,
170    /// it immediately allocates the first fragment to keep the `SplitVec` struct smaller.
171    ///
172    /// # Panics
173    ///
174    /// Panics if `constant_fragment_capacity_exponent` is not within:
175    /// * 1..32 for 64-bit platforms, or
176    /// * 1..29 for 32-bit platforms.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// use orx_split_vec::*;
182    ///
183    /// // SplitVec<usize, Linear>
184    /// let mut vec = SplitVec::with_linear_growth(4);
185    ///
186    /// assert_eq!(1, vec.fragments().len());
187    /// assert_eq!(Some(16), vec.fragments().first().map(|f| f.capacity()));
188    /// assert_eq!(Some(0), vec.fragments().first().map(|f| f.len()));
189    ///
190    /// // push 160 elements
191    /// for i in 0..10 * 16 {
192    ///     vec.push(i);
193    /// }
194    ///
195    /// assert_eq!(10, vec.fragments().len());
196    /// for fragment in vec.fragments() {
197    ///     assert_eq!(16, fragment.len());
198    ///     assert_eq!(16, fragment.capacity());
199    /// }
200    ///
201    /// // push the 161-st element
202    /// vec.push(42);
203    /// assert_eq!(11, vec.fragments().len());
204    /// assert_eq!(Some(16), vec.fragments().last().map(|f| f.capacity()));
205    /// assert_eq!(Some(1), vec.fragments().last().map(|f| f.len()));
206    /// ```
207    pub fn with_linear_growth(constant_fragment_capacity_exponent: usize) -> Self {
208        assert!(constant_fragment_capacity_exponent > 0 && constant_fragment_capacity_exponent < FIXED_CAPACITIES.len(),
209            "constant_fragment_capacity_exponent must be within 1..32 (1..29) for 64-bit (32-bit) platforms.");
210
211        let constant_fragment_capacity = FIXED_CAPACITIES[constant_fragment_capacity_exponent];
212        let fragments = Fragment::new(constant_fragment_capacity).into_fragments();
213        let growth = Linear::new(constant_fragment_capacity_exponent);
214        Self::from_raw_parts(0, fragments, growth)
215    }
216
217    /// Creates a new split vector with `Linear` growth and initial `fragments_capacity`.
218    ///
219    /// This method differs from [`SplitVec::with_linear_growth`] only by the pre-allocation of fragments collection.
220    /// Note that this (only) important for concurrent programs:
221    /// * SplitVec already keeps all elements pinned to their locations;
222    /// * Creating a buffer for storing the meta information is important for keeping the meta information pinned as well.
223    ///   This is relevant and important for concurrent programs.
224    ///
225    /// # Panics
226    ///
227    /// Panics if `fragments_capacity == 0`.
228    pub fn with_linear_growth_and_fragments_capacity(
229        constant_fragment_capacity_exponent: usize,
230        fragments_capacity: usize,
231    ) -> Self {
232        assert!(constant_fragment_capacity_exponent > 0);
233        assert!(fragments_capacity > 0);
234
235        let constant_fragment_capacity = FIXED_CAPACITIES[constant_fragment_capacity_exponent];
236        let fragments = Fragment::new(constant_fragment_capacity)
237            .into_fragments_with_capacity(fragments_capacity);
238        let growth = Linear::new(constant_fragment_capacity_exponent);
239        Self::from_raw_parts(0, fragments, growth)
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246    use orx_pinned_vec::PinnedVec;
247
248    #[test]
249    fn get_fragment_and_inner_indices() {
250        let growth = Linear::new(2);
251
252        let get = |index| growth.get_fragment_and_inner_indices::<char>(usize::MAX, &[], index);
253        let get_none = |index| growth.get_fragment_and_inner_indices::<char>(index, &[], index);
254
255        assert_eq!((0, 0), growth.get_fragment_and_inner_indices_unchecked(0));
256        assert_eq!((0, 1), growth.get_fragment_and_inner_indices_unchecked(1));
257        assert_eq!((1, 0), growth.get_fragment_and_inner_indices_unchecked(4));
258        assert_eq!((2, 1), growth.get_fragment_and_inner_indices_unchecked(9));
259        assert_eq!((4, 0), growth.get_fragment_and_inner_indices_unchecked(16));
260
261        assert_eq!(Some((0, 0)), get(0));
262        assert_eq!(Some((0, 1)), get(1));
263        assert_eq!(Some((1, 0)), get(4));
264        assert_eq!(Some((2, 1)), get(9));
265        assert_eq!(Some((4, 0)), get(16));
266
267        assert_eq!(None, get_none(0));
268        assert_eq!(None, get_none(1));
269        assert_eq!(None, get_none(4));
270        assert_eq!(None, get_none(9));
271        assert_eq!(None, get_none(16));
272    }
273
274    #[test]
275    fn get_fragment_and_inner_indices_exhaustive() {
276        let growth = Linear::new(5);
277
278        let get = |index| growth.get_fragment_and_inner_indices::<char>(usize::MAX, &[], index);
279        let get_none = |index| growth.get_fragment_and_inner_indices::<char>(index, &[], index);
280
281        let curr_capacity = 32;
282
283        let mut f = 0;
284        let mut prev_cumulative_capacity = 0;
285        let mut cumulative_capacity = curr_capacity;
286
287        for index in 0..51_111 {
288            if index == cumulative_capacity {
289                prev_cumulative_capacity = cumulative_capacity;
290                cumulative_capacity += curr_capacity;
291                f += 1;
292            }
293
294            let (f, i) = (f, index - prev_cumulative_capacity);
295            assert_eq!(
296                (f, i),
297                growth.get_fragment_and_inner_indices_unchecked(index)
298            );
299            assert_eq!(Some((f, i)), get(index));
300            assert_eq!(None, get_none(index));
301        }
302    }
303
304    #[test]
305    fn maximum_concurrent_capacity() {
306        fn max_cap<T>(vec: &SplitVec<T, Linear>) -> usize {
307            vec.growth()
308                .maximum_concurrent_capacity(vec.fragments(), vec.fragments.capacity())
309        }
310
311        let mut vec: SplitVec<char, Linear> = SplitVec::with_linear_growth(5);
312        assert_eq!(max_cap(&vec), 4 * 2usize.pow(5));
313
314        let until = max_cap(&vec);
315        for _ in 0..until {
316            vec.push('x');
317            assert_eq!(max_cap(&vec), 4 * 2usize.pow(5));
318        }
319
320        // fragments allocate beyond max_cap
321        vec.push('x');
322        assert_eq!(max_cap(&vec), 8 * 2usize.pow(5));
323    }
324
325    #[test]
326    fn with_linear_growth_and_fragments_capacity_normal_growth() {
327        let mut vec: SplitVec<char, _> = SplitVec::with_linear_growth_and_fragments_capacity(10, 1);
328
329        assert_eq!(1, vec.fragments.capacity());
330
331        for _ in 0..100_000 {
332            vec.push('x');
333        }
334
335        assert!(vec.fragments.capacity() > 4);
336    }
337
338    #[test]
339    #[should_panic]
340    fn with_linear_growth_and_fragments_capacity_zero() {
341        let _: SplitVec<char, _> = SplitVec::with_linear_growth_and_fragments_capacity(10, 0);
342    }
343
344    #[test]
345    fn required_fragments_len() {
346        let vec: SplitVec<char, Linear> = SplitVec::with_linear_growth(5);
347        let num_fragments = |max_cap| {
348            vec.growth()
349                .required_fragments_len(vec.fragments(), max_cap)
350        };
351
352        assert_eq!(num_fragments(0), Ok(0));
353        assert_eq!(num_fragments(1), Ok(1));
354        assert_eq!(num_fragments(2), Ok(1));
355        assert_eq!(num_fragments(32), Ok(1));
356        assert_eq!(num_fragments(33), Ok(2));
357        assert_eq!(num_fragments(32 * 7), Ok(7));
358        assert_eq!(num_fragments(32 * 7 + 1), Ok(8));
359    }
360}