memapi2/ffi/c_alloc.rs
1use core::{
2 ffi::c_void,
3 ptr::{self, null_mut}
4};
5
6const NULL: *mut c_void = null_mut();
7
8/// Copies `size` bytes from `old_ptr` to `ptr` when `ptr` is non-null, then deallocates `old_ptr`.
9///
10/// If `ptr` is `NULL`, this is a no-op and `old_ptr` is not freed.
11///
12/// # Safety
13///
14/// - `old_ptr` must point to a C allocation of at least `size` bytes.
15/// - `ptr` must point to an allocation of at least `size` bytes.
16pub unsafe fn try_move(ptr: *mut c_void, old_ptr: *mut c_void, size: usize) {
17 if ptr != NULL {
18 // SAFETY: `ptr` validated nonnull, caller guarantees `old_ptr` is valid. caller guarantees
19 // `size` is <= size of allocation at `ptr` and <= size of allocation at `old_ptr`,
20 // so copying that many bytes is safe.
21 unsafe {
22 memcpy(ptr, old_ptr, size);
23 }
24 // SAFETY: caller guarantees that `old_ptr` is valid
25 unsafe {
26 c_dealloc(old_ptr);
27 }
28 }
29}
30
31/// Allocates `size` bytes with at least `align` alignment.
32///
33/// The closest Rust equivalent is [`alloc`](stdalloc::alloc::alloc).
34///
35/// On non-Windows platforms this forwards to `aligned_alloc`, which requires `align` to be a
36/// power of two and a multiple of `size_of::<*mut c_void>()`, and `size` to be a multiple of
37/// `align`.
38///
39/// # Returns
40///
41/// - On success returns a nonnull pointer to the allocated memory.
42/// - On allocation failure returns `NULL`.
43#[must_use = "this function allocates memory on success, and dropping the returned pointer will \
44 leak memory"]
45pub fn c_alloc(align: usize, size: usize) -> *mut c_void {
46 #[cfg(windows)]
47 // SAFETY: this function is safe to call
48 unsafe {
49 _aligned_malloc(size, align)
50 }
51 #[cfg(not(windows))]
52 // SAFETY: this function is safe to call
53 unsafe {
54 aligned_alloc(align, size)
55 }
56}
57
58/// Frees memory previously returned by the primary C allocator.
59///
60/// The closest Rust equivalent is [`dealloc`](stdalloc::alloc::dealloc).
61///
62/// # Safety
63///
64/// The caller must ensure:
65/// - `ptr` points to the start of a valid allocation returned by this allocator, or is `NULL`.
66/// - `ptr` has not yet been deallocated.
67pub unsafe fn c_dealloc(ptr: *mut c_void) {
68 #[cfg(windows)]
69 {
70 _aligned_free(ptr);
71 }
72 #[cfg(not(windows))]
73 {
74 free(ptr);
75 }
76}
77
78/// Allocates `size` bytes with at least `align` alignment and zeroes the allocation.
79///
80/// # Returns
81///
82/// - On success returns a nonnull pointer to `size` bytes of memory which are guaranteed to be
83/// zeroed.
84/// - On allocation failure returns `NULL`.
85///
86/// # Safety
87///
88/// The caller must ensure:
89/// - `align` is a power of two and a multiple of <code>[size_of]::<*mut [c_void]>()</code>.
90/// - `size` is a multiple of `align`.
91#[must_use = "this function allocates memory on success, and dropping the returned pointer will \
92 leak memory"]
93pub unsafe fn c_zalloc(align: usize, size: usize) -> *mut c_void {
94 // allocate
95 // SAFETY: requirements are passed on to the caller.
96 let ptr = unsafe { c_alloc(align, size) };
97
98 // zero memory if allocation was successful
99 if ptr != NULL {
100 // SAFETY: `ptr` is nonnull, and at least size bytes in length. `0i32` fits in a `u8`.
101 unsafe {
102 ptr::write_bytes(ptr, 0, size);
103 }
104 }
105
106 ptr
107}
108
109/// Grows an existing allocation.
110///
111/// Allocates a new block of `size` bytes with at least `align` alignment, copies `old_size`
112/// bytes from `old_ptr` into the new block, frees the old block, and returns the new pointer.
113/// New bytes are set by `alloc` and may be uninitialized depending on what allocation function
114/// is passed.
115///
116/// # Returns
117///
118/// - On success returns a nonnull pointer to the new allocation.
119/// - On allocation failure returns `NULL` and does **not** free the original allocation.
120///
121/// # Safety
122///
123/// The caller must ensure:
124/// - `old_ptr` was allocated by this allocator and is valid for reads of `old_size` bytes.
125/// - `old_size` equals the size of the allocation requested at `old_ptr`.
126/// - `align` is a power of two and a multiple of <code>[size_of]::<*mut [c_void]>()</code>.
127/// - `size` is greater than or equal to `old_size` and a multiple of `align`.
128#[cfg_attr(miri, track_caller)]
129pub unsafe fn grow_aligned(
130 old_ptr: *mut c_void,
131 old_size: usize,
132 align: usize,
133 size: usize,
134 alloc: unsafe fn(usize, usize) -> *mut c_void
135) -> *mut c_void {
136 // allocate new aligned memory
137 // SAFETY: requirements are passed on to the caller
138 let ptr = unsafe { alloc(align, size) };
139
140 // if successful, move data to new pointer
141 // SAFETY: requirements are passed on to the caller
142 unsafe {
143 try_move(ptr, old_ptr, old_size);
144 }
145
146 ptr
147}
148
149/// Shrinks an existing allocation.
150///
151/// Allocates a new block of `size` bytes with at least `align` alignment, copies `size` bytes
152/// from `old_ptr` into the new block, frees the old block, and returns the new pointer.
153///
154/// # Returns
155///
156/// - On success returns a nonnull pointer to the new allocation.
157/// - If `size == 0`, the old allocation is freed and a [`dangling`](ptr::dangling) pointer is
158/// returned.
159/// - On allocation failure returns `NULL` and does __not__ free the original allocation.
160///
161/// # Safety
162///
163/// The caller must ensure:
164/// - `old_ptr` was allocated by this allocator and is valid for reads of at least `size` bytes.
165/// - `align` is a power of two and a multiple of <code>[size_of]::<*mut [c_void]>()</code>.
166/// - `size` is less than or equal to the size of the allocation at `old_ptr` and a multiple of
167/// `align`.
168#[cfg_attr(miri, track_caller)]
169pub unsafe fn shrink_aligned(
170 old_ptr: *mut c_void,
171 align: usize,
172 size: usize // a memset-ing alloc here is useless, as it will just be overwritten anyway.
173) -> *mut c_void {
174 // allocate new aligned memory
175 // SAFETY: requirements are passed on to the caller
176 let ptr = unsafe { c_alloc(align, size) };
177
178 // if successful, move data to new pointer
179 // SAFETY: requirements are passed on to the caller
180 unsafe {
181 try_move(ptr, old_ptr, size);
182 }
183
184 ptr
185}
186
187// public in case the user wants them for some reason
188extern "C" {
189 /// Allocates `size` bytes.
190 ///
191 /// The closest Rust equivalent is [`alloc`](stdalloc::alloc::alloc) with the `layout`
192 /// parameter's alignment being <code>[align_of]::\<usize\>()</code>
193 ///
194 /// # Safety
195 ///
196 /// This function is safe to call but may return `NULL` if allocation fails, or `size` is 0.
197 pub fn malloc(size: usize) -> *mut c_void;
198
199 #[cfg(not(windows))]
200 /// Allocates `size` bytes with at least `align` alignment.
201 ///
202 /// The closest Rust equivalent is [`alloc`](stdalloc::alloc::alloc).
203 ///
204 /// # Returns
205 ///
206 /// - On success returns a nonnull pointer to the allocated memory.
207 /// - On allocation failure returns `NULL`.
208 ///
209 /// # Safety
210 ///
211 /// This function is safe to call but may return `NULL` if:
212 /// - `align` is not a power of two and a multiple of `size_of::<*mut c_void>()`.
213 /// - `size` is not a multiple of `align`.
214 pub fn aligned_alloc(align: usize, size: usize) -> *mut c_void;
215
216 #[cfg(not(windows))]
217 /// Frees memory previously returned by the primary C allocator.
218 ///
219 /// The closest Rust equivalent is [`dealloc`](stdalloc::alloc::dealloc).
220 ///
221 /// # Safety
222 ///
223 /// The caller must ensure:
224 /// - `ptr` points to the start of a valid allocation returned by this allocator _or_ is `NULL`.
225 /// - `ptr` has not yet been deallocated.
226 pub fn free(ptr: *mut c_void);
227
228 #[cfg(windows)]
229 /// Windows version of [`aligned_alloc`].
230 pub fn _aligned_malloc(size: usize, alignment: usize) -> *mut c_void;
231 #[cfg(windows)]
232 /// Windows version of [`free`] specifically for memory returned by [`_aligned_malloc`].
233 pub fn _aligned_free(ptr: *mut c_void);
234
235 /// Sets `count` bytes at `ptr` to `val`. The returned pointer is a copy of `ptr`.
236 ///
237 /// The closest Rust equivalent is [`write_bytes`](ptr::write_bytes).
238 ///
239 /// # Safety
240 ///
241 /// The caller must ensure:
242 /// - `ptr` points to `count` valid bytes.
243 /// - `val` contains a value less than [`u8::MAX`].
244 pub fn memset(ptr: *mut c_void, val: i32, count: usize) -> *mut c_void;
245
246 /// Copies `count` bytes from `src` to `dest`. The returned pointer is a copy of `dest`.
247 ///
248 /// `src` and `dest` must not overlap, or the result stored in `dest` may be unexpected.
249 ///
250 /// The closest Rust equivalent is [`copy_nonoverlapping`](ptr::copy_nonoverlapping)
251 ///
252 /// # Safety
253 ///
254 /// The caller must ensure:
255 /// - `src` points to a valid block of memory of at least `count` bytes.
256 /// - `dest` points to a valid block of memory of at least `count` bytes.
257 /// - `src` and `dest` do not overlap.
258 pub fn memcpy(dest: *mut c_void, src: *const c_void, count: usize) -> *mut c_void;
259
260 /// Copies `count` bytes from `src` to `dest`. The returned pointer is a copy of `dest`.
261 ///
262 /// Unlike [`memcpy`], `src` and `dest` may overlap.
263 ///
264 /// The closest Rust equivalent is [`copy`](ptr::copy)
265 ///
266 /// # Safety
267 ///
268 /// The caller must ensure:
269 /// - `src` points to a valid block of memory of at least `count` bytes.
270 /// - `dest` points to a valid block of memory of at least `count` bytes.
271 pub fn memmove(dest: *mut c_void, src: *const c_void, count: usize) -> *mut c_void;
272}