1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! Memory allocation for Unix-like systems.
use crate::{NSTDAnyMut, NSTDUInt};
#[cfg(all(feature = "asm", target_arch = "x86_64", not(target_os = "macos")))]
core::arch::global_asm!(include_str!("alloc/x86_64/alloc.asm"));
#[cfg(all(feature = "asm", target_arch = "x86_64", target_os = "macos"))]
core::arch::global_asm!(include_str!("alloc/x86_64/macos/alloc.asm"));

/// Describes an error returned from an `nstd.os.unix.alloc` function.
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub enum NSTDUnixAllocError {
    /// No error occurred.
    NSTD_UNIX_ALLOC_ERROR_NONE,
    /// Allocating or reallocating failed.
    NSTD_UNIX_ALLOC_ERROR_OUT_OF_MEMORY,
    /// An allocation function received input parameters that resulted in an invalid memory layout.
    NSTD_UNIX_ALLOC_ERROR_INVALID_LAYOUT,
}

extern "C" {
    /// Allocates a block of memory on the heap, returning a pointer to it.
    ///
    /// # Parameters:
    ///
    /// - `NSTDUInt size` - The number of bytes to allocate for the new block of memory.
    ///
    /// # Returns
    ///
    /// `NSTDAnyMut ptr` - A pointer to the newly allocated block of memory, or null on error.
    ///
    /// # Safety
    ///
    /// See <https://man7.org/linux/man-pages/man3/malloc.3.html>.
    ///
    /// # Example
    ///
    /// ```
    /// use nstd_sys::os::unix::alloc::{nstd_os_unix_alloc_allocate, nstd_os_unix_alloc_deallocate};
    ///
    /// unsafe {
    ///     let mut mem = nstd_os_unix_alloc_allocate(24);
    ///     assert!(!mem.is_null());
    ///     nstd_os_unix_alloc_deallocate(&mut mem);
    /// }
    /// ```
    pub fn nstd_os_unix_alloc_allocate(size: NSTDUInt) -> NSTDAnyMut;

    /// Allocates a block of zero initialized memory on the heap, returning a pointer to it.
    ///
    /// # Parameters:
    ///
    /// - `NSTDUInt size` - The number of bytes to allocate for the new block of memory.
    ///
    /// # Returns
    ///
    /// `NSTDAnyMut ptr` - A pointer to the newly allocated block of memory, or null on error.
    ///
    /// # Safety
    ///
    /// See <https://man7.org/linux/man-pages/man3/calloc.3p.html>.
    ///
    /// # Example
    ///
    /// ```
    /// use nstd_sys::os::unix::alloc::{
    ///     nstd_os_unix_alloc_allocate_zeroed, nstd_os_unix_alloc_deallocate,
    /// };
    ///
    /// const SIZE: usize = core::mem::size_of::<isize>();
    ///
    /// unsafe {
    ///     let mut mem = nstd_os_unix_alloc_allocate_zeroed(SIZE);
    ///     assert!(!mem.is_null());
    ///     assert!(*mem.cast::<isize>() == 0);
    ///     nstd_os_unix_alloc_deallocate(&mut mem);
    /// }
    /// ```
    pub fn nstd_os_unix_alloc_allocate_zeroed(size: NSTDUInt) -> NSTDAnyMut;

    /// Reallocates a block of memory previously allocated by `nstd_os_unix_alloc_allocate[_zeroed]`.
    ///
    /// # Parameters:
    ///
    /// - `NSTDAnyMut *ptr` - A pointer to the block of memory to reallocate.
    ///
    /// - `NSTDUInt new_size` - The new size of the memory block.
    ///
    /// # Returns
    ///
    /// `NSTDUnixAllocError errc` - The allocation operation error code.
    ///
    /// # Safety
    ///
    /// See <https://man7.org/linux/man-pages/man3/realloc.3p.html>.
    ///
    /// # Example
    ///
    /// ```
    /// use nstd_sys::os::unix::alloc::{
    ///     nstd_os_unix_alloc_allocate_zeroed, nstd_os_unix_alloc_deallocate,
    ///     nstd_os_unix_alloc_reallocate, NSTDUnixAllocError::NSTD_UNIX_ALLOC_ERROR_NONE,
    /// };
    ///
    /// const SIZE: usize = core::mem::size_of::<u64>();
    /// const NEW_SIZE: usize = core::mem::size_of::<u32>();
    ///
    /// unsafe {
    ///     let mut mem = nstd_os_unix_alloc_allocate_zeroed(SIZE);
    ///     assert!(!mem.is_null());
    ///     let errc = nstd_os_unix_alloc_reallocate(&mut mem, NEW_SIZE);
    ///     assert!(errc == NSTD_UNIX_ALLOC_ERROR_NONE);
    ///     assert!(*mem.cast::<u32>() == 0);
    ///     nstd_os_unix_alloc_deallocate(&mut mem);
    /// }
    /// ```
    pub fn nstd_os_unix_alloc_reallocate(
        ptr: &mut NSTDAnyMut,
        new_size: NSTDUInt,
    ) -> NSTDUnixAllocError;

    /// Deallocates a block of memory previously allocated by `nstd_os_unix_alloc_allocate[_zeroed]`.
    ///
    /// # Parameters:
    ///
    /// - `NSTDAnyMut *ptr` - A pointer to the block of memory to free.
    ///
    /// # Safety
    ///
    /// See <https://man7.org/linux/man-pages/man3/free.3p.html>.
    ///
    /// # Example
    ///
    /// ```
    /// use nstd_sys::os::unix::alloc::{nstd_os_unix_alloc_allocate, nstd_os_unix_alloc_deallocate};
    ///
    /// unsafe {
    ///     let mut mem = nstd_os_unix_alloc_allocate(32);
    ///     assert!(!mem.is_null());
    ///     nstd_os_unix_alloc_deallocate(&mut mem);
    /// }
    /// ```
    pub fn nstd_os_unix_alloc_deallocate(ptr: &mut NSTDAnyMut);
}