Skip to main content

slop_alloc/
allocator.rs

1use core::{alloc::Layout, ptr::NonNull};
2use std::{rc::Rc, sync::Arc};
3
4use thiserror::Error;
5
6#[derive(Copy, Clone, PartialEq, Eq, Debug, Error)]
7#[error("allocation error")]
8pub struct AllocError;
9
10/// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of
11/// data described via [`Layout`][]
12///
13/// # Safety
14///
15/// * Memory blocks returned from an allocator that are [*currently allocated*] must point to valid
16///   memory and retain their validity while they are [*currently allocated*] and the shorter of:
17///   - the borrow-checker lifetime of the allocator type itself.
18///   - as long as at least one of the instance and all of its clones has not been dropped.
19///
20/// * copying, cloning, or moving the allocator must not invalidate memory blocks returned from this
21///   allocator. A copied or cloned allocator must behave like the same allocator, and
22///
23/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other
24///   method of the allocator.
25pub unsafe trait Allocator {
26    /// Attempts to allocate a block of memory.
27    ///
28    /// On success, returns a [`NonNull<[u8]>`][NonNull] meeting the size and alignment guarantees
29    /// of `layout`.
30    ///
31    /// The returned block may have a larger size than specified by `layout.size()`, and may or may
32    /// not have its contents initialized.
33    ///
34    /// The returned block of memory remains valid as long as it is [*currently allocated*] and the
35    /// shorter of:
36    ///   - the borrow-checker lifetime of the allocator type itself.
37    ///   - as long as at the allocator and all its clones has not been dropped.
38    ///
39    /// # Errors
40    ///
41    /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
42    /// allocator's size or alignment constraints.
43    ///
44    /// # Safety
45    ///
46    /// The memory is not necessarily available upon return from this function. The caller must
47    /// ensure that the proper synchronization is performed before using the memory, if necessary.
48    unsafe fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
49
50    /// Deallocates the memory referenced by `ptr`.
51    ///
52    /// # Safety
53    ///
54    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and
55    /// * `layout` must [*fit*] that block of memory.
56    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
57
58    fn by_ref(&self) -> &Self
59    where
60        Self: Sized,
61    {
62        self
63    }
64}
65
66unsafe impl<A> Allocator for &A
67where
68    A: Allocator + ?Sized,
69{
70    #[inline]
71    unsafe fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
72        (**self).allocate(layout)
73    }
74
75    #[inline]
76    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
77        // SAFETY: the safety contract must be upheld by the caller
78        unsafe { (**self).deallocate(ptr, layout) }
79    }
80}
81
82unsafe impl<A> Allocator for Rc<A>
83where
84    A: Allocator + ?Sized,
85{
86    #[inline]
87    unsafe fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
88        (**self).allocate(layout)
89    }
90
91    #[inline]
92    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
93        // SAFETY: the safety contract must be upheld by the caller
94        unsafe { (**self).deallocate(ptr, layout) }
95    }
96}
97
98unsafe impl<A> Allocator for Arc<A>
99where
100    A: Allocator + ?Sized,
101{
102    #[inline]
103    unsafe fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
104        (**self).allocate(layout)
105    }
106
107    #[inline]
108    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
109        // SAFETY: the safety contract must be upheld by the caller
110        unsafe { (**self).deallocate(ptr, layout) }
111    }
112}