use super::Backend;
use crate::error;
use std::cmp;
use std::io;
#[derive(Debug)]
struct Mmap {
inner: memmap::MmapMut,
pub end: usize,
pub len: usize,
}
impl Mmap {
fn new(len: usize) -> io::Result<Self> {
let inner = memmap::MmapOptions::new().len(len).map_anon()?;
Ok(Self { inner, end: 0, len })
}
fn as_slice(&self) -> &[u8] {
&self.inner[..self.end]
}
fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.inner[..self.end]
}
fn write(&mut self, data: &[u8]) -> error::BackendResult<()> {
if data.len() > self.len {
return Err(error::BackendError::Internal(format!(
"Unexpected write beyond mmap's backend capacity."
)));
}
self.end = data.len();
self.as_mut_slice().copy_from_slice(data);
Ok(())
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
fn resize_no_copy(&mut self, new_size: usize) -> io::Result<()> {
let len = cmp::max(self.len + self.len, new_size);
let new_mmap = Self::new(len)?;
*self = new_mmap;
Ok(())
}
}
#[derive(Debug)]
pub struct MmapStorage {
mmap: Mmap,
}
impl MmapStorage {
pub fn new() -> error::BackendResult<Self> {
Self::with_size(1024)
}
pub fn with_size(len: usize) -> error::BackendResult<Self> {
let mmap = Mmap::new(len)?;
Ok(Self { mmap })
}
}
impl Backend for MmapStorage {
fn get_data(&mut self) -> error::BackendResult<Vec<u8>> {
let mmap = self.mmap.as_slice();
let mut buffer = Vec::with_capacity(mmap.len());
buffer.extend_from_slice(mmap);
Ok(buffer)
}
fn put_data(&mut self, data: &[u8]) -> error::BackendResult<()> {
if self.mmap.len < data.len() {
self.mmap.resize_no_copy(data.len())?;
}
self.mmap.write(data)?;
self.mmap.flush()?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{Backend, MmapStorage};
#[test]
#[cfg_attr(miri, ignore)]
fn test_mmap_storage() {
let data = [4, 5, 1, 6, 8, 1];
let mut storage = MmapStorage::new().expect("To crate mmap storage");
storage.put_data(&data).expect("To put data");
assert_eq!(storage.mmap.end, data.len());
assert_eq!(storage.get_data().expect("To get data"), data);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_mmap_storage_extend() {
let data = [4, 5, 1, 6, 8, 1];
let mut storage = MmapStorage::with_size(4).expect("To crate mmap storage");
storage.put_data(&data).expect("To put data");
assert_eq!(storage.mmap.end, data.len());
assert_eq!(storage.mmap.len, 8);
assert_eq!(storage.get_data().expect("To get data"), data);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_mmap_storage_increase_by_new_data_size() {
let data = [4, 5, 1, 6, 8, 1];
let mut storage = MmapStorage::with_size(1).expect("To crate mmap storage");
storage.put_data(&data).expect("To put data");
assert_eq!(storage.mmap.end, data.len());
assert_eq!(storage.mmap.len, data.len());
assert_eq!(storage.get_data().expect("To get data"), data);
}
}