rotunda/lib.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3#![no_std]
4#![cfg_attr(
5 feature = "nightly",
6 feature(
7 ptr_metadata,
8 derive_coerce_pointee,
9 allocator_api,
10 read_buf,
11 core_io_borrowed_buf
12 )
13)]
14#![cfg_attr(all(feature = "nightly", feature = "std"), feature(can_vector))]
15#![warn(
16 missing_docs,
17 clippy::empty_line_after_doc_comments,
18 clippy::missing_safety_doc
19)]
20#![deny(unsafe_attr_outside_unsafe, unsafe_op_in_unsafe_fn)]
21
22//! This module contains types for using the arena allocation strategy. See the [`Arena`] type
23//! for more information on how to use arena allocation.
24//!
25//! # Arena allocation
26//!
27//! In many workloads, the lifetime of a heap allocation is scoped to a particular function call.
28//! For example:
29//!
30//! ```
31//! # #[derive(Default)]
32//! # struct SomeType {};
33//! # #[derive(Default)]
34//! # struct OtherType {};
35//! # fn use_values(_: &SomeType, _: &OtherType) {}
36//! fn some_function() {
37//! let value = Box::new(SomeType::default());
38//! let value_2 = Box::new(OtherType::default());
39//!
40//! // Use `value` and `value_2` in the body of `some_function()`
41//! // ...
42//! use_values(&*value, &*value_2);
43//!
44//! // `value` and `value_2` are dropped and deallocated here as the function returns.
45//! }
46//! ```
47//!
48//! If many values need to be allocated and deallocated for a function body, this can be
49//! inefficient as the heap allocator needs to do work to allocate and deallocate each
50//! individual value.
51//!
52//! To compensate for this, many strategies can be used so that values are allocated from
53//! a single block of memory, reducing the work that the memory allocator needs to do.
54//! One of these strategies is called arena allocation.
55//!
56//! The idea behind arena allocation is to preallocate a large block of memory, and then
57//! write values into that block as necessary. Values cannot be freed until the entire block
58//! is finished with, when every value is implicitly deallocated at once.
59//!
60//! # Examples
61//!
62//! To rewrite the above example `some_function()` using arena allocation provided by this crate,
63//! an `Arena` would need to be constructed or passed in to the function. Any transient data allocated
64//! by the function would then be allocated from the `Arena`:
65//!
66//! ```
67//! use rotunda::{Arena, handle::Handle};
68//!
69//! # #[derive(Default)]
70//! # struct SomeType {};
71//! # #[derive(Default)]
72//! # struct OtherType {};
73//! # fn use_values(_: &SomeType, _: &OtherType) {}
74//! fn some_function(arena: &Arena) {
75//! // Allocate the values as before:
76//! let value = Handle::new_in(&arena, SomeType::default());
77//! let value_2 = Handle::new_in(&arena, OtherType::default());
78//!
79//! // Use the values as before:
80//! use_values(&*value, &*value_2);
81//!
82//! // The values are dropped here, but the memory backing them in the arena is
83//! // not deallocated until the `Arena` is dropped, or the `Arena::reset()` method
84//! // is called.
85//! }
86//! ```
87//!
88//! The `Arena` can be visualised as an array of memory, with an integer marking the current
89//! end of the `Arena`. When the `Arena` is first initialised and has a block allocated, then
90//! the stack is empty:
91//!
92//! ```text
93//! +-------+-----------------------------------------------------------+
94//! | (H) | |
95//! +-------+-----------------------------------------------------------+
96//! ^ ^
97//! Header Arena end
98//! ```
99//!
100//! Then, when `value` and `value_2` are allocated into the arena, they
101//! are written into the memory, and the end is adjusted to point into the
102//! next available memory space:
103//!
104//! ```text
105//! +-------+---------------+---------------+--------------------------------------+
106//! | (H) | (data) | (data) | |
107//! +-------+---------------+---------------+--------------------------------------+
108//! ^ ^ ^ ^
109//! Header value value_2 Arena end
110//! ```
111//!
112//! Then, when `value` and `value_2` are dropped, their `drop` logic will be
113//! called, but the backing storage of the `Arena` will not shrink to release
114//! the memory; it will remain inaccessible for future allocations:
115//!
116//! ```text
117//! +-------+-------------------------------+--------------------------------------+
118//! | (H) |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| |
119//! +-------+-------------------------------+--------------------------------------+
120//! ^ ^
121//! Header Arena end
122//! ```
123//!
124//! When the [`Arena::reset()`] method is called then the memory will be reclaimed,
125//! and will be usable for future allocations. Note that no active references are
126//! allowed into the `Arena` when [`Arena::reset()`] is called, so there is no
127//! danger of referring to dangling memory. When the `Arena` is dropped, the
128//! backing memory will be deallocated by the system allocator and returned to
129//! the operating system.
130//!
131//! If a block of memory in the arena is completely used up, then a new one will be
132//! allocated. Each block stores a pointer to the next block in its header, so blocks
133//! will be accessible. reseting an `Arena` will cause blocks to be stored in a free
134//! list so that they can be re-used later, reducing pressure on the memory allocator.
135//!
136//! # Handle Types
137//!
138//! The `arena` module comes with a few specialised handle types to make working with `Arena`s
139//! more ergonomic. These provide a few useful conveniences:
140//!
141//! * They have a lifetime which ensures that data allocated from the `Arena` cannot outlive the
142//! `Arena`, preventing use after frees.
143//! * They handle `Drop` logic when the handle goes out of scope, preventing resource leaks.
144//!
145//! ## [`Handle`]
146//!
147//! The basic handle type is [`handle::Handle`], which is analogous to a `Box<T>` - it provides unique ownership
148//! of an object allocated in an [`Arena`], allows mutation, and drops the object when it goes out of scope.
149//!
150//! Read more in the [`handle`] module.
151//!
152//! ## [`RcHandle`] and [`WeakHandle`]
153//!
154//! These are reference counted shared ownership handle types, analogous to `Rc<T>` and `Weak<T>`.
155//!
156//! Read more in the [`rc_handle`] module.
157//!
158//! ## [`Buffer`]
159//!
160//! An owned, growable buffer of elements backed by an allocation into an `Arena`. The [`Buffer`] has
161//! a fixed maximum size which cannot be changed in the `Arena`.
162//!
163//! Read more in the [`buffer`] module.
164//!
165//! ## [`StringBuffer`]
166//!
167//! An owned, growable buffer containing utf8 encoded bytes.
168//!
169//! Read more in the [`string_buffer`] module.
170//!
171//! ## [`LinkedList`]
172//!
173//! A linked list of nodes, backed by an `Arena`. This allows an ordered collection of elements backed by
174//! an `Arena` without requiring contiguous space for all elements in the `Arena`.
175//!
176//! Read more in the [`linked_list`] module.
177//!
178//! # Features
179//!
180//! This crate can be customised with a few optional features:
181//!
182//! ## `allocator-api2`
183//!
184//! This feature uses the `allocator-api2` crate for its definitions of the `Allocator` trait
185//! and other supporting APIs. This allows `rotunda` to be built on the `stable` and `beta`
186//! rust compilers.
187//!
188//! It is required that at least one of either this feature or the `nightly` feature are enabled so that
189//! an allocator API is provided.
190//!
191//! ## `nightly`
192//!
193//! This feature enables usage of nightly features in `rotunda`, such as [`CoercePointee`]. It also
194//! allows using the `Allocator` trait and supporting APIs from [`alloc::alloc`]. Note that this feature
195//! will supersede `allocator-api2` (i.e. `alloc::alloc::Allocator` will be used over `allocator_api2::alloc::Allocator`)
196//! if both are enabled.
197//!
198//! ## `std`
199//!
200//! This feature enables integration with `std` traits, such as [`std::io::Read`].
201//!
202//! ## `serde`
203//!
204//! This feature allows the contents of handle types to be serialized transparently using [`serde`].
205//!
206//! [`Arena`]: ./struct.Arena.html
207//! [`Arena::reset()`]: ./struct.Arena.html#method.reset
208//! [`handle::Handle`]: ./handle/struct.Handle.html
209//! [`handle`]: ./handle/index.html
210//! [`Handle`]: ./handle/struct.Handle.html
211//! [`WeakHandle`]: ./rc_handle/struct.WeakHandle.html
212//! [`RcHandle`]: ./rc_handle/struct.RcHandle.html
213//! [`rc_handle`]: ./rc_handle/index.html
214//! [`Buffer`]: ./buffer/struct.Buffer.html
215//! [`StringBuffer`]: ./string_buffer/struct.StringBuffer.html
216//! [`string_buffer`]: ./string_buffer/index.html
217//! [`LinkedList`]: ./linked_list/struct.LinkedList.html
218//! [`linked_list`]: ./linked_list/index.html
219//! [`CoercePointee`]: https://doc.rust-lang.org/stable/core/marker/derive.CoercePointee.html
220//! [`alloc::alloc`]: https://doc.rust-lang.org/stable/core/alloc/alloc/index.html
221//! [`std::io::Read`]: https://doc.rust-lang.org/stable/std/io/trait.Read.html
222//! [`serde`]: https://docs.rs/serde/latest
223
224#[cfg(not(any(feature = "allocator-api2", feature = "nightly")))]
225compile_error!("An allocator must be provided, either through `nightly` or `allocator-api2`");
226
227#[cfg(feature = "nightly")]
228extern crate alloc;
229
230#[cfg(all(feature = "allocator-api2", not(feature = "nightly")))]
231extern crate allocator_api2 as alloc;
232
233#[cfg(any(test, feature = "std"))]
234extern crate std;
235
236use crate::blocks::{Block, BlockIter, Blocks, ScopedRestore};
237use alloc::alloc::{AllocError, Allocator, Global, Layout, LayoutError, handle_alloc_error};
238use core::{
239 error::Error as ErrorTrait,
240 ffi::c_void,
241 fmt,
242 iter::FusedIterator,
243 marker::PhantomData,
244 mem::{ManuallyDrop, MaybeUninit},
245 ptr::{self, NonNull},
246 str,
247};
248
249pub mod buffer;
250pub mod handle;
251pub mod linked_list;
252pub mod rc_handle;
253pub mod string_buffer;
254
255mod blocks;
256#[cfg(test)]
257mod tests;
258
259/// An arena allocator, parameterised by global allocator.
260///
261/// See the [module documentation](index.html) for more info.
262pub struct Arena<A: Allocator = Global> {
263 blocks: Blocks,
264 alloc: A,
265 _boo: PhantomData<*mut c_void>,
266}
267
268impl Arena {
269 /// Creates a new `Arena` with the default allocator and block size.
270 ///
271 /// See [`Arena::new_in()`] for more details.
272 ///
273 /// # Example
274 ///
275 /// ```
276 /// use rotunda::Arena;
277 ///
278 /// let arena = Arena::new();
279 /// ```
280 ///
281 /// [`Arena::new_in()`]: ./struct.Arena.html#method.new_in
282 #[must_use]
283 #[inline]
284 pub const fn new() -> Self {
285 const { Self::new_in(Global) }
286 }
287
288 /// Creates a new `Arena` with the default allocator and a custom block size
289 /// in bytes.
290 ///
291 /// See [`Arena::with_block_size_in()`] for more details.
292 ///
293 /// # Panics
294 ///
295 /// This method will panic if `block_size` is greater than `isize::MAX`, as this is a
296 /// requirement rust imposes on its allocator trait.
297 ///
298 /// # Example
299 ///
300 /// ```
301 /// use rotunda::Arena;
302 ///
303 /// let block_size = 1024 * 1024 * 3;
304 /// let arena = Arena::with_block_size(block_size);
305 /// ```
306 ///
307 /// [`Arena::with_block_size_in()`]: ./struct.Arena.html#method.with_block_size_in
308 #[must_use]
309 #[inline]
310 pub const fn with_block_size(block_size: usize) -> Self {
311 Self::with_block_size_in(block_size, Global)
312 }
313}
314
315impl<A: Allocator> Arena<A> {
316 /// Create a new `Arena`, using the provided `allocator` to allocate
317 /// backing storage from.
318 ///
319 /// The default block size is `isize::MAX + 1`. This provides a good
320 /// trade-off between blocks which are large enough to store most types
321 /// and not require too much reallocation, but not too large that
322 /// the allocator may run out of memory when allocating the backing
323 /// memory for the `Arena`.
324 ///
325 /// See [`Arena::with_block_size_in()`] for more details.
326 ///
327 /// # Notes
328 ///
329 /// This function requires the `allocator_api` feature to be enabled to
330 /// use it.
331 ///
332 /// # Example
333 ///
334 /// ```
335 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
336 ///
337 /// #[cfg(all(feature = "allocator-api2", not(feature = "nightly")))]
338 /// use allocator_api2::alloc::Global;
339 ///
340 /// #[cfg(feature = "nightly")]
341 /// use std::alloc::Global;
342 ///
343 /// use rotunda::Arena;
344 ///
345 /// let arena = Arena::new_in(Global);
346 /// ```
347 ///
348 /// [`Arena::with_block_size_in()`]: ./struct.Arena.html#method.with_block_size_in
349 #[must_use]
350 #[inline]
351 pub const fn new_in(allocator: A) -> Self {
352 let default_block_size = i16::MAX as usize + 1;
353 Self::with_block_size_in(default_block_size, allocator)
354 }
355
356 /// Create a new `Arena` with the given `block_size` in bytes, and the provided
357 /// `allocator` to allocate backing storage from.
358 ///
359 /// # Panics
360 ///
361 /// This method will panic if `block_size` is greater than `isize::MAX`, as this is a
362 /// requirement rust imposes on its allocator trait.
363 ///
364 /// # Example
365 ///
366 /// ```
367 /// #![cfg_attr(feature = "nightly", feature(allocator_api))]
368 ///
369 /// #[cfg(all(feature = "allocator-api2", not(feature = "nightly")))]
370 /// use allocator_api2::alloc::Global;
371 ///
372 /// #[cfg(feature = "nightly")]
373 /// use std::alloc::Global;
374 ///
375 /// use rotunda::Arena;
376 ///
377 /// let block_size = 4 * 1024 * 1024;
378 /// let arena = Arena::with_block_size_in(block_size, Global);
379 /// ```
380 #[must_use]
381 #[inline]
382 pub const fn with_block_size_in(block_size: usize, allocator: A) -> Self {
383 Self {
384 blocks: Blocks::new(block_size),
385 alloc: allocator,
386 _boo: PhantomData,
387 }
388 }
389
390 /// Returns the total number of bytes which can be allocated into the current
391 /// block before a new block will need to be allocated.
392 ///
393 /// If a current block is not available, returns `None`.
394 ///
395 /// # Example
396 ///
397 /// ```
398 /// use rotunda::{Arena, handle::Handle};
399 ///
400 /// let block_size = 4 * 1024 * 1024;
401 /// let arena = Arena::with_block_size(block_size);
402 ///
403 /// let handle = Handle::new_in(&arena, 54i32);
404 /// assert_eq!(arena.curr_block_capacity().unwrap(), block_size - std::mem::size_of::<i32>());
405 /// ```
406 #[must_use]
407 #[inline]
408 pub fn curr_block_capacity(&self) -> Option<usize> {
409 self.blocks.curr_block_capacity()
410 }
411
412 /// Reserves the given number of `num_blocks` into the `Arena`, placing them in the free list.
413 ///
414 /// Should the current block overflow while servicing allocation requests, the free blocks
415 /// can be used without needing to call the allocator.
416 ///
417 /// # Panics
418 ///
419 /// If the allocator cannot allocate the blocks, then `handle_alloc_err()` will be called,
420 /// which may panic or abort the process.
421 ///
422 /// # Example
423 ///
424 /// ```
425 /// use rotunda::Arena;
426 ///
427 /// let arena = Arena::new();
428 /// # let mut arena = arena;
429 ///
430 /// arena.reserve_blocks(3);
431 /// # assert_eq!(arena.free_blocks().count(), 3);
432 /// ```
433 #[track_caller]
434 #[inline]
435 pub fn reserve_blocks(&self, num_blocks: usize) {
436 let (layout, alloc) = (self.blocks.block_layout(), self.allocator());
437 for _ in 0..num_blocks {
438 let block = Block::alloc(layout, alloc);
439 self.blocks.push_free_block(block);
440 }
441 }
442
443 /// Tries to reserve the given number of `num_blocks` into the `Arena`, placing them in the free list.
444 ///
445 /// Should the current block overflow while servicing allocation requests, the free blocks
446 /// can be used without needing to call the allocator.
447 ///
448 /// # Errors
449 ///
450 /// If the allocator cannot allocate a `Block`, then `AllocError` is returned. If other blocks were allocated
451 /// as part of this call, then they will remain in the `Arena`.
452 ///
453 /// # Example
454 ///
455 /// ```
456 /// # #![cfg_attr(feature = "nightly", feature(allocator_api))]
457 ///
458 /// # #[cfg(all(feature = "allocator-api2", not(feature = "nightly")))]
459 /// # use allocator_api2::alloc::AllocError;
460 ///
461 /// # #[cfg(feature = "nightly")]
462 /// # use std::alloc::AllocError;
463 ///
464 /// use rotunda::Arena;
465 ///
466 /// let arena = Arena::new();
467 /// # let mut arena = arena;
468 ///
469 /// # fn inner(arena: &Arena) -> Result<(), AllocError> {
470 /// arena.try_reserve_blocks(3)?;
471 /// # Ok(())
472 /// # }
473 /// # inner(&arena);
474 /// # assert_eq!(arena.free_blocks().count(), 3);
475 /// ```
476 #[inline]
477 pub fn try_reserve_blocks(&self, num_blocks: usize) -> Result<(), AllocError> {
478 let (layout, alloc) = (self.blocks.block_layout(), self.allocator());
479 for _ in 0..num_blocks {
480 let block = Block::try_alloc(layout, alloc)?;
481 self.blocks.push_free_block(block);
482 }
483 Ok(())
484 }
485
486 /// Returns a pointer into the head of the current block.
487 ///
488 /// The available space in the current block is returned as the slice length.
489 ///
490 /// # Examples
491 ///
492 /// ```
493 /// use core::{mem, ptr};
494 /// use rotunda::{Arena, handle::Handle};
495 ///
496 /// let capacity = 3100;
497 ///
498 /// let arena = Arena::with_block_size(capacity);
499 ///
500 /// assert!(arena.curr_block_head().is_none());
501 ///
502 /// arena.force_push_new_block();
503 ///
504 /// let block = arena.curr_block_head().unwrap();
505 /// assert_eq!(block.len(), capacity);
506 ///
507 /// let data = Handle::new_in(&arena, 24i32);
508 /// assert!(ptr::eq(Handle::as_ptr(&data).cast::<u8>(), block.as_ptr().cast()));
509 ///
510 /// let block = arena.curr_block_head().unwrap();
511 /// assert_eq!(block.len(), capacity - mem::size_of::<i32>());
512 /// ```
513 #[inline]
514 pub fn curr_block_head(&self) -> Option<NonNull<[MaybeUninit<u8>]>> {
515 let curr_block = self.blocks.curr_block().get()?;
516
517 let (block_size, block_pos) = (self.block_size(), self.blocks.curr_block_pos().get());
518
519 let data = unsafe {
520 Block::data_ptr(curr_block, block_size)
521 .cast::<MaybeUninit<u8>>()
522 .as_ptr()
523 .map_addr(|data| data + block_pos)
524 };
525
526 NonNull::new(ptr::slice_from_raw_parts_mut(data, block_size - block_pos))
527 }
528
529 /// Forces the `Arena` to push all allocations into a new block of memory.
530 ///
531 /// The current block of memory will be moved to the used list, and will be
532 /// inaccessible until the `Arena` is cleared.
533 ///
534 /// This function will take a block from the free list if it is available, or allocate
535 /// a new one otherwise.
536 ///
537 /// # Panics
538 ///
539 /// This method will panic if the global allocator is unable to allocate a new block.
540 ///
541 /// # Examples
542 ///
543 /// ```
544 /// use rotunda::{Arena, handle::Handle};
545 /// use core::mem;
546 ///
547 /// let arena = Arena::with_block_size(640);
548 ///
549 /// let handle_1 = Handle::new_in(&arena, 543);
550 /// let handle_2 = Handle::new_in(&arena, 123);
551 ///
552 /// assert_eq!(
553 /// Handle::as_ptr(&handle_2).addr() - Handle::as_ptr(&handle_1).addr(),
554 /// mem::size_of::<i32>());
555 ///
556 /// arena.force_push_new_block();
557 ///
558 /// let handle_3 = Handle::new_in(&arena, 456);
559 ///
560 /// assert_ne!(
561 /// Handle::as_ptr(&handle_3).addr() - Handle::as_ptr(&handle_2).addr(),
562 /// mem::size_of::<i32>());
563 /// ```
564 #[track_caller]
565 #[inline]
566 pub fn force_push_new_block(&self) {
567 let _ = unsafe {
568 self.get_free_block()
569 .map_err(|_| handle_alloc_error(self.blocks.block_layout()))
570 };
571 }
572
573 /// Forces the `Arena` to push all allocations into a new block of memory.
574 ///
575 /// The current block of memory will be moved to the used list, and will be
576 /// inaccessible until the `Arena` is cleared.
577 ///
578 /// This function will take a block from the free list if it is available, or allocate
579 /// a new one otherwise.
580 ///
581 /// # Errors
582 ///
583 /// This method will return an error if the global allocator is unable to allocate a new block.
584 #[inline]
585 pub fn try_force_push_new_block(&self) -> Result<(), AllocError> {
586 unsafe { self.get_free_block().map(|_| ()) }
587 }
588
589 /// Checks whether the current block has the capacity to allocate up to `block_capacity_bytes`.
590 ///
591 /// # Examples
592 ///
593 /// ```
594 /// #[cfg(all(feature = "allocator-api2", not(feature = "nightly")))]
595 /// use allocator_api2::alloc::Layout;
596 ///
597 /// #[cfg(feature = "nightly")]
598 /// use std::alloc::Layout;
599 ///
600 /// use rotunda::Arena;
601 ///
602 /// let arena = Arena::with_block_size(25);
603 /// arena.force_push_new_block();
604 ///
605 /// assert!(arena.has_block_capacity(24));
606 ///
607 /// let layout = Layout::new::<[u8; 5]>();
608 /// arena.alloc_raw(layout);
609 ///
610 /// assert!(!arena.has_block_capacity(24));
611 /// ```
612 #[inline]
613 pub fn has_block_capacity(&self, block_capacity_bytes: usize) -> bool {
614 if self.block_size() < block_capacity_bytes {
615 return false;
616 }
617
618 self.curr_block_capacity()
619 .map(|cap| cap >= block_capacity_bytes)
620 .unwrap_or_default()
621 }
622
623 /// Checks whether the current block has the capacity to allocate up to `block_capacity_bytes`,
624 /// and allocates a new block if it doesn't.
625 ///
626 ///
627 /// # Examples
628 ///
629 /// ```
630 /// #[cfg(all(feature = "allocator-api2", not(feature = "nightly")))]
631 /// use allocator_api2::alloc::Layout;
632 ///
633 /// #[cfg(feature = "nightly")]
634 /// use std::alloc::Layout;
635 ///
636 /// use rotunda::Arena;
637 ///
638 /// let arena = Arena::with_block_size(25);
639 /// assert_eq!(arena.curr_block_capacity(), None);
640 ///
641 /// arena.ensure_block_capacity(20);
642 /// assert_eq!(arena.curr_block_capacity(), Some(25));
643 ///
644 /// let _ = arena.alloc_raw(Layout::new::<[u8; 23]>());
645 /// assert!(!arena.has_block_capacity(20));
646 ///
647 /// arena.ensure_block_capacity(20);
648 /// assert!(arena.has_block_capacity(20));
649 /// ```
650 #[inline]
651 pub fn ensure_block_capacity(&self, block_capacity_bytes: usize) {
652 if self.block_size() < block_capacity_bytes {
653 return;
654 }
655
656 if !self.has_block_capacity(block_capacity_bytes) {
657 self.force_push_new_block();
658 }
659 }
660
661 /// Resets the `Arena` so that all used blocks are moved into the free list, and
662 /// the current block counter is reset to `0`.
663 ///
664 /// This method does not use the `Arena`'s allocator to perform any memory
665 /// deallocation.
666 ///
667 /// # Examples
668 ///
669 /// ```
670 /// use rotunda::{Arena, handle::Handle};
671 ///
672 /// let mut arena = Arena::new();
673 /// {
674 /// let handle_1 = Handle::new_in(&arena, 23);
675 /// arena.force_push_new_block();
676 ///
677 /// let handle_2 = Handle::new_in(&arena, 45);
678 /// arena.force_push_new_block();
679 ///
680 /// let handle_2 = Handle::new_in(&arena, 67);
681 /// arena.force_push_new_block();
682 /// }
683 ///
684 /// arena.reset();
685 /// arena.trim();
686 ///
687 /// assert!(arena.curr_block().is_some());
688 /// ```
689 #[inline]
690 pub fn reset(&mut self) {
691 self.blocks.reset();
692 }
693
694 /// Resets the `Arena` so that all used blocks, and the current block, are moved into
695 /// the free list.
696 ///
697 /// This method does not use the `Arena`'s allocator to perform any memory
698 /// deallocation.
699 ///
700 /// # Examples
701 ///
702 /// ```
703 /// use rotunda::{Arena, handle::Handle};
704 ///
705 /// let mut arena = Arena::new();
706 /// {
707 /// let handle_1 = Handle::new_in(&arena, 123);
708 /// arena.force_push_new_block();
709 ///
710 /// let handle_2 = Handle::new_in(&arena, 456);
711 /// arena.force_push_new_block();
712 ///
713 /// let handle_2 = Handle::new_in(&arena, 789);
714 /// arena.force_push_new_block();
715 /// }
716 ///
717 /// arena.reset_all();
718 /// arena.trim();
719 ///
720 /// assert!(arena.curr_block().is_none());
721 #[inline]
722 pub fn reset_all(&mut self) {
723 self.blocks.reset_all();
724 }
725
726 /// Deallocates all blocks in the `Arena`'s free list.
727 ///
728 /// # Examples
729 ///
730 /// ```
731 /// use rotunda::{Arena, handle::Handle};
732 ///
733 /// let mut arena = Arena::new();
734 /// arena.reserve_blocks(1);
735 ///
736 /// assert_eq!(arena.free_blocks().count(), 1);
737 ///
738 /// arena.trim();
739 ///
740 /// assert_eq!(arena.free_blocks().count(), 0);
741 /// ```
742 #[inline]
743 pub fn trim(&self) {
744 self.blocks.trim(self.allocator());
745 }
746
747 /// Deallocates `n` blocks in the `Arena`'s free list.
748 ///
749 /// # Examples
750 ///
751 /// ```
752 /// use rotunda::Arena;
753 ///
754 /// let mut arena = Arena::new();
755 /// arena.reserve_blocks(5);
756 ///
757 /// assert_eq!(arena.free_blocks().count(), 5);
758 ///
759 /// arena.trim_n(2);
760 ///
761 /// assert_eq!(arena.free_blocks().count(), 3);
762 /// ```
763 #[track_caller]
764 #[inline]
765 pub fn trim_n(&self, n: usize) {
766 self.blocks.trim_n(n, self.allocator());
767 }
768
769 /// Runs the given closure within a scope that frees all memory allocated from the arena within
770 /// it.
771 ///
772 /// # Examples
773 ///
774 /// ```
775 /// use rotunda::{Arena, handle::Handle};
776 ///
777 /// let arena = Arena::new();
778 /// let result = arena.with_scope(|| {
779 /// let h1 = Handle::new_in(&arena, 5);
780 /// let h2 = Handle::new_in(&arena, 10);
781 /// *h1 + *h2
782 /// });
783 ///
784 /// // `h1` and `h2` have been deallocated here, and the memory in the arena has been reset to
785 /// // from before they were allocated, so the space can be re-used.
786 ///
787 /// assert_eq!(result, 15);
788 /// ```
789 ///
790 /// # Notes
791 ///
792 /// The lifetime of `T` must be `'static`, which means that the closure cannot return borrowed data.
793 ///
794 /// If this is too restrictive, you can use [`Arena::with_scope_dynamic()`].
795 ///
796 /// The arena state is restored using a guard type, so the arena's memory will be restored if the given
797 /// closure panics.
798 ///
799 /// [`Arena::with_scope_dynamic()`]: ./struct.Arena.html#method.with_scope_dynamic
800 #[inline]
801 pub fn with_scope<T: 'static, F: FnOnce() -> T>(&self, f: F) -> T {
802 let _scope = ScopedRestore::new(&self.blocks);
803 f()
804 }
805
806 /// Runs the given closure within a scope that frees all memory allocated from the arena within
807 /// it.
808 ///
809 /// This function is similar to [`Arena::with_scope()`], except that it relaxes the
810 /// `'static` lifetime requirement on the return type. This allows the caller to
811 /// return borrowed data from the scope closure.
812 ///
813 /// As the closure taken by the function does not take ownership of items moved into it, it
814 /// can freely use items from the caller's scope, and does not have an `Arena` parameter.
815 ///
816 /// # Examples
817 ///
818 /// ```
819 /// use rotunda::{Arena, handle::Handle};
820 /// use core::cell::Cell;
821 ///
822 /// let data = vec![Cell::new(23)];
823 /// let arena = Arena::new();
824 /// let value = unsafe {
825 /// arena.with_scope_dynamic(|| {
826 /// let handle = Handle::new_in(&arena, 25);
827 /// let value = &data[0];
828 /// value.update(|old_val| old_val + *handle);
829 /// value
830 /// })
831 /// };
832 ///
833 /// assert_eq!(value.get(), 48);
834 /// ```
835 ///
836 /// # Safety
837 ///
838 /// This method is unsafe as it is possible to return a handle to memory which would
839 /// be freed in the Arena automatically as the scope exits. This would allow safe code
840 /// to overwrite the memory in the `Arena` with a new value while still allowing the
841 /// old `Handle` to be accessed, which could cause memory unsafety.
842 ///
843 /// To avoid this safety issue, never return data allocated in the arena during the scope.
844 ///
845 /// ```
846 /// use rotunda::{Arena, handle::Handle};
847 ///
848 /// let arena = Arena::new();
849 ///
850 /// let handle = unsafe {
851 /// arena.with_scope_dynamic(|| {
852 /// let handle = Handle::new_in(&arena, vec![1, 2, 3, 4, 5]);
853 /// # let mut handle = handle;
854 /// # unsafe { core::ptr::drop_in_place(Handle::as_mut_ptr(&mut handle)); } // Keep miri happy
855 /// handle
856 /// })
857 /// };
858 ///
859 /// // Warning ⚠️: `handle` has simultaneously been leakded and points to uninitialised memory.
860 /// // It is undefined behaviour to dereference it in any way (including via non-trivial drop).
861 /// # core::mem::forget(handle);
862 ///
863 /// // Warning ⚠️: This call to `Handle::new()` will use the same memory as which was used in the
864 /// // `with_scope_dynamic()` call above.
865 /// let handle = Handle::new_in(&arena, [128usize, 255, 300]);
866 /// ```
867 ///
868 /// Note that this includes 'smuggling' handles out through a collection type which
869 /// uses the `Arena` as a backing store:
870 ///
871 /// ```
872 /// use rotunda::{Arena, buffer::Buffer, handle::Handle};
873 ///
874 /// let arena = Arena::new();
875 /// let mut buffer = Buffer::with_capacity_in(&arena, 5);
876 ///
877 /// unsafe {
878 /// arena.with_scope_dynamic(|| {
879 /// let handle = Handle::new_in(&arena, String::from("This is a message!"));
880 /// # let mut handle = handle;
881 /// # unsafe { core::ptr::drop_in_place(Handle::as_mut_ptr(&mut handle)); } // Keep miri happy
882 /// buffer.push(handle);
883 /// });
884 /// };
885 ///
886 /// // Warning ⚠️: `buffer[0]` has simultaneously been leakded and points to uninitialised memory.
887 /// // It is undefined behaviour to dereference it in any way (including via non-trivial drop).
888 /// # core::mem::forget(buffer.remove(0));
889 /// ```
890 ///
891 /// [`Arena::with_scope()`]: ./struct.Arena.html#method.with_scope
892 #[inline]
893 pub unsafe fn with_scope_dynamic<T, F: FnOnce() -> T>(&self, f: F) -> T {
894 let _scope = ScopedRestore::new(&self.blocks);
895 f()
896 }
897
898 /// Returns a reference to the underlying [`Allocator`] type used to allocate/deallocate
899 /// memory.
900 ///
901 /// # Examples
902 ///
903 /// ```
904 /// # use rotunda::Arena;
905 /// let arena = Arena::new();
906 /// let allocator = arena.allocator();
907 /// # let _ = allocator;
908 /// ```
909 ///
910 /// [`Allocator`]: https://doc.rust-lang.org/stable/alloc/alloc/trait.Allocator.html
911 #[must_use]
912 #[inline]
913 pub fn allocator(&self) -> &A {
914 &self.alloc
915 }
916
917 /// Returns the block size of the `Arena`. This is specified using the [`Arena::with_block_size()`]
918 /// and [`Arena::with_block_size_in()`] constructors.
919 ///
920 /// # Examples
921 ///
922 /// ```
923 /// use rotunda::Arena;
924 ///
925 /// const BLOCK_SIZE: usize = 6 * 1024 * 1024;
926 /// let arena = Arena::with_block_size(BLOCK_SIZE);
927 ///
928 /// assert_eq!(arena.block_size(), BLOCK_SIZE);
929 /// ```
930 ///
931 /// [`Arena::with_block_size()`]:
932 /// [`Arena::with_block_size_in()`]:
933 #[must_use]
934 #[inline]
935 pub fn block_size(&self) -> usize {
936 self.blocks.block_size()
937 }
938
939 /// Returns a new allocation matching `layout` from the current block in the `Arena`.
940 ///
941 /// # Panics
942 ///
943 /// This method can panic if:
944 ///
945 /// * The requested `Layout` can never be satisfied by a block with the size of [`Arena::block_size()`].
946 /// * The underlying allocator raises an error which is handled by the [`handle_alloc_error()`] function.
947 ///
948 /// [`Arena::block_size()`]: ./struct.Arena.html#method.block_size
949 /// [`handle_alloc_error()`]: https://doc.rust-lang.org/stable/alloc/alloc/fn.handle_alloc_error.html
950 #[track_caller]
951 #[must_use]
952 pub fn alloc_raw(&self, layout: Layout) -> NonNull<c_void> {
953 if layout.size() == 0 {
954 return self.alloc_zst(layout.align());
955 }
956
957 #[cold]
958 #[track_caller]
959 #[inline(never)]
960 fn insert_fresh_block_fail(layout: &Layout) -> ! {
961 panic!(
962 "can never insert a type with layout `Layout (size = {}, align = {})` into arena",
963 layout.size(),
964 layout.align()
965 );
966 }
967
968 if let Err(e) = self.get_block_for_layout(layout) {
969 match e {
970 Error::AllocErr(_) => handle_alloc_error(layout),
971 _ => insert_fresh_block_fail(&layout),
972 }
973 }
974
975 unsafe {
976 let slot = self.blocks.bump_layout(layout);
977 NonNull::new_unchecked(slot.as_ptr())
978 }
979 }
980
981 /// Returns a new allocation matching `layout` in the `Arena`.
982 ///
983 /// # Errors
984 ///
985 /// If the underlying allocator could not service a request, then
986 /// an `Error::AllocErr` is returned.
987 ///
988 /// If the given `layout` could not fit in an empty block, then
989 /// `Error::BadLayout` is returned.
990 ///
991 /// # Examples
992 ///
993 /// ```
994 /// #[cfg(all(feature = "allocator-api2", not(feature = "nightly")))]
995 /// use allocator_api2::alloc::Layout;
996 ///
997 /// #[cfg(feature = "nightly")]
998 /// use std::alloc::Layout;
999 ///
1000 /// # use rotunda::Error;
1001 /// use rotunda::Arena;
1002 ///
1003 /// let arena = Arena::with_block_size(4);
1004 ///
1005 /// # fn inner(arena: &Arena) -> Result<(), rotunda::Error> {
1006 /// let data = arena.try_alloc_raw(Layout::new::<u16>()).expect("Will succeed");
1007 /// let will_error = arena.try_alloc_raw(Layout::new::<u64>())?;
1008 /// # Ok(())
1009 /// # }
1010 /// # let result = inner(&arena);
1011 /// # let layout = match result.unwrap_err() { Error::BadLayout(l) => l, _ => unreachable!() };
1012 /// # assert_eq!(layout, Layout::new::<u64>());
1013 /// ```
1014 #[inline]
1015 pub fn try_alloc_raw(&self, layout: Layout) -> Result<NonNull<c_void>, Error> {
1016 if layout.size() == 0 {
1017 return Ok(self.alloc_zst(layout.align()));
1018 }
1019
1020 self.get_block_for_layout(layout)?;
1021
1022 unsafe {
1023 let slot = self.blocks.bump_layout(layout);
1024 Ok(NonNull::new_unchecked(slot.as_ptr()))
1025 }
1026 }
1027
1028 /// Returns a new zeroed allocation from the current block in the `Arena`.
1029 ///
1030 /// If the current block in the `Arena` is full, then a new one will be allocated and used.
1031 ///
1032 /// # Panics
1033 ///
1034 /// This method can panic if:
1035 ///
1036 /// * The requested `Layout` can never be satisfied by a block with the size of [`Arena::block_size()`].
1037 /// * The underlying allocator raises an error which is handled by the [`handle_alloc_error()`] function.
1038 ///
1039 /// [`Arena::block_size()`]: ./struct.Arena.html#method.block_size
1040 /// [`handle_alloc_error()`]: https://doc.rust-lang.org/stable/alloc/alloc/fn.handle_alloc_error.html
1041 #[track_caller]
1042 #[must_use]
1043 #[inline]
1044 pub fn alloc_raw_zeroed(&self, layout: Layout) -> NonNull<c_void> {
1045 let slot = self.alloc_raw(layout);
1046 unsafe {
1047 ptr::write_bytes(slot.as_ptr().cast::<u8>(), b'\0', layout.size());
1048 }
1049 slot
1050 }
1051
1052 /// Allocate the given `value` into this `Arena` and returns an exclusive reference to it.
1053 ///
1054 /// The allocated value is wrapped in a [`ManuallyDrop`] to indicate that its `drop` method
1055 /// will not be called when the value goes out of scope. If the value has a meaningful
1056 /// [`Drop::drop()`] implementation, then you should call [`ManuallyDrop::drop()`] on it.
1057 ///
1058 /// # Errors
1059 ///
1060 /// This method can fail if the arena cannot allocate a new block to store the value.
1061 ///
1062 /// # Example
1063 ///
1064 /// ```
1065 /// use core::mem::ManuallyDrop;
1066 /// use rotunda::Arena;
1067 ///
1068 /// let mut arena = Arena::new();
1069 ///
1070 /// let value = arena.alloc_ref("Hello!");
1071 /// assert_eq!(**value, "Hello!");
1072 ///
1073 /// unsafe {
1074 /// ManuallyDrop::drop(value);
1075 /// }
1076 /// ```
1077 ///
1078 /// [`ManuallyDrop`]: https://doc.rust-lang.org/stable/core/mem/struct.ManuallyDrop.html
1079 /// [`Drop::drop()`]: https://doc.rust-lang.org/stable/core/ops/trait.Drop.html#tymethod.drop
1080 /// [`ManuallyDrop::drop()`]: https://doc.rust-lang.org/stable/core/mem/struct.ManuallyDrop.html#method.drop
1081 #[allow(clippy::mut_from_ref)]
1082 #[track_caller]
1083 #[must_use]
1084 #[inline]
1085 pub fn alloc_ref<T>(&self, value: T) -> &mut ManuallyDrop<T> {
1086 let mut slot = self.alloc_raw(Layout::new::<T>()).cast::<ManuallyDrop<T>>();
1087 unsafe {
1088 slot.write(ManuallyDrop::new(value));
1089 slot.as_mut()
1090 }
1091 }
1092
1093 /// Allocate the given `string` into the `Arena`, returning a exclusive reference to the string
1094 /// in the arena.
1095 ///
1096 /// # Panics
1097 ///
1098 /// This method will panic if the string cannot be allocated in the `Arena`, or if the length of the
1099 /// `string` is larger than `isize::MAX`.
1100 ///
1101 /// # Examples
1102 ///
1103 /// ```
1104 /// use rotunda::Arena;
1105 ///
1106 /// let arena = Arena::new();
1107 ///
1108 /// let message = arena.alloc_str("hello ❤️");
1109 ///
1110 /// message.make_ascii_uppercase();
1111 ///
1112 /// assert_eq!(message, "HELLO ❤️");
1113 /// ```
1114 #[allow(clippy::mut_from_ref)]
1115 #[track_caller]
1116 #[must_use]
1117 #[inline]
1118 pub fn alloc_str<S: ?Sized + AsRef<str>>(&self, string: &S) -> &mut str {
1119 let string = string.as_ref();
1120 let len = string.len();
1121
1122 unsafe {
1123 let slot = self
1124 .alloc_raw(Layout::array::<u8>(len).expect("could not create layout for string"));
1125
1126 let slot = slot.as_ptr().cast::<u8>();
1127 ptr::copy_nonoverlapping(string.as_ptr(), slot, len);
1128 let bytes = ptr::slice_from_raw_parts_mut(slot, len);
1129 &mut *(bytes as *mut str)
1130 }
1131 }
1132
1133 /// Returns a reference to the currently in-use block, if it is available.
1134 ///
1135 /// This method requires exclusive access to the `Arena`, so there
1136 /// cannot be any objects allocated live from the arena.
1137 ///
1138 /// # Examples
1139 ///
1140 /// ```
1141 /// # use core::mem::{drop, MaybeUninit};
1142 /// use rotunda::{Arena, handle::Handle};
1143 /// let mut arena = Arena::new();
1144 ///
1145 /// let no_block = arena.curr_block();
1146 /// assert!(no_block.is_none());
1147 ///
1148 /// let handle = Handle::new_str_in(&arena, "Hello, world!");
1149 /// drop(handle);
1150 ///
1151 /// let block = arena.curr_block().expect("block must be allocated");
1152 ///
1153 /// block.fill(MaybeUninit::new(0xcd));
1154 /// ```
1155 #[must_use]
1156 #[inline]
1157 pub fn curr_block(&mut self) -> Option<&mut [MaybeUninit<u8>]> {
1158 unsafe {
1159 self.blocks
1160 .curr_block()
1161 .get()
1162 .map(|block| Block::data_mut(block, self.block_size()))
1163 }
1164 }
1165
1166 /// Returns an iterator over free blocks in the `Arena`.
1167 ///
1168 /// This method requires exclusive access to the `Arena`, so there
1169 /// cannot be any objects allocated live from the arena.
1170 ///
1171 /// # Examples
1172 ///
1173 /// ```
1174 /// use rotunda::{Arena, handle::Handle};
1175 /// use core::mem::MaybeUninit;
1176 ///
1177 /// let mut arena = Arena::new();
1178 /// # arena.force_push_new_block();
1179 /// # arena.force_push_new_block();
1180 ///
1181 /// for free_block in arena.free_blocks() {
1182 /// free_block.fill(MaybeUninit::new(0x00));
1183 /// }
1184 /// ```
1185 #[must_use]
1186 #[inline]
1187 pub fn free_blocks(&mut self) -> FreeBlocksMut<'_, A> {
1188 FreeBlocksMut::new(self)
1189 }
1190
1191 /// Returns an iterator over all blocks in the `Arena`.
1192 ///
1193 /// This method requires exclusive access to the `Arena`, so there
1194 /// cannot be any objects allocated live from the arena.
1195 ///
1196 /// # Examples
1197 ///
1198 /// ```
1199 /// use rotunda::{Arena, handle::Handle};
1200 /// use core::mem::MaybeUninit;
1201 ///
1202 /// let mut arena = Arena::new();
1203 /// # arena.force_push_new_block();
1204 /// # arena.force_push_new_block();
1205 ///
1206 /// for block in arena.all_blocks() {
1207 /// block.fill(MaybeUninit::new(0xff));
1208 /// }
1209 /// ```
1210 #[must_use]
1211 #[inline]
1212 pub fn all_blocks(&mut self) -> AllBlocksMut<'_, A> {
1213 AllBlocksMut::new(self)
1214 }
1215
1216 #[inline]
1217 unsafe fn get_free_block(&self) -> Result<NonNull<Block>, AllocError> {
1218 let block = match self.blocks.free_blocks().get() {
1219 // If we have a free block, grab it
1220 Some(block) => unsafe {
1221 let next_free_block = block.as_ref().next.get();
1222 block.as_ref().next.set(None);
1223
1224 self.blocks.free_blocks().set(next_free_block);
1225 block
1226 },
1227 // otherwise, alloc another one
1228 _ => self.alloc_block()?,
1229 };
1230
1231 self.blocks.curr_block_pos().set(0);
1232 if let Some(curr_block) = self.blocks.curr_block().get() {
1233 self.blocks.push_used_block(curr_block);
1234 }
1235
1236 self.blocks.curr_block().set(Some(block));
1237 Ok(block)
1238 }
1239
1240 #[inline]
1241 fn alloc_block(&self) -> Result<NonNull<Block>, AllocError> {
1242 let layout = self.blocks.block_layout();
1243 Block::try_alloc(layout, self.allocator())
1244 }
1245
1246 fn get_block_for_layout(&self, layout: Layout) -> Result<NonNull<Block>, Error> {
1247 let block = self
1248 .blocks
1249 .curr_block()
1250 .get()
1251 .filter(|_| self.blocks.can_write_layout(&layout))
1252 .map(Ok)
1253 .unwrap_or_else(|| {
1254 let block = unsafe { self.get_free_block()? };
1255 if !self.blocks.can_write_layout(&layout) {
1256 return Err(Error::BadLayout(layout));
1257 }
1258
1259 Ok(block)
1260 });
1261
1262 if let Ok(block) = block {
1263 debug_assert!(ptr::eq(
1264 block.as_ptr(),
1265 self.blocks
1266 .curr_block()
1267 .get()
1268 .map(|p| p.as_ptr())
1269 .unwrap_or(ptr::null_mut())
1270 ));
1271 }
1272
1273 block
1274 }
1275
1276 #[must_use]
1277 fn alloc_zst(&self, align: usize) -> NonNull<c_void> {
1278 let mut ptr = NonNull::dangling();
1279 let offset = ptr.align_offset(align);
1280
1281 if offset != 0 || offset != usize::MAX {
1282 ptr = ptr.map_addr(|addr| addr.saturating_add(offset));
1283 }
1284
1285 ptr
1286 }
1287}
1288
1289impl<A: Allocator + Default> Default for Arena<A> {
1290 #[inline]
1291 fn default() -> Self {
1292 Self::new_in(Default::default())
1293 }
1294}
1295
1296impl<A: Allocator> fmt::Debug for Arena<A> {
1297 #[inline]
1298 fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
1299 self.blocks.write_debug("Arena", fmtr)
1300 }
1301}
1302
1303unsafe impl<A: Allocator + Sync> Send for Arena<A> {}
1304
1305impl<A: Allocator> Drop for Arena<A> {
1306 #[inline]
1307 fn drop(&mut self) {
1308 unsafe {
1309 self.blocks.dealloc_all_memory(self.allocator());
1310 }
1311 }
1312}
1313
1314/// An iterator type over the free blocks of an [`Arena`].
1315///
1316/// See the [`Arena::free_blocks()`] method for more information.
1317///
1318/// [`Arena`]: ./struct.Arena.html
1319/// [`Arena::free_blocks()`]: ./struct.Arena.html#method.free_blocks
1320pub struct FreeBlocksMut<'a, A: Allocator = Global> {
1321 arena: &'a mut Arena<A>,
1322 it: BlockIter,
1323}
1324
1325impl<'a, A: Allocator> FreeBlocksMut<'a, A> {
1326 #[must_use]
1327 #[inline]
1328 const fn new(arena: &'a mut Arena<A>) -> Self {
1329 let it = BlockIter::new(arena.blocks.free_blocks().get());
1330 Self { arena, it }
1331 }
1332}
1333
1334impl<'a, A: Allocator> Iterator for FreeBlocksMut<'a, A> {
1335 type Item = &'a mut [MaybeUninit<u8>];
1336
1337 #[inline]
1338 fn next(&mut self) -> Option<Self::Item> {
1339 let block = self.it.next()?;
1340
1341 let data = unsafe {
1342 let block_size = self.arena.block_size();
1343 Block::data_mut(block, block_size)
1344 };
1345
1346 Some(data)
1347 }
1348}
1349
1350impl<'a, A: Allocator> FusedIterator for FreeBlocksMut<'a, A> {}
1351
1352impl<'a, A: Allocator> fmt::Debug for FreeBlocksMut<'a, A> {
1353 #[inline]
1354 fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
1355 fmtr.debug_struct("FreeBlocksMut").finish_non_exhaustive()
1356 }
1357}
1358
1359/// An iterator type over every block of an [`Arena`].
1360///
1361/// See the [`Arena::all_blocks()`] method for more information.
1362///
1363/// [`Arena`]: ./struct.Arena.html
1364/// [`Arena::all_blocks()`]: ./struct.Arena.html#method.all_blocks
1365pub struct AllBlocksMut<'a, A: Allocator = Global> {
1366 arena: &'a mut Arena<A>,
1367 curr: Option<NonNull<Block>>,
1368 free_blocks: BlockIter,
1369 used_blocks: BlockIter,
1370}
1371
1372impl<'a, A: Allocator> AllBlocksMut<'a, A> {
1373 #[inline]
1374 const fn new(arena: &'a mut Arena<A>) -> Self {
1375 let curr = arena.blocks.curr_block().get();
1376
1377 let free_blocks = BlockIter::new(arena.blocks.free_blocks().get());
1378 let used_blocks = BlockIter::new(arena.blocks.used_blocks().get());
1379
1380 Self {
1381 curr,
1382 arena,
1383 free_blocks,
1384 used_blocks,
1385 }
1386 }
1387}
1388
1389impl<'a, A: Allocator> Iterator for AllBlocksMut<'a, A> {
1390 type Item = &'a mut [MaybeUninit<u8>];
1391
1392 #[inline]
1393 fn next(&mut self) -> Option<Self::Item> {
1394 self.curr
1395 .take()
1396 .or_else(|| self.free_blocks.next())
1397 .or_else(|| self.used_blocks.next())
1398 .map(|block| {
1399 let block_size = self.arena.block_size();
1400 unsafe { Block::data_mut(block, block_size) }
1401 })
1402 }
1403}
1404
1405impl<'a, A: Allocator> fmt::Debug for AllBlocksMut<'a, A> {
1406 #[inline]
1407 fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
1408 fmtr.debug_struct("AllBlocksMut").finish_non_exhaustive()
1409 }
1410}
1411
1412/// Represents error types which may be returned while using an `Arena`.
1413#[derive(Debug)]
1414pub enum Error {
1415 /// A `Layout` could not be constructed for a particular type.
1416 LayoutErr(LayoutError),
1417 /// The underlying allocator could not service the request.
1418 AllocErr(AllocError),
1419 /// The allocation request with the given `Layout` could not be serviced.
1420 BadLayout(Layout),
1421}
1422
1423impl From<LayoutError> for Error {
1424 #[inline]
1425 fn from(value: LayoutError) -> Self {
1426 Self::LayoutErr(value)
1427 }
1428}
1429
1430impl From<AllocError> for Error {
1431 #[inline]
1432 fn from(value: AllocError) -> Self {
1433 Self::AllocErr(value)
1434 }
1435}
1436
1437impl fmt::Display for Error {
1438 #[inline]
1439 fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
1440 match *self {
1441 Self::AllocErr(ref e) => fmt::Display::fmt(e, fmtr),
1442 Self::LayoutErr(ref e) => fmt::Display::fmt(e, fmtr),
1443 Self::BadLayout(layout) => write!(
1444 fmtr,
1445 "Arena cannot allocate a value of size {}, alignment {}",
1446 layout.size(),
1447 layout.align()
1448 ),
1449 }
1450 }
1451}
1452
1453impl ErrorTrait for Error {
1454 #[inline]
1455 fn source(&self) -> Option<&(dyn ErrorTrait + 'static)> {
1456 match *self {
1457 Self::AllocErr(ref e) => e.source(),
1458 Self::LayoutErr(ref e) => e.source(),
1459 Self::BadLayout(_) => None,
1460 }
1461 }
1462}
1463
1464pub(crate) type InvariantLifetime<'a, T> = PhantomData<fn(&'a T) -> &'a T>;
1465
1466#[inline]
1467fn layout_repeat(layout: &Layout, n: usize) -> Result<(Layout, usize), LayoutError> {
1468 let padded = layout.pad_to_align();
1469 match layout.size().checked_mul(n) {
1470 Some(array_size) => Layout::from_size_align(array_size, layout.align())
1471 .map(|layout| (layout, padded.size())),
1472 // Generate a guaranteed `LayoutError` by creating a `Layout` with alignment `0`.
1473 None => Layout::from_size_align(1, 0).map(|layout| (layout, 0)),
1474 }
1475}