Skip to main content

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_empty(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 mut new_fragment = Fragment::new_empty(capacity);
251        new_fragment.push(first_value);
252        self.fragments.push(new_fragment);
253    }
254
255    pub(crate) fn drop_last_empty_fragment(&mut self) {
256        let drop_empty_last_fragment = self.fragments.last().map(|f| f.is_empty()).unwrap_or(false);
257        if drop_empty_last_fragment {
258            _ = self.fragments.pop();
259        }
260    }
261
262    #[inline(always)]
263    pub(crate) fn growth_get_ptr(&self, index: usize) -> Option<*const T> {
264        self.growth.get_ptr(&self.fragments, index)
265    }
266
267    #[inline(always)]
268    pub(crate) fn growth_get_ptr_mut(&mut self, index: usize) -> Option<*mut T> {
269        self.growth.get_ptr_mut(&mut self.fragments, index)
270    }
271
272    /// Makes sure that the split vector can safely reach the given `maximum_capacity` in a concurrent program.
273    ///
274    /// Returns new maximum capacity.
275    ///
276    /// Note that this method does not allocate the `maximum_capacity`, it only ensures that the concurrent growth to this capacity is safe.
277    /// In order to achieve this, it might need to extend allocation of the fragments collection.
278    /// However, note that by definition number of fragments is insignificant in a split vector.
279    ///
280    /// # Panics
281    ///
282    /// Panics if the vector fails to reserve the requested capacity.
283    pub fn reserve_maximum_concurrent_capacity(&mut self, new_maximum_capacity: usize) -> usize {
284        let current_max = self.maximum_concurrent_capacity();
285        match current_max < new_maximum_capacity {
286            true => {
287                self.concurrent_reserve(new_maximum_capacity)
288                    .expect("Failed to reserve maximum capacity");
289                self.maximum_concurrent_capacity()
290            }
291            false => self.maximum_concurrent_capacity(),
292        }
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use crate::growth::growth_trait::GrowthWithConstantTimeAccess;
299    use crate::test_all_growth_types;
300    use crate::*;
301    use alloc::vec;
302
303    #[test]
304    fn fragments() {
305        fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
306            for i in 0..42 {
307                vec.push(i);
308            }
309
310            let mut combined = vec![];
311            let mut combined_mut = vec![];
312            for fra in vec.fragments() {
313                combined.extend_from_slice(fra.as_slice());
314            }
315            for fra in unsafe { vec.fragments_mut() } {
316                combined_mut.extend_from_slice(fra.as_slice());
317            }
318
319            for i in 0..42 {
320                assert_eq!(i, vec[i]);
321                assert_eq!(i, combined[i]);
322                assert_eq!(i, combined_mut[i]);
323            }
324        }
325        test_all_growth_types!(test);
326    }
327
328    #[test]
329    fn get_fragment_and_inner_indices() {
330        #[cfg(not(miri))]
331        const LEN: usize = 432;
332        #[cfg(miri)]
333        const LEN: usize = 57;
334
335        fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
336            for i in 0..LEN {
337                vec.push(i);
338                assert_eq!(None, vec.get_fragment_and_inner_indices(i + 1));
339            }
340
341            for i in 0..LEN {
342                let (f, ii) = vec.get_fragment_and_inner_indices(i).expect("is-some");
343                assert_eq!(vec[i], vec.fragments[f][ii]);
344            }
345        }
346        test_all_growth_types!(test);
347    }
348
349    #[test]
350    fn get_ptr_mut() {
351        fn test<G: GrowthWithConstantTimeAccess>(mut vec: SplitVec<usize, G>) {
352            for i in 0..65 {
353                vec.push(i);
354            }
355            for i in 0..64 {
356                let p = vec.get_ptr_mut(i).expect("is-some");
357                assert_eq!(i, unsafe { *p });
358            }
359            for i in 64..vec.capacity() {
360                let p = vec.get_ptr_mut(i);
361                assert!(p.is_some());
362            }
363
364            for i in vec.capacity()..(vec.capacity() * 2) {
365                let p = vec.get_ptr_mut(i);
366                assert!(p.is_none());
367            }
368        }
369
370        test(SplitVec::with_doubling_growth());
371        test(SplitVec::with_linear_growth(6));
372    }
373
374    #[test]
375    fn add_fragment() {
376        fn test<G: Growth>(mut vec: SplitVec<usize, G>) {
377            for _ in 0..10 {
378                let expected_new_fragment_cap = vec.growth.new_fragment_capacity(&vec.fragments);
379                let new_fragment_cap = vec.add_fragment();
380                assert_eq!(expected_new_fragment_cap, new_fragment_cap);
381            }
382
383            vec.clear();
384
385            let mut expected_capacity = vec.capacity();
386            for _ in 0..2 {
387                let expected_new_fragment_cap = vec.growth.new_fragment_capacity(&vec.fragments);
388                expected_capacity += expected_new_fragment_cap;
389                vec.add_fragment();
390            }
391
392            assert_eq!(expected_capacity, vec.capacity());
393        }
394
395        test_all_growth_types!(test);
396    }
397}