fixed_bump/bump.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::marker::PhantomData;
25use core::mem;
26use core::ptr::NonNull;
27
28struct ConstLayout<Size, Align>(PhantomData<fn() -> (Size, Align)>);
29
30impl<Size, Align> Clone for ConstLayout<Size, Align> {
31 fn clone(&self) -> Self {
32 *self
33 }
34}
35
36impl<Size, Align> Copy for ConstLayout<Size, Align> {}
37
38impl<Size, Align> From<ConstLayout<Size, Align>> for Layout {
39 fn from(_: ConstLayout<Size, Align>) -> Self {
40 Self::from_size_align(mem::size_of::<Size>(), mem::align_of::<Align>())
41 .unwrap()
42 }
43}
44
45// SAFETY: `<Self as Into<Layout>>::into` forwards to
46// `<Layout as From<Self>>:from`, which does not run any code that could call
47// methods of any [`GenericBump`].
48unsafe impl<Size, Align> IntoLayout for ConstLayout<Size, Align> {}
49
50/// A bump allocator that allocates memory in non-amortized O(1) (constant)
51/// time.
52///
53/// The allocator internally uses fixed-size chunks of memory. The size and
54/// alignment of each chunk of memory is determined by the type parameters
55/// `Size` and `Align`: the size is [`mem::size_of::<Size>()`] and the
56/// alignment is [`mem::align_of::<Align>()`]. The default value of `Align` is
57/// `Size`, so you can specify both the size and alignment with a single type
58/// parameter.
59///
60/// A common use of this type, and the most space-efficient way to use it, is
61/// to allocate many values of the same type (or at least the same size and
62/// alignment). In this case, it may be convenient to specify the chunk size
63/// using an array type: to use properly aligned chunks large enough to
64/// allocate `n` values of type `T`, pass `[T; n]` as the `Size` parameter,
65/// which will also be the `Align` parameter by default.
66pub struct Bump<Size, Align = Size>(GenericBump<ConstLayout<Size, Align>>);
67
68impl<Size, Align> Bump<Size, Align> {
69 /// Creates a new [`Bump`].
70 pub fn new() -> Self {
71 Self(GenericBump::new(ConstLayout(PhantomData)))
72 }
73
74 /// Tries to allocate memory with a size and alignment matching `layout`.
75 ///
76 /// Returns a pointer to the memory on success, or [`None`] on failure.
77 /// The memory is valid until the [`Bump`] is dropped. Note that the
78 /// returned memory could be larger than [`layout.size()`].
79 ///
80 /// This method is similar to [`Allocator::allocate`], except it returns an
81 /// [`Option`] instead of a [`Result`].
82 ///
83 /// Allocation is guaranteed to succeed, assuming the global allocator
84 /// succeeds, if [`layout.size()`] is less than or equal to
85 /// [`mem::size_of::<Size>()`] and [`layout.align()`] is less than or equal
86 /// to [`mem::align_of::<Align>()`]. See [`Self::can_allocate`].
87 ///
88 /// [`layout.size()`]: Layout::size
89 /// [`layout.align()`]: Layout::align
90 /// [`Allocator::allocate`]: alloc::alloc::Allocator::allocate
91 pub fn allocate(&self, layout: Layout) -> Option<NonNull<[u8]>> {
92 self.0.allocate(layout)
93 }
94
95 /// Allocates a value of type `T`.
96 ///
97 /// The memory is initialized with `value` and a reference to the value is
98 /// returned. Note that the value's destructor will not be called
99 /// automatically.
100 ///
101 /// # Panics
102 ///
103 /// Panics if this allocator cannot allocate memory matching
104 /// [`Layout::new::<T>()`] (see [`Self::can_allocate`]). Note that if the
105 /// global allocator fails, [`handle_alloc_error`] is called instead of
106 /// panicking.
107 ///
108 /// For an equivalent that doesn't panic or call [`handle_alloc_error`],
109 /// see [`Self::try_alloc_value`].
110 ///
111 /// [`handle_alloc_error`]: alloc::alloc::handle_alloc_error
112 #[allow(clippy::mut_from_ref)]
113 #[must_use]
114 pub fn alloc_value<T>(&self, value: T) -> &mut T {
115 self.0.alloc_value(value)
116 }
117
118 /// Tries to allocate a value of type `T`.
119 ///
120 /// If the allocation succeeds, the memory is initialized with `value` and
121 /// a reference to the value is returned. Note that the value's destructor
122 /// will not be called automatically.
123 ///
124 /// Allocation succeeds if and only if [`Self::allocate`] is able to
125 /// allocate memory matching [`Layout::new::<T>()`]. See [`Self::allocate`]
126 /// for details regarding the circumstances in which allocation can fail.
127 ///
128 /// # Errors
129 ///
130 /// If allocation fails, <code>[Err]\(value)</code> is returned.
131 #[allow(clippy::mut_from_ref)]
132 pub fn try_alloc_value<T>(&self, value: T) -> Result<&mut T, T> {
133 self.0.try_alloc_value(value)
134 }
135
136 /// Returns whether this allocator can allocate memory matching `layout`.
137 ///
138 /// This is guaranteed to return true if [`layout.size()`] is less than or
139 /// equal to [`mem::size_of::<Size>()`] and [`layout.align()`] is less than
140 /// or equal to [`mem::align_of::<Align>()`]. It *may* return true if the
141 /// alignment is bigger, but never if the size is.
142 ///
143 /// [`layout.size()`]: Layout::size
144 /// [`layout.align()`]: Layout::align
145 pub fn can_allocate(&self, layout: Layout) -> bool {
146 self.0.can_allocate(layout)
147 }
148}
149
150impl<Size, Align> Default for Bump<Size, Align> {
151 fn default() -> Self {
152 Self::new()
153 }
154}
155
156#[cfg(any(feature = "allocator_api", feature = "allocator-fallback"))]
157#[cfg_attr(
158 feature = "doc_cfg",
159 doc(cfg(any(
160 feature = "allocator_api",
161 feature = "allocator-fallback",
162 )))
163)]
164// SAFETY: `Bump::allocate` (when not returning `None`) returns pointers to
165// valid memory that matches the provided `Layout`.
166//
167// `Bump` cannot be cloned, as it does not implement `Clone`. Moving it will
168// not invalidate any returned memory, as all returned memory is allocated on
169// the heap via the global allocator.
170unsafe impl<Size, Align> Allocator for Bump<Size, Align> {
171 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
172 self.allocate(layout).ok_or(AllocError)
173 }
174
175 unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
176 // No-op: `Bump` deallocates all its memory when dropped.
177 }
178}
179
180#[cfg(any(doc, doctest))]
181/// [`Bump`] cannot implement [`Clone`], as this would make it unsound to
182/// implement [`Allocator`](alloc::alloc::Allocator).
183///
184/// ```
185/// use fixed_bump::Bump;
186/// struct Test<T = Bump<u8>>(T);
187/// ```
188///
189/// ```compile_fail
190/// use fixed_bump::Bump;
191/// struct Test<T: Clone = Bump<u8>>(T);
192/// ```
193mod bump_does_not_impl_clone {}