nstd_sys/
heap_ptr.rs

1//! A pointer type for single value heap allocation.
2use crate::{
3    core::{
4        alloc::{
5            nstd_core_alloc_layout_new_unchecked, nstd_core_alloc_layout_size, NSTDAllocLayout,
6            NSTDAllocator,
7        },
8        mem::nstd_core_mem_copy,
9        optional::NSTDOptional,
10    },
11    NSTDAny, NSTDAnyMut, NSTDUInt, NSTD_NULL,
12};
13use nstdapi::nstdapi;
14
15/// A pointer type for single value heap allocation.
16#[nstdapi]
17pub struct NSTDHeapPtr<'a> {
18    /// The memory allocator.
19    allocator: &'a NSTDAllocator,
20    /// A raw pointer to the value on the heap.
21    ptr: NSTDAnyMut,
22    /// The heap object's memory layout.
23    layout: NSTDAllocLayout,
24}
25impl<'a> NSTDHeapPtr<'a> {
26    /// Constructs a zero-sized [`NSTDHeapPtr`].
27    #[inline]
28    #[allow(clippy::missing_const_for_fn)]
29    fn zero_sized(allocator: &'a NSTDAllocator) -> Self {
30        Self {
31            allocator,
32            ptr: NSTD_NULL,
33            // SAFETY: `size` is 0, `align` is 1.
34            layout: unsafe { nstd_core_alloc_layout_new_unchecked(0, 1) },
35        }
36    }
37}
38impl Drop for NSTDHeapPtr<'_> {
39    /// [`NSTDHeapPtr`]'s destructor.
40    #[inline]
41    fn drop(&mut self) {
42        #[allow(unused_unsafe)]
43        // SAFETY: This operation is safe.
44        if unsafe { nstd_core_alloc_layout_size(self.layout) } > 0 {
45            // SAFETY: The heap object's size is non-zero.
46            unsafe { (self.allocator.deallocate)(self.allocator.state, self.ptr, self.layout) };
47        }
48    }
49}
50/// # Safety
51///
52/// The data that the heap pointer holds must be able to be safely sent between threads.
53// SAFETY: The user guarantees that the data is thread-safe.
54unsafe impl Send for NSTDHeapPtr<'_> {}
55/// # Safety
56///
57/// The data that the heap pointer holds must be able to be safely shared between threads.
58// SAFETY: The user guarantees that the data is thread-safe.
59unsafe impl Sync for NSTDHeapPtr<'_> {}
60
61/// Represents an optional value of type `NSTDHeapPtr`.
62pub type NSTDOptionalHeapPtr<'a> = NSTDOptional<NSTDHeapPtr<'a>>;
63
64/// Creates a new initialized heap allocated object.
65///
66/// # Parameters:
67///
68/// - `const NSTDAllocator *allocator` - The memory allocator.
69///
70/// - `NSTDAllocLayout layout` - The heap object's memory layout.
71///
72/// - `NSTDAny init` - A pointer to the object to initialize the heap object with.
73///
74/// # Returns
75///
76/// `NSTDOptionalHeapPtr hptr` - The new heap allocated object, or an uninitialized "none" variant
77/// if allocating fails.
78///
79/// # Safety
80///
81/// `init` must be a pointer to a value that is valid for reads based on `layout`.
82///
83/// # Example
84///
85/// ```
86/// use core::ptr::addr_of;
87/// use nstd_sys::{
88///     alloc::NSTD_ALLOCATOR, core::alloc::nstd_core_alloc_layout_new, heap_ptr::nstd_heap_ptr_new,
89/// };
90///
91/// let v = '🦀';
92/// let size = core::mem::size_of::<char>();
93/// let align = core::mem::align_of::<char>();
94/// let layout = nstd_core_alloc_layout_new(size, align).unwrap();
95/// let hptr = unsafe { nstd_heap_ptr_new(&NSTD_ALLOCATOR, layout, addr_of!(v).cast()).unwrap() };
96/// ```
97#[nstdapi]
98pub unsafe fn nstd_heap_ptr_new(
99    allocator: &NSTDAllocator,
100    layout: NSTDAllocLayout,
101    init: NSTDAny,
102) -> NSTDOptionalHeapPtr<'_> {
103    let size = nstd_core_alloc_layout_size(layout);
104    if size == 0 {
105        NSTDOptional::Some(NSTDHeapPtr::zero_sized(allocator))
106    } else {
107        let ptr = (allocator.allocate)(allocator.state, layout);
108        if ptr.is_null() {
109            return NSTDOptional::None;
110        }
111        nstd_core_mem_copy(ptr.cast(), init.cast(), size);
112        NSTDOptional::Some(NSTDHeapPtr {
113            allocator,
114            ptr,
115            layout,
116        })
117    }
118}
119
120/// Creates a new zero-initialized heap allocated object.
121///
122/// # Parameters:
123///
124/// - `const NSTDAllocator *allocator` - The memory allocator.
125///
126/// - `NSTDAllocLayout layout` - The heap object's memory layout.
127///
128/// # Returns
129///
130/// `NSTDOptionalHeapPtr hptr` - The new heap allocated object, or an uninitialized "none" variant
131/// if allocating fails.
132///
133/// # Safety
134///
135/// The data to be stored in the heap pointer must be safely representable by an all-zero byte
136/// pattern.
137///
138/// # Example
139///
140/// ```
141/// use nstd_sys::{
142///     alloc::NSTD_ALLOCATOR,
143///     core::alloc::nstd_core_alloc_layout_new,
144///     heap_ptr::{nstd_heap_ptr_get, nstd_heap_ptr_new_zeroed},
145/// };
146///
147/// unsafe {
148///     let size = core::mem::size_of::<u64>();
149///     let align = core::mem::align_of::<u64>();
150///     let layout = nstd_core_alloc_layout_new(size, align).unwrap();
151///     let hptr = nstd_heap_ptr_new_zeroed(&NSTD_ALLOCATOR, layout).unwrap();
152///     assert!(*nstd_heap_ptr_get(&hptr).cast::<u64>() == 0);
153/// }
154/// ```
155#[nstdapi]
156pub unsafe fn nstd_heap_ptr_new_zeroed(
157    allocator: &NSTDAllocator,
158    layout: NSTDAllocLayout,
159) -> NSTDOptionalHeapPtr<'_> {
160    if nstd_core_alloc_layout_size(layout) == 0 {
161        NSTDOptional::Some(NSTDHeapPtr::zero_sized(allocator))
162    } else {
163        // SAFETY: `size` is not 0.
164        let ptr = unsafe { (allocator.allocate_zeroed)(allocator.state, layout) };
165        if ptr.is_null() {
166            return NSTDOptional::None;
167        }
168        NSTDOptional::Some(NSTDHeapPtr {
169            allocator,
170            ptr,
171            layout,
172        })
173    }
174}
175
176/// Creates a clone of a heap allocated object.
177///
178/// # Parameters:
179///
180/// - `const NSTDHeapPtr *hptr` - The heap pointer.
181///
182/// # Returns
183///
184/// `NSTDOptionalHeapPtr cloned` - A new clone of the original heap object, or an uninitialized
185/// "none" variant if allocating fails.
186#[nstdapi]
187pub fn nstd_heap_ptr_clone<'a>(hptr: &NSTDHeapPtr<'a>) -> NSTDOptionalHeapPtr<'a> {
188    let size = nstd_heap_ptr_size(hptr);
189    if size == 0 {
190        NSTDOptional::Some(NSTDHeapPtr::zero_sized(hptr.allocator))
191    } else {
192        // SAFETY: `size` is not 0.
193        let mem = unsafe { (hptr.allocator.allocate)(hptr.allocator.state, hptr.layout) };
194        if mem.is_null() {
195            return NSTDOptional::None;
196        }
197        // SAFETY: Both pointers are non-null.
198        unsafe { nstd_core_mem_copy(mem.cast(), hptr.ptr.cast(), size) };
199        NSTDOptional::Some(NSTDHeapPtr {
200            allocator: hptr.allocator,
201            ptr: mem,
202            layout: hptr.layout,
203        })
204    }
205}
206
207/// Returns an immutable reference to a heap object's allocator.
208///
209/// # Parameters:
210///
211/// - `const NSTDHeapPtr *hptr` - The heap object.
212///
213/// # Returns
214///
215/// `const NSTDAllocator *allocator` - The heap object's allocator.
216#[inline]
217#[nstdapi]
218pub const fn nstd_heap_ptr_allocator<'a>(hptr: &NSTDHeapPtr<'a>) -> &'a NSTDAllocator {
219    hptr.allocator
220}
221
222/// Returns the size of the heap allocated object.
223///
224/// # Parameters:
225///
226/// - `const NSTDHeapPtr *hptr` - The heap pointer.
227///
228/// # Returns
229///
230/// `NSTDUInt size` - The size of the heap allocated object.
231///
232/// # Example
233///
234/// ```
235/// use nstd_sys::{
236///     alloc::NSTD_ALLOCATOR,
237///     core::alloc::nstd_core_alloc_layout_new,
238///     heap_ptr::{nstd_heap_ptr_new_zeroed, nstd_heap_ptr_size},
239/// };
240///
241/// unsafe {
242///     let size = core::mem::size_of::<i32>();
243///     let align = core::mem::align_of::<i32>();
244///     let layout = nstd_core_alloc_layout_new(size, align).unwrap();
245///     let hptr = nstd_heap_ptr_new_zeroed(&NSTD_ALLOCATOR, layout).unwrap();
246///     assert!(nstd_heap_ptr_size(&hptr) == size);
247/// }
248/// ```
249#[inline]
250#[nstdapi]
251pub const fn nstd_heap_ptr_size(hptr: &NSTDHeapPtr<'_>) -> NSTDUInt {
252    nstd_core_alloc_layout_size(hptr.layout)
253}
254
255/// Returns an immutable raw pointer to the object on the heap.
256///
257/// # Note
258///
259/// This will always return null if the size of the object being stored on the heap is 0.
260///
261/// # Parameters:
262///
263/// - `const NSTDHeapPtr *hptr` - The heap pointer.
264///
265/// # Returns
266///
267/// `NSTDAny ptr` - A raw pointer to the object on the heap.
268///
269/// # Example
270///
271/// ```
272/// use core::ptr::addr_of;
273/// use nstd_sys::{
274///     alloc::NSTD_ALLOCATOR,
275///     core::alloc::nstd_core_alloc_layout_new,
276///     heap_ptr::{nstd_heap_ptr_get, nstd_heap_ptr_new},
277/// };
278///
279/// unsafe {
280///     let v = -46923i128;
281///     let size = core::mem::size_of::<i128>();
282///     let align = core::mem::align_of::<i128>();
283///     let layout = nstd_core_alloc_layout_new(size, align).unwrap();
284///     let hptr = nstd_heap_ptr_new(&NSTD_ALLOCATOR, layout, addr_of!(v).cast()).unwrap();
285///     assert!(*nstd_heap_ptr_get(&hptr).cast::<i128>() == v);
286/// }
287/// ```
288#[inline]
289#[nstdapi]
290pub const fn nstd_heap_ptr_get(hptr: &NSTDHeapPtr<'_>) -> NSTDAny {
291    hptr.ptr
292}
293
294/// Returns a raw pointer to the object on the heap.
295///
296/// # Note
297///
298/// This will always return null if the size of the object being stored on the heap is 0.
299///
300/// # Parameters:
301///
302/// - `NSTDHeapPtr *hptr` - The heap pointer.
303///
304/// # Returns
305///
306/// `NSTDAnyMut ptr` - A raw pointer to the object on the heap.
307///
308/// # Example
309///
310/// ```
311/// use core::ptr::addr_of;
312/// use nstd_sys::{
313///     alloc::NSTD_ALLOCATOR,
314///     core::alloc::nstd_core_alloc_layout_new,
315///     heap_ptr::{nstd_heap_ptr_get_mut, nstd_heap_ptr_new},
316/// };
317///
318/// unsafe {
319///     let v = 32964i128;
320///     let size = core::mem::size_of::<i128>();
321///     let align = core::mem::align_of::<i128>();
322///     let layout = nstd_core_alloc_layout_new(size, align).unwrap();
323///     let mut hptr = nstd_heap_ptr_new(&NSTD_ALLOCATOR, layout, addr_of!(v).cast()).unwrap();
324///     let hv = nstd_heap_ptr_get_mut(&mut hptr).cast::<i128>();
325///     assert!(*hv == v);
326///     *hv = -46923;
327///     assert!(*hv != v);
328/// }
329/// ```
330#[inline]
331#[nstdapi]
332pub fn nstd_heap_ptr_get_mut(hptr: &mut NSTDHeapPtr<'_>) -> NSTDAnyMut {
333    hptr.ptr
334}
335
336/// Frees an instance of `NSTDHeapPtr`.
337///
338/// # Parameters:
339///
340/// - `NSTDHeapPtr hptr` - A pointer to the heap object.
341#[inline]
342#[nstdapi]
343#[allow(
344    unused_variables,
345    clippy::missing_const_for_fn,
346    clippy::needless_pass_by_value
347)]
348pub fn nstd_heap_ptr_free(hptr: NSTDHeapPtr<'_>) {}
349
350/// Frees an instance of `NSTDHeapPtr` after invoking `callback` with the heap object's data.
351///
352/// # Parameters:
353///
354/// - `NSTDHeapPtr hptr` - A pointer to the heap object.
355///
356/// - `void (*callback)(NSTDAnyMut)` - The heap object's destructor.
357///
358/// # Safety
359///
360/// This operation makes a direct call on a C function pointer (`callback`).
361#[inline]
362#[nstdapi]
363#[allow(clippy::needless_pass_by_value)]
364pub unsafe fn nstd_heap_ptr_drop(
365    hptr: NSTDHeapPtr<'_>,
366    callback: unsafe extern "C" fn(NSTDAnyMut),
367) {
368    callback(hptr.ptr);
369}