nstd_sys/core/alloc.rs
1//! Provides useful types for memory allocation support.
2use crate::{
3 core::optional::{gen_optional, NSTDOptional},
4 NSTDAny, NSTDAnyMut, NSTDUInt, NSTD_INT_MAX,
5};
6use nstdapi::nstdapi;
7
8/// Describes a valid layout for a block of memory.
9#[repr(C)]
10#[derive(Clone, Copy, PartialEq, Eq)]
11pub struct NSTDAllocLayout {
12 /// The size of the memory block.
13 size: NSTDUInt,
14 /// The alignment of the memory block.
15 align: NSTDUInt,
16}
17gen_optional!(NSTDOptionalAllocLayout, NSTDAllocLayout);
18
19/// Describes an error returned from allocation functions.
20#[repr(C)]
21#[derive(Clone, Copy, PartialEq, Eq)]
22#[allow(non_camel_case_types)]
23pub enum NSTDAllocError {
24 /// No error occurred.
25 NSTD_ALLOC_ERROR_NONE,
26 /// Allocating or reallocating failed.
27 NSTD_ALLOC_ERROR_OUT_OF_MEMORY,
28 /// Deallocating memory failed.
29 NSTD_ALLOC_ERROR_MEMORY_NOT_FOUND,
30 /// Getting a handle to a heap failed.
31 NSTD_ALLOC_ERROR_HEAP_NOT_FOUND,
32 /// A heap is invalid.
33 NSTD_ALLOC_ERROR_INVALID_HEAP,
34 /// An allocation function received input parameters that resulted in an invalid memory layout.
35 NSTD_ALLOC_ERROR_INVALID_LAYOUT,
36}
37
38/// A structure of function pointers making up an allocator's virtual function table.
39#[nstdapi]
40#[derive(Clone, Copy)]
41pub struct NSTDAllocator {
42 /// An opaque pointer to the allocator's state.
43 pub state: NSTDAny,
44 /// Allocates a new block of memory.
45 ///
46 /// If allocation fails, a null pointer is returned.
47 ///
48 /// If allocation succeeds, this returns a pointer to the new memory that is suitably aligned
49 /// for `layout`'s alignment and the number of bytes allocated is at least equal to `layout`'s
50 /// size.
51 ///
52 /// # Parameters:
53 ///
54 /// - `NSTDAllocLayout layout` - Describes the memory layout to allocate for.
55 ///
56 /// # Returns
57 ///
58 /// `NSTDAnyMut ptr` - A pointer to the allocated memory, null on error.
59 ///
60 /// # Safety
61 ///
62 /// - Behavior is undefined if `layout`'s size is zero.
63 ///
64 /// - The new memory buffer should be considered uninitialized.
65 pub allocate: unsafe extern "C" fn(NSTDAny, NSTDAllocLayout) -> NSTDAnyMut,
66 /// Allocates a new block of zero-initialized memory.
67 ///
68 /// If allocation fails, a null pointer is returned.
69 ///
70 /// If allocation succeeds, this returns a pointer to the new memory that is suitably aligned
71 /// for `layout`'s alignment and the number of bytes allocated is at least equal to `layout`'s
72 /// size.
73 ///
74 /// # Parameters:
75 ///
76 /// - `NSTDAllocLayout layout` - Describes the memory layout to allocate for.
77 ///
78 /// # Returns
79 ///
80 /// `NSTDAnyMut ptr` - A pointer to the allocated memory, null on error.
81 ///
82 /// # Safety
83 ///
84 /// Behavior is undefined if `layout`'s size is zero.
85 pub allocate_zeroed: unsafe extern "C" fn(NSTDAny, NSTDAllocLayout) -> NSTDAnyMut,
86 /// Reallocates memory that was previously allocated by this allocator.
87 ///
88 /// On successful reallocation, `ptr` will point to the new memory location and
89 /// `NSTD_ALLOC_ERROR_NONE` will be returned. If this is not the case and reallocation fails,
90 /// the pointer will remain untouched and the appropriate error is returned.
91 ///
92 /// # Parameters:
93 ///
94 /// - `NSTDAnyMut *ptr` - A pointer to the allocated memory.
95 ///
96 /// - `NSTDAllocLayout old_layout` - Describes the previous memory layout.
97 ///
98 /// - `NSTDAllocLayout new_layout` - Describes the new memory layout to allocate for.
99 ///
100 /// # Returns
101 ///
102 /// `NSTDAllocError errc` - The allocation operation error code.
103 ///
104 /// # Safety
105 ///
106 /// - Behavior is undefined if `new_layout`'s size is zero.
107 ///
108 /// - Behavior is undefined if `ptr` is not a pointer to memory allocated by this allocator.
109 ///
110 /// - `old_layout` must be the same value that was used to allocate the memory buffer.
111 pub reallocate: unsafe extern "C" fn(
112 NSTDAny,
113 &mut NSTDAnyMut,
114 NSTDAllocLayout,
115 NSTDAllocLayout,
116 ) -> NSTDAllocError,
117 /// Deallocates memory that was previously allocated by this allocator.
118 ///
119 /// # Parameters:
120 ///
121 /// - `NSTDAnyMut ptr` - A pointer to the allocated memory.
122 ///
123 /// - `NSTDAllocLayout layout` - Describes the layout of memory that `ptr` points to.
124 ///
125 /// # Returns
126 ///
127 /// `NSTDAllocError errc` - The allocation operation error code.
128 ///
129 /// # Safety
130 ///
131 /// - Behavior is undefined if `ptr` is not a pointer to memory allocated by this allocator.
132 ///
133 /// - `layout` must be the same value that was used to allocate the memory buffer.
134 pub deallocate: unsafe extern "C" fn(NSTDAny, NSTDAnyMut, NSTDAllocLayout) -> NSTDAllocError,
135}
136/// # Safety
137///
138/// The allocator's state must be able to be safely *shared* between threads.
139// SAFETY: The user guarantees that the state is thread-safe.
140unsafe impl Send for NSTDAllocator {}
141/// # Safety
142///
143/// The allocator's state must be able to be safely shared between threads.
144// SAFETY: The user guarantees that the state is thread-safe.
145unsafe impl Sync for NSTDAllocator {}
146
147/// Creates a new memory layout from a size and alignment.
148///
149/// # Parameters:
150///
151/// - `NSTDUInt size` - The size of the memory block.
152///
153/// - `NSTDUInt align` - The alignment of the memory block.
154///
155/// # Returns
156///
157/// `NSTDOptionalAllocLayout layout` - The memory layout on success, or an uninitialized "none"
158/// variant if either `size` is greater than `NSTDInt`'s max value or `align` is not a power of two.
159#[inline]
160#[nstdapi]
161pub const fn nstd_core_alloc_layout_new(
162 size: NSTDUInt,
163 align: NSTDUInt,
164) -> NSTDOptionalAllocLayout {
165 match size <= NSTD_INT_MAX && crate::core::mem::is_power_of_two(align) {
166 true => NSTDOptional::Some(NSTDAllocLayout { size, align }),
167 false => NSTDOptional::None,
168 }
169}
170
171/// Creates a new memory layout from a size and alignment without performing safety checks.
172///
173/// # Parameters:
174///
175/// - `NSTDUInt size` - The size of the memory block.
176///
177/// - `NSTDUInt align` - The alignment of the memory block.
178///
179/// # Returns
180///
181/// `NSTDAllocLayout layout` - The memory layout.
182///
183/// # Safety
184///
185/// - `size` must not be greater than `NSTDInt`'s max value.
186///
187/// - `align` must be a nonzero power of two.
188#[inline]
189#[nstdapi]
190pub const unsafe fn nstd_core_alloc_layout_new_unchecked(
191 size: NSTDUInt,
192 align: NSTDUInt,
193) -> NSTDAllocLayout {
194 NSTDAllocLayout { size, align }
195}
196
197/// Creates a new memory layout for an array of elements.
198///
199/// # Parameters:
200///
201/// - `NSTDUInt size` - The size of each element in the array.
202///
203/// - `NSTDUInt align` - The alignment of each element in the array.
204///
205/// - `NSTDUInt len` - The length of the array to create a memory layout for.
206///
207/// # Returns
208///
209/// `NSTDOptionalAllocLayout layout` - The memory layout on success, or an uninitialized "none"
210/// variant if the calculated size is greater than `NSTDInt`'s max value, `size` is not a multiple
211/// of `align`, or `align` is not a power of two.
212#[inline]
213#[nstdapi]
214pub const fn nstd_core_alloc_layout_array(
215 size: NSTDUInt,
216 align: NSTDUInt,
217 len: NSTDUInt,
218) -> NSTDOptionalAllocLayout {
219 #[allow(clippy::arithmetic_side_effects)]
220 if crate::core::mem::is_power_of_two(align) && size % align == 0 {
221 if let Some(size) = size.checked_mul(len) {
222 if size <= NSTD_INT_MAX {
223 return NSTDOptional::Some(NSTDAllocLayout { size, align });
224 }
225 }
226 }
227 NSTDOptional::None
228}
229
230/// Creates a new memory layout for an array of elements without performing safety checks.
231///
232/// # Parameters:
233///
234/// - `NSTDUInt size` - The size of each element in the array.
235///
236/// - `NSTDUInt align` - The alignment of each element in the array.
237///
238/// - `NSTDUInt len` - The length of the array to create a memory layout for.
239///
240/// # Returns
241///
242/// `NSTDAllocLayout layout` - The new memory layout.
243///
244/// # Panics
245///
246/// This operation will panic if `align` is 0.
247///
248/// # Safety
249///
250/// - `align` must be a power of two.
251///
252/// - `size` must be a multiple of `align`.
253///
254/// - The calculated size must not be greater than `NSTDInt`'s max value.
255#[inline]
256#[nstdapi]
257#[allow(clippy::arithmetic_side_effects)]
258pub const unsafe fn nstd_core_alloc_layout_array_unchecked(
259 size: NSTDUInt,
260 align: NSTDUInt,
261 len: NSTDUInt,
262) -> NSTDAllocLayout {
263 NSTDAllocLayout {
264 size: size.wrapping_mul(len),
265 align,
266 }
267}
268
269/// Returns the size of an `NSTDAllocLayout`.
270///
271/// # Parameters:
272///
273/// - `NSTDAllocLayout layout` - The memory layout.
274///
275/// # Returns
276///
277/// `NSTDUInt size` - The layout's size.
278#[inline]
279#[nstdapi]
280pub const fn nstd_core_alloc_layout_size(layout: NSTDAllocLayout) -> NSTDUInt {
281 layout.size
282}
283
284/// Returns the alignment of an `NSTDAllocLayout`.
285///
286/// # Parameters:
287///
288/// - `NSTDAllocLayout layout` - The memory layout.
289///
290/// # Returns
291///
292/// `NSTDUInt align` - The layout's alignment.
293#[inline]
294#[nstdapi]
295pub const fn nstd_core_alloc_layout_align(layout: NSTDAllocLayout) -> NSTDUInt {
296 layout.align
297}