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}