pulith-fs 0.1.0

Cross-platform atomic filesystem primitives
Documentation
use std::alloc::{Layout, alloc, dealloc};
use std::marker::PhantomData;

pub const PAGE_SIZE: usize = 4096;

#[derive(Debug)]
pub struct AlignedBuf {
    ptr: *mut u8,
    layout: Layout,
    _marker: PhantomData<[u8]>,
}

impl AlignedBuf {
    pub fn new(size: usize, align: usize) -> Result<Self, std::io::Error> {
        let layout = Layout::from_size_align(size, align)
            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;

        let ptr = unsafe { alloc(layout) };
        if ptr.is_null() {
            return Err(std::io::Error::new(
                std::io::ErrorKind::OutOfMemory,
                "allocation failed",
            ));
        }

        Ok(Self {
            ptr,
            layout,
            _marker: PhantomData,
        })
    }

    pub fn new_page_aligned(size: usize) -> Result<Self, std::io::Error> {
        Self::new(size, PAGE_SIZE)
    }

    pub fn from_slice(data: &[u8], align: usize) -> Result<Self, std::io::Error> {
        let mut buf = Self::new(data.len(), align)?;
        buf.as_mut_slice().copy_from_slice(data);
        Ok(buf)
    }

    pub fn from_slice_page_aligned(data: &[u8]) -> Result<Self, std::io::Error> {
        Self::from_slice(data, PAGE_SIZE)
    }

    pub fn as_slice(&self) -> &[u8] {
        unsafe { std::slice::from_raw_parts(self.ptr, self.layout.size()) }
    }

    pub fn as_mut_slice(&mut self) -> &mut [u8] {
        unsafe { std::slice::from_raw_parts_mut(self.ptr, self.layout.size()) }
    }

    pub fn as_mut_ptr(&mut self) -> *mut u8 {
        self.ptr
    }

    pub fn as_ptr(&self) -> *const u8 {
        self.ptr
    }

    pub fn len(&self) -> usize {
        self.layout.size()
    }

    pub fn is_empty(&self) -> bool {
        self.layout.size() == 0
    }
}

impl Drop for AlignedBuf {
    fn drop(&mut self) {
        unsafe { dealloc(self.ptr, self.layout) }
    }
}

unsafe impl Send for AlignedBuf {}
unsafe impl Sync for AlignedBuf {}

pub fn align_down(n: usize, align: usize) -> usize {
    n & !(align - 1)
}

pub fn align_up(n: usize, align: usize) -> usize {
    (n + align - 1) & !(align - 1)
}

pub fn is_aligned(n: usize, align: usize) -> bool {
    n & (align - 1) == 0
}

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

    #[test]
    fn test_aligned_buf() {
        let mut buf = AlignedBuf::new(8192, 4096).unwrap();
        assert_eq!(buf.len(), 8192);
        buf.as_mut_slice()[0] = 42;
        assert_eq!(buf.as_slice()[0], 42);
    }

    #[test]
    fn test_from_slice() {
        let data = b"hello world";
        let buf = AlignedBuf::from_slice(data, 4096).unwrap();
        assert_eq!(buf.as_slice(), data);
    }

    #[test]
    fn test_align_functions() {
        assert_eq!(align_down(100, 16), 96);
        assert_eq!(align_up(100, 16), 112);
        assert!(is_aligned(16, 16));
        assert!(!is_aligned(17, 16));
    }
}