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}