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}