vmcircbuffer 0.0.14

Double Mapped Circular Buffer
Documentation
use winapi::shared::minwindef::DWORD;
use winapi::shared::minwindef::LPCVOID;
use winapi::shared::minwindef::LPVOID;
use winapi::um::handleapi::CloseHandle;
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::memoryapi::MapViewOfFileEx;
use winapi::um::memoryapi::VirtualAlloc;
use winapi::um::memoryapi::VirtualFree;
use winapi::um::winnt::HANDLE;
use winapi::um::winnt::MEM_RELEASE;
use winapi::um::winnt::MEM_RESERVE;
use winapi::um::winnt::PAGE_NOACCESS;
use winapi::um::winnt::PAGE_READWRITE;
use winapi::um::{
    memoryapi::{UnmapViewOfFile, FILE_MAP_WRITE},
    winbase::CreateFileMappingA,
};

use super::pagesize;
use super::DoubleMappedBufferError;

#[derive(Debug)]
pub struct DoubleMappedBufferImpl {
    addr: usize,
    handle: usize,
    size_bytes: usize,
    item_size: usize,
}

impl DoubleMappedBufferImpl {
    pub fn new(
        min_items: usize,
        item_size: usize,
        alignment: usize,
    ) -> Result<Self, DoubleMappedBufferError> {
        for _ in 0..5 {
            let ret = Self::new_try(min_items, item_size, alignment);
            if ret.is_ok() {
                return ret;
            }
        }
        Self::new_try(min_items, item_size, alignment)
    }

    fn new_try(
        min_items: usize,
        item_size: usize,
        alignment: usize,
    ) -> Result<Self, DoubleMappedBufferError> {
        let ps = pagesize();
        let mut size = ps;
        while size < min_items * item_size || !size.is_multiple_of(item_size) {
            size += ps;
        }

        unsafe {
            let handle = CreateFileMappingA(
                INVALID_HANDLE_VALUE,
                std::mem::zeroed(),
                PAGE_READWRITE,
                0,
                size as DWORD,
                std::ptr::null(),
            );

            if handle == INVALID_HANDLE_VALUE || handle == 0 as LPVOID {
                return Err(DoubleMappedBufferError::Placeholder);
            }

            let first_tmp =
                VirtualAlloc(std::ptr::null_mut(), 2 * size, MEM_RESERVE, PAGE_NOACCESS);
            if first_tmp.is_null() {
                CloseHandle(handle);
                return Err(DoubleMappedBufferError::MapFirst);
            }

            let res = VirtualFree(first_tmp, 0, MEM_RELEASE);
            if res == 0 {
                CloseHandle(handle);
                return Err(DoubleMappedBufferError::MapSecond);
            }

            let first_cpy = MapViewOfFileEx(handle, FILE_MAP_WRITE, 0, 0, size, first_tmp);
            if first_tmp != first_cpy {
                CloseHandle(handle);
                return Err(DoubleMappedBufferError::MapFirst);
            }

            if !(first_tmp as usize).is_multiple_of(alignment) {
                CloseHandle(handle);
                return Err(DoubleMappedBufferError::Alignment);
            }

            let first_ptr = (first_tmp as *mut u8).add(size) as LPVOID;
            let second_cpy = MapViewOfFileEx(handle, FILE_MAP_WRITE, 0, 0, size, first_ptr);
            if second_cpy != first_ptr {
                UnmapViewOfFile(first_cpy);
                CloseHandle(handle);
                return Err(DoubleMappedBufferError::MapSecond);
            }

            Ok(DoubleMappedBufferImpl {
                addr: first_tmp as usize,
                handle: handle as usize,
                size_bytes: size,
                item_size,
            })
        }
    }

    pub fn addr(&self) -> usize {
        self.addr
    }

    pub fn capacity(&self) -> usize {
        self.size_bytes / self.item_size
    }
}

impl Drop for DoubleMappedBufferImpl {
    fn drop(&mut self) {
        unsafe {
            UnmapViewOfFile(self.addr as LPCVOID);
            UnmapViewOfFile((self.addr + self.size_bytes) as LPCVOID);
            CloseHandle(self.handle as HANDLE);
        }
    }
}