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