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