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) mode: Mode,
pub(crate) shape: VolumeShape,
}
impl Reader {
pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
use std::io::{Read, Seek};
let mut file = std::fs::File::open(&path)?;
let mut magic = [0u8; 2];
let n = file.read(&mut magic)?;
if n >= 2 {
match magic {
#[cfg(feature = "gzip")]
[0x1f, 0x8b] => {
let _ = file.seek(std::io::SeekFrom::Start(0));
return Self::_open_gzip_file(
file,
false,
crate::io::reader_common::DEFAULT_MAX_DECOMPRESSED_BYTES,
)
.map(|(r, _)| r);
}
#[cfg(feature = "bzip2")]
[b'B', b'Z'] => {
let _ = file.seek(std::io::SeekFrom::Start(0));
return Self::_open_bzip2_file(
file,
false,
crate::io::reader_common::DEFAULT_MAX_DECOMPRESSED_BYTES,
)
.map(|(r, _)| r);
}
_ => {}
}
}
let _ = file.seek(std::io::SeekFrom::Start(0));
Self::_open_plain_file(file, false).map(|(r, _)| r)
}
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> {
use std::io::{Read, Seek};
let mut file = std::fs::File::open(&path)?;
let mut magic = [0u8; 2];
let n = file.read(&mut magic)?;
if n >= 2 {
match magic {
#[cfg(feature = "gzip")]
[0x1f, 0x8b] => {
let _ = file.seek(std::io::SeekFrom::Start(0));
return Self::_open_gzip_file(
file,
true,
crate::io::reader_common::DEFAULT_MAX_DECOMPRESSED_BYTES,
);
}
#[cfg(feature = "bzip2")]
[b'B', b'Z'] => {
let _ = file.seek(std::io::SeekFrom::Start(0));
return Self::_open_bzip2_file(
file,
true,
crate::io::reader_common::DEFAULT_MAX_DECOMPRESSED_BYTES,
);
}
_ => {}
}
}
let _ = file.seek(std::io::SeekFrom::Start(0));
Self::_open_plain_file(file, true)
}
fn _open_plain_file(
mut file: std::fs::File,
permissive: bool,
) -> Result<(Self, Vec<String>), Error> {
use std::io::Read;
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);
let mode = Mode::from_i32(header.mode).ok_or(Error::UnsupportedMode)?;
let mut warnings = warnings;
if mode == Mode::Int8 {
if let Some(imod) = header.detect_imod() {
if !imod.bytes_are_signed {
warnings.push(
"IMOD file with unsigned Mode 0 detected: use slices_mode0() \
or convert::<f32>() for correct values"
.into(),
);
}
}
}
Ok((
Self {
header,
ext_header,
data,
endian,
mode,
shape,
},
warnings,
))
}
fn _open_plain<P: AsRef<std::path::Path>>(
path: P,
permissive: bool,
) -> Result<(Self, Vec<String>), Error> {
Self::_open_plain_file(std::fs::File::open(path)?, permissive)
}
pub fn shape(&self) -> VolumeShape {
self.shape
}
pub fn mode(&self) -> Mode {
self.mode
}
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,
))
}
#[deprecated(since = "0.2.4", note = "use `subregion` instead")]
pub fn read_block<T: Voxel>(
&self,
offset: [usize; 3],
shape: [usize; 3],
) -> Result<crate::engine::block::VoxelBlock<T>, Error> {
self.subregion(offset, shape)
}
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
}
}