typed_shmem 0.3.0

Typed shared memory crate for *nix and Windows.
Documentation
use std::convert::TryFrom;

use winapi::{
    ctypes::c_void,
    um::{memoryapi::UnmapViewOfFile, winnt::HANDLE},
};
use zerocopy::{AsBytes, FromBytes};

use crate::{common::ShMemOps, error::ShMemErr, ShMemCfg};

#[allow(dead_code)]
pub(super) struct ShObj<T>
where
    T: AsBytes + FromBytes + Default,
{
    data: *mut T,
    handle: HANDLE,
    file_name: Vec<u16>,
    owner: bool,
}

impl<T> TryFrom<ShMemCfg<T>> for ShObj<T>
where
    T: AsBytes + FromBytes + Default,
{
    type Error = ShMemErr;

    fn try_from(value: ShMemCfg<T>) -> Result<Self, Self::Error> {
        let mut name_utf16 = value.file_name.encode_utf16().collect::<Vec<u16>>();

        let file_map_handle = if value.owner {
            windows_fn::create_handle::<T>(name_utf16.as_mut_ptr())?
        } else {
            windows_fn::open_handle(name_utf16.as_mut_ptr())?
        };

        let data_ptr = windows_fn::map_file_view::<T>(file_map_handle)? as *mut T;

        if value.owner {
            let init = match value.init_value {
                Some(v) => v,
                None => T::default(),
            };

            unsafe {
                *data_ptr = init;
            }
        }

        Ok(ShObj {
            data: data_ptr,
            handle: file_map_handle,
            file_name: name_utf16,
            owner: value.owner,
        })
    }
}

impl<T> Drop for ShObj<T>
where
    T: AsBytes + FromBytes + Default,
{
    fn drop(&mut self) {
        unsafe {
            UnmapViewOfFile(self.data as *mut c_void);
            windows_fn::close_handle(self.handle);
        }
    }
}

impl<T> ShMemOps<T> for ShObj<T>
where
    T: AsBytes + FromBytes + Default,
{
    unsafe fn get_t(&self) -> &T {
        &(*self.data)
    }

    unsafe fn get_t_mut(&mut self) -> &mut T {
        &mut (*self.data)
    }
}

mod windows_fn {
    use crate::error::{Result, ShMemErr};
    use winapi::{
        ctypes::c_void,
        shared::minwindef::FALSE,
        shared::winerror::ERROR_ALREADY_EXISTS,
        um::{
            errhandlingapi::GetLastError,
            handleapi::{CloseHandle, INVALID_HANDLE_VALUE},
            memoryapi::{CreateFileMappingW, OpenFileMappingW, FILE_MAP_ALL_ACCESS},
            winnt::{HANDLE, PAGE_READWRITE},
        },
    };
    use zerocopy::{AsBytes, FromBytes};

    pub fn create_handle<T>(name: *mut u16) -> Result<HANDLE>
    where
        T: AsBytes + FromBytes + Default,
    {
        let handle = unsafe {
            CreateFileMappingW(
                INVALID_HANDLE_VALUE,
                std::ptr::null_mut(),
                PAGE_READWRITE,
                0,
                std::mem::size_of::<T>() as u32,
                name,
            )
        };

        let error_code = unsafe { GetLastError() };

        if handle.is_null() {
            return Err(ShMemErr::Windows(format!(
                "Creating HANDLE error: {}",
                error_code
            )));
        } else if error_code == ERROR_ALREADY_EXISTS {
            return Err(ShMemErr::Windows("HANDLE already exists!".to_string()));
        };

        Ok(handle)
    }

    pub fn close_handle(handle: HANDLE) {
        unsafe {
            CloseHandle(handle);
        }
    }

    pub fn map_file_view<T>(handle: HANDLE) -> Result<*mut c_void>
    where
        T: AsBytes + FromBytes + Default,
    {
        let map = unsafe {
            winapi::um::memoryapi::MapViewOfFile(
                handle,
                FILE_MAP_ALL_ACCESS,
                0,
                0,
                std::mem::size_of::<T>(),
            )
        };

        if map.is_null() {
            let error_code = unsafe { GetLastError() };
            return Err(ShMemErr::Windows(format!(
                "Mapping HANDLE error: {}",
                error_code
            )));
        }

        Ok(map)
    }

    pub fn open_handle(name: *mut u16) -> Result<HANDLE> {
        let handle = unsafe { OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, name) };

        if handle.is_null() {
            let error_code = unsafe { GetLastError() };
            return Err(ShMemErr::Windows(format!(
                "Opening HANDLE error: {}",
                error_code
            )));
        }

        Ok(handle)
    }
}