orx_split_vec/growth/doubling/doubling_growth.rs
1use super::constants::*;
2use crate::growth::growth_trait::{Growth, GrowthWithConstantTimeAccess};
3use crate::{Fragment, SplitVec};
4use alloc::string::String;
5use orx_pseudo_default::PseudoDefault;
6
7/// Strategy which allows creates a fragment with double the capacity
8/// of the prior fragment every time the split vector needs to expand.
9///
10/// Assuming it is the common case compared to empty vector scenarios,
11/// it immediately allocates the first fragment to keep the `SplitVec` struct smaller.
12///
13/// # Examples
14///
15/// ```
16/// use orx_split_vec::*;
17///
18/// // SplitVec<usize, Doubling>
19/// let mut vec = SplitVec::with_doubling_growth();
20///
21/// assert_eq!(1, vec.fragments().len());
22/// assert_eq!(Some(4), vec.fragments().first().map(|f| f.capacity()));
23/// assert_eq!(Some(0), vec.fragments().first().map(|f| f.len()));
24///
25/// // fill the first 5 fragments
26/// let expected_fragment_capacities = vec![4, 8, 16, 32];
27/// let num_items: usize = expected_fragment_capacities.iter().sum();
28/// for i in 0..num_items {
29/// vec.push(i);
30/// }
31///
32/// assert_eq!(
33/// expected_fragment_capacities,
34/// vec.fragments()
35/// .iter()
36/// .map(|f| f.capacity())
37/// .collect::<Vec<_>>()
38/// );
39/// assert_eq!(
40/// expected_fragment_capacities,
41/// vec.fragments().iter().map(|f| f.len()).collect::<Vec<_>>()
42/// );
43///
44/// // create the 6-th fragment doubling the capacity
45/// vec.push(42);
46/// assert_eq!(
47/// vec.fragments().len(),
48/// expected_fragment_capacities.len() + 1
49/// );
50///
51/// assert_eq!(vec.fragments().last().map(|f| f.capacity()), Some(32 * 2));
52/// assert_eq!(vec.fragments().last().map(|f| f.len()), Some(1));
53/// ```
54#[derive(Debug, Default, Clone, PartialEq)]
55pub struct Doubling;
56
57impl PseudoDefault for Doubling {
58 fn pseudo_default() -> Self {
59 Default::default()
60 }
61}
62
63impl Growth for Doubling {
64 #[inline(always)]
65 fn new_fragment_capacity_from(
66 &self,
67 fragment_capacities: impl ExactSizeIterator<Item = usize>,
68 ) -> usize {
69 fragment_capacities.last().map(|x| x * 2).unwrap_or(4)
70 }
71
72 #[inline(always)]
73 fn get_fragment_and_inner_indices<T>(
74 &self,
75 vec_len: usize,
76 _fragments: &[Fragment<T>],
77 element_index: usize,
78 ) -> Option<(usize, usize)> {
79 match element_index < vec_len {
80 true => Some(self.get_fragment_and_inner_indices_unchecked(element_index)),
81 false => None,
82 }
83 }
84
85 /// ***O(1)*** Returns a pointer to the `index`-th element of the split vector of the `fragments`.
86 ///
87 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
88 ///
89 /// # Safety
90 ///
91 /// This method allows to write to a memory which is greater than the split vector's length.
92 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
93 #[inline(always)]
94 fn get_ptr<T>(&self, fragments: &[Fragment<T>], index: usize) -> Option<*const T> {
95 <Self as GrowthWithConstantTimeAccess>::get_ptr(self, fragments, index)
96 }
97
98 /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`.
99 ///
100 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
101 ///
102 /// # Safety
103 ///
104 /// This method allows to write to a memory which is greater than the split vector's length.
105 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
106 #[inline(always)]
107 fn get_ptr_mut<T>(&self, fragments: &mut [Fragment<T>], index: usize) -> Option<*mut T> {
108 <Self as GrowthWithConstantTimeAccess>::get_ptr_mut(self, fragments, index)
109 }
110
111 /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`
112 /// together with the index of the fragment that the element belongs to
113 /// and index of the element withing the respective fragment.
114 ///
115 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
116 ///
117 /// # Safety
118 ///
119 /// This method allows to write to a memory which is greater than the split vector's length.
120 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
121 #[inline(always)]
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 CUMULATIVE_CAPACITIES[fragments_capacity]
138 }
139
140 /// Returns the number of fragments with this growth strategy in order to be able to reach a capacity of `maximum_capacity` of elements.
141 ///
142 /// This method is relevant and useful for concurrent programs, which helps in avoiding the fragments to allocate.
143 ///
144 /// # Panics
145 ///
146 /// Panics if `maximum_capacity` is greater than sum { 2^f | for f in 2..34 }.
147 fn required_fragments_len<T>(
148 &self,
149 _: &[Fragment<T>],
150 maximum_capacity: usize,
151 ) -> Result<usize, String> {
152 for (f, capacity) in CUMULATIVE_CAPACITIES.iter().enumerate() {
153 if maximum_capacity <= *capacity {
154 return Ok(f);
155 }
156 }
157
158 Err(alloc::format!(
159 "Maximum cumulative capacity that can be reached by the Doubling strategy is {}.",
160 CUMULATIVE_CAPACITIES[CUMULATIVE_CAPACITIES.len() - 1]
161 ))
162 }
163
164 fn maximum_concurrent_capacity_bound<T>(&self, _: &[Fragment<T>], _: usize) -> usize {
165 *CUMULATIVE_CAPACITIES
166 .last()
167 .expect("cumulative capacities is not empty")
168 }
169}
170
171impl GrowthWithConstantTimeAccess for Doubling {
172 #[inline(always)]
173 fn get_fragment_and_inner_indices_unchecked(&self, element_index: usize) -> (usize, usize) {
174 let element_index_offset = element_index + FIRST_FRAGMENT_CAPACITY;
175 let leading_zeros = usize::leading_zeros(element_index_offset) as usize;
176 let f = OFFSET_FRAGMENT_IDX - leading_zeros;
177 (f, element_index - CUMULATIVE_CAPACITIES[f])
178 }
179
180 fn fragment_capacity_of(&self, fragment_index: usize) -> usize {
181 CAPACITIES[fragment_index]
182 }
183}
184
185impl<T> SplitVec<T, Doubling> {
186 /// Strategy which allows to create a fragment with double the capacity
187 /// of the prior fragment every time the split vector needs to expand.
188 ///
189 /// Assuming it is the common case compared to empty vector scenarios,
190 /// it immediately allocates the first fragment to keep the `SplitVec` struct smaller.
191 ///
192 /// # Panics
193 /// Panics if `first_fragment_capacity` is zero.
194 ///
195 /// # Examples
196 ///
197 /// ```
198 /// use orx_split_vec::*;
199 ///
200 /// // SplitVec<usize, Doubling>
201 /// let mut vec = SplitVec::with_doubling_growth();
202 ///
203 /// assert_eq!(1, vec.fragments().len());
204 /// assert_eq!(Some(4), vec.fragments().first().map(|f| f.capacity()));
205 /// assert_eq!(Some(0), vec.fragments().first().map(|f| f.len()));
206 ///
207 /// // fill the first 5 fragments
208 /// let expected_fragment_capacities = vec![4, 8, 16, 32];
209 /// let num_items: usize = expected_fragment_capacities.iter().sum();
210 /// for i in 0..num_items {
211 /// vec.push(i);
212 /// }
213 ///
214 /// assert_eq!(
215 /// expected_fragment_capacities,
216 /// vec.fragments()
217 /// .iter()
218 /// .map(|f| f.capacity())
219 /// .collect::<Vec<_>>()
220 /// );
221 /// assert_eq!(
222 /// expected_fragment_capacities,
223 /// vec.fragments().iter().map(|f| f.len()).collect::<Vec<_>>()
224 /// );
225 ///
226 /// // create the 6-th fragment doubling the capacity
227 /// vec.push(42);
228 /// assert_eq!(
229 /// vec.fragments().len(),
230 /// expected_fragment_capacities.len() + 1
231 /// );
232 ///
233 /// assert_eq!(vec.fragments().last().map(|f| f.capacity()), Some(32 * 2));
234 /// assert_eq!(vec.fragments().last().map(|f| f.len()), Some(1));
235 /// ```
236 pub fn with_doubling_growth() -> Self {
237 let fragments = Fragment::new_empty(FIRST_FRAGMENT_CAPACITY).into_fragments();
238 Self::from_raw_parts(0, fragments, Doubling)
239 }
240
241 /// Creates a new split vector with `Doubling` growth and initial `fragments_capacity`.
242 ///
243 /// This method differs from [`SplitVec::with_doubling_growth`] only by the pre-allocation of fragments collection.
244 /// Note that this (only) important for concurrent programs:
245 /// * SplitVec already keeps all elements pinned to their locations;
246 /// * Creating a buffer for storing the meta information is important for keeping the meta information pinned as well.
247 /// This is relevant and important for concurrent programs.
248 ///
249 /// # Panics
250 ///
251 /// Panics if `fragments_capacity == 0`.
252 ///
253 /// # Panics
254 ///
255 /// Panics if `fragments_capacity` is zero or `fragments_capacity > MAX_FRAGMENTS_CAPACITY` where `MAX_FRAGMENTS_CAPACITY` is:
256 ///
257 /// * 29 in 32-bit targets,
258 /// * 32 in 64-bit.
259 pub fn with_doubling_growth_and_fragments_capacity(fragments_capacity: usize) -> Self {
260 assert!(
261 fragments_capacity > 0 && fragments_capacity <= CAPACITIES_LEN,
262 "fragments_capacity must be within 1..{CAPACITIES_LEN}; however, {fragments_capacity} is provided."
263 );
264 assert!(fragments_capacity > 0);
265 let fragments = Fragment::new_empty(FIRST_FRAGMENT_CAPACITY)
266 .into_fragments_with_capacity(fragments_capacity);
267 Self::from_raw_parts(0, fragments, Doubling)
268 }
269
270 /// Creates a new split vector with `Doubling` growth and maximum concurrent capacity which depends
271 /// on the pointer size of the target architecture.
272 ///
273 /// This method differs from [`SplitVec::with_doubling_growth`] only by the pre-allocation of fragments collection,
274 /// which never contains more elements than 33.
275 pub fn with_doubling_growth_and_max_concurrent_capacity() -> Self {
276 let fragments = Fragment::new_empty(FIRST_FRAGMENT_CAPACITY)
277 .into_fragments_with_capacity(CAPACITIES_LEN);
278 Self::from_raw_parts(0, fragments, Doubling)
279 }
280}