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}