std_fork\alloc/selfless_allocator.rs
1use core::alloc::Layout;
2use core::ptr::{self, NonNull};
3
4use stdx::alloc::AllocError;
5
6/// A global allocator interface that does not rely on `self`.
7///
8/// `SelflessAllocator` is suitable for stateless or global allocators
9/// where the allocation logic does not require access to instance state.
10///
11/// This trait is intended for use cases such as custom global allocators,
12/// embedded environments, or statically known allocators.
13///
14/// # Safety
15///
16/// Implementors must ensure that all memory returned:
17/// - is non-null,
18/// - properly aligned as per the `Layout`,
19/// - does not alias any existing live memory,
20/// - and is freed safely using `deallocate`.
21///
22/// Additionally, `grow`, `grow_zeroed`, and `shrink` must behave correctly
23/// even if called in rapid succession.
24pub unsafe trait SelflessAllocator {
25 /// Allocates a block of memory described by the given layout.
26 ///
27 /// # Errors
28 ///
29 /// Returns `Err(AllocError)` if:
30 /// - the system is out of memory,
31 /// - the alignment is invalid or unsupported,
32 /// - or the size exceeds allocator or platform limits.
33 fn allocate(layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
34
35 /// Allocates zero-initialized memory.
36 ///
37 /// This is equivalent to `allocate` followed by `write_bytes` with `0`.
38 ///
39 /// # Errors
40 ///
41 /// Returns `Err(AllocError)` under the same conditions as `allocate`,
42 /// or if zeroing the memory would cause undefined behavior.
43 fn allocate_zeroed(layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
44 let ptr = Self::allocate(layout)?;
45 unsafe { ptr.cast::<u8>().as_ptr().write_bytes(0, ptr.len()) }
46 Ok(ptr)
47 }
48
49 /// Deallocates a previously allocated block of memory.
50 ///
51 /// # Safety
52 ///
53 /// - `ptr` must have been returned by a previous call to `allocate` or `allocate_zeroed`.
54 /// - `layout` must match the original layout used in the allocation.
55 /// - Double-free or invalid layout can lead to undefined behavior.
56 unsafe fn deallocate(ptr: NonNull<u8>, layout: Layout);
57
58 /// Grows a memory block to a new layout.
59 ///
60 /// The contents up to the lesser of the old and new sizes are preserved.
61 /// The old memory is deallocated.
62 ///
63 /// # Errors
64 ///
65 /// Returns `Err(AllocError)` if:
66 /// - a new allocation fails,
67 /// - or copying or deallocation fails.
68 ///
69 /// # Safety
70 ///
71 /// - `ptr` must have been allocated by this allocator.
72 /// - `old_layout` must accurately reflect the original allocation.
73 /// - `new_layout.size()` must be greater than or equal to `old_layout.size()`.
74 unsafe fn grow(
75 ptr: NonNull<u8>,
76 old_layout: Layout,
77 new_layout: Layout,
78 ) -> Result<NonNull<[u8]>, AllocError> {
79 debug_assert!(
80 new_layout.size() >= old_layout.size(),
81 "`new_layout.size()` must be >= `old_layout.size()`"
82 );
83
84 let new_ptr = Self::allocate(new_layout)?;
85
86 unsafe {
87 ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.cast().as_ptr(), old_layout.size());
88 Self::deallocate(ptr, old_layout);
89 }
90
91 Ok(new_ptr)
92 }
93
94 /// Grows and zero-initializes additional memory beyond the old size.
95 ///
96 /// The contents of the original allocation are preserved. The added
97 /// space between `old_layout.size()` and `new_layout.size()` is set to zero.
98 ///
99 /// # Errors
100 ///
101 /// Returns `Err(AllocError)` if:
102 /// - memory cannot be allocated,
103 /// - or copying/zeroing fails.
104 ///
105 /// # Safety
106 ///
107 /// Same as `grow`.
108 unsafe fn grow_zeroed(
109 ptr: NonNull<u8>,
110 old_layout: Layout,
111 new_layout: Layout,
112 ) -> Result<NonNull<[u8]>, AllocError> {
113 debug_assert!(
114 new_layout.size() >= old_layout.size(),
115 "`new_layout.size()` must be >= `old_layout.size()`"
116 );
117
118 let new_ptr = Self::allocate_zeroed(new_layout)?;
119
120 unsafe {
121 ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.cast().as_ptr(), old_layout.size());
122 Self::deallocate(ptr, old_layout);
123 }
124
125 Ok(new_ptr)
126 }
127
128 /// Attempts to shrink a memory block to a smaller size.
129 ///
130 /// The contents up to the new layout size are preserved.
131 ///
132 /// # Errors
133 ///
134 /// Returns `Err(AllocError)` if:
135 /// - memory cannot be reallocated,
136 /// - or copying fails.
137 ///
138 /// # Safety
139 ///
140 /// - `ptr` must have been allocated by this allocator.
141 /// - `old_layout` must match the original allocation.
142 /// - `new_layout.size()` must be less than or equal to `old_layout.size()`.
143 unsafe fn shrink(
144 ptr: NonNull<u8>,
145 old_layout: Layout,
146 new_layout: Layout,
147 ) -> Result<NonNull<[u8]>, AllocError> {
148 debug_assert!(
149 new_layout.size() <= old_layout.size(),
150 "`new_layout.size()` must be <= `old_layout.size()`"
151 );
152
153 let new_ptr = Self::allocate(new_layout)?;
154
155 unsafe {
156 ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.cast().as_ptr(), new_layout.size());
157 Self::deallocate(ptr, old_layout);
158 }
159
160 Ok(new_ptr)
161 }
162}