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}