orx_split_vec/growth/growth_trait.rs
1use crate::Fragment;
2use alloc::{string::String, vec::Vec};
3use orx_pseudo_default::PseudoDefault;
4
5/// Growth strategy of a split vector.
6pub trait Growth: Clone + PseudoDefault + Send + Sync {
7 /// Given that the split vector has no fragments yet,
8 /// returns the capacity of the first fragment.
9 fn first_fragment_capacity(&self) -> usize {
10 self.new_fragment_capacity_from([].into_iter())
11 }
12
13 /// Given that the split vector contains the given `fragments`,
14 /// returns the capacity of the next fragment.
15 #[inline(always)]
16 fn new_fragment_capacity<T>(&self, fragments: &[Fragment<T>]) -> usize {
17 self.new_fragment_capacity_from(fragments.iter().map(|x| x.capacity()))
18 }
19
20 /// Given that the split vector contains fragments with the given `fragment_capacities`,
21 /// returns the capacity of the next fragment.
22 fn new_fragment_capacity_from(
23 &self,
24 fragment_capacities: impl ExactSizeIterator<Item = usize>,
25 ) -> usize;
26
27 /// ***O(fragments.len())*** Returns the location of the element with the given `element_index` on the split vector as a tuple of (fragment-index, index-within-fragment).
28 ///
29 /// Returns None if the element index is out of bounds.
30 fn get_fragment_and_inner_indices<T>(
31 &self,
32 _vec_len: usize,
33 fragments: &[Fragment<T>],
34 element_index: usize,
35 ) -> Option<(usize, usize)> {
36 let mut prev_end = 0;
37 let mut end = 0;
38 for (f, fragment) in fragments.iter().enumerate() {
39 end += fragment.len();
40 if element_index < end {
41 return Some((f, element_index - prev_end));
42 }
43 prev_end = end;
44 }
45 None
46 }
47
48 /// ***O(fragments.len())*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`.
49 ///
50 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
51 ///
52 /// # Safety
53 ///
54 /// This method allows to write to a memory which is greater than the vector's length.
55 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
56 fn get_ptr<T>(&self, fragments: &[Fragment<T>], index: usize) -> Option<*const T> {
57 self.get_ptr_and_indices(fragments, index).map(|x| x.0)
58 }
59
60 /// ***O(fragments.len())*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`.
61 ///
62 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
63 ///
64 /// # Safety
65 ///
66 /// This method allows to write to a memory which is greater than the vector's length.
67 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
68 fn get_ptr_mut<T>(&self, fragments: &mut [Fragment<T>], index: usize) -> Option<*mut T> {
69 self.get_ptr_mut_and_indices(fragments, index).map(|x| x.0)
70 }
71
72 /// ***O(fragments.len())*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`
73 /// together with the index of the fragment that the element belongs to
74 /// and index of the element withing the respective fragment.
75 ///
76 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
77 ///
78 /// # Safety
79 ///
80 /// This method allows to write to a memory which is greater than the vector's length.
81 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
82 fn get_ptr_and_indices<T>(
83 &self,
84 fragments: &[Fragment<T>],
85 index: usize,
86 ) -> Option<(*const T, usize, usize)> {
87 let mut prev_cumulative_capacity = 0;
88 let mut cumulative_capacity = 0;
89 for (f, fragment) in fragments.iter().enumerate() {
90 cumulative_capacity += fragment.capacity();
91 if index < cumulative_capacity {
92 let index_in_fragment = index - prev_cumulative_capacity;
93 return Some((
94 unsafe { fragment.as_ptr().add(index_in_fragment) },
95 f,
96 index_in_fragment,
97 ));
98 }
99 prev_cumulative_capacity = cumulative_capacity;
100 }
101 None
102 }
103
104 /// ***O(fragments.len())*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`
105 /// together with the index of the fragment that the element belongs to
106 /// and index of the element withing the respective fragment.
107 ///
108 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
109 ///
110 /// # Safety
111 ///
112 /// This method allows to write to a memory which is greater than the vector's length.
113 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
114 fn get_ptr_mut_and_indices<T>(
115 &self,
116 fragments: &mut [Fragment<T>],
117 index: usize,
118 ) -> Option<(*mut T, usize, usize)> {
119 let mut prev_cumulative_capacity = 0;
120 let mut cumulative_capacity = 0;
121 for (f, fragment) in fragments.iter_mut().enumerate() {
122 cumulative_capacity += fragment.capacity();
123 if index < cumulative_capacity {
124 let index_in_fragment = index - prev_cumulative_capacity;
125 return Some((
126 unsafe { fragment.as_mut_ptr().add(index_in_fragment) },
127 f,
128 index_in_fragment,
129 ));
130 }
131 prev_cumulative_capacity = cumulative_capacity;
132 }
133 None
134 }
135
136 /// Returns the maximum number of elements that can safely be stored in a concurrent program.
137 ///
138 /// Note that pinned vectors already keep the elements pinned to their memory locations.
139 /// Therefore, concurrently safe growth here corresponds to growth without requiring `fragments` collection to allocate.
140 /// Recall that `fragments` contains meta information about the splits of the `SplitVec`, such as the capacity of each split.
141 ///
142 /// This default implementation is not the most efficient as it allocates a small vector to compute the capacity.
143 /// However, it is almost always possible to provide a non-allocating implementation provided that the concurrency is relevant.
144 /// `Doubling`, `Recursive` and `Linear` growth strategies introduced in this crate all override this method.
145 ///
146 /// # Panics
147 ///
148 /// Panics if `fragments.len() < fragments_capacity`, which must not hold.
149 fn maximum_concurrent_capacity<T>(
150 &self,
151 fragments: &[Fragment<T>],
152 fragments_capacity: usize,
153 ) -> usize {
154 assert!(fragments_capacity >= fragments.len());
155
156 if fragments_capacity == fragments.len() {
157 fragments.iter().map(|x| x.capacity()).sum()
158 } else {
159 let mut cloned: Vec<Fragment<T>> = Vec::with_capacity(fragments_capacity);
160 for fragment in fragments {
161 cloned.push(Vec::with_capacity(fragment.capacity()).into());
162 }
163 for _ in fragments.len()..fragments_capacity {
164 let new_capacity = self.new_fragment_capacity(&cloned);
165 let fragment = Vec::with_capacity(new_capacity).into();
166 cloned.push(fragment);
167 }
168 cloned.iter().map(|x| x.capacity()).sum()
169 }
170 }
171
172 /// Returns the number of fragments with this growth strategy in order to be able to reach a capacity of `maximum_capacity` of elements.
173 /// Returns the error if it the growth strategy does not allow the required number of fragments.
174 ///
175 /// This method is relevant and useful for concurrent programs, which helps in avoiding the fragments to allocate.
176 fn required_fragments_len<T>(
177 &self,
178 fragments: &[Fragment<T>],
179 maximum_capacity: usize,
180 ) -> Result<usize, String> {
181 fn overflown_err() -> String {
182 alloc::format!(
183 "Maximum cumulative capacity that can be reached is {}.",
184 usize::MAX
185 )
186 }
187
188 let mut cloned: Vec<Fragment<T>> = Vec::new();
189 for fragment in fragments {
190 cloned.push(Vec::with_capacity(fragment.capacity()).into());
191 }
192
193 let mut num_fragments = cloned.len();
194 let mut current_capacity: usize = cloned.iter().map(|x| x.capacity()).sum();
195
196 while current_capacity < maximum_capacity {
197 let new_capacity = self.new_fragment_capacity(&cloned);
198 let (new_current_capacity, overflown) = current_capacity.overflowing_add(new_capacity);
199 if overflown {
200 return Err(overflown_err());
201 }
202
203 let fragment = Vec::with_capacity(new_capacity).into();
204 cloned.push(fragment);
205
206 current_capacity = new_current_capacity;
207 num_fragments += 1;
208 }
209
210 Ok(num_fragments)
211 }
212
213 /// Returns the maximum possible bound on concurrent capacity.
214 fn maximum_concurrent_capacity_bound<T>(&self, _: &[Fragment<T>], _: usize) -> usize {
215 usize::MAX
216 }
217}
218
219/// Growth strategy of a split vector which allows for constant time access to the elements.
220pub trait GrowthWithConstantTimeAccess: Growth {
221 /// ***O(1)*** Returns the location of the element with the given `element_index` on the split vector as a tuple of (fragment-index, index-within-fragment).
222 ///
223 /// Notice that unlike the [`Growth::get_fragment_and_inner_indices`]:
224 /// * this method does not receive the current state of the split vector,
225 /// * therefore, it does not perform bounds check,
226 /// * and hence, returns the expected fragment and within-fragment indices for any index computed by the constant access time function.
227 fn get_fragment_and_inner_indices_unchecked(&self, element_index: usize) -> (usize, usize);
228
229 /// ***O(1)*** Returns a pointer to the `index`-th element of the split vector of the `fragments`.
230 ///
231 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
232 ///
233 /// # Safety
234 ///
235 /// This method allows to write to a memory which is greater than the split vector's length.
236 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
237 fn get_ptr<T>(&self, fragments: &[Fragment<T>], index: usize) -> Option<*const T> {
238 let (f, i) = self.get_fragment_and_inner_indices_unchecked(index);
239 fragments
240 .get(f)
241 .map(|fragment| unsafe { fragment.as_ptr().add(i) })
242 }
243
244 /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`.
245 ///
246 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
247 ///
248 /// # Safety
249 ///
250 /// This method allows to write to a memory which is greater than the split vector's length.
251 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
252 fn get_ptr_mut<T>(&self, fragments: &mut [Fragment<T>], index: usize) -> Option<*mut T> {
253 let (f, i) = self.get_fragment_and_inner_indices_unchecked(index);
254 fragments
255 .get_mut(f)
256 .map(|fragment| unsafe { fragment.as_mut_ptr().add(i) })
257 }
258
259 /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`
260 /// together with the index of the fragment that the element belongs to
261 /// and index of the element withing the respective fragment.
262 ///
263 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
264 ///
265 /// # Safety
266 ///
267 /// This method allows to write to a memory which is greater than the split vector's length.
268 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
269 fn get_ptr_mut_and_indices<T>(
270 &self,
271 fragments: &mut [Fragment<T>],
272 index: usize,
273 ) -> Option<(*mut T, usize, usize)> {
274 let (f, i) = self.get_fragment_and_inner_indices_unchecked(index);
275 fragments
276 .get_mut(f)
277 .map(|fragment| (unsafe { fragment.as_mut_ptr().add(i) }, f, i))
278 }
279
280 /// ***O(1)*** Returns the capacity of the fragment with the given `fragment_index`.
281 fn fragment_capacity_of(&self, fragment_index: usize) -> usize;
282}