scoped_arena/lib.rs
1//!
2//! Scoped-Arena provides arena allocator with explicit scopes.
3//!
4//! ## Arena allocation
5//!
6//! Arena allocators are simple and provides ludicrously fast allocation.\
7//! Basically allocation requires only increment of internal pointer in the memory block to alignment of allocated object and then to size of allocated object and that's it.\
8//! When memory block is exhausted arena will allocate new bigger memory block.\
9//! Then arena can be reset after all allocated objects are not used anymore, keeping only last memory block and reuse it.\
10//! After several warmup iterations the only memory block is large enough to handle all allocations until next reset.
11//!
12//!
13//! ### Example
14//!
15//! ```rust
16//! use scoped_arena::Scope;
17//!
18//! struct Cat {
19//! name: String,
20//! hungry: bool,
21//! }
22//!
23//! /// Create new arena with `Global` allocator.
24//! let mut scope = Scope::new();
25//!
26//! /// Construct a cat and move it to the scope.
27//! let cat: &mut Cat = scope.to_scope(Cat {
28//! name: "Fluffy".to_owned(),
29//! hungry: true,
30//! });
31//!
32//! // Now `cat` is a mutable reference bound to scope borrow lifetime.
33//!
34//! assert_eq!(&cat.name, "Fluffy");
35//! assert!(cat.hungry);
36//!
37//! cat.hungry = false;
38//!
39//! // This cat instance on scope will be automatically dropped when `scope` is dropped or reset.
40//! // It is impossible to reset before last usage of `cat`.
41//!
42//! // Next line will drop cat value and free memory occupied by it.
43//! scope.reset();
44//!
45//! // If there were more cats or any other objects put on scope they all would be dropped and memory freed.
46//! ```
47//!
48//! ## Scopes
49//!
50//! To reuse memory earlier this crates provides `Scope` with methods to create sub-`Scope`s.\
51//! When sub-`Scope` is reset or dropped it will `Drop` all stored values and free memory allocated by the scope and flush last of new allocated memory block into parent.\
52//! While objects allocated with parent `Scope` are unchanged and still valid.
53//!
54//! Well placed scopes can significantly reduce memory consumption.\
55//! For example if few function calls use a lot of dynamic memory but don't need it to be available in caller\
56//! they can be provided with sub-scope.\
57//! At the same time any memory allocated in parent scope stays allocated.
58//!
59//! Creating sub-scope is cheap and allocating within sub-scope is as fast as allocating in parent scope.\
60//!
61//! ### Example
62//!
63//! ```rust
64//! use scoped_arena::{Scope, ScopeProxy};
65//!
66//!
67//! fn heavy_on_memory(mut scope: Scope<'_>, foobar: &String) {
68//! for _ in 0 .. 42 {
69//! let foobar: &mut String = scope.to_scope(foobar.clone());
70//! }
71//!
72//! // new `scope` is dropped here and drops all allocated strings and frees memory.
73//! }
74//!
75//! let mut scope = Scope::new();
76//!
77//! // Proxy is required to be friends with borrow checker.
78//! // Creating sub-scope must lock parent `Scope` from being used, which requires mutable borrow, but any allocation borrows `Scope`.
79//! // `Proxy` relaxes this a bit. `Proxy` borrows `Scope` mutably and tie allocated objects lifetime to scopes' borrow lifetime.
80//! // So sub-scope can borrow proxy mutably while there are objects allocated from it.
81//! let mut proxy = scope.proxy();
82//!
83//! let foobar: &mut String = proxy.to_scope("foobar".to_owned());
84//!
85//! // Make sub-scope for the call.
86//! heavy_on_memory(proxy.scope(), &*foobar);
87//!
88//! // If `heavy_on_memory` didn't trigger new memory object allocation in the scope,
89//! // sub-scope drop would rewind scope's internals to exactly the same state.
90//! // Otherwise last of new blocks will become current block in parent scope.
91//! //
92//! // Note that `foobar` is still alive.
93//!
94//! heavy_on_memory(proxy.scope(), &*foobar);
95//! heavy_on_memory(proxy.scope(), &*foobar);
96//! heavy_on_memory(proxy.scope(), &*foobar);
97//! heavy_on_memory(proxy.scope(), &*foobar);
98//!
99//! // Once peak memory consumption is reached, any number of `heavy_on_memory` calls would not require new memory blocks to be allocated.
100//! // Even `loop { heavy_on_memory(proxy.scope(), &*foobar) }` will settle on some big enough block.
101//! ```
102//!
103//! ## Dropping
104//!
105//! `to_scope` and `try_to_scope` methods store drop-glue for values that `needs_drop`.
106//! On reset or drop scope iterates and properly drops all values.
107//! No drop-glue is added for types that doesn't need drop. `Scope` allocates enough memory and writes value there, no bookkeeping overhead.
108//!
109//! ## Iterator collecting
110//!
111//! `to_scope_from_iter` method acts as `to_scope` but works on iterators and returns slices.
112//! The limitation is that `to_scope_from_iter` need to allocate memory enough for upper bound of what iterator can yield.
113//! If upper bound is too large or iterator is unbounded it will always fail.
114//! One can use `try_to_scope_from_iter` so fail is `Err` and not panic.
115//! It is safe for iterator to yield more items then upper bound it reports, `to_scope_from_iter` would not iterate past upper bound.
116//! On success it returns mutable reference to slice with items from iterator in order.
117//! All values will be dropped on scope reset or drop, same as with `to_scope`.
118//!
119//! This method is especially useful to deal with API that requires slices (*glares at FFI*), collecting into temporary `Vec` would cost much more.
120//!
121
122#![no_std]
123#![cfg(any(feature = "allocator_api", feature = "alloc"))]
124#![cfg_attr(feature = "allocator_api", feature(allocator_api))]
125
126#[cfg(feature = "alloc")]
127extern crate alloc;
128
129mod allocator_api;
130mod bucket;
131mod drop;
132
133use core::{
134 alloc::Layout,
135 fmt::{self, Debug},
136 iter::IntoIterator,
137 mem::{align_of, needs_drop, MaybeUninit},
138 ptr::{self, write, NonNull},
139 slice,
140};
141
142#[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
143use alloc::alloc::handle_alloc_error;
144
145use self::{
146 bucket::Buckets,
147 drop::{DropList, WithDrop},
148};
149
150use self::allocator_api::{AllocError, Allocator};
151
152#[cfg(feature = "alloc")]
153use self::allocator_api::Global;
154
155/// Scope associated with `Scope` allocator.
156/// Allows placing values on the scope returning reference bound to scope borrow.
157/// On drop scope drops all values placed onto it.
158/// On drop scope frees all memory allocated from it.
159#[cfg(not(feature = "alloc"))]
160pub struct Scope<'arena, A: Allocator> {
161 buckets: Buckets<'arena>,
162 alloc: &'arena A,
163 drop_list: DropList<'static>,
164}
165
166/// Scope associated with `Scope` allocator.
167/// Allows placing values on the scope returning reference bound to scope borrow.
168/// On drop scope drops all values placed onto it.
169/// On drop scope frees all memory allocated from it.
170#[cfg(feature = "alloc")]
171pub struct Scope<'arena, A: Allocator = Global> {
172 buckets: Buckets<'arena>,
173 alloc: A,
174 drop_list: DropList<'static>,
175}
176
177impl<A> Debug for Scope<'_, A>
178where
179 A: Allocator,
180{
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 f.debug_struct("Scope")
183 .field("buckets", &self.buckets)
184 .finish_non_exhaustive()
185 }
186}
187
188impl<A> Drop for Scope<'_, A>
189where
190 A: Allocator,
191{
192 #[inline(always)]
193 fn drop(&mut self) {
194 unsafe {
195 self.drop_list.reset();
196 self.buckets.reset(&self.alloc, false);
197 }
198 }
199}
200
201#[cfg(feature = "alloc")]
202impl Scope<'_, Global> {
203 /// Returns new instance of arena allocator based on [`Global`] allocator.
204 #[inline(always)]
205 pub fn new() -> Self {
206 Scope::new_in(Global)
207 }
208
209 /// Returns new instance of arena allocator based on [`Global`] allocator
210 /// with preallocated capacity in bytes.
211 #[inline(always)]
212 pub fn with_capacity(capacity: usize) -> Self {
213 Scope::with_capacity_in(capacity, Global)
214 }
215}
216
217impl<A> Scope<'_, A>
218where
219 A: Allocator,
220{
221 /// Returns new instance of arena allocator based on provided allocator.
222 #[inline(always)]
223 pub fn new_in(alloc: A) -> Self {
224 Scope::with_capacity_in(0, alloc)
225 }
226
227 /// Returns new instance of arena allocator based on provided allocator
228 /// with preallocated capacity in bytes.
229 #[inline(always)]
230 pub fn with_capacity_in(capacity: usize, alloc: A) -> Self {
231 Scope {
232 buckets: Buckets::new(capacity, &alloc).expect(ALLOCATOR_CAPACITY_OVERFLOW),
233 alloc,
234 drop_list: DropList::new(),
235 }
236 }
237}
238
239impl<A> Scope<'_, A>
240where
241 A: Allocator,
242{
243 #[inline(always)]
244 pub fn reset(&mut self) {
245 unsafe {
246 self.drop_list.reset();
247 self.buckets.reset(&self.alloc, true);
248 }
249 }
250
251 /// Allocates a block of memory.
252 /// Returns a [`&mut [MaybeUninit<u8>]`] meeting the size and alignment guarantees of layout.
253 /// Actual size of the returned size MAY be larger than requested.
254 /// The returned block should be initialized before use.
255 ///
256 /// Returned block will be deallocated when scope is dropped.
257 #[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
258 #[inline(always)]
259 pub fn alloc(&self, layout: Layout) -> &mut [MaybeUninit<u8>] {
260 match self.try_alloc(layout) {
261 Ok(buf) => buf,
262 Err(_) => handle_alloc_error(layout),
263 }
264 }
265
266 /// Attempts to allocate a block of memory.
267 /// On success, returns a [`&mut [MaybeUninit<u8>]`] meeting the size and alignment guarantees of layout.
268 /// Actual size of the returned size MAY be larger than requested.
269 /// The returned block should be initialized before use.
270 ///
271 /// Returned block will be deallocated when scope is dropped.
272 ///
273 /// # Errors
274 ///
275 /// Returning `Err` indicates that memory is exhausted.
276 #[inline(always)]
277 pub fn try_alloc(&self, layout: Layout) -> Result<&mut [MaybeUninit<u8>], AllocError> {
278 unsafe { self.buckets.allocate(layout, &self.alloc) }
279 }
280
281 /// Allocates a block of memory.
282 /// Returns a [`&mut [u8]`] meeting the size and alignment guarantees of layout.
283 /// Actual size of the returned size MAY be larger than requested.
284 /// The returned block contents is zero-initialized.
285 ///
286 /// Returned block will be deallocated when scope is dropped.
287 #[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
288 #[inline(always)]
289 pub fn alloc_zeroed(&self, layout: Layout) -> &mut [u8] {
290 match self.try_alloc_zeroed(layout) {
291 Ok(buf) => buf,
292 Err(_) => handle_alloc_error(layout),
293 }
294 }
295
296 /// Attempts to allocate a block of memory.
297 /// On success, returns a [`&mut [u8]`] meeting the size and alignment guarantees of layout.
298 /// Actual size of the returned size MAY be larger than requested.
299 /// The returned block contents is zero-initialized.
300 ///
301 /// Returned block will be deallocated when scope is dropped.
302 ///
303 /// # Errors
304 ///
305 /// Returning `Err` indicates that memory is exhausted.
306 #[inline(always)]
307 pub fn try_alloc_zeroed(&self, layout: Layout) -> Result<&mut [u8], AllocError> {
308 let buf = unsafe { self.buckets.allocate(layout, &self.alloc) }?;
309
310 let buf = unsafe {
311 // Zeroing bytes buffer should be safe.
312 ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len());
313
314 // Zero-initialized.
315 slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len())
316 };
317
318 Ok(buf)
319 }
320
321 /// Move value onto the scope.
322 /// Returns mutable reference to value with lifetime equal to scope borrow lifetime.
323 /// Value on scope will be dropped when scope is dropped.
324 ///
325 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
326 #[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
327 #[inline(always)]
328 pub fn to_scope<T>(&self, value: T) -> &mut T {
329 self.to_scope_with(|| value)
330 }
331
332 /// Places value returned from function onto the scope.
333 /// Returns mutable reference to value with lifetime equal to scope borrow lifetime.
334 /// Value on scope will be dropped when scope is dropped.
335 ///
336 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
337 #[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
338 #[inline(always)]
339 pub fn to_scope_with<F, T>(&self, f: F) -> &mut T
340 where
341 F: FnOnce() -> T,
342 {
343 match self.try_to_scope_with(f) {
344 Ok(value) => value,
345 Err(_) => handle_alloc_error(Layout::new::<T>()),
346 }
347 }
348
349 /// Tries to move value onto the scope.
350 /// On success, returns mutable reference to value with lifetime equal to scope borrow lifetime.
351 /// Value on scope will be dropped when scope is dropped.
352 ///
353 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
354 ///
355 /// # Errors
356 ///
357 /// Returning `Err` indicates that memory is exhausted.
358 /// Returning `Err` contains original value.
359 #[inline(always)]
360 pub fn try_to_scope<T>(&self, value: T) -> Result<&mut T, (AllocError, T)> {
361 self.try_to_scope_with(|| value)
362 .map_err(|(err, f)| (err, f()))
363 }
364
365 /// Tries to place value return from function onto the scope.
366 /// On success, returns mutable reference to value with lifetime equal to scope borrow lifetime.
367 /// Value on scope will be dropped when scope is dropped.
368 ///
369 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
370 ///
371 /// # Errors
372 ///
373 /// Returning `Err` indicates that memory is exhausted.
374 /// Returning `Err` contains original value.
375 #[inline(always)]
376 pub fn try_to_scope_with<F, T>(&self, f: F) -> Result<&mut T, (AllocError, F)>
377 where
378 F: FnOnce() -> T,
379 {
380 try_to_scope_with(|layout| self.try_alloc(layout), &self.drop_list, f)
381 }
382
383 /// Move values from iterator onto the scope.
384 /// Returns mutable reference to slice with lifetime equal to scope borrow lifetime.
385 /// Values on scope will be dropped when scope is dropped.
386 ///
387 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
388 ///
389 /// This method allocates memory to hold iterator's upper bound number of items. See [`core::iter::Iterator::size_hint`].
390 /// It will not consume more items.
391 /// This method will always fail for unbound iterators.
392 #[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
393 #[inline(always)]
394 pub fn to_scope_from_iter<T, I>(&self, iter: I) -> &mut [T]
395 where
396 I: IntoIterator<Item = T>,
397 {
398 let too_large_layout = unsafe {
399 Layout::from_size_align_unchecked(usize::MAX - align_of::<T>(), align_of::<T>())
400 };
401 let iter = iter.into_iter();
402 let upper_bound = iter
403 .size_hint()
404 .1
405 .unwrap_or_else(|| handle_alloc_error(too_large_layout));
406
407 match self.try_to_scope_from_iter(iter) {
408 Ok(slice) => slice,
409 Err(_) => {
410 handle_alloc_error(Layout::array::<T>(upper_bound).unwrap_or(too_large_layout))
411 }
412 }
413 }
414
415 /// Tries to move values from iterator onto the scope.
416 /// On success, returns mutable reference to slice with lifetime equal to scope borrow lifetime.
417 /// Values on scope will be dropped when scope is dropped.
418 ///
419 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
420 ///
421 /// This method allocates memory to hold iterator's upper bound number of items. See [`core::iter::Iterator::size_hint`].
422 /// It will not consume more items.
423 /// This method will always fail for unbound iterators.
424 ///
425 /// # Errors
426 ///
427 /// Returning `Err` indicates that memory is exhausted.
428 /// Returning `Err` contains original iterator.
429 #[inline(always)]
430 pub fn try_to_scope_from_iter<T, I>(
431 &self,
432 iter: I,
433 ) -> Result<&mut [T], (AllocError, I::IntoIter)>
434 where
435 I: IntoIterator<Item = T>,
436 {
437 try_to_scope_from_iter(|layout| self.try_alloc(layout), &self.drop_list, iter)
438 }
439
440 /// Put multiple clones of the value onto the scope.
441 /// Returns mutable reference to slice with lifetime equal to scope borrow lifetime.
442 /// Values on scope will be dropped when scope is dropped.
443 ///
444 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
445 #[inline(always)]
446 pub fn to_scope_many<T>(&self, count: usize, value: T) -> &mut [T]
447 where
448 T: Clone,
449 {
450 let too_large_layout = unsafe {
451 Layout::from_size_align_unchecked(usize::MAX - align_of::<T>(), align_of::<T>())
452 };
453 match self.try_to_scope_many(count, value) {
454 Ok(slice) => slice,
455 Err(_) => handle_alloc_error(Layout::array::<T>(count).unwrap_or(too_large_layout)),
456 }
457 }
458
459 /// Tries to put multiple clones of the value onto the scope.
460 /// On success, returns mutable reference to slice with lifetime equal to scope borrow lifetime.
461 /// Returns mutable reference to slice with lifetime equal to scope borrow lifetime.
462 /// Values on scope will be dropped when scope is dropped.
463 ///
464 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
465 #[inline(always)]
466 pub fn try_to_scope_many<T>(&self, count: usize, value: T) -> Result<&mut [T], AllocError>
467 where
468 T: Clone,
469 {
470 self.try_to_scope_many_with(count, || value.clone())
471 }
472
473 /// Put multiple values created by calls to the specified function onto the scope.
474 /// Returns mutable reference to slice with lifetime equal to scope borrow lifetime.
475 /// Values on scope will be dropped when scope is dropped.
476 ///
477 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
478 #[inline(always)]
479 pub fn to_scope_many_with<T, F>(&self, count: usize, f: F) -> &mut [T]
480 where
481 F: FnMut() -> T,
482 {
483 let too_large_layout = unsafe {
484 Layout::from_size_align_unchecked(usize::MAX - align_of::<T>(), align_of::<T>())
485 };
486 match self.try_to_scope_many_with(count, f) {
487 Ok(slice) => slice,
488 Err(_) => handle_alloc_error(Layout::array::<T>(count).unwrap_or(too_large_layout)),
489 }
490 }
491
492 /// Tries to put multiple values created by calls to the specified function onto the scope.
493 /// On success, returns mutable reference to slice with lifetime equal to scope borrow lifetime.
494 /// Returns mutable reference to slice with lifetime equal to scope borrow lifetime.
495 /// Values on scope will be dropped when scope is dropped.
496 ///
497 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
498 #[inline(always)]
499 pub fn try_to_scope_many_with<T, F>(&self, count: usize, f: F) -> Result<&mut [T], AllocError>
500 where
501 F: FnMut() -> T,
502 {
503 try_to_scope_many_with(|layout| self.try_alloc(layout), &self.drop_list, count, f)
504 }
505
506 /// Reports total memory allocated from underlying allocator by associated arena.
507 #[inline(always)]
508 pub fn total_memory_usage(&self) -> usize {
509 self.buckets.total_memory_usage()
510 }
511
512 /// Creates scope proxy bound to the scope.
513 /// Any objects allocated through proxy will be attached to the scope.
514 /// Returned proxy will use reference to the underlying allocator.
515 #[inline(always)]
516 pub fn proxy_ref<'a>(&'a mut self) -> ScopeProxy<'a, &'a A> {
517 ScopeProxy {
518 buckets: self.buckets.fork(),
519 alloc: &self.alloc,
520 drop_list: self.drop_list.fork(),
521 }
522 }
523}
524
525impl<A> Scope<'_, A>
526where
527 A: Allocator + Clone,
528{
529 /// Creates scope proxy bound to the scope.
530 /// Any objects allocated through proxy will be attached to the scope.
531 /// Returned proxy will use clone of the underlying allocator.
532 #[inline(always)]
533 pub fn proxy<'a>(&'a mut self) -> ScopeProxy<'a, A> {
534 ScopeProxy {
535 buckets: self.buckets.fork(),
536 alloc: self.alloc.clone(),
537 drop_list: self.drop_list.fork(),
538 }
539 }
540}
541
542/// Proxy for `Scope` which allocates memory bound to the scope lifetime and not itself.
543/// This allows to create sub-scopes while keeping references to scoped values.
544/// Does not frees memory and does not drops values moved on scope when dropped.
545/// Parent `Scope` will do this.
546#[cfg(not(feature = "alloc"))]
547pub struct ScopeProxy<'scope, A: Allocator> {
548 buckets: Buckets<'scope>,
549 alloc: &'scope A,
550 drop_list: DropList<'scope>,
551}
552
553/// Proxy for `Scope` which allocates memory bound to the scope lifetime and not itself.
554/// This allows to create sub-scopes while keeping references to scoped values.
555/// Does not frees memory and does not drops values moved on scope when dropped.
556/// Parent `Scope` will do this.
557#[cfg(feature = "alloc")]
558pub struct ScopeProxy<'scope, A: Allocator = Global> {
559 buckets: Buckets<'scope>,
560 alloc: A,
561 drop_list: DropList<'scope>,
562}
563
564impl<A> Debug for ScopeProxy<'_, A>
565where
566 A: Allocator,
567{
568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569 f.debug_struct("ScopeProxy")
570 .field("buckets", &self.buckets)
571 .finish_non_exhaustive()
572 }
573}
574
575impl<A> Drop for ScopeProxy<'_, A>
576where
577 A: Allocator,
578{
579 #[inline(always)]
580 fn drop(&mut self) {
581 unsafe {
582 self.drop_list.flush_fork();
583 self.buckets.flush_fork();
584 }
585 }
586}
587
588impl<'scope, A> ScopeProxy<'scope, A>
589where
590 A: Allocator,
591{
592 /// Allocates a block of memory.
593 /// Returns a [`&mut [MaybeUninit<u8>]`] meeting the size and alignment guarantees of layout.
594 /// The returned block should be initialized before use.
595 ///
596 /// Returned block will be deallocated when scope is dropped.
597 #[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
598 #[inline(always)]
599 pub fn alloc(&self, layout: Layout) -> &'scope mut [MaybeUninit<u8>] {
600 match self.try_alloc(layout) {
601 Ok(buf) => buf,
602 Err(_) => handle_alloc_error(layout),
603 }
604 }
605
606 /// Attempts to allocate a block of memory.
607 /// On success, returns a [`&mut [MaybeUninit<u8>]`] meeting the size and alignment guarantees of layout.
608 /// The returned block should be initialized before use.
609 ///
610 /// Returned block will be deallocated when scope is dropped.
611 ///
612 /// # Errors
613 ///
614 /// Returning `Err` indicates that memory is exhausted.
615 #[inline(always)]
616 pub fn try_alloc(&self, layout: Layout) -> Result<&'scope mut [MaybeUninit<u8>], AllocError> {
617 unsafe { self.buckets.allocate(layout, &self.alloc) }
618 }
619
620 /// Allocates a block of memory.
621 /// Returns a [`&mut [u8]`] meeting the size and alignment guarantees of layout.
622 /// The returned block contents is zero-initialized.
623 ///
624 /// Returned block will be deallocated when scope is dropped.
625 #[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
626 #[inline(always)]
627 pub fn alloc_zeroed(&self, layout: Layout) -> &mut [u8] {
628 match self.try_alloc_zeroed(layout) {
629 Ok(buf) => buf,
630 Err(_) => handle_alloc_error(layout),
631 }
632 }
633
634 /// Attempts to allocate a block of memory.
635 /// On success, returns a [`&mut [u8]`] meeting the size and alignment guarantees of layout.
636 /// The returned block contents is zero-initialized.
637 ///
638 /// Returned block will be deallocated when scope is dropped.
639 ///
640 /// # Errors
641 ///
642 /// Returning `Err` indicates that memory is exhausted.
643 #[inline(always)]
644 pub fn try_alloc_zeroed(&self, layout: Layout) -> Result<&mut [u8], AllocError> {
645 let buf = unsafe { self.buckets.allocate(layout, &self.alloc) }?;
646
647 let buf = unsafe {
648 // Zeroing bytes buffer should be safe.
649 ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len());
650
651 // Zero-initialized.
652 slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, buf.len())
653 };
654
655 Ok(buf)
656 }
657
658 /// Move value onto the scope.
659 /// Returns mutable reference to value with lifetime equal to 'scope lifetime.
660 /// Value on scope will be dropped when scope is dropped.
661 ///
662 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
663 #[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
664 #[inline(always)]
665 pub fn to_scope<T>(&self, value: T) -> &'scope mut T {
666 self.to_scope_with(|| value)
667 }
668
669 /// Places value returned from function onto the scope.
670 /// Returns mutable reference to value with lifetime equal to scope borrow lifetime.
671 /// Value on scope will be dropped when scope is dropped.
672 ///
673 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
674 #[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
675 #[inline(always)]
676 pub fn to_scope_with<F, T>(&self, f: F) -> &'scope mut T
677 where
678 F: FnOnce() -> T,
679 {
680 match self.try_to_scope_with(f) {
681 Ok(value) => value,
682 Err(_) => handle_alloc_error(Layout::new::<T>()),
683 }
684 }
685
686 /// Tries to move value onto the scope.
687 /// On success, returns mutable reference to value with lifetime to equal 'scope lifetime.
688 /// Value on scope will be dropped when scope is dropped.
689 ///
690 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
691 ///
692 /// # Errors
693 ///
694 /// Returning `Err` indicates that memory is exhausted.
695 /// Returning `Err` contains original value.
696 #[inline(always)]
697 pub fn try_to_scope<T>(&self, value: T) -> Result<&'scope mut T, (AllocError, T)> {
698 self.try_to_scope_with(|| value)
699 .map_err(|(err, f)| (err, f()))
700 }
701
702 /// Tries to place value return from function onto the scope.
703 /// On success, returns mutable reference to value with lifetime equal to scope borrow lifetime.
704 /// Value on scope will be dropped when scope is dropped.
705 ///
706 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
707 ///
708 /// # Errors
709 ///
710 /// Returning `Err` indicates that memory is exhausted.
711 /// Returning `Err` contains original value.
712 #[inline(always)]
713 pub fn try_to_scope_with<F, T>(&self, f: F) -> Result<&'scope mut T, (AllocError, F)>
714 where
715 F: FnOnce() -> T,
716 {
717 try_to_scope_with(|layout| self.try_alloc(layout), &self.drop_list, f)
718 }
719
720 /// Move values from iterator onto the scope.
721 /// Returns mutable reference to slice with lifetime equal to 'scope lifetime.
722 /// Values on scope will be dropped when scope is dropped.
723 ///
724 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
725 ///
726 /// This method allocates memory to hold iterator's upper bound number of items. See [`core::iter::Iterator::size_hint`].
727 /// It will not consume more items.
728 /// This method will always fail for unbound iterators.
729 #[cfg(all(not(no_global_oom_handling), feature = "alloc"))]
730 #[inline(always)]
731 pub fn to_scope_from_iter<T, I>(&self, iter: I) -> &'scope mut [T]
732 where
733 I: IntoIterator<Item = T>,
734 {
735 let too_large_layout = unsafe {
736 Layout::from_size_align_unchecked(usize::MAX - align_of::<T>(), align_of::<T>())
737 };
738 let iter = iter.into_iter();
739 let upper_bound = iter
740 .size_hint()
741 .1
742 .unwrap_or_else(|| handle_alloc_error(too_large_layout));
743
744 match self.try_to_scope_from_iter(iter) {
745 Ok(slice) => slice,
746 Err(_) => {
747 handle_alloc_error(Layout::array::<T>(upper_bound).unwrap_or(too_large_layout))
748 }
749 }
750 }
751
752 /// Tries to move values from iterator onto the scope.
753 /// On success, returns mutable reference to slice with lifetime equal to 'scope lifetime.
754 /// Values on scope will be dropped when scope is dropped.
755 ///
756 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
757 ///
758 /// This method allocates memory to hold iterator's upper bound number of items. See [`core::iter::Iterator::size_hint`].
759 /// It will not consume more items.
760 /// This method will always fail for unbound iterators.
761 ///
762 /// # Errors
763 ///
764 /// Returning `Err` indicates that memory is exhausted.
765 /// Returning `Err` contains original iterator.
766 #[inline(always)]
767 pub fn try_to_scope_from_iter<T, I>(
768 &self,
769 iter: I,
770 ) -> Result<&'scope mut [T], (AllocError, I::IntoIter)>
771 where
772 I: IntoIterator<Item = T>,
773 {
774 try_to_scope_from_iter(|layout| self.try_alloc(layout), &self.drop_list, iter)
775 }
776
777 /// Put multiple clones of the value onto the scope.
778 /// Returns mutable reference to slice with lifetime equal to scope borrow lifetime.
779 /// Values on scope will be dropped when scope is dropped.
780 ///
781 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
782 #[inline(always)]
783 pub fn to_scope_many<T>(&self, count: usize, value: T) -> &'scope mut [T]
784 where
785 T: Clone,
786 {
787 let too_large_layout = unsafe {
788 Layout::from_size_align_unchecked(usize::MAX - align_of::<T>(), align_of::<T>())
789 };
790 match self.try_to_scope_many(count, value) {
791 Ok(slice) => slice,
792 Err(_) => handle_alloc_error(Layout::array::<T>(count).unwrap_or(too_large_layout)),
793 }
794 }
795
796 /// Tries to put multiple clones of the value onto the scope.
797 /// On success, returns mutable reference to slice with lifetime equal to scope borrow lifetime.
798 /// Returns mutable reference to slice with lifetime equal to scope borrow lifetime.
799 /// Values on scope will be dropped when scope is dropped.
800 ///
801 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
802 #[inline(always)]
803 pub fn try_to_scope_many<T>(
804 &self,
805 count: usize,
806 value: T,
807 ) -> Result<&'scope mut [T], AllocError>
808 where
809 T: Clone,
810 {
811 self.try_to_scope_many_with(count, || value.clone())
812 }
813
814 /// Put multiple values created by calls to the specified function onto the scope.
815 /// Returns mutable reference to slice with lifetime equal to scope borrow lifetime.
816 /// Values on scope will be dropped when scope is dropped.
817 ///
818 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
819 #[inline(always)]
820 pub fn to_scope_many_with<T, F>(&self, count: usize, f: F) -> &'scope mut [T]
821 where
822 F: FnMut() -> T,
823 {
824 let too_large_layout = unsafe {
825 Layout::from_size_align_unchecked(usize::MAX - align_of::<T>(), align_of::<T>())
826 };
827 match self.try_to_scope_many_with(count, f) {
828 Ok(slice) => slice,
829 Err(_) => handle_alloc_error(Layout::array::<T>(count).unwrap_or(too_large_layout)),
830 }
831 }
832
833 /// Tries to put multiple values created by calls to the specified function onto the scope.
834 /// On success, returns mutable reference to slice with lifetime equal to scope borrow lifetime.
835 /// Returns mutable reference to slice with lifetime equal to scope borrow lifetime.
836 /// Values on scope will be dropped when scope is dropped.
837 ///
838 /// This method is as cheap as allocation if value does not needs dropping as reported by [`core::mem::needs_drop`].
839 #[inline(always)]
840 pub fn try_to_scope_many_with<T, F>(
841 &self,
842 count: usize,
843 f: F,
844 ) -> Result<&'scope mut [T], AllocError>
845 where
846 F: FnMut() -> T,
847 {
848 try_to_scope_many_with(|layout| self.try_alloc(layout), &self.drop_list, count, f)
849 }
850
851 /// Reports total memory allocated from underlying allocator by associated arena.
852 #[inline(always)]
853 pub fn total_memory_usage(&self) -> usize {
854 self.buckets.total_memory_usage()
855 }
856
857 /// Creates new scope which inherits from the proxy's scope.
858 /// This scope becomes locked until returned scope is dropped.
859 /// Returned scope will use reference to the underlying allocator.
860 #[inline(always)]
861 pub fn scope_ref<'a>(&'a mut self) -> Scope<'a, &'a A> {
862 Scope {
863 buckets: self.buckets.fork(),
864 alloc: &self.alloc,
865 drop_list: DropList::new(),
866 }
867 }
868}
869
870impl<A> ScopeProxy<'_, A>
871where
872 A: Allocator + Clone,
873{
874 /// Creates new scope which inherits from the proxy's scope.
875 /// This scope becomes locked until returned scope is dropped.
876 /// Returned scope will use clone of the underlying allocator.
877 #[inline(always)]
878 pub fn scope<'a>(&'a mut self) -> Scope<'a, A> {
879 Scope {
880 buckets: self.buckets.fork(),
881 alloc: self.alloc.clone(),
882 drop_list: DropList::new(),
883 }
884 }
885}
886
887const ALLOCATOR_CAPACITY_OVERFLOW: &'static str = "Allocator capacity overflow";
888
889unsafe impl<A> Allocator for &'_ Scope<'_, A>
890where
891 A: Allocator,
892{
893 #[inline(always)]
894 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
895 let buf = self.try_alloc(layout)?;
896 let ptr = unsafe {
897 NonNull::new_unchecked(core::ptr::slice_from_raw_parts_mut(
898 buf.as_mut_ptr() as *mut u8,
899 buf.len(),
900 ))
901 };
902 Ok(ptr)
903 }
904
905 #[inline(always)]
906 unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
907 // Will be deallocated on scope drop.
908 }
909
910 #[cfg(feature = "allocator_api")]
911 #[inline(always)]
912 unsafe fn shrink(
913 &self,
914 ptr: NonNull<u8>,
915 old_layout: Layout,
916 new_layout: Layout,
917 ) -> Result<NonNull<[u8]>, AllocError> {
918 debug_assert!(
919 new_layout.size() <= old_layout.size(),
920 "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
921 );
922
923 // Returns same memory unchanged.
924 // This is valid behavior as change in layout won't affect deallocation
925 // and for `grow{_zeroed}` methods new layout with smaller size will only affect numbers of bytes copied.
926 Ok(NonNull::new_unchecked(core::slice::from_raw_parts_mut(
927 ptr.as_ptr(),
928 old_layout.size(),
929 )))
930 }
931}
932
933unsafe impl<A> Allocator for ScopeProxy<'_, A>
934where
935 A: Allocator,
936{
937 #[inline(always)]
938 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
939 let buf = self.try_alloc(layout)?;
940 let ptr = unsafe {
941 NonNull::new_unchecked(core::ptr::slice_from_raw_parts_mut(
942 buf.as_mut_ptr() as *mut u8,
943 buf.len(),
944 ))
945 };
946 Ok(ptr)
947 }
948
949 #[inline(always)]
950 unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
951 // Will be deallocated on scope drop.
952 }
953
954 #[cfg(feature = "allocator_api")]
955 #[inline(always)]
956 unsafe fn shrink(
957 &self,
958 ptr: NonNull<u8>,
959 old_layout: Layout,
960 new_layout: Layout,
961 ) -> Result<NonNull<[u8]>, AllocError> {
962 debug_assert!(
963 new_layout.size() <= old_layout.size(),
964 "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
965 );
966
967 // Returns same memory unchanged.
968 // This is valid behavior as change in layout won't affect deallocation
969 // and for `grow{_zeroed}` methods new layout with smaller size will only affect numbers of bytes copied.
970 Ok(NonNull::new_unchecked(core::slice::from_raw_parts_mut(
971 ptr.as_ptr(),
972 old_layout.size(),
973 )))
974 }
975}
976
977#[inline(always)]
978fn try_to_scope_with<'a, F, T>(
979 try_alloc: impl FnOnce(Layout) -> Result<&'a mut [MaybeUninit<u8>], AllocError>,
980 drop_list: &DropList,
981 f: F,
982) -> Result<&'a mut T, (AllocError, F)>
983where
984 F: FnOnce() -> T,
985{
986 if needs_drop::<T>() {
987 match try_alloc(Layout::new::<WithDrop<T>>()) {
988 Ok(buf) => {
989 let value = unsafe { WithDrop::init(buf, f(), drop_list) };
990 Ok(value)
991 }
992 Err(err) => Err((err, f)),
993 }
994 } else {
995 match try_alloc(Layout::new::<T>()) {
996 Ok(buf) => {
997 let uninit = unsafe { cast_buf(buf) };
998 unsafe { write(uninit.as_mut_ptr(), f()) };
999 Ok(unsafe { uninit.assume_init_mut() })
1000 }
1001 Err(err) => Err((err, f)),
1002 }
1003 }
1004}
1005
1006fn try_to_scope_from_iter<'a, T, I>(
1007 try_alloc: impl FnOnce(Layout) -> Result<&'a mut [MaybeUninit<u8>], AllocError>,
1008 drop_list: &DropList,
1009 iter: I,
1010) -> Result<&'a mut [T], (AllocError, I::IntoIter)>
1011where
1012 I: IntoIterator<Item = T>,
1013{
1014 let iter = iter.into_iter();
1015 let upper_bound = match iter.size_hint().1 {
1016 Some(upper_bound) => upper_bound,
1017 None => return Err((AllocError, iter)),
1018 };
1019
1020 if needs_drop::<T>() {
1021 match WithDrop::<T>::array_layout(upper_bound) {
1022 Some(layout) => match try_alloc(layout) {
1023 Ok(buf) => {
1024 let slice = unsafe { WithDrop::init_iter(buf, iter, drop_list) };
1025 Ok(slice)
1026 }
1027 Err(err) => Err((err, iter)),
1028 },
1029 None => Err((AllocError, iter)),
1030 }
1031 } else {
1032 match Layout::array::<T>(upper_bound) {
1033 Ok(layout) => match try_alloc(layout) {
1034 Ok(buf) => {
1035 let (uninit, _) = unsafe {
1036 // Buffer with layout for `[T; upper_bound]` was requested.
1037 cast_buf_array::<T>(buf)
1038 };
1039
1040 let item_count = iter.take(uninit.len()).fold(0, |idx, item| {
1041 uninit[idx].write(item);
1042 idx + 1
1043 });
1044
1045 let slice = unsafe {
1046 // First `item_count` elements of the array were initialized from iterator
1047 core::slice::from_raw_parts_mut(uninit.as_mut_ptr() as *mut T, item_count)
1048 };
1049 Ok(slice)
1050 }
1051 Err(err) => Err((err, iter)),
1052 },
1053 Err(_) => Err((AllocError, iter)),
1054 }
1055 }
1056}
1057
1058fn try_to_scope_many_with<'a, T>(
1059 try_alloc: impl FnOnce(Layout) -> Result<&'a mut [MaybeUninit<u8>], AllocError>,
1060 drop_list: &DropList,
1061 count: usize,
1062 mut f: impl FnMut() -> T,
1063) -> Result<&'a mut [T], AllocError> {
1064 if needs_drop::<T>() {
1065 match WithDrop::<T>::array_layout(count) {
1066 Some(layout) => match try_alloc(layout) {
1067 Ok(buf) => {
1068 let slice = unsafe { WithDrop::init_many(buf, count, f, drop_list) };
1069 Ok(slice)
1070 }
1071 Err(err) => Err(err),
1072 },
1073 None => Err(AllocError),
1074 }
1075 } else {
1076 match Layout::array::<T>(count) {
1077 Ok(layout) => match try_alloc(layout) {
1078 Ok(buf) => {
1079 let (uninit, _) = unsafe {
1080 // Buffer with layout for `[T; upper_bound]` was requested.
1081 cast_buf_array::<T>(buf)
1082 };
1083
1084 for i in 0..count {
1085 uninit[i].write(f());
1086 }
1087
1088 let slice = unsafe {
1089 // First `item_count` elements of the array were initialized from iterator
1090 core::slice::from_raw_parts_mut(uninit.as_mut_ptr() as *mut T, count)
1091 };
1092 Ok(slice)
1093 }
1094 Err(err) => Err(err),
1095 },
1096 Err(_) => Err(AllocError),
1097 }
1098 }
1099}
1100
1101unsafe fn cast_buf<T>(buf: &mut [MaybeUninit<u8>]) -> &mut MaybeUninit<T> {
1102 let layout = Layout::new::<T>();
1103 debug_assert_eq!(0, buf.as_mut_ptr() as usize % layout.align());
1104 debug_assert!(buf.len() >= layout.size());
1105 &mut *(buf.as_mut_ptr() as *mut MaybeUninit<T>)
1106}
1107
1108unsafe fn cast_buf_array<T>(
1109 buf: &mut [MaybeUninit<u8>],
1110) -> (&mut [MaybeUninit<T>], &mut [MaybeUninit<u8>]) {
1111 let layout = Layout::new::<T>();
1112 debug_assert_eq!(0, buf.as_mut_ptr() as usize % layout.align());
1113 let len = buf.len() / layout.size();
1114
1115 let (head, tail) = buf.split_at_mut(len * layout.size());
1116 let head = slice::from_raw_parts_mut(head.as_mut_ptr() as *mut MaybeUninit<T>, len);
1117 (head, tail)
1118}