rushm 0.2.1

Tiny wrapper of POSIX shared memory for Rust
Documentation
use crate::INVALID_FD;
use std::ffi::CString;
use std::marker::PhantomData;
use std::ptr;
use std::slice;

/// POSIX shared memory wrapper for Rust with slicing
pub struct POSIXShmSliced<T, const N: usize> {
    file_path: String,
    fd_shm: i32,
    mem_size: usize,
    ptr_data: *mut libc::c_void,
    phantom: PhantomData<T>,
}

impl<T, const N: usize> POSIXShmSliced<T, N> {
    /// Create a new shared memory object
    /// # Arguments
    /// * `path` - Path to shared memory
    /// * `size` - Size of shared memory
    /// # Example
    /// ```
    /// use rushm::posixslicedaccessor::POSIXShmSliced;
    /// use std::mem;
    /// let shm_name = "test_sliced_shared_memory_file";
    /// let mut sliced_shm = POSIXShmSliced::<u64, 2>::new(shm_name.to_string(), std::mem::size_of::<u64>() * 2);
    /// ```
    pub fn new(path: String, size: usize) -> Self {
        POSIXShmSliced {
            file_path: path,
            fd_shm: INVALID_FD,
            mem_size: size,
            ptr_data: std::ptr::null_mut(),
            phantom: PhantomData,
        }
    }

    /// Open shared memory
    /// # Safety
    /// This function is unsafe because it uses raw pointers
    /// and can cause undefined behavior if misused
    /// # Returns
    /// An empty result if successful, otherwise an error message
    /// # Panics
    /// This function panics if the pointer to shared memory is null
    /// or if the shared memory cannot be truncated
    pub unsafe fn open(&mut self) -> Result<(), String> {
        let c_str = CString::new(self.file_path.to_string()).unwrap();

        self.fd_shm = libc::shm_open(
            c_str.as_ptr(),
            libc::O_CREAT | libc::O_RDWR,
            libc::S_IRUSR | libc::S_IWUSR,
        );

        if self.fd_shm > 0 {
            let ret = libc::ftruncate(self.fd_shm, self.mem_size.try_into().unwrap());
            if ret < 0 {
                return Err(String::from("Cannot truncate"));
            }
        } else {
            let e = libc::__errno_location();

            return Err(String::from(format!(
                "Error opening shared memory {} errno {}",
                self.fd_shm, *e
            )));
        }

        self.ptr_data = libc::mmap(
            ptr::null_mut(),
            self.mem_size,
            libc::PROT_WRITE,
            libc::MAP_SHARED,
            self.fd_shm,
            0,
        );

        if self.ptr_data.is_null() {
            return Err(String::from("Error, mmap failed"));
        }

        Ok(())
    }

    /// Read shared memory
    /// # Safety
    /// This function is unsafe because it uses raw pointers
    /// and can cause undefined behavior if misused
    /// # Returns
    /// A slice of shared memory if successful, otherwise an error message
    /// # Panics
    /// This function panics if the pointer to shared memory is null
    /// or if the shared memory cannot be read
    pub unsafe fn read(&mut self) -> Result<&[T], String> {
        if self.ptr_data.is_null() {
            return Err(String::from("Error. Shared memory destination is NULL"));
        }
        let p = self.ptr_data as *const T;

        let ret = slice::from_raw_parts(p, N);

        Ok(ret)
    }

    /// Get a mutable pointer to shared memory
    /// # Returns
    /// A mutable pointer to shared memory
    pub fn get_cptr_mut(&self) -> *mut libc::c_void {
        self.ptr_data
    }

    /// Get a mutable pointer to shared memory
    /// # Returns
    /// A mutable pointer to shared memory
    pub fn get_as_mut(&self) -> *mut T {
        self.ptr_data as *mut T
    }

    /// Close shared memory
    /// # Arguments
    /// * `unlink` - Unlink shared memory
    /// # Returns
    /// An empty result if successful, otherwise an error message
    /// # Panics
    /// This function panics if the shared memory cannot be unmapped
    /// or if the shared memory cannot be closed
    pub unsafe fn close(&mut self, unlink: bool) -> Result<(), String> {
        let ret = libc::munmap(self.ptr_data, self.mem_size);
        if ret < 0 {
            return Err(String::from("Error unmapping shared memory"));
        }

        let ret = libc::close(self.fd_shm);
        if ret < 0 {
            return Err(String::from("Error closing shared memory"));
        }

        if unlink {
            let c_shm_name = CString::new(self.file_path.to_string()).unwrap();
            let ret = libc::shm_unlink(c_shm_name.as_ptr());
            if ret < 0 {
                return Err(String::from("Error unlinking shared memory"));
            }
        }

        Ok(())
    }
}

#[cfg(test)]
use std::mem;
#[test]
fn test_sliced() {
    unsafe {
        let shm_name = "test_sliced_shared_memory_file";

        const NUM_ELEMENTS: usize = 2;
        const MEM_SIZE: usize = mem::size_of::<u64>() * NUM_ELEMENTS;

        let mut sliced_shm =
            POSIXShmSliced::<u64, NUM_ELEMENTS>::new(shm_name.to_string(), MEM_SIZE);
        let ret = sliced_shm.open();
        assert_eq!(ret.is_ok(), true);

        let data = [1u64, 2u64];
        let ptr_slice: *mut u64 = sliced_shm.get_as_mut();
        for i in 0..NUM_ELEMENTS {
            *ptr_slice.add(i) = data[i];
        }

        let ptr_sliced = sliced_shm.read();
        assert_eq!(ptr_sliced.is_ok(), true);
        let ptr_sliced = ptr_sliced.unwrap();
        for i in 0..NUM_ELEMENTS {
            assert_eq!(ptr_sliced[i], data[i]);
        }

        let ret = sliced_shm.close(true);
        assert_eq!(ret.is_ok(), true);
    }
}