fixed_bump/dynamic.rs
1/*
2 * Copyright (C) 2021-2022 taylor.fish <contact@taylor.fish>
3 *
4 * This file is part of fixed-bump.
5 *
6 * fixed-bump is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * fixed-bump is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with fixed-bump. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20use super::generic::{GenericBump, IntoLayout};
21#[cfg(any(feature = "allocator_api", feature = "allocator-fallback"))]
22use super::{AllocError, Allocator};
23use alloc::alloc::Layout;
24use core::ptr::NonNull;
25
26// SAFETY: Trivially, `<Layout as Into<Layout>>::into` cannot call any methods
27// of any [`GenericBump`] as it is a no-op.
28unsafe impl IntoLayout for Layout {}
29
30/// Like [`Bump`], but uses chunk size and alignment values provided at runtime
31/// rather than compile time.
32///
33/// Instead of passing `Size` and `Align` type parameters, [`Self::new`]
34/// accepts a [`Layout`]. Otherwise, this type behaves identically to [`Bump`].
35///
36/// [`Bump`]: crate::Bump
37pub struct DynamicBump(GenericBump<Layout>);
38
39impl DynamicBump {
40 /// Creates a new [`DynamicBump`]. `layout` specifies the size and
41 /// alignment of the chunks allocated internally by the allocator.
42 pub fn new(layout: Layout) -> Self {
43 Self(GenericBump::new(layout))
44 }
45
46 /// The layout passed to [`Self::new`].
47 pub fn layout(&self) -> Layout {
48 self.0.layout()
49 }
50
51 /// Tries to allocate memory with a size and alignment matching `layout`.
52 ///
53 /// Returns a pointer to the memory on success, or [`None`] on failure.
54 /// The memory is valid until the [`DynamicBump`] is dropped. Note that the
55 /// returned memory could be larger than [`layout.size()`].
56 ///
57 /// This method is similar to [`Allocator::allocate`], except it returns an
58 /// [`Option`] instead of a [`Result`].
59 ///
60 /// Allocation is guaranteed to succeed, assuming the global allocator
61 /// succeeds, if [`layout.size()`] is less than or equal to
62 /// <code>[self.layout()].[size()]</code> and [`layout.align()`] is less
63 /// than or equal to <code>[self.layout()].[align()]</code>. See
64 /// [`Self::can_allocate`].
65 ///
66 /// [`layout.size()`]: Layout::size
67 /// [`layout.align()`]: Layout::align
68 /// [`Allocator::allocate`]: alloc::alloc::Allocator::allocate
69 /// [self.layout()]: Self::layout
70 /// [size()]: Layout::size
71 /// [align()]: Layout::align
72 pub fn allocate(&self, layout: Layout) -> Option<NonNull<[u8]>> {
73 self.0.allocate(layout)
74 }
75
76 /// Allocates a value of type `T`.
77 ///
78 /// The memory is initialized with `value` and a reference to the value is
79 /// returned. Note that the value's destructor will not be called
80 /// automatically.
81 ///
82 /// # Panics
83 ///
84 /// Panics if this allocator cannot allocate memory matching
85 /// [`Layout::new::<T>()`] (see [`Self::can_allocate`]). Note that if the
86 /// global allocator fails, [`handle_alloc_error`] is called instead of
87 /// panicking.
88 ///
89 /// For an equivalent that doesn't panic or call [`handle_alloc_error`],
90 /// see [`Self::try_alloc_value`].
91 ///
92 /// [`handle_alloc_error`]: alloc::alloc::handle_alloc_error
93 #[allow(clippy::mut_from_ref)]
94 #[must_use]
95 pub fn alloc_value<T>(&self, value: T) -> &mut T {
96 self.0.alloc_value(value)
97 }
98
99 /// Tries to allocate a value of type `T`.
100 ///
101 /// If the allocation succeeds, the memory is initialized with `value` and
102 /// a reference to the value is returned. Note that the value's destructor
103 /// will not be called automatically.
104 ///
105 /// Allocation succeeds if and only if [`Self::allocate`] is able to
106 /// allocate memory matching [`Layout::new::<T>()`]. See [`Self::allocate`]
107 /// for details regarding the circumstances in which allocation can fail.
108 ///
109 /// # Errors
110 ///
111 /// If allocation fails, <code>[Err]\(value)</code> is returned.
112 #[allow(clippy::mut_from_ref)]
113 pub fn try_alloc_value<T>(&self, value: T) -> Result<&mut T, T> {
114 self.0.try_alloc_value(value)
115 }
116
117 /// Returns whether this allocator can allocate memory matching `layout`.
118 ///
119 /// This is guaranteed to return true if [`layout.size()`] is less than or
120 /// equal to <code>[self.layout()].[size()]</code> and [`layout.align()`]
121 /// is less than or equal to <code>[self.layout()].[align()]</code>. It
122 /// *may* return true if the alignment is bigger, but never if the size is.
123 ///
124 /// [`layout.size()`]: Layout::size
125 /// [`layout.align()`]: Layout::align
126 /// [self.layout()]: Self::layout
127 /// [size()]: Layout::size
128 /// [align()]: Layout::align
129 pub fn can_allocate(&self, layout: Layout) -> bool {
130 self.0.can_allocate(layout)
131 }
132}
133
134#[cfg(any(feature = "allocator_api", feature = "allocator-fallback"))]
135#[cfg_attr(
136 feature = "doc_cfg",
137 doc(cfg(any(
138 feature = "allocator_api",
139 feature = "allocator-fallback",
140 )))
141)]
142// SAFETY: `DynamicBump::allocate` (when not returning `None`) returns pointers
143// to valid memory that matches the provided `Layout`.
144//
145// `DynamicBump` cannot be cloned, as it does not implement `Clone`. Moving it
146// will not invalidate any returned memory, as all returned memory is allocated
147// on the heap via the global allocator.
148unsafe impl Allocator for DynamicBump {
149 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
150 self.allocate(layout).ok_or(AllocError)
151 }
152
153 unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
154 // No-op: `DynamicBump` deallocates all its memory when dropped.
155 }
156}
157
158#[cfg(any(doc, doctest))]
159/// [`DynamicBump`] cannot implement [`Clone`], as this would make it unsound
160/// to implement [`Allocator`](alloc::alloc::Allocator).
161///
162/// ```
163/// use fixed_bump::DynamicBump;
164/// struct Test<T = DynamicBump>(T);
165/// ```
166///
167/// ```compile_fail
168/// use fixed_bump::DynamicBump;
169/// struct Test<T: Clone = DynamicBump>(T);
170/// ```
171mod dynamic_bump_does_not_impl_clone {}