allocator_fallback/fallback.rs
1/*
2 * Copyright 2022 taylor.fish <contact@taylor.fish>
3 *
4 * This file is part of allocator-fallback.
5 *
6 * allocator-fallback is licensed under the Apache License, Version 2.0
7 * (the "License"); you may not use allocator-fallback except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19use alloc::alloc::Layout;
20use core::fmt::{self, Display, Formatter};
21use core::mem::MaybeUninit;
22use core::ptr::{self, NonNull};
23
24/// A fallback for [`alloc::alloc::AllocError`], which is currently unstable.
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub struct AllocError;
27
28impl Display for AllocError {
29 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
30 write!(f, "memory allocation failed")
31 }
32}
33
34#[cfg(feature = "std")]
35#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "std")))]
36impl std::error::Error for AllocError {}
37
38/// A fallback for [`alloc::alloc::Allocator`], which is currently unstable.
39///
40/// # Safety
41///
42/// See [`alloc::alloc::Allocator`].
43pub unsafe trait Allocator {
44 /// See [`alloc::alloc::Allocator::allocate`].
45 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
46
47 /// See [`alloc::alloc::Allocator::deallocate`].
48 ///
49 /// # Safety
50 ///
51 /// See [`alloc::alloc::Allocator::deallocate`].
52 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
53
54 /// See [`alloc::alloc::Allocator::allocate_zeroed`].
55 fn allocate_zeroed(
56 &self,
57 layout: Layout,
58 ) -> Result<NonNull<[u8]>, AllocError> {
59 let ptr = self.allocate(layout)?;
60 // SAFETY: `Self::allocate` always returns a pointer to valid memory.
61 unsafe {
62 let len = (*(ptr.as_ptr() as *mut [MaybeUninit<u8>])).len();
63 (ptr.as_ptr() as *mut u8).write_bytes(0_u8, len);
64 }
65 Ok(ptr)
66 }
67
68 /// See [`alloc::alloc::Allocator::grow`].
69 ///
70 /// # Safety
71 ///
72 /// See [`alloc::alloc::Allocator::grow`].
73 unsafe fn grow(
74 &self,
75 ptr: NonNull<u8>,
76 old_layout: Layout,
77 new_layout: Layout,
78 ) -> Result<NonNull<[u8]>, AllocError> {
79 let new = self.allocate(new_layout)?;
80 // SAFETY: Checked by caller.
81 unsafe {
82 (new.as_ptr() as *mut u8)
83 .copy_from_nonoverlapping(ptr.as_ptr(), old_layout.size());
84 self.deallocate(ptr, old_layout);
85 }
86 Ok(new)
87 }
88
89 /// See [`alloc::alloc::Allocator::grow_zeroed`].
90 ///
91 /// # Safety
92 ///
93 /// See [`alloc::alloc::Allocator::grow_zeroed`].
94 unsafe fn grow_zeroed(
95 &self,
96 ptr: NonNull<u8>,
97 old_layout: Layout,
98 new_layout: Layout,
99 ) -> Result<NonNull<[u8]>, AllocError> {
100 let new = self.allocate(new_layout)?;
101 // SAFETY: `Self::allocate` always returns a pointer to valid memory.
102 // Sizes are checked by caller (new size must not be less than old
103 // size).
104 unsafe {
105 let len = (*(new.as_ptr() as *mut [MaybeUninit<u8>])).len();
106 (new.as_ptr() as *mut u8)
107 .copy_from_nonoverlapping(ptr.as_ptr(), old_layout.size());
108 (new.as_ptr() as *mut u8)
109 .add(old_layout.size())
110 .write_bytes(0_u8, len - old_layout.size());
111 self.deallocate(ptr, old_layout);
112 }
113 Ok(new)
114 }
115
116 /// See [`alloc::alloc::Allocator::shrink`].
117 ///
118 /// # Safety
119 ///
120 /// See [`alloc::alloc::Allocator::shrink`].
121 unsafe fn shrink(
122 &self,
123 ptr: NonNull<u8>,
124 old_layout: Layout,
125 new_layout: Layout,
126 ) -> Result<NonNull<[u8]>, AllocError> {
127 let new = self.allocate(new_layout)?;
128 // SAFETY: `Self::allocate` always returns a pointer to valid memory.
129 // Sizes are checked by caller (new size must not be greater than old
130 // size).
131 unsafe {
132 let len = (*(new.as_ptr() as *mut [MaybeUninit<u8>])).len();
133 (new.as_ptr() as *mut u8)
134 .copy_from_nonoverlapping(ptr.as_ptr(), len);
135 self.deallocate(ptr, old_layout);
136 }
137 Ok(new)
138 }
139
140 /// See [`alloc::alloc::Allocator::by_ref`].
141 fn by_ref(&self) -> &Self
142 where
143 Self: Sized,
144 {
145 self
146 }
147}
148
149// SAFETY: This impl simply forwards to `A`.
150unsafe impl<A> Allocator for &A
151where
152 A: Allocator + ?Sized,
153{
154 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
155 A::allocate(*self, layout)
156 }
157
158 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
159 // SAFETY: Checked by caller.
160 unsafe {
161 A::deallocate(*self, ptr, layout);
162 }
163 }
164}
165
166/// A fallback for [`alloc::alloc::Global`], which is currently unstable.
167#[derive(Clone, Copy, Debug, Default)]
168pub struct Global;
169
170// SAFETY: The `alloc` and `dealloc` functions in the standard library behave
171// as required. Clones of this allocator will necessarily behave the same, as
172// they forward to the global allocator.
173unsafe impl Allocator for Global {
174 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
175 assert!(layout.size() != 0);
176 NonNull::new(ptr::slice_from_raw_parts_mut(
177 // SAFETY: We ensured that the size of the layout is not 0.
178 unsafe { alloc::alloc::alloc(layout) },
179 layout.size(),
180 ))
181 .ok_or(AllocError)
182 }
183
184 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
185 // SAFETY: Ensured by caller.
186 unsafe { alloc::alloc::dealloc(ptr.as_ptr(), layout) };
187 }
188}