use crate::engine::block::{VolumeShape, VoxelBlock};
use crate::engine::endian::FileEndian;
use crate::mode::Voxel;
use crate::{Error, Header, Mode};
use std::vec::Vec;
#[cfg(feature = "mmap")]
#[derive(Debug)]
pub struct MmapReader {
mmap: memmap2::Mmap,
header: Header,
data_offset: usize,
endian: FileEndian,
shape: VolumeShape,
}
#[cfg(feature = "mmap")]
impl MmapReader {
pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
Self::_open(path, false).map(|(r, _)| r)
}
pub fn open_permissive<P: AsRef<std::path::Path>>(
path: P,
) -> Result<(Self, Vec<String>), Error> {
Self::_open(path, true)
}
fn _open<P: AsRef<std::path::Path>>(
path: P,
permissive: bool,
) -> Result<(Self, Vec<String>), Error> {
use std::fs::File;
use std::io::Read;
let mut file = File::open(path)?;
let mut header_bytes = [0u8; 1024];
file.read_exact(&mut header_bytes)?;
let (header, warnings, endian, data_size) =
crate::io::reader_common::parse_header(&header_bytes, permissive)?;
let mmap = unsafe {
memmap2::MmapOptions::new()
.map(&file)
.map_err(|_| Error::Mmap)?
};
let shape = VolumeShape::new(header.nx as usize, header.ny as usize, header.nz as usize);
let expected_size = header
.data_offset()
.checked_add(data_size)
.ok_or(Error::InvalidHeader)?;
if !permissive {
if mmap.len() != expected_size {
return Err(Error::FileSizeMismatch {
expected: expected_size,
actual: mmap.len(),
});
}
} else if mmap.len() < header.data_offset() {
return Err(Error::FileSizeMismatch {
expected: header.data_offset(),
actual: mmap.len(),
});
}
let _mode = Mode::from_i32(header.mode).ok_or(Error::UnsupportedMode)?;
Ok((
Self {
mmap,
header,
data_offset: header.data_offset(),
endian,
shape,
},
warnings,
))
}
pub fn shape(&self) -> VolumeShape {
self.shape
}
pub fn mode(&self) -> Mode {
Mode::from_i32(self.header.mode).unwrap_or(Mode::Float32)
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn endian(&self) -> FileEndian {
self.endian
}
pub fn ext_header_bytes(&self) -> &[u8] {
let ext_size = self.header.nsymbt as usize;
if ext_size == 0 {
return &[];
}
let end = 1024 + ext_size;
&self.mmap[1024..end]
}
pub fn data_bytes(&self) -> &[u8] {
let data_size = self.header.data_size().unwrap_or(0);
let end = self.data_offset + data_size;
if end > self.mmap.len() {
return &self.mmap[self.data_offset..];
}
&self.mmap[self.data_offset..end]
}
pub fn slab_as<T: Voxel>(&self, z: usize, k: usize) -> Result<&[T], Error> {
if T::MODE != self.mode() {
return Err(Error::ModeMismatch {
file_mode: self.mode(),
requested_mode: T::MODE,
});
}
if !self.endian.is_native() {
return Err(Error::TypeMismatch {
expected: T::BYTE_SIZE,
actual: T::BYTE_SIZE,
});
}
let b = T::BYTE_SIZE;
let [nx, ny, nz] = [self.shape.nx, self.shape.ny, self.shape.nz];
if k == 0 || z + k > nz {
return Err(Error::BoundsError);
}
let linear_start = z * nx * ny;
let byte_start = self.data_offset + linear_start * b;
let count = nx * ny * k;
let byte_end = byte_start + count * b;
if byte_end > self.mmap.len() {
return Err(Error::BoundsError);
}
unsafe {
let ptr = self.mmap.as_ptr().add(byte_start) as *const T;
Ok(core::slice::from_raw_parts(ptr, count))
}
}
pub fn validate_header_stats(&self) -> Result<(), Error> {
crate::engine::stats::validate_header_stats(&self.header, self.data_bytes())
}
pub fn read_block_bytes(
&self,
offset: [usize; 3],
shape: [usize; 3],
) -> Result<Vec<u8>, Error> {
self.read_block_bytes_cow(offset, shape)
.map(|c| c.into_owned())
}
pub(crate) fn read_block_bytes_cow<'a>(
&'a self,
offset: [usize; 3],
shape: [usize; 3],
) -> Result<std::borrow::Cow<'a, [u8]>, Error> {
let [nx, ny, _nz] = [self.shape.nx, self.shape.ny, self.shape.nz];
let [ox, oy, oz] = offset;
let [sx, sy, sz] = shape;
let b = self.mode().byte_size();
let data_len = self.mmap.len().saturating_sub(self.data_offset);
crate::io::reader_common::validate_block_bounds(
self.shape,
self.mode(),
data_len,
offset,
shape,
)?;
if ox == 0 && sx == nx && oy == 0 && sy == ny {
let linear = oz * nx * ny;
let start = self.data_offset + linear * b;
let byte_len = sx * sy * sz * b;
return Ok(std::borrow::Cow::Borrowed(
&self.mmap[start..start + byte_len],
));
}
Ok(std::borrow::Cow::Owned(
crate::io::reader_common::gather_block_bytes(
&self.mmap[self.data_offset..],
self.shape,
self.mode(),
offset,
shape,
),
))
}
pub fn read_block<T: Voxel>(
&self,
offset: [usize; 3],
shape: [usize; 3],
) -> Result<VoxelBlock<T>, Error> {
let bytes = self.read_block_bytes(offset, shape)?;
let data = self.decode_block::<T>(&bytes)?;
Ok(VoxelBlock {
offset,
shape,
data,
})
}
pub(crate) fn decode_block<T: Voxel>(&self, bytes: &[u8]) -> Result<Vec<T>, Error> {
crate::io::reader_common::decode_block(bytes, self.mode(), self.endian)
}
}