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,
11 alloc::{AllocError, Allocator},
12 maybe_default_allocator,
13 settings::{BumpAllocatorSettings, BumpSettings},
14};
15
16#[cfg(feature = "panic-on-alloc")]
17use crate::panic_on_error;
18
19macro_rules! make_pool {
20 ($($allocator_parameter:tt)*) => {
21 #[doc(alias = "Herd")]
74 #[derive(Debug)]
75 pub struct BumpPool<$($allocator_parameter)*, S = BumpSettings>
76 where
77 A: Allocator,
78 S: BumpAllocatorSettings,
79 {
80 bumps: Mutex<Vec<Bump<A, S>>>,
81 allocator: A,
82 }
83 };
84}
85
86maybe_default_allocator!(make_pool);
87
88impl<A, S> Default for BumpPool<A, S>
89where
90 A: Allocator + Default,
91 S: BumpAllocatorSettings,
92{
93 fn default() -> Self {
94 Self {
95 bumps: Mutex::default(),
96 allocator: Default::default(),
97 }
98 }
99}
100
101impl<A, S> BumpPool<A, S>
102where
103 A: Allocator + Default,
104 S: BumpAllocatorSettings,
105{
106 #[inline]
108 #[must_use]
109 pub fn new() -> Self {
110 Self::default()
111 }
112}
113
114impl<A, S> BumpPool<A, S>
115where
116 A: Allocator,
117 S: BumpAllocatorSettings,
118{
119 #[inline]
121 #[must_use]
122 pub const fn new_in(allocator: A) -> Self {
123 Self {
124 bumps: Mutex::new(Vec::new()),
125 allocator,
126 }
127 }
128
129 pub fn reset(&mut self) {
131 for bump in self.bumps() {
132 bump.reset();
133 }
134 }
135
136 pub fn bumps(&mut self) -> &mut Vec<Bump<A, S>> {
138 self.bumps.get_mut().unwrap_or_else(PoisonError::into_inner)
139 }
140
141 fn lock(&self) -> MutexGuard<'_, Vec<Bump<A, S>>> {
142 self.bumps.lock().unwrap_or_else(PoisonError::into_inner)
143 }
144}
145
146impl<A, S> BumpPool<A, S>
147where
148 A: Allocator + Clone,
149 S: BumpAllocatorSettings,
150{
151 #[must_use]
158 #[inline(always)]
159 pub fn get(&self) -> BumpPoolGuard<'_, A, S> {
160 let bump = match self.lock().pop() {
161 Some(bump) => bump,
162 None => Bump::new_in(self.allocator.clone()),
163 };
164
165 BumpPoolGuard {
166 pool: self,
167 bump: ManuallyDrop::new(bump),
168 }
169 }
170
171 #[must_use]
181 #[inline(always)]
182 #[cfg(feature = "panic-on-alloc")]
183 pub fn get_with_size(&self, size: usize) -> BumpPoolGuard<'_, A, S> {
184 panic_on_error(self.generic_get_with_size(size))
185 }
186
187 #[inline(always)]
197 pub fn try_get_with_size(&self, size: usize) -> Result<BumpPoolGuard<'_, A, S>, AllocError> {
198 self.generic_get_with_size(size)
199 }
200
201 pub(crate) fn generic_get_with_size<E: ErrorBehavior>(&self, size: usize) -> Result<BumpPoolGuard<'_, A, S>, E> {
202 let bump = match self.lock().pop() {
203 Some(bump) => bump,
204 None => Bump::generic_with_size_in(size, self.allocator.clone())?,
205 };
206
207 Ok(BumpPoolGuard {
208 pool: self,
209 bump: ManuallyDrop::new(bump),
210 })
211 }
212
213 #[must_use]
223 #[inline(always)]
224 #[cfg(feature = "panic-on-alloc")]
225 pub fn get_with_capacity(&self, layout: Layout) -> BumpPoolGuard<'_, A, S> {
226 panic_on_error(self.generic_get_with_capacity(layout))
227 }
228
229 #[inline(always)]
239 pub fn try_get_with_capacity(&self, layout: Layout) -> Result<BumpPoolGuard<'_, A, S>, AllocError> {
240 self.generic_get_with_capacity(layout)
241 }
242
243 pub(crate) fn generic_get_with_capacity<E: ErrorBehavior>(&self, layout: Layout) -> Result<BumpPoolGuard<'_, A, S>, E> {
244 let bump = match self.lock().pop() {
245 Some(bump) => bump,
246 None => Bump::generic_with_capacity_in(layout, self.allocator.clone())?,
247 };
248
249 Ok(BumpPoolGuard {
250 pool: self,
251 bump: ManuallyDrop::new(bump),
252 })
253 }
254}
255
256macro_rules! make_pool_guard {
257 ($($allocator_parameter:tt)*) => {
258
259 #[derive(Debug)]
261 pub struct BumpPoolGuard<'a, $($allocator_parameter)*, S = BumpSettings>
262 where
263 A: Allocator,
264 S: BumpAllocatorSettings,
265 {
266 bump: ManuallyDrop<Bump<A, S>>,
267 pool: &'a BumpPool<A, S>,
268 }
269 };
270}
271
272maybe_default_allocator!(make_pool_guard);
273
274impl<'a, A, S> BumpPoolGuard<'a, A, S>
275where
276 A: Allocator,
277 S: BumpAllocatorSettings,
278{
279 pub fn pool(&self) -> &'a BumpPool<A, S> {
281 self.pool
282 }
283}
284
285impl<'a, A, S> Deref for BumpPoolGuard<'a, A, S>
286where
287 A: Allocator,
288 S: BumpAllocatorSettings,
289{
290 type Target = BumpScope<'a, A, S>;
291
292 #[inline(always)]
293 fn deref(&self) -> &Self::Target {
294 unsafe { transmute_lifetime(self.bump.as_scope()) }
295 }
296}
297
298impl<A, S> DerefMut for BumpPoolGuard<'_, A, S>
299where
300 A: Allocator,
301 S: BumpAllocatorSettings,
302{
303 #[inline(always)]
304 fn deref_mut(&mut self) -> &mut Self::Target {
305 unsafe { transmute_lifetime_mut(self.bump.as_mut_scope()) }
306 }
307}
308
309impl<A, S> Drop for BumpPoolGuard<'_, A, S>
310where
311 A: Allocator,
312 S: BumpAllocatorSettings,
313{
314 fn drop(&mut self) {
315 let bump = unsafe { ManuallyDrop::take(&mut self.bump) };
316 self.pool.lock().push(bump);
317 }
318}
319
320#[expect(clippy::elidable_lifetime_names)]
322unsafe fn transmute_lifetime<'from, 'to, 'b, A, S>(scope: &'b BumpScope<'from, A, S>) -> &'b BumpScope<'to, A, S>
323where
324 S: BumpAllocatorSettings,
325{
326 unsafe { mem::transmute(scope) }
327}
328
329#[expect(clippy::elidable_lifetime_names)]
331unsafe fn transmute_lifetime_mut<'from, 'to, 'b, A, S>(scope: &'b mut BumpScope<'from, A, S>) -> &'b mut BumpScope<'to, A, S>
332where
333 S: BumpAllocatorSettings,
334{
335 unsafe { mem::transmute(scope) }
336}