use crate::engine::block::VolumeShape;
use crate::engine::endian::FileEndian;
use crate::mode::Voxel;
use crate::{Error, Header, Mode};
use std::vec::Vec;
#[derive(Debug)]
pub struct Reader {
pub(crate) header: Header,
pub(crate) ext_header: Vec<u8>,
pub(crate) data: Vec<u8>,
pub(crate) endian: FileEndian,
pub(crate) shape: VolumeShape,
}
impl Reader {
pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
match crate::io::reader::detect_compression(&path)? {
crate::io::reader::CompressionType::Plain => Self::open_plain(path),
#[cfg(feature = "gzip")]
crate::io::reader::CompressionType::Gzip => Self::open_gzip(path),
#[cfg(feature = "bzip2")]
crate::io::reader::CompressionType::Bzip2 => Self::open_bzip2(path),
}
}
pub fn open_plain<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
Self::_open_plain(path, false).map(|(r, _)| r)
}
pub fn open_permissive<P: AsRef<std::path::Path>>(
path: P,
) -> Result<(Self, Vec<String>), Error> {
match crate::io::reader::detect_compression(&path)? {
crate::io::reader::CompressionType::Plain => Self::_open_plain(path, true),
#[cfg(feature = "gzip")]
crate::io::reader::CompressionType::Gzip => Self::open_gzip_permissive(path),
#[cfg(feature = "bzip2")]
crate::io::reader::CompressionType::Bzip2 => Self::open_bzip2_permissive(path),
}
}
fn _open_plain<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 ext_size = header.nsymbt as usize;
let mut ext_header = vec![0u8; ext_size];
if ext_size > 0 {
file.read_exact(&mut ext_header)?;
}
let mut data = vec![0u8; data_size];
file.read_exact(&mut data)?;
if !permissive {
let file_len = file.metadata()?.len() as usize;
let expected_len = header.data_offset() + data_size;
if file_len != expected_len {
return Err(Error::FileSizeMismatch {
expected: expected_len,
actual: file_len,
});
}
}
let shape = VolumeShape::new(header.nx as usize, header.ny as usize, header.nz as usize);
Ok((
Self {
header,
ext_header,
data,
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 read_block_bytes(
&self,
offset: [usize; 3],
shape: [usize; 3],
) -> Result<Vec<u8>, Error> {
crate::io::reader_common::validate_block_bounds(
self.shape,
self.mode(),
self.data.len(),
offset,
shape,
)?;
Ok(crate::io::reader_common::gather_block_bytes(
&self.data,
self.shape,
self.mode(),
offset,
shape,
))
}
pub fn read_block<T: Voxel>(
&self,
offset: [usize; 3],
shape: [usize; 3],
) -> Result<crate::engine::block::VoxelBlock<T>, Error> {
let bytes = self.read_block_bytes(offset, shape)?;
let data = self.decode_block::<T>(&bytes)?;
Ok(crate::engine::block::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)
}
}
impl Reader {
pub fn data_bytes(&self) -> &[u8] {
&self.data
}
pub fn validate_header_stats(&self) -> Result<(), Error> {
crate::engine::stats::validate_header_stats(&self.header, &self.data)
}
pub fn endian(&self) -> FileEndian {
self.endian
}
pub fn ext_header_bytes(&self) -> &[u8] {
&self.ext_header
}
}