Skip to main content

noop_allocator/
lib.rs

1#![no_std]
2#![feature(allocator_api)]
3#![warn(rust_2018_idioms)]
4
5#[cfg(feature = "alloc")]
6extern crate alloc;
7use core::{
8    alloc::{AllocError, Allocator, Layout},
9    marker::PhantomData,
10    ptr::NonNull,
11};
12
13/// An [`Allocator`] that does nothing.
14///
15/// Specifically:
16/// * [`allocate`][NoopAllocator::allocate] and
17///   [`allocate_zeroed`][NoopAllocator::allocate_zeroed] will return `Err` for
18///   any non-zero-sized allocation requests
19/// * [`deallocate`][NoopAllocator::deallocate] is a no-op, and does not require
20///   that `ptr` be "currently allocated", or fit `layout`.
21/// * [`shrink`][NoopAllocator::shrink], [`grow`][NoopAllocator::grow], and
22///   [`grow_zeroed`][NoopAllocator::grow_zeroed] do not require that `ptr` be
23///   "currently allocated", or fit `old_layout`, and will successfully return
24///   the original pointer unchanged (with the length of the new layout) if the
25///   `ptr` is aligned for the new layout and the new layout is smaller or the
26///   same size as the old layout.
27///
28/// This type is usable as an [`Allocator`] when you want to borrow an existing
29/// memory range for use in a single-allocation collection type, for example in
30/// [`Box`][alloc::boxed::Box] or [`Vec`][alloc::vec::Vec].
31///
32/// # Safety:
33///
34/// Many functions in this crate assume that `impl Allocator for
35/// NoopAllocator<'_>` as described above is sound, but `feature(allocator_api)`
36/// is unstable and the preconditions may change.
37#[repr(transparent)]
38pub struct NoopAllocator<'a>(PhantomData<&'a ()>);
39
40impl<'a> NoopAllocator<'a> {
41    /// Creates a new `NoopAllocator<'a>`.
42    pub const fn new() -> Self {
43        Self(PhantomData)
44    }
45}
46
47impl<'a> Default for NoopAllocator<'a> {
48    fn default() -> Self {
49        Self::new()
50    }
51}
52
53unsafe impl Allocator for NoopAllocator<'_> {
54    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
55        if layout.size() == 0 {
56            Ok(NonNull::slice_from_raw_parts(layout.dangling_ptr(), 0))
57        } else {
58            Err(AllocError)
59        }
60    }
61
62    unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
63        // intentionally empty
64    }
65
66    unsafe fn grow(
67        &self,
68        ptr: NonNull<u8>,
69        old_layout: Layout,
70        new_layout: Layout,
71    ) -> Result<NonNull<[u8]>, AllocError> {
72        self.grow_zeroed(ptr, old_layout, new_layout)
73    }
74
75    unsafe fn grow_zeroed(
76        &self,
77        ptr: NonNull<u8>,
78        old_layout: Layout,
79        new_layout: Layout,
80    ) -> Result<NonNull<[u8]>, AllocError> {
81        debug_assert!(
82            new_layout.size() >= old_layout.size(),
83            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
84        );
85        if new_layout.size() > old_layout.size()
86            || (ptr.as_ptr() as usize & (new_layout.align() - 1) != 0)
87        {
88            return Err(AllocError);
89        }
90
91        let new_ptr = NonNull::slice_from_raw_parts(ptr, new_layout.size());
92
93        Ok(new_ptr)
94    }
95
96    unsafe fn shrink(
97        &self,
98        ptr: NonNull<u8>,
99        old_layout: Layout,
100        new_layout: Layout,
101    ) -> Result<NonNull<[u8]>, AllocError> {
102        debug_assert!(
103            new_layout.size() <= old_layout.size(),
104            "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
105        );
106
107        if new_layout.size() > old_layout.size()
108            || (ptr.as_ptr() as usize & (new_layout.align() - 1) != 0)
109        {
110            return Err(AllocError);
111        }
112
113        let new_ptr = NonNull::slice_from_raw_parts(ptr, new_layout.size());
114
115        Ok(new_ptr)
116    }
117}
118
119#[cfg(feature = "alloc")]
120pub mod owning_ref;
121#[cfg(feature = "alloc")]
122pub mod owning_slice;