use crate::core::error::Error;
use crate::{Path, Result};
#[derive(Default)]
pub struct MappedFile {
path: Path,
inner: Option<MappedInner>,
}
enum MappedInner {
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
Mmap(memmap2::Mmap),
#[allow(dead_code)]
Owned(Vec<u8>),
}
impl MappedFile {
pub fn open_read_only(&mut self, path: &Path) -> Result<()> {
self.close();
self.path = path.clone();
self.inner = Some(Self::map_file(path)?);
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
fn map_file(path: &Path) -> Result<MappedInner> {
let file = std::fs::File::open(std::ops::Deref::deref(path))
.map_err(|e| Error::io("MappedFile::open", e))?;
let mmap =
unsafe { memmap2::Mmap::map(&file) }.map_err(|e| Error::io("MappedFile::mmap", e))?;
Ok(MappedInner::Mmap(mmap))
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
fn map_file(path: &Path) -> Result<MappedInner> {
let data = std::fs::read(path).map_err(|e| Error::io("MappedFile::read", e))?;
Ok(MappedInner::Owned(data))
}
pub fn close(&mut self) {
self.inner = None;
self.path = Path::default();
}
pub fn is_open(&self) -> bool {
self.inner.is_some()
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn bytes(&self) -> &[u8] {
match &self.inner {
None => &[],
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
Some(MappedInner::Mmap(m)) => m,
Some(MappedInner::Owned(v)) => v,
}
}
pub fn size(&self) -> usize {
self.bytes().len()
}
pub fn slice(&self, offset: u64, len: u64) -> Result<&[u8]> {
let data = self.bytes();
let start = offset as usize;
let end = start
.checked_add(len as usize)
.filter(|&e| e <= data.len())
.ok_or_else(|| {
Error::out_of_range(format!(
"MappedFile::slice: [{offset}, {}) out of bounds (size={})",
offset + len,
data.len()
))
})?;
Ok(&data[start..end])
}
}