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 {}