vmcircbuffer 0.0.9

Double Mapped Circular Buffer
Documentation
use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;

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

#[derive(Debug)]
pub struct DoubleMappedBufferImpl {
    addr: 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 % item_size != 0 {
            size += ps;
        }

        let tmp = std::env::temp_dir();
        let mut path = PathBuf::new();
        path.push(tmp);
        path.push("buffer-XXXXXX");
        let cstring = CString::new(path.into_os_string().as_bytes()).unwrap();
        let path = cstring.as_bytes_with_nul().as_ptr();

        let fd;
        let buff;
        unsafe {
            fd = libc::mkstemp(path as *mut libc::c_char);
            if fd < 0 {
                return Err(DoubleMappedBufferError::Create);
            }

            let ret = libc::unlink(path.cast::<libc::c_char>());
            if ret < 0 {
                libc::close(fd);
                return Err(DoubleMappedBufferError::Unlink);
            }

            let ret = libc::ftruncate(fd, 2 * size as libc::off_t);
            if ret < 0 {
                libc::close(fd);
                return Err(DoubleMappedBufferError::Truncate);
            }

            buff = libc::mmap(
                std::ptr::null_mut::<libc::c_void>(),
                2 * size,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                fd,
                0,
            );
            if buff == libc::MAP_FAILED {
                libc::close(fd);
                return Err(DoubleMappedBufferError::Placeholder);
            }
            if buff as usize % alignment != 0 {
                libc::close(fd);
                return Err(DoubleMappedBufferError::Alignment);
            }

            let ret = libc::munmap(buff.add(size), size);
            if ret < 0 {
                libc::munmap(buff, size);
                libc::close(fd);
                return Err(DoubleMappedBufferError::UnmapSecond);
            }

            let buff2 = libc::mmap(
                buff.add(size),
                size,
                libc::PROT_READ | libc::PROT_WRITE,
                libc::MAP_SHARED,
                fd,
                0,
            );
            if buff2 != buff.add(size) {
                libc::munmap(buff, size);
                libc::close(fd);
                return Err(DoubleMappedBufferError::MapSecond);
            }

            let ret = libc::ftruncate(fd, size as libc::off_t);
            if ret < 0 {
                libc::munmap(buff, size);
                libc::munmap(buff2, size);
                libc::close(fd);
                return Err(DoubleMappedBufferError::Truncate);
            }

            let ret = libc::close(fd);
            if ret < 0 {
                return Err(DoubleMappedBufferError::Close);
            }
        }

        Ok(DoubleMappedBufferImpl {
            addr: buff 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 {
            libc::munmap(self.addr as *mut libc::c_void, self.size_bytes * 2);
        }
    }
}