|
77 | 77 | //! This is mostly useful when space optimization is very important.
|
78 | 78 | //! This is still useful when using an arena: it reduces the allocations in the arena
|
79 | 79 | //! in exchange for moving node payloads to the heap alongside the children array.
|
| 80 | +//! |
| 81 | +//! # But how? |
| 82 | +//! |
| 83 | +//! This is possible because of the following key building blocks: |
| 84 | +//! |
| 85 | +//! - `Box`'s [memory layout][boxed-memory-layout] is defined and uses the |
| 86 | +//! [global allocator][std::alloc::Global], and is allowed to be manually allocated. |
| 87 | +//! - [Array layout][array-layout] and [slice layout][slice-layout] are defined. |
| 88 | +//! - [`#[repr(C)]`][repr-c-layout] allows us to make compound types with defined layout. |
| 89 | +//! - We can turn an opaque pointer into a slice fat pointer with |
| 90 | +//! [`ptr::slice_from_raw_parts`][slice_from_raw_parts]. |
| 91 | +//! - We can cast a slice pointer to a pointer to our compound type |
| 92 | +//! in order to keep the correct fat pointer metadata. |
| 93 | +//! |
| 94 | +//! So with these guarantees, we can "just" manually allocate some space, initialize it |
| 95 | +//! for some custom `repr(C)` structure, and convert it into a `Box`. From that point, |
| 96 | +//! `Box` handles managing the memory, including deallocation or moving it into another |
| 97 | +//! smart pointer, such as `Arc`. |
| 98 | +//! |
| 99 | +//! [boxed-memory-layout]: <https://doc.rust-lang.org/stable/std/boxed/index.html#memory-layout> |
| 100 | +//! [array-layout]: <https://doc.rust-lang.org/stable/reference/type-layout.html#array-layout> |
| 101 | +//! [slice-layout]: <https://doc.rust-lang.org/stable/reference/type-layout.html#slice-layout> |
| 102 | +//! [repr-c-layout]: <https://doc.rust-lang.org/stable/reference/type-layout.html#reprc-structs> |
| 103 | +//! [std::alloc::Global]: <https://doc.rust-lang.org/stable/std/alloc/index.html#the-global_allocator-attribute> |
| 104 | +//! |
| 105 | +//! [`SliceDst`] defines the capabilities required of the pointee type. It must be able to |
| 106 | +//! turn a trailing slice length into a [`Layout`] for the whole pointee, and it must provide |
| 107 | +//! a way to turn a untyped slice pointer `*mut [()]` into a correctly typed pointer. |
| 108 | +//! |
| 109 | +//! The functions [`alloc_slice_dst`] and [`alloc_slice_dst_in`] provide a way |
| 110 | +//! to allocate space for a `SliceDst` type via the global allocator. |
| 111 | +//! |
| 112 | +//! [`AllocSliceDst`] types are owning heap pointers that can create a new slice DST. |
| 113 | +//! They take an initialization routine that is responsible for initializing the |
| 114 | +//! uninitialized allocated place, and do the ceremony required to allocate the place |
| 115 | +//! and turn it into the proper type by delgating to `SliceDst` and `alloc_slice_dst`. |
| 116 | +//! They also handle panic/unwind safety of the initialization routine and prevent |
| 117 | +//! leaking of the allocated place due to an initialization panic. |
| 118 | +//! |
| 119 | +//! [`TryAllocSliceDst`] is the potentially fallible initialization version. |
| 120 | +//! |
| 121 | +//! All of these pieces are the glue, but [`SliceWithHeader`] and [`StrWithHeader`] |
| 122 | +//! put the pieces together into a safe package. They take a header and an iterator |
| 123 | +//! (or copyable slice) and put together all of the pieces to allocate a dynamically |
| 124 | +//! sized custom type. |
| 125 | +//! |
| 126 | +//! Additionaly, though not strictly required, these types store the slice length inline. |
| 127 | +//! This gives them the ability to reconstruct pointers from fully type erased pointers |
| 128 | +#![cfg_attr(feature = "erasable", doc = "via the [`Erasable`] trait")] |
| 129 | +//! . |
80 | 130 |
|
81 | 131 | // All hail Chairity!
|
82 | 132 | // The one who saves our sanity -
|
|
0 commit comments