1use std::{
2 alloc::Layout,
3 mem::{self, ManuallyDrop},
4 ops::{Deref, DerefMut},
5 sync::{Mutex, MutexGuard, PoisonError},
6 vec::Vec,
7};
8
9use crate::{
10 Bump, BumpScope, ErrorBehavior, MinimumAlignment, SupportedMinimumAlignment,
11 alloc::{AllocError, Allocator},
12 maybe_default_allocator,
13};
14
15#[cfg(feature = "panic-on-alloc")]
16use crate::panic_on_error;
17
18macro_rules! make_pool {
19 ($($allocator_parameter:tt)*) => {
20 #[doc(alias = "Herd")]
73 #[derive(Debug)]
74 pub struct BumpPool<
75 $($allocator_parameter)*,
76 const MIN_ALIGN: usize = 1,
77 const UP: bool = true,
78 > where
79 MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
80 A: Allocator,
81 {
82 bumps: Mutex<Vec<Bump<A, MIN_ALIGN, UP, true, true>>>,
83 allocator: A,
84 }
85 };
86}
87
88maybe_default_allocator!(make_pool);
89
90impl<A, const MIN_ALIGN: usize, const UP: bool> Default for BumpPool<A, MIN_ALIGN, UP>
91where
92 MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
93 A: Allocator + Default,
94{
95 fn default() -> Self {
96 Self {
97 bumps: Mutex::default(),
98 allocator: Default::default(),
99 }
100 }
101}
102
103impl<A, const MIN_ALIGN: usize, const UP: bool> BumpPool<A, MIN_ALIGN, UP>
104where
105 MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
106 A: Allocator + Default,
107{
108 #[inline]
110 #[must_use]
111 pub fn new() -> Self {
112 Self::default()
113 }
114}
115
116impl<A, const MIN_ALIGN: usize, const UP: bool> BumpPool<A, MIN_ALIGN, UP>
117where
118 MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
119 A: Allocator,
120{
121 #[inline]
123 #[must_use]
124 pub const fn new_in(allocator: A) -> Self {
125 Self {
126 bumps: Mutex::new(Vec::new()),
127 allocator,
128 }
129 }
130
131 pub fn reset(&mut self) {
133 for bump in self.bumps() {
134 bump.reset();
135 }
136 }
137
138 pub fn bumps(&mut self) -> &mut Vec<Bump<A, MIN_ALIGN, UP, true, true>> {
140 self.bumps.get_mut().unwrap_or_else(PoisonError::into_inner)
141 }
142
143 fn lock(&self) -> MutexGuard<'_, Vec<Bump<A, MIN_ALIGN, UP, true, true>>> {
144 self.bumps.lock().unwrap_or_else(PoisonError::into_inner)
145 }
146}
147
148impl<A, const MIN_ALIGN: usize, const UP: bool> BumpPool<A, MIN_ALIGN, UP>
149where
150 MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
151 A: Allocator + Clone,
152{
153 #[must_use]
163 #[inline(always)]
164 #[cfg(feature = "panic-on-alloc")]
165 pub fn get(&self) -> BumpPoolGuard<'_, A, MIN_ALIGN, UP> {
166 panic_on_error(self.generic_get())
167 }
168
169 #[inline(always)]
179 pub fn try_get(&self) -> Result<BumpPoolGuard<'_, A, MIN_ALIGN, UP>, AllocError> {
180 self.generic_get()
181 }
182
183 pub(crate) fn generic_get<E: ErrorBehavior>(&self) -> Result<BumpPoolGuard<'_, A, MIN_ALIGN, UP>, E> {
184 let bump = match self.lock().pop() {
185 Some(bump) => bump,
186 None => Bump::generic_new_in(self.allocator.clone())?,
187 };
188
189 #[expect(deprecated)]
190 Ok(BumpPoolGuard {
191 pool: self,
192 bump: ManuallyDrop::new(bump),
193 })
194 }
195
196 #[must_use]
206 #[inline(always)]
207 #[cfg(feature = "panic-on-alloc")]
208 pub fn get_with_size(&self, size: usize) -> BumpPoolGuard<'_, A, MIN_ALIGN, UP> {
209 panic_on_error(self.generic_get_with_size(size))
210 }
211
212 #[inline(always)]
222 pub fn try_get_with_size(&self, size: usize) -> Result<BumpPoolGuard<'_, A, MIN_ALIGN, UP>, AllocError> {
223 self.generic_get_with_size(size)
224 }
225
226 pub(crate) fn generic_get_with_size<E: ErrorBehavior>(
227 &self,
228 size: usize,
229 ) -> Result<BumpPoolGuard<'_, A, MIN_ALIGN, UP>, E> {
230 let bump = match self.lock().pop() {
231 Some(bump) => bump,
232 None => Bump::generic_with_size_in(size, self.allocator.clone())?,
233 };
234
235 #[expect(deprecated)]
236 Ok(BumpPoolGuard {
237 pool: self,
238 bump: ManuallyDrop::new(bump),
239 })
240 }
241
242 #[must_use]
252 #[inline(always)]
253 #[cfg(feature = "panic-on-alloc")]
254 pub fn get_with_capacity(&self, layout: Layout) -> BumpPoolGuard<'_, A, MIN_ALIGN, UP> {
255 panic_on_error(self.generic_get_with_capacity(layout))
256 }
257
258 #[inline(always)]
268 pub fn try_get_with_capacity(&self, layout: Layout) -> Result<BumpPoolGuard<'_, A, MIN_ALIGN, UP>, AllocError> {
269 self.generic_get_with_capacity(layout)
270 }
271
272 pub(crate) fn generic_get_with_capacity<E: ErrorBehavior>(
273 &self,
274 layout: Layout,
275 ) -> Result<BumpPoolGuard<'_, A, MIN_ALIGN, UP>, E> {
276 let bump = match self.lock().pop() {
277 Some(bump) => bump,
278 None => Bump::generic_with_capacity_in(layout, self.allocator.clone())?,
279 };
280
281 #[expect(deprecated)]
282 Ok(BumpPoolGuard {
283 pool: self,
284 bump: ManuallyDrop::new(bump),
285 })
286 }
287}
288
289macro_rules! make_pool_guard {
290 ($($allocator_parameter:tt)*) => {
291
292 #[derive(Debug)]
294 pub struct BumpPoolGuard<
295 'a,
296 $($allocator_parameter)*,
297 const MIN_ALIGN: usize = 1,
298 const UP: bool = true,
299 > where
300 MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
301 A: Allocator,
302 {
303 bump: ManuallyDrop<Bump<A, MIN_ALIGN, UP, true, true>>,
304
305 #[doc(hidden)]
306 #[deprecated = "Swapping the pool can lead to Undefined Behavior due to dangling pointers, use `pool()` instead!"]
307 pub pool: &'a BumpPool<A, MIN_ALIGN, UP>,
308 }
309 };
310}
311
312maybe_default_allocator!(make_pool_guard);
313
314impl<'a, A, const MIN_ALIGN: usize, const UP: bool> BumpPoolGuard<'a, A, MIN_ALIGN, UP>
315where
316 MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
317 A: Allocator,
318{
319 pub fn pool(&self) -> &'a BumpPool<A, MIN_ALIGN, UP> {
321 #[expect(deprecated)]
322 self.pool
323 }
324}
325
326impl<'a, A, const MIN_ALIGN: usize, const UP: bool> Deref for BumpPoolGuard<'a, A, MIN_ALIGN, UP>
327where
328 MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
329 A: Allocator,
330{
331 type Target = BumpScope<'a, A, MIN_ALIGN, UP, true, true>;
332
333 #[inline(always)]
334 fn deref(&self) -> &Self::Target {
335 unsafe { transmute_lifetime(self.bump.as_scope()) }
336 }
337}
338
339impl<A, const MIN_ALIGN: usize, const UP: bool> DerefMut for BumpPoolGuard<'_, A, MIN_ALIGN, UP>
340where
341 MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
342 A: Allocator,
343{
344 #[inline(always)]
345 fn deref_mut(&mut self) -> &mut Self::Target {
346 unsafe { transmute_lifetime_mut(self.bump.as_mut_scope()) }
347 }
348}
349
350impl<A, const MIN_ALIGN: usize, const UP: bool> Drop for BumpPoolGuard<'_, A, MIN_ALIGN, UP>
351where
352 MinimumAlignment<MIN_ALIGN>: SupportedMinimumAlignment,
353 A: Allocator,
354{
355 fn drop(&mut self) {
356 let bump = unsafe { ManuallyDrop::take(&mut self.bump) };
357 #[expect(deprecated)]
358 self.pool.lock().push(bump);
359 }
360}
361
362#[expect(clippy::elidable_lifetime_names)]
364unsafe fn transmute_lifetime<'from, 'to, 'b, A, const MIN_ALIGN: usize, const UP: bool>(
365 scope: &'b BumpScope<'from, A, MIN_ALIGN, UP, true, true>,
366) -> &'b BumpScope<'to, A, MIN_ALIGN, UP, true, true> {
367 unsafe { mem::transmute(scope) }
368}
369
370#[expect(clippy::elidable_lifetime_names)]
372unsafe fn transmute_lifetime_mut<'from, 'to, 'b, A, const MIN_ALIGN: usize, const UP: bool>(
373 scope: &'b mut BumpScope<'from, A, MIN_ALIGN, UP, true, true>,
374) -> &'b mut BumpScope<'to, A, MIN_ALIGN, UP, true, true> {
375 unsafe { mem::transmute(scope) }
376}