orx_split_vec/growth/linear/linear_growth.rs
1use super::constants::FIXED_CAPACITIES;
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 the split vector to grow linearly.
8///
9/// In other words, each new fragment will have equal capacity,
10/// which is equal to the capacity of the first fragment.
11///
12/// # Examples
13///
14/// ```
15/// use orx_split_vec::*;
16///
17/// // SplitVec<usize, Linear>
18/// let mut vec = SplitVec::with_linear_growth(4);
19///
20/// assert_eq!(1, vec.fragments().len());
21/// assert_eq!(Some(16), vec.fragments().first().map(|f| f.capacity()));
22/// assert_eq!(Some(0), vec.fragments().first().map(|f| f.len()));
23///
24/// // push 160 elements
25/// for i in 0..10 * 16 {
26/// vec.push(i);
27/// }
28///
29/// assert_eq!(10, vec.fragments().len());
30/// for fragment in vec.fragments() {
31/// assert_eq!(16, fragment.len());
32/// assert_eq!(16, fragment.capacity());
33/// }
34///
35/// // push the 161-st element
36/// vec.push(42);
37/// assert_eq!(11, vec.fragments().len());
38/// assert_eq!(Some(16), vec.fragments().last().map(|f| f.capacity()));
39/// assert_eq!(Some(1), vec.fragments().last().map(|f| f.len()));
40/// ```
41#[derive(Debug, Clone, PartialEq)]
42pub struct Linear {
43 constant_fragment_capacity_exponent: usize,
44 constant_fragment_capacity: usize,
45}
46
47impl Linear {
48 /// Creates a linear growth where each fragment will have a capacity of `2 ^ constant_fragment_capacity_exponent`.
49 ///
50 /// # Panics
51 ///
52 /// Panics if `constant_fragment_capacity_exponent` is zero or `constant_fragment_capacity_exponent >= MAX_EXPONENT` where `MAX_EXPONENT` is:
53 ///
54 /// * 29 in 32-bit targets,
55 /// * 32 in 64-bit.
56 pub fn new(constant_fragment_capacity_exponent: usize) -> Self {
57 assert!(
58 constant_fragment_capacity_exponent > 0
59 && constant_fragment_capacity_exponent < FIXED_CAPACITIES.len(),
60 "constant_fragment_capacity_exponent must be within 1..32 (1..29) for 64-bit (32-bit) platforms."
61 );
62 let constant_fragment_capacity = FIXED_CAPACITIES[constant_fragment_capacity_exponent];
63 Self {
64 constant_fragment_capacity_exponent,
65 constant_fragment_capacity,
66 }
67 }
68}
69
70impl PseudoDefault for Linear {
71 fn pseudo_default() -> Self {
72 Self::new(10)
73 }
74}
75
76impl Growth for Linear {
77 #[inline(always)]
78 fn new_fragment_capacity_from(
79 &self,
80 _fragment_capacities: impl ExactSizeIterator<Item = usize>,
81 ) -> usize {
82 self.constant_fragment_capacity
83 }
84
85 #[inline(always)]
86 fn get_fragment_and_inner_indices<T>(
87 &self,
88 vec_len: usize,
89 _fragments: &[Fragment<T>],
90 element_index: usize,
91 ) -> Option<(usize, usize)> {
92 match element_index < vec_len {
93 true => Some(self.get_fragment_and_inner_indices_unchecked(element_index)),
94 false => None,
95 }
96 }
97
98 /// ***O(1)*** Returns a pointer 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<T>(&self, fragments: &[Fragment<T>], index: usize) -> Option<*const T> {
108 <Self as GrowthWithConstantTimeAccess>::get_ptr(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 ///
113 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
114 ///
115 /// # Safety
116 ///
117 /// This method allows to write to a memory which is greater than the split vector's length.
118 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
119 #[inline(always)]
120 fn get_ptr_mut<T>(&self, fragments: &mut [Fragment<T>], index: usize) -> Option<*mut T> {
121 <Self as GrowthWithConstantTimeAccess>::get_ptr_mut(self, fragments, index)
122 }
123
124 /// ***O(1)*** Returns a mutable reference to the `index`-th element of the split vector of the `fragments`
125 /// together with the index of the fragment that the element belongs to
126 /// and index of the element withing the respective fragment.
127 ///
128 /// Returns `None` if `index`-th position does not belong to the split vector; i.e., if `index` is out of cumulative capacity of fragments.
129 ///
130 /// # Safety
131 ///
132 /// This method allows to write to a memory which is greater than the split vector's length.
133 /// On the other hand, it will never return a pointer to a memory location that the vector does not own.
134 fn get_ptr_mut_and_indices<T>(
135 &self,
136 fragments: &mut [Fragment<T>],
137 index: usize,
138 ) -> Option<(*mut T, usize, usize)> {
139 <Self as GrowthWithConstantTimeAccess>::get_ptr_mut_and_indices(self, fragments, index)
140 }
141
142 fn maximum_concurrent_capacity<T>(
143 &self,
144 fragments: &[Fragment<T>],
145 fragments_capacity: usize,
146 ) -> usize {
147 assert!(fragments_capacity >= fragments.len());
148
149 fragments_capacity * self.constant_fragment_capacity
150 }
151
152 fn required_fragments_len<T>(
153 &self,
154 _: &[Fragment<T>],
155 maximum_capacity: usize,
156 ) -> Result<usize, String> {
157 let bound = self.maximum_concurrent_capacity_bound::<T>(&[], 0);
158 if maximum_capacity > bound {
159 return Err(alloc::format!(
160 "Maximum cumulative capacity that can be reached by the Linear strategy is {}.",
161 bound,
162 ));
163 }
164
165 let num_full_fragments = maximum_capacity / self.constant_fragment_capacity;
166 let remainder = maximum_capacity % self.constant_fragment_capacity;
167 let additional_fragment = if remainder > 0 { 1 } else { 0 };
168
169 Ok(num_full_fragments + additional_fragment)
170 }
171
172 fn maximum_concurrent_capacity_bound<T>(&self, _: &[Fragment<T>], _: usize) -> usize {
173 *FIXED_CAPACITIES
174 .last()
175 .expect("fixed capacities is non-empty")
176 }
177}
178
179impl GrowthWithConstantTimeAccess for Linear {
180 #[inline(always)]
181 fn get_fragment_and_inner_indices_unchecked(&self, element_index: usize) -> (usize, usize) {
182 let f = element_index >> self.constant_fragment_capacity_exponent;
183 let i = element_index % self.constant_fragment_capacity;
184 (f, i)
185 }
186
187 fn fragment_capacity_of(&self, _: usize) -> usize {
188 self.constant_fragment_capacity
189 }
190}
191
192impl<T> SplitVec<T, Linear> {
193 /// Creates a split vector with linear growth where each fragment will have a capacity of `2 ^ constant_fragment_capacity_exponent`.
194 ///
195 /// Assuming it is the common case compared to empty vector scenarios,
196 /// it immediately allocates the first fragment to keep the `SplitVec` struct smaller.
197 ///
198 /// # Panics
199 ///
200 /// Panics if `constant_fragment_capacity_exponent` is zero or `constant_fragment_capacity_exponent >= MAX_EXPONENT` where `MAX_EXPONENT` is:
201 ///
202 /// * 29 in 32-bit targets,
203 /// * 32 in 64-bit.
204 ///
205 /// # Examples
206 ///
207 /// ```
208 /// use orx_split_vec::*;
209 ///
210 /// // SplitVec<usize, Linear>
211 /// let mut vec = SplitVec::with_linear_growth(4);
212 ///
213 /// assert_eq!(1, vec.fragments().len());
214 /// assert_eq!(Some(16), vec.fragments().first().map(|f| f.capacity()));
215 /// assert_eq!(Some(0), vec.fragments().first().map(|f| f.len()));
216 ///
217 /// // push 160 elements
218 /// for i in 0..10 * 16 {
219 /// vec.push(i);
220 /// }
221 ///
222 /// assert_eq!(10, vec.fragments().len());
223 /// for fragment in vec.fragments() {
224 /// assert_eq!(16, fragment.len());
225 /// assert_eq!(16, fragment.capacity());
226 /// }
227 ///
228 /// // push the 161-st element
229 /// vec.push(42);
230 /// assert_eq!(11, vec.fragments().len());
231 /// assert_eq!(Some(16), vec.fragments().last().map(|f| f.capacity()));
232 /// assert_eq!(Some(1), vec.fragments().last().map(|f| f.len()));
233 /// ```
234 pub fn with_linear_growth(constant_fragment_capacity_exponent: usize) -> Self {
235 assert!(
236 constant_fragment_capacity_exponent > 0
237 && constant_fragment_capacity_exponent < FIXED_CAPACITIES.len(),
238 "constant_fragment_capacity_exponent must be within 1..32 (1..29) for 64-bit (32-bit) platforms."
239 );
240
241 let constant_fragment_capacity = FIXED_CAPACITIES[constant_fragment_capacity_exponent];
242 let fragments = Fragment::new_empty(constant_fragment_capacity).into_fragments();
243 let growth = Linear::new(constant_fragment_capacity_exponent);
244 Self::from_raw_parts(0, fragments, growth)
245 }
246
247 /// Creates a new split vector with `Linear` growth and initial `fragments_capacity`.
248 ///
249 /// This method differs from [`SplitVec::with_linear_growth`] only by the pre-allocation of fragments collection.
250 /// Note that this (only) important for concurrent programs:
251 /// * SplitVec already keeps all elements pinned to their locations;
252 /// * Creating a buffer for storing the meta information is important for keeping the meta information pinned as well.
253 /// This is relevant and important for concurrent programs.
254 ///
255 /// # Panics
256 ///
257 /// Panics if `constant_fragment_capacity_exponent` is zero or `constant_fragment_capacity_exponent >= MAX_EXPONENT` where `MAX_EXPONENT` is:
258 ///
259 /// * 29 in 32-bit targets,
260 /// * 32 in 64-bit.
261 pub fn with_linear_growth_and_fragments_capacity(
262 constant_fragment_capacity_exponent: usize,
263 fragments_capacity: usize,
264 ) -> Self {
265 assert!(
266 constant_fragment_capacity_exponent > 0
267 && constant_fragment_capacity_exponent < FIXED_CAPACITIES.len(),
268 "constant_fragment_capacity_exponent must be within 1..{}",
269 FIXED_CAPACITIES.len()
270 );
271 assert!(fragments_capacity > 0);
272
273 let constant_fragment_capacity = FIXED_CAPACITIES[constant_fragment_capacity_exponent];
274 let fragments = Fragment::new_empty(constant_fragment_capacity)
275 .into_fragments_with_capacity(fragments_capacity);
276 let growth = Linear::new(constant_fragment_capacity_exponent);
277 Self::from_raw_parts(0, fragments, growth)
278 }
279}