1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// All methods just delegate to `Bump`'s methods
#![expect(clippy::inline_always)]
use std::{
alloc::{Layout, handle_alloc_error},
ptr::NonNull,
};
use allocator_api2::alloc::Allocator;
use bumpalo::Bump;
/// Trait describing an allocator.
///
/// It's a simpler version of `allocator_api2`'s [`Allocator`] trait.
///
/// The difference between these methods and [`Allocator`]'s versions of them are:
///
/// * `shrink` and `grow` return a pointer and panic/abort if allocation fails,
/// instead of returning `Result::Err`.
/// * All methods return a `NonNull<u8>`, instead of `NonNull<[u8]>`.
pub trait Alloc {
/// Allocate space for an object with the given [`Layout`].
///
/// The returned pointer points at uninitialized memory, and should be initialized
/// with [`std::ptr::write`].
///
/// # Panics
///
/// Panics if reserving space for `layout` fails.
fn alloc(&self, layout: Layout) -> NonNull<u8>;
/// Deallocate the memory referenced by `ptr`.
///
/// # SAFETY
///
/// * `ptr` must denote a block of memory currently allocated via this allocator.
/// * `layout` must be the same [`Layout`] that block was originally allocated with.
unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout);
/// Grow an existing allocation to new [`Layout`].
///
/// If the allocation cannot be grown in place, the data from the whole of the old allocation
/// is copied to the start of the new allocation.
///
/// If the allocation is grown in place, no memory copying will occur.
///
/// Either way, the pointer returned points to the new allocation.
///
/// Any access to the old `ptr` is Undefined Behavior, even if the allocation was grown in-place.
/// The newly returned pointer is the only valid pointer for accessing this memory now.
///
/// # SAFETY
///
/// * `ptr` must denote a block of memory currently allocated via this allocator.
/// * `old_layout` must be the same [`Layout`] that block was originally allocated with.
/// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
unsafe fn grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> NonNull<u8>;
/// Shrink an existing allocation to new [`Layout`].
///
/// If the allocation cannot be shrunk in place, `new_layout.size()` bytes of data
/// from the old allocation are copied to the new allocation.
///
/// If the allocation is shrunk in place, no memory copying will occur.
///
/// Either way, the pointer returned points to the new allocation.
///
/// Any access to the old `ptr` is Undefined Behavior, even if the allocation was shrunk in-place.
/// The newly returned pointer is the only valid pointer for accessing this memory now.
///
/// # SAFETY
///
/// * `ptr` must denote a block of memory currently allocated via this allocator.
/// * `old_layout` must be the same [`Layout`] that block was originally allocated with.
/// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`.
unsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> NonNull<u8>;
}
/// Implement [`Alloc`] for [`bumpalo::Bump`].
///
/// All methods except `alloc` delegate to [`Bump`]'s impl of `allocator_api2`'s [`Allocator`] trait.
impl Alloc for Bump {
/// Allocate space for an object with the given [`Layout`].
///
/// The returned pointer points at uninitialized memory, and should be initialized
/// with [`std::ptr::write`].
///
/// # Panics
///
/// Panics if reserving space for `layout` fails.
#[inline(always)]
fn alloc(&self, layout: Layout) -> NonNull<u8> {
self.alloc_layout(layout)
}
/// Deallocate the memory referenced by `ptr`.
///
/// # SAFETY
///
/// * `ptr` must denote a block of memory currently allocated via this allocator.
/// * `layout` must be the same [`Layout`] that block was originally allocated with.
#[inline(always)]
unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
// SAFETY: Safety requirements of `Allocator::deallocate` are the same as for this method
unsafe { self.deallocate(ptr, layout) }
}
/// Grow an existing allocation to new [`Layout`].
///
/// If the allocation cannot be grown in place, the data from the whole of the old allocation
/// is copied to the start of the new allocation.
///
/// If the allocation is grown in place, no memory copying will occur.
///
/// Either way, the pointer returned points to the new allocation.
///
/// Any access to the old `ptr` is Undefined Behavior, even if the allocation was grown in-place.
/// The newly returned pointer is the only valid pointer for accessing this memory now.
///
/// # SAFETY
///
/// * `ptr` must denote a block of memory currently allocated via this allocator.
/// * `old_layout` must be the same [`Layout`] that block was originally allocated with.
/// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
///
/// # Panics
///
/// Panics / aborts if reserving space for `new_layout` fails.
#[inline(always)]
unsafe fn grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> NonNull<u8> {
// SAFETY: Safety requirements of `Allocator::grow` are the same as for this method
let res = unsafe { Allocator::grow(&self, ptr, old_layout, new_layout) };
match res {
Ok(new_ptr) => new_ptr.cast::<u8>(),
Err(_) => handle_alloc_error(new_layout), // panic/abort
}
}
/// Shrink an existing allocation to new [`Layout`].
///
/// If the allocation cannot be shrunk in place, the `layout.new()` bytes of data
/// from the old allocation are copied to the new allocation.
///
/// If the allocation is shrunk in place, no memory copying will occur.
///
/// Either way, the pointer returned points to the new allocation.
///
/// Any access to the old `ptr` is Undefined Behavior, even if the allocation was shrunk in-place.
/// The newly returned pointer is the only valid pointer for accessing this memory now.
///
/// # SAFETY
///
/// * `ptr` must denote a block of memory currently allocated via this allocator.
/// * `old_layout` must be the same [`Layout`] that block was originally allocated with.
/// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`.
///
/// # Panics
///
/// Panics / aborts if reserving space for `new_layout` fails.
#[inline(always)]
unsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> NonNull<u8> {
// SAFETY: Safety requirements of `Allocator::shrink` are the same as for this method
let res = unsafe { Allocator::shrink(&self, ptr, old_layout, new_layout) };
match res {
Ok(new_ptr) => new_ptr.cast::<u8>(),
Err(_) => handle_alloc_error(new_layout), // panic/abort
}
}
}