use std::fs::File;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use memmap2::{Mmap, MmapOptions};
use crate::error::{Result, TeraError};
#[derive(Clone)]
pub struct MmapReader {
path: PathBuf,
mmap: Arc<Mmap>,
}
impl MmapReader {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
let path_buf = path.as_ref().to_path_buf();
let file = File::open(&path_buf)?;
let mmap = unsafe {
MmapOptions::new()
.map(&file)
.map_err(|e| TeraError::Mmap(format!("Falló el mapeo de memoria para {:?}: {}", path_buf, e)))?
};
Ok(Self {
path: path_buf,
mmap: Arc::new(mmap),
})
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn len(&self) -> usize {
self.mmap.len()
}
pub fn is_empty(&self) -> bool {
self.mmap.is_empty()
}
pub fn as_slice(&self) -> &[u8] {
&self.mmap[..]
}
pub fn as_slice_range(&self, range: std::ops::Range<usize>) -> Result<&[u8]> {
if range.end > self.mmap.len() || range.start > range.end {
return Err(TeraError::OutOfRange(format!(
"Rango {:?} fuera de los límites del archivo (tamaño total: {} bytes)",
range,
self.mmap.len()
)));
}
Ok(&self.mmap[range])
}
#[cfg(unix)]
pub fn advise_will_need(&self, offset: usize, length: usize) -> Result<()> {
let end = offset.saturating_add(length).min(self.mmap.len());
if offset >= self.mmap.len() {
return Ok(());
}
self.mmap
.advise_range(memmap2::Advice::WillNeed, offset, end - offset)
.map_err(|e| TeraError::Mmap(format!("Error en madvise(WillNeed): {}", e)))
}
#[cfg(not(unix))]
pub fn advise_will_need(&self, _offset: usize, _length: usize) -> Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
#[test]
fn test_mmap_reader_basic() -> Result<()> {
let temp_dir = std::env::temp_dir();
let test_file = temp_dir.join("petaplot_test_mmap.bin");
let sample_data = b"PetaPlot high performance zero-copy time series data engine.";
{
let mut file = File::create(&test_file)?;
file.write_all(sample_data)?;
}
let reader = MmapReader::open(&test_file)?;
assert_eq!(reader.len(), sample_data.len());
assert_eq!(reader.as_slice(), sample_data);
assert_eq!(reader.as_slice_range(0..8)?, b"PetaPlot");
let _ = std::fs::remove_file(&test_file);
Ok(())
}
}