polished_memory 1.0.1

Memory management for the Polished OS project.
Documentation
//! # memory
//!
//! This crate provides fundamental memory manipulation routines (`memset`, `memcmp`, `memcpy`, and `memmove`) for use in `no_std` Rust environments, such as kernels, bootloaders, or embedded systems.
//!
//! ## Why is this needed?
//!
//! In `no_std` environments, the Rust standard library is unavailable, including its implementations of essential C library functions like `memset`, `memcmp`, `memcpy`, and `memmove`. However, the Rust compiler and core library expect these functions to exist, as they are often used for low-level memory operations, optimizations, and code generation. If these symbols are missing, linking will fail or runtime errors may occur.
//!
//! By providing these functions with the correct signatures and `#[no_mangle]` attributes, this crate ensures that Rust code (and any C code linked in) can safely and efficiently perform basic memory operations, even in bare-metal or OS development contexts.
//!
//! ## Safety
//!
//! All functions in this crate are `unsafe` and require the caller to uphold strict invariants regarding pointer validity, alignment, and region overlap. See each function's documentation for details.
//!
//! ## Usage
//!
//! Link this crate into your `no_std` project to satisfy the compiler's requirements for these memory routines. You may also use these functions directly if needed.

#![no_std]

use core::ptr;

/// Sets `count` bytes starting at `dest` to the given `value`.
///
/// # Safety
///
/// - `dest` must be valid for writes of `count` bytes.
/// - The memory regions must not overlap with any other references for the duration of this call.
/// - Behavior is undefined if `dest` is null or not properly aligned.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn memset(dest: *mut u8, value: u8, count: usize) {
    let mut ptr = dest;
    for _ in 0..count {
        unsafe { ptr::write(ptr, value) };
        ptr = unsafe { ptr.add(1) };
    }
}

/// Compares the first `n` bytes of the memory areas `s1` and `s2`.
///
/// # Safety
///
/// - Both `s1` and `s2` must be valid for reads of `n` bytes.
/// - The memory regions must not overlap with any mutable references for the duration of this call.
/// - Behavior is undefined if either pointer is null or not properly aligned.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 {
    for i in 0..n {
        let a = unsafe { ptr::read(s1.add(i)) };
        let b = unsafe { ptr::read(s2.add(i)) };
        if a != b {
            return a as i32 - b as i32;
        }
    }
    0
}

/// Copies `count` bytes from `src` to `dest`.
///
/// # Safety
///
/// - Both `dest` and `src` must be valid for reads and writes of `count` bytes.
/// - The memory regions must not overlap with any other references for the duration of this call.
/// - Behavior is undefined if either pointer is null or not properly aligned.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, count: usize) {
    for i in 0..count {
        unsafe { ptr::write(dest.add(i), ptr::read(src.add(i))) };
    }
}

/// Moves `count` bytes from `src` to `dest`, correctly handling overlapping regions.
///
/// # Safety
///
/// - Both `dest` and `src` must be valid for reads and writes of `count` bytes.
/// - The memory regions must not overlap with any other references for the duration of this call.
/// - Behavior is undefined if either pointer is null or not properly aligned.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, count: usize) {
    if dest as usize <= src as usize || dest as usize >= src as usize + count {
        // Non-overlapping regions, can copy forward
        for i in 0..count {
            unsafe { ptr::write(dest.add(i), ptr::read(src.add(i))) };
        }
    } else {
        // Overlapping regions, copy backward
        for i in (0..count).rev() {
            unsafe { ptr::write(dest.add(i), ptr::read(src.add(i))) };
        }
    }
}

#[cfg(test)]
extern crate std;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_memset() {
        let mut buf = [0u8; 8];
        unsafe { memset(buf.as_mut_ptr(), 0xAA, buf.len()) };
        assert!(buf.iter().all(|&b| b == 0xAA));
    }

    #[test]
    fn test_memcmp_equal() {
        let a = [1u8, 2, 3, 4];
        let b = [1u8, 2, 3, 4];
        let res = unsafe { memcmp(a.as_ptr(), b.as_ptr(), a.len()) };
        assert_eq!(res, 0);
    }

    #[test]
    fn test_memcmp_unequal() {
        let a = [1u8, 2, 3, 4];
        let b = [1u8, 2, 4, 4];
        let res = unsafe { memcmp(a.as_ptr(), b.as_ptr(), a.len()) };
        assert!(res < 0);
        let res2 = unsafe { memcmp(b.as_ptr(), a.as_ptr(), a.len()) };
        assert!(res2 > 0);
    }

    #[test]
    fn test_memcpy() {
        let src = [1u8, 2, 3, 4, 5];
        let mut dest = [0u8; 5];
        unsafe { memcpy(dest.as_mut_ptr(), src.as_ptr(), src.len()) };
        assert_eq!(src, dest);
    }

    #[test]
    fn test_memmove_nonoverlapping() {
        let src = [10u8, 20, 30, 40];
        let mut dest = [0u8; 4];
        unsafe { memmove(dest.as_mut_ptr(), src.as_ptr(), src.len()) };
        assert_eq!(src, dest);
    }

    #[test]
    fn test_memmove_overlapping_forward() {
        // Move bytes forward in the same buffer
        let mut buf = [1u8, 2, 3, 4, 5];
        let src = buf.as_ptr();
        let dest = unsafe { buf.as_mut_ptr().add(2) };
        unsafe { memmove(dest, src, 3) };
        assert_eq!(buf, [1, 2, 1, 2, 3]);
    }

    #[test]
    fn test_memmove_overlapping_backward() {
        // Move bytes backward in the same buffer
        let mut buf = [1u8, 2, 3, 4, 5];
        let src = unsafe { buf.as_ptr().add(2) };
        let dest = buf.as_mut_ptr();
        unsafe { memmove(dest, src, 3) };
        assert_eq!(buf, [3, 4, 5, 4, 5]);
    }
}