nstd_sys/core/mem.rs
1//! Contains mostly unsafe functions for interacting with raw memory.
2use crate::{core::def::NSTDByte, NSTDAny, NSTDAnyMut, NSTDBool, NSTDUInt};
3use cfg_if::cfg_if;
4use nstdapi::nstdapi;
5
6/// The default alignment suitable for any scalar type.
7///
8/// Corresponds to `alignof(max_align_t)`.
9/// The C/C++ standards specify that this value should be at least 8 or 16, I'm going with 16 for
10/// safety but of course this is platform dependent so if you (the reader) know of a platform that
11/// this value is smaller (or larger for that matter) on, please submit an issue/pull request.
12pub(crate) const MAX_ALIGN: usize = 16;
13
14/// Checks if `align` is a power of 2.
15#[inline]
16#[allow(clippy::arithmetic_side_effects)]
17pub(crate) const fn is_power_of_two(align: NSTDUInt) -> NSTDBool {
18 (align != 0) && ((align & (align - 1)) == 0)
19}
20
21/// Compares two memory buffers of `num` bytes.
22///
23/// # Parameters:
24///
25/// - `const NSTDByte *buf1` - A pointer to the first memory buffer.
26///
27/// - `const NSTDByte *buf2` - A pointer to the second memory buffer.
28///
29/// - `NSTDUInt num` - The number of bytes to compare.
30///
31/// # Returns
32///
33/// `NSTDBool is_eq` - `NSTD_TRUE` if the memory buffers carry the same data.
34///
35/// # Safety
36///
37/// - This function is highly unsafe as it does not know how large either of the memory buffers
38/// actually are, which can lead to undefined behavior if either of the buffers' length are less
39/// than `num`.
40///
41/// - `buf1` and `buf2` must be non-null.
42///
43/// # Example
44///
45/// ```
46/// use nstd_sys::{
47/// core::mem::{nstd_core_mem_compare, nstd_core_mem_copy},
48/// NSTD_TRUE,
49/// };
50///
51/// let buf1 = [0u32; 12];
52/// let mut buf2 = [u32::MAX; 12];
53///
54/// let num = core::mem::size_of::<[u32; 12]>();
55/// let ptr1 = buf1.as_ptr().cast();
56/// let ptr2 = buf2.as_mut_ptr().cast();
57///
58/// unsafe {
59/// nstd_core_mem_copy(ptr2, ptr1, num);
60/// assert!(nstd_core_mem_compare(ptr1, ptr2, num) == NSTD_TRUE);
61/// }
62/// ```
63#[nstdapi]
64#[allow(unused_mut)]
65pub unsafe fn nstd_core_mem_compare(
66 mut buf1: *const NSTDByte,
67 mut buf2: *const NSTDByte,
68 num: NSTDUInt,
69) -> NSTDBool {
70 cfg_if! {
71 if #[cfg(all(
72 any(
73 unix,
74 windows,
75 any(target_env = "wasi", target_os = "wasi"),
76 target_os = "solid_asp3",
77 target_os = "teeos"
78 ),
79 feature = "libc"
80 ))] {
81 libc::memcmp(buf1.cast(), buf2.cast(), num) == 0
82 } else {
83 use crate::{NSTD_FALSE, NSTD_TRUE};
84 if buf1 == buf2 || num == 0 {
85 return NSTD_TRUE;
86 }
87 let mut i = 0;
88 #[allow(clippy::arithmetic_side_effects)]
89 while i < num {
90 if *buf1 != *buf2 {
91 return NSTD_FALSE;
92 }
93 buf1 = buf1.add(1);
94 buf2 = buf2.add(1);
95 i += 1;
96 }
97 NSTD_TRUE
98 }
99 }
100}
101
102/// Iterates through each byte in a raw memory buffer until `delim` is reached, returning a pointer
103/// to the delimiter byte if it is found.
104///
105/// # Parameters:
106///
107/// - `const NSTDByte *buf` - The memory buffer to search.
108///
109/// - `NSTDUInt size` - The number of bytes to search.
110///
111/// - `NSTDByte delim` - The delimiter byte.
112///
113/// # Returns
114///
115/// `const NSTDByte *delim_ptr` - A pointer to the delimiter byte, or null if it was not found.
116///
117/// # Safety
118///
119/// This operation makes access to raw pointer data, leading to undefined behavior if `buf`'s
120/// data is invalid.
121///
122/// # Example
123///
124/// ```
125/// use nstd_sys::core::mem::nstd_core_mem_search;
126///
127/// let buffer = b"Hello, world!\0";
128/// let ptr = buffer.as_ptr().cast();
129/// unsafe {
130/// assert!(nstd_core_mem_search(ptr, buffer.len(), b'H') == ptr);
131/// assert!(nstd_core_mem_search(ptr, buffer.len(), b' ') == ptr.add(6));
132/// assert!(nstd_core_mem_search(ptr, buffer.len(), 0) == ptr.add(13));
133/// }
134/// ```
135#[nstdapi]
136#[allow(unused_mut, clippy::missing_const_for_fn)]
137pub unsafe fn nstd_core_mem_search(
138 mut buf: *const NSTDByte,
139 size: NSTDUInt,
140 delim: NSTDByte,
141) -> *const NSTDByte {
142 cfg_if! {
143 if #[cfg(all(
144 any(
145 unix,
146 windows,
147 any(target_env = "wasi", target_os = "wasi"),
148 target_os = "solid_asp3",
149 target_os = "teeos"
150 ),
151 feature = "libc"
152 ))] {
153 libc::memchr(buf.cast(), delim.into(), size) as _
154 } else {
155 let mut i = 0;
156 #[allow(clippy::arithmetic_side_effects)]
157 while i < size {
158 if *buf == delim {
159 return buf;
160 }
161 buf = buf.add(1);
162 i += 1;
163 }
164 core::ptr::null()
165 }
166 }
167}
168
169/// Zeros out a memory buffer.
170///
171/// # Parameters:
172///
173/// - `NSTDByte *buf` - A pointer to the first byte in the memory buffer.
174///
175/// - `NSTDUInt size` - The number of bytes to set to 0.
176///
177/// # Safety
178///
179/// The caller must ensure that `buf` is valid for reads of `size` contiguous bytes.
180///
181/// # Example
182///
183/// ```
184/// use nstd_sys::core::mem::nstd_core_mem_zero;
185///
186/// unsafe {
187/// let mut buf = [i32::MAX; 10];
188/// nstd_core_mem_zero(buf.as_mut_ptr().cast(), core::mem::size_of::<i32>() * 10);
189/// assert!(buf == [0i32; 10]);
190/// }
191/// ```
192#[inline]
193#[nstdapi]
194#[allow(unused_mut, clippy::missing_const_for_fn)]
195pub unsafe fn nstd_core_mem_zero(mut buf: *mut NSTDByte, size: NSTDUInt) {
196 cfg_if! {
197 if #[cfg(all(
198 any(
199 unix,
200 windows,
201 any(target_env = "wasi", target_os = "wasi"),
202 target_os = "solid_asp3",
203 target_os = "teeos"
204 ),
205 feature = "libc"
206 ))] {
207 libc::memset(buf.cast(), 0, size);
208 } else {
209 let mut i = 0;
210 #[allow(clippy::arithmetic_side_effects)]
211 while i < size {
212 *buf = 0;
213 buf = buf.add(1);
214 i += 1;
215 }
216 }
217 }
218}
219
220/// Fills the memory buffer `buf` with byte `fill`.
221///
222/// # Parameters:
223///
224/// - `NSTDByte *buf` - The memory buffer to fill.
225///
226/// - `NSTDUInt size` - The size of the memory buffer.
227///
228/// - `NSTDByte fill` - The byte value to fill the memory buffer with.
229///
230/// # Safety
231///
232/// This operation can cause undefined behavior if the caller does not ensure that the memory
233/// buffer is at least `size` bytes in size.
234///
235/// # Example
236///
237/// ```
238/// use nstd_sys::core::mem::nstd_core_mem_fill;
239///
240/// unsafe {
241/// let mut buf = [u8::MAX; 10];
242/// nstd_core_mem_fill(buf.as_mut_ptr(), 10, 0);
243/// assert!(buf == [0u8; 10]);
244/// }
245/// ```
246#[inline]
247#[nstdapi]
248#[allow(unused_mut, clippy::missing_const_for_fn)]
249pub unsafe fn nstd_core_mem_fill(mut buf: *mut NSTDByte, size: NSTDUInt, fill: NSTDByte) {
250 cfg_if! {
251 if #[cfg(all(
252 any(
253 unix,
254 windows,
255 any(target_env = "wasi", target_os = "wasi"),
256 target_os = "solid_asp3",
257 target_os = "teeos"
258 ),
259 feature = "libc"
260 ))] {
261 libc::memset(buf.cast(), fill.into(), size);
262 } else {
263 let mut i = 0;
264 #[allow(clippy::arithmetic_side_effects)]
265 while i < size {
266 *buf = fill;
267 buf = buf.add(1);
268 i += 1;
269 }
270 }
271 }
272}
273
274/// Copies `num` bytes from `src` to `dest`.
275///
276/// # Parameters:
277///
278/// - `NSTDByte *dest` - A pointer to the memory buffer to copy `src`'s bytes to.
279///
280/// - `const NSTDByte *src` - A pointer to the memory buffer to copy from.
281///
282/// - `NSTDUInt num` - The number of bytes to copy from `src` to `dest`.
283///
284/// # Safety
285///
286/// This function is highly unsafe as it does not know how large either of the memory buffers are,
287/// quickly leading to undefined behavior if this function ends up reading or writing past the end
288/// of a buffer.
289///
290/// # Example
291///
292/// ```
293/// use nstd_sys::core::mem::nstd_core_mem_copy;
294///
295/// unsafe {
296/// let buf1 = [0u8; 25];
297/// let mut buf2 = [u8::MAX; 25];
298/// nstd_core_mem_copy(buf2.as_mut_ptr(), buf1.as_ptr(), 25);
299/// assert!(buf1 == buf2);
300/// }
301/// ```
302#[inline]
303#[nstdapi]
304#[allow(clippy::missing_const_for_fn)]
305pub unsafe fn nstd_core_mem_copy(dest: *mut NSTDByte, src: *const NSTDByte, num: NSTDUInt) {
306 core::ptr::copy_nonoverlapping(src, dest, num);
307}
308
309/// Copies `num` bytes from `src` to `dest`. Unlike `nstd_core_mem_copy` this operation can be used
310/// when the two memory buffers overlap.
311///
312/// # Parameters:
313///
314/// - `NSTDByte *dest` - A pointer to the memory buffer to copy `src`'s bytes to.
315///
316/// - `const NSTDByte *src` - A pointer to the memory buffer to copy from.
317///
318/// - `NSTDUInt num` - The number of bytes to copy from `src` to `dest`.
319///
320/// # Safety
321///
322/// This function is highly unsafe as it does not know how large either of the memory buffers are,
323/// quickly leading to undefined behavior if this function ends up reading or writing past the end
324/// of a buffer.
325#[inline]
326#[nstdapi]
327#[allow(clippy::missing_const_for_fn)]
328pub unsafe fn nstd_core_mem_copy_overlapping(
329 dest: *mut NSTDByte,
330 src: *const NSTDByte,
331 num: NSTDUInt,
332) {
333 core::ptr::copy(src, dest, num);
334}
335
336/// Swaps `num` bytes between the memory buffers `x` and `y`.
337///
338/// # Parameters:
339///
340/// - `NSTDByte *x` - A pointer to the first memory buffer.
341///
342/// - `NSTDByte *y` - A pointer to the second memory buffer.
343///
344/// - `NSTDUInt num` - The number of bytes to swap.
345///
346/// # Safety
347///
348/// This function is highly unsafe as it does not know how large either of the memory buffers are,
349/// quickly leading to undefined behavior if this function ends up reading or writing past the end
350/// of a buffer.
351///
352/// # Example
353///
354/// ```
355/// use nstd_sys::core::mem::nstd_core_mem_swap;
356///
357/// unsafe {
358/// let mut buf1 = [0u8; 25];
359/// let mut buf2 = [u8::MAX; 25];
360/// nstd_core_mem_swap(buf1.as_mut_ptr(), buf2.as_mut_ptr(), 25);
361/// assert!(buf1 == [u8::MAX; 25] && buf2 == [0u8; 25]);
362/// }
363/// ```
364#[inline]
365#[nstdapi]
366pub unsafe fn nstd_core_mem_swap(x: *mut NSTDByte, y: *mut NSTDByte, num: NSTDUInt) {
367 core::ptr::swap_nonoverlapping(x, y, num);
368}
369
370/// Creates a new dangling pointer to some immutable memory. The pointer is guaranteed to have valid
371/// alignment for any scalar type.
372///
373/// # Returns
374///
375/// `NSTDAny dangling` - The new dangling raw pointer.
376///
377/// # Example
378///
379/// ```
380/// use nstd_sys::core::{mem::nstd_core_mem_dangling, slice::nstd_core_slice_new};
381///
382/// let slice = unsafe { nstd_core_slice_new(nstd_core_mem_dangling(), 1, 1, 0).unwrap() };
383/// ```
384#[inline]
385#[nstdapi]
386pub const fn nstd_core_mem_dangling() -> NSTDAny {
387 MAX_ALIGN as NSTDAny
388}
389
390/// Creates a new dangling pointer to some mutable memory. The pointer is guaranteed to have valid
391/// alignment for any scalar type.
392///
393/// # Returns
394///
395/// `NSTDAnyMut dangling` - The new dangling raw pointer.
396///
397/// # Example
398///
399/// ```
400/// use nstd_sys::core::{mem::nstd_core_mem_dangling_mut, slice::nstd_core_slice_mut_new};
401///
402/// let slice = unsafe { nstd_core_slice_mut_new(nstd_core_mem_dangling_mut(), 1, 1, 0).unwrap() };
403/// ```
404#[inline]
405#[nstdapi]
406pub const fn nstd_core_mem_dangling_mut() -> NSTDAnyMut {
407 MAX_ALIGN as NSTDAnyMut
408}
409
410/// Returns a pointer that is properly aligned to `align` based on the offset `ptr`.
411///
412/// # Parameters:
413///
414/// - `NSTDAny ptr` - The pointer to align.
415///
416/// - `NSTDUInt align` - The alignment requirement. This must be a power of two.
417///
418/// # Returns
419///
420/// `NSTDAny aligned` - The properly aligned pointer.
421///
422/// # Panics
423///
424/// This operation will panic if `align` is not a power of two or overflow occurs.
425///
426/// # Safety
427///
428/// Both `ptr` and the resulting pointer must be either in bounds or one byte past the end of the
429/// same allocated object.
430///
431/// # Example
432///
433/// ```
434/// use nstd_sys::core::mem::{nstd_core_mem_align, nstd_core_mem_is_aligned};
435///
436/// unsafe {
437/// let ptr = 2 as _;
438/// let aligned = nstd_core_mem_align(ptr, 16);
439/// assert!(nstd_core_mem_is_aligned(aligned, 16));
440/// }
441/// ```
442#[nstdapi]
443#[allow(clippy::arithmetic_side_effects)]
444pub unsafe fn nstd_core_mem_align(ptr: NSTDAny, align: NSTDUInt) -> NSTDAny {
445 assert!(is_power_of_two(align));
446 ((ptr as NSTDUInt)
447 .checked_add(align - 1)
448 .expect("pointer arithmetic should not overflow")
449 & !(align - 1)) as NSTDAny
450}
451
452/// Returns a pointer that is properly aligned to `align` based on the offset `ptr`.
453///
454/// # Parameters:
455///
456/// - `NSTDAnyMut ptr` - The pointer to align.
457///
458/// - `NSTDUInt align` - The alignment requirement. This must be a power of two.
459///
460/// # Returns
461///
462/// `NSTDAnyMut aligned` - The properly aligned pointer.
463///
464/// # Panics
465///
466/// This operation will panic if `align` is not a power of two or overflow occurs.
467///
468/// # Safety
469///
470/// Both `ptr` and the resulting pointer must be either in bounds or one byte past the end of the
471/// same allocated object.
472///
473/// # Example
474///
475/// ```
476/// use nstd_sys::core::mem::{nstd_core_mem_align_mut, nstd_core_mem_is_aligned};
477///
478/// unsafe {
479/// let ptr = 2 as _;
480/// let aligned = nstd_core_mem_align_mut(ptr, 16);
481/// assert!(nstd_core_mem_is_aligned(aligned, 16));
482/// }
483/// ```
484#[inline]
485#[nstdapi]
486pub unsafe fn nstd_core_mem_align_mut(ptr: NSTDAnyMut, align: NSTDUInt) -> NSTDAnyMut {
487 nstd_core_mem_align(ptr, align).cast_mut()
488}
489
490/// Checks if `ptr` is aligned to `align`.
491///
492/// # Parameters:
493///
494/// - `NSTDAny ptr` - The pointer to check.
495///
496/// - `NSTDUInt align` - The alignment to check for. This must be a power of two.
497///
498/// # Returns
499///
500/// `NSTDBool is_aligned` - `NSTD_TRUE` if the pointer is aligned to `align`.
501///
502/// # Panics
503///
504/// This operation will panic if `align` is not a power of two.
505///
506/// # Example
507///
508/// ```
509/// use nstd_sys::{
510/// core::mem::{nstd_core_mem_align, nstd_core_mem_is_aligned},
511/// NSTDAny,
512/// };
513///
514/// unsafe {
515/// let mut a = 1usize as NSTDAny;
516/// a = nstd_core_mem_align(a, 8);
517/// assert!(!nstd_core_mem_is_aligned(a, 16));
518/// a = nstd_core_mem_align(a, 16);
519/// assert!(nstd_core_mem_is_aligned(a, 16));
520/// }
521/// ```
522#[inline]
523#[nstdapi]
524#[allow(clippy::arithmetic_side_effects)]
525pub fn nstd_core_mem_is_aligned(ptr: NSTDAny, align: NSTDUInt) -> NSTDBool {
526 assert!(is_power_of_two(align));
527 ptr as NSTDUInt & (align - 1) == 0
528}