1use std::collections::{HashMap, HashSet, TryReserveError};
7use std::hash::{BuildHasher, Hash};
8
9use smallvec::{Array, SmallVec};
10
11use crate::BackendError;
12
13fn reserve_error(
14 context: &'static str,
15 requested: usize,
16 item: &'static str,
17 source: impl std::fmt::Display,
18 fix: &'static str,
19) -> BackendError {
20 BackendError::new(format!(
21 "{context} could not reserve {requested} {item}(s): {source}. Fix: {fix}."
22 ))
23}
24
25pub fn reserve_vec_additional<T>(
31 vec: &mut Vec<T>,
32 additional: usize,
33 context: &'static str,
34 item: &'static str,
35 fix: &'static str,
36) -> Result<(), BackendError> {
37 vec.try_reserve(additional)
38 .map_err(|source| reserve_error(context, additional, item, source, fix))
39}
40
41pub fn try_reserve_vec_to_capacity<T>(
48 vec: &mut Vec<T>,
49 target_capacity: usize,
50) -> Result<(), TryReserveError> {
51 vyre_foundation::allocation::try_reserve_vec_to_capacity(vec, target_capacity)
52}
53
54pub fn reserve_vec_to_capacity<T>(
64 vec: &mut Vec<T>,
65 target_capacity: usize,
66 context: &'static str,
67 item: &'static str,
68 fix: &'static str,
69) -> Result<(), BackendError> {
70 try_reserve_vec_to_capacity(vec, target_capacity)
71 .map_err(|source| reserve_error(context, target_capacity, item, source, fix))
72}
73
74pub fn reserve_smallvec_additional<A>(
80 vec: &mut SmallVec<A>,
81 additional: usize,
82 context: &'static str,
83 item: &'static str,
84 fix: &'static str,
85) -> Result<(), BackendError>
86where
87 A: Array,
88{
89 vec.try_reserve(additional)
90 .map_err(|source| reserve_error(context, additional, item, source, fix))
91}
92
93pub fn reserve_smallvec_to_capacity<A>(
100 vec: &mut SmallVec<A>,
101 target_capacity: usize,
102 context: &'static str,
103 item: &'static str,
104 fix: &'static str,
105) -> Result<(), BackendError>
106where
107 A: Array,
108{
109 vyre_foundation::allocation::try_reserve_smallvec_to_capacity(vec, target_capacity)
110 .map_err(|source| reserve_error(context, target_capacity, item, source, fix))
111}
112
113pub fn try_reserve_hash_map_to_capacity<K, V, S>(
121 map: &mut HashMap<K, V, S>,
122 target_capacity: usize,
123) -> Result<(), TryReserveError>
124where
125 K: Eq + Hash,
126 S: BuildHasher,
127{
128 vyre_foundation::allocation::try_reserve_hash_map_to_capacity(map, target_capacity)
129}
130
131pub fn try_reserve_hash_set_to_capacity<T, S>(
139 set: &mut HashSet<T, S>,
140 target_capacity: usize,
141) -> Result<(), TryReserveError>
142where
143 T: Eq + Hash,
144 S: BuildHasher,
145{
146 vyre_foundation::allocation::try_reserve_hash_set_to_capacity(set, target_capacity)
147}
148
149pub fn reserve_hash_map_to_capacity<K, V, S>(
156 map: &mut HashMap<K, V, S>,
157 target_capacity: usize,
158 context: &'static str,
159 item: &'static str,
160 fix: &'static str,
161) -> Result<(), BackendError>
162where
163 K: Eq + Hash,
164 S: BuildHasher,
165{
166 try_reserve_hash_map_to_capacity(map, target_capacity)
167 .map_err(|source| reserve_error(context, target_capacity, item, source, fix))
168}
169
170pub fn reserve_hash_set_to_capacity<T, S>(
177 set: &mut HashSet<T, S>,
178 target_capacity: usize,
179 context: &'static str,
180 item: &'static str,
181 fix: &'static str,
182) -> Result<(), BackendError>
183where
184 T: Eq + Hash,
185 S: BuildHasher,
186{
187 try_reserve_hash_set_to_capacity(set, target_capacity)
188 .map_err(|source| reserve_error(context, target_capacity, item, source, fix))
189}
190
191#[cfg(test)]
192mod tests {
193 use std::collections::{HashMap, HashSet};
194
195 use smallvec::SmallVec;
196
197 use super::{
198 reserve_hash_map_to_capacity, reserve_hash_set_to_capacity, reserve_smallvec_additional,
199 reserve_smallvec_to_capacity, reserve_vec_additional, reserve_vec_to_capacity,
200 };
201
202 #[test]
203 fn reserve_vec_to_capacity_grows_after_clear() {
204 let mut bytes = Vec::with_capacity(16);
205 bytes.extend_from_slice(&[1_u8; 12]);
206 bytes.clear();
207
208 reserve_vec_to_capacity(
209 &mut bytes,
210 20,
211 "generated reserve test",
212 "byte",
213 "split generated dispatch",
214 )
215 .expect("Fix: reserve_vec_to_capacity should grow cleared vectors");
216
217 assert!(bytes.capacity() >= 20);
218 assert!(bytes.is_empty());
219 }
220
221 #[test]
222 fn reserve_smallvec_to_capacity_grows_after_clear() {
223 let mut words = SmallVec::<[u32; 4]>::new();
224 words.extend_from_slice(&[1, 2, 3, 4]);
225 words.clear();
226
227 reserve_smallvec_to_capacity(
228 &mut words,
229 8,
230 "generated reserve test",
231 "word",
232 "split generated dispatch",
233 )
234 .expect("Fix: reserve_smallvec_to_capacity should grow cleared smallvecs");
235
236 assert!(words.capacity() >= 8);
237 assert!(words.is_empty());
238 }
239
240 #[test]
241 fn additional_reservations_preserve_length() {
242 let mut bytes = vec![1_u8, 2, 3];
243 reserve_vec_additional(
244 &mut bytes,
245 10,
246 "generated reserve test",
247 "byte",
248 "split generated dispatch",
249 )
250 .expect("Fix: reserve_vec_additional should not mutate length");
251 assert_eq!(bytes, vec![1, 2, 3]);
252
253 let mut small = SmallVec::<[u8; 2]>::new();
254 small.push(9);
255 reserve_smallvec_additional(
256 &mut small,
257 10,
258 "generated reserve test",
259 "byte",
260 "split generated dispatch",
261 )
262 .expect("Fix: reserve_smallvec_additional should not mutate length");
263 assert_eq!(small.as_slice(), &[9]);
264 }
265
266 #[test]
267 fn hash_collection_reservations_grow_after_clear_without_reinserting() {
268 let mut map = HashMap::<u32, u32>::with_capacity(4);
269 let mut set = HashSet::<u32>::with_capacity(4);
270 for value in 0..4 {
271 map.insert(value, value * 10);
272 set.insert(value);
273 }
274 map.clear();
275 set.clear();
276
277 for target in [8, 32, 128, 1024] {
278 reserve_hash_map_to_capacity(
279 &mut map,
280 target,
281 "generated reserve test",
282 "entry",
283 "split generated dispatch",
284 )
285 .expect("Fix: hash map target reservation should grow cleared maps");
286 reserve_hash_set_to_capacity(
287 &mut set,
288 target,
289 "generated reserve test",
290 "entry",
291 "split generated dispatch",
292 )
293 .expect("Fix: hash set target reservation should grow cleared sets");
294
295 assert!(map.capacity() >= target);
296 assert!(set.capacity() >= target);
297 assert!(map.is_empty());
298 assert!(set.is_empty());
299 }
300 }
301}