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}