use crate::engine::block::{VolumeShape, VoxelBlock};
use crate::engine::codec::{EndianCodec, decode_slice, encode_slice};
use crate::engine::endian::FileEndian;
use crate::iter::{RegionIter, SlabStepper, SliceStepper, Stepper, TileStepper};
use crate::mode::{M0Interpretation, Voxel};
use crate::{Error, Header, Mode};
use std::borrow::Cow;
use std::io::Read;
type VoxelIter<'a, T> = Box<dyn Iterator<Item = Result<VoxelBlock<T>, Error>> + 'a>;
pub(crate) struct RawRegionIter<'a, R: VoxelSource, S> {
reader: &'a R,
volume_shape: VolumeShape,
stepper: S,
}
impl<'a, R: VoxelSource, S> RawRegionIter<'a, R, S> {
pub(crate) fn new(reader: &'a R, volume_shape: VolumeShape, stepper: S) -> Self {
Self {
reader,
volume_shape,
stepper,
}
}
}
impl<'a, R: VoxelSource, S: Stepper> Iterator for RawRegionIter<'a, R, S> {
type Item = Result<(Vec<u8>, [usize; 3], [usize; 3]), Error>;
fn next(&mut self) -> Option<Self::Item> {
let (offset, shape) = self.stepper.next(self.volume_shape)?;
match self.reader.vs_read_block_bytes(offset, shape) {
Ok(bytes) => Some(Ok((bytes.into_owned(), offset, shape))),
Err(e) => Some(Err(e)),
}
}
}
pub(crate) fn convert_iter<'a, R: VoxelSource, S: Stepper + 'a, T>(
raw: RawRegionIter<'a, R, S>,
mode: Mode,
endian: FileEndian,
nx: usize,
ny: usize,
) -> impl Iterator<Item = Result<VoxelBlock<T>, Error>> + 'a
where
T: crate::engine::convert::ConvertFrom<i8>
+ crate::engine::convert::ConvertFrom<i16>
+ crate::engine::convert::ConvertFrom<u16>
+ crate::engine::convert::ConvertFrom<f32>
+ Voxel,
{
raw.map(move |result| {
let (bytes, offset, shape) = result?;
let data = crate::engine::convert::convert_block::<T>(&bytes, mode, endian, nx, ny)?;
Ok(VoxelBlock {
offset,
shape,
data,
})
})
}
pub struct ConvertReader<'a, R, T> {
inner: &'a R,
_target: core::marker::PhantomData<T>,
}
impl<'a, R: VoxelSource + ReaderCore, T> ConvertReader<'a, R, T>
where
T: Voxel
+ crate::engine::convert::ConvertFrom<i8>
+ crate::engine::convert::ConvertFrom<i16>
+ crate::engine::convert::ConvertFrom<u16>
+ crate::engine::convert::ConvertFrom<f32>,
{
pub fn slices(&self) -> VoxelIter<'_, T> {
let shape = self.inner.shape();
Box::new(convert_iter::<_, SliceStepper, T>(
RawRegionIter::new(self.inner, shape, SliceStepper::new()),
self.inner.mode(),
self.inner.endian(),
shape.nx,
shape.ny,
))
}
pub fn slabs(&self, k: usize) -> VoxelIter<'_, T> {
let shape = self.inner.shape();
Box::new(convert_iter::<_, SlabStepper, T>(
RawRegionIter::new(self.inner, shape, SlabStepper::new(k)),
self.inner.mode(),
self.inner.endian(),
shape.nx,
shape.ny,
))
}
pub fn tiles(&self, tile_shape: [usize; 3]) -> VoxelIter<'_, T> {
let shape = self.inner.shape();
Box::new(convert_iter::<_, TileStepper, T>(
RawRegionIter::new(self.inner, shape, TileStepper::new(tile_shape)),
self.inner.mode(),
self.inner.endian(),
shape.nx,
shape.ny,
))
}
pub fn subregion(&self, offset: [usize; 3], shape: [usize; 3]) -> Result<VoxelBlock<T>, Error> {
let bytes = self.inner.vs_read_block_bytes(offset, shape)?;
let s = self.inner.shape();
let data = crate::engine::convert::convert_block::<T>(
&bytes,
self.inner.mode(),
self.inner.endian(),
s.nx,
s.ny,
)?;
Ok(VoxelBlock {
offset,
shape,
data,
})
}
pub fn read_volume(&self) -> Result<VoxelBlock<T>, Error> {
let s = self.inner.shape();
let block_shape = [s.nx, s.ny, s.nz];
self.subregion([0, 0, 0], block_shape)
}
}
macro_rules! impl_convert_method {
($ty:ty) => {
impl $ty {
pub fn convert<T>(&self) -> ConvertReader<'_, $ty, T>
where
T: Voxel
+ crate::engine::convert::ConvertFrom<i8>
+ crate::engine::convert::ConvertFrom<i16>
+ crate::engine::convert::ConvertFrom<u16>
+ crate::engine::convert::ConvertFrom<f32>,
{
ConvertReader {
inner: self,
_target: core::marker::PhantomData,
}
}
}
};
}
mod private {
pub trait Sealed {}
}
#[doc(hidden)]
pub trait VoxelSource: private::Sealed {
fn vs_read_block_bytes<'a>(
&'a self,
offset: [usize; 3],
shape: [usize; 3],
) -> Result<Cow<'a, [u8]>, Error>;
fn vs_decode_block<T: Voxel>(&self, bytes: &[u8]) -> Result<Vec<T>, Error>;
}
#[doc(hidden)]
#[allow(dead_code)]
pub trait ReaderCore: VoxelSource {
fn shape(&self) -> VolumeShape;
fn mode(&self) -> Mode;
fn endian(&self) -> FileEndian;
fn header(&self) -> &Header;
fn ext_header_bytes(&self) -> &[u8];
}
macro_rules! impl_inherent_reader_methods {
($ty:ty) => {
impl $ty {
pub fn slices<T: Voxel>(&self) -> RegionIter<'_, T, $ty, SliceStepper> {
RegionIter::with_stepper(self, self.shape(), SliceStepper::new())
}
pub fn slabs<T: Voxel>(&self, k: usize) -> RegionIter<'_, T, $ty, SlabStepper> {
RegionIter::with_stepper(self, self.shape(), SlabStepper::new(k))
}
pub fn tiles<T: Voxel>(
&self,
tile_shape: [usize; 3],
) -> RegionIter<'_, T, $ty, TileStepper> {
RegionIter::with_stepper(self, self.shape(), TileStepper::new(tile_shape))
}
pub fn volumes<T: Voxel>(&self) -> Result<RegionIter<'_, T, $ty, SlabStepper>, Error> {
let mz = self.header().mz.max(0) as usize;
if !self.header().is_volume_stack() || mz == 0 {
return Err(Error::NotAVolumeStack {
ispg: self.header().ispg,
mz: self.header().mz,
});
}
Ok(self.slabs(mz))
}
pub fn subregion<T: Voxel>(
&self,
offset: [usize; 3],
shape: [usize; 3],
) -> Result<VoxelBlock<T>, Error> {
let bytes = self.vs_read_block_bytes(offset, shape)?;
let data = self.vs_decode_block::<T>(&bytes)?;
Ok(VoxelBlock {
offset,
shape,
data,
})
}
pub fn read_volume<T: Voxel>(&self) -> Result<VoxelBlock<T>, Error> {
let s = self.shape();
self.subregion([0, 0, 0], [s.nx, s.ny, s.nz])
}
pub fn read_volume_u8(&self) -> Result<VoxelBlock<u8>, Error> {
if self.mode() != Mode::Packed4Bit {
return Err(Error::ModeMismatch {
file_mode: self.mode(),
requested_mode: Mode::Packed4Bit,
});
}
let shape = self.shape();
let block_shape = [shape.nx, shape.ny, shape.nz];
let bytes = self.vs_read_block_bytes([0, 0, 0], block_shape)?;
let data = crate::engine::convert::unpack_u4_bytes_to_u8(
&bytes,
shape.nx,
shape.ny * shape.nz,
);
Ok(VoxelBlock {
offset: [0, 0, 0],
shape: block_shape,
data,
})
}
pub fn slices_u8(&self) -> VoxelIter<'_, u8> {
if self.mode() == Mode::Packed4Bit {
let shape = self.shape();
let nx = shape.nx;
let ny = shape.ny;
let nz = shape.nz;
return Box::new((0..nz).map(move |z| {
let bytes = self.vs_read_block_bytes([0, 0, z], [nx, ny, 1])?;
let data = crate::engine::convert::unpack_u4_bytes_to_u8(&bytes, nx, ny);
Ok(VoxelBlock {
offset: [0, 0, z],
shape: [nx, ny, 1],
data,
})
}));
}
if self.mode() != Mode::Uint16 {
return Box::new(std::iter::once(Err(Error::ModeMismatch {
file_mode: self.mode(),
requested_mode: Mode::Uint16,
})));
}
Box::new(self.slices::<u16>().map(|b| {
let b = b?;
Ok(VoxelBlock {
offset: b.offset,
shape: b.shape,
data: crate::engine::convert::convert_u16_slice_to_u8(&b.data)?,
})
}))
}
pub fn slabs_u8(&self, k: usize) -> VoxelIter<'_, u8> {
if self.mode() == Mode::Packed4Bit {
let volume_shape = self.shape();
let nx = volume_shape.nx;
let ny = volume_shape.ny;
let k = k.max(1);
let mut z = 0usize;
return Box::new(std::iter::from_fn(move || {
if z >= volume_shape.nz {
return None;
}
let start = z;
let sz = k.min(volume_shape.nz - z);
z += sz;
let bytes = match self.vs_read_block_bytes([0, 0, start], [nx, ny, sz]) {
Ok(b) => b,
Err(e) => return Some(Err(e)),
};
let data =
crate::engine::convert::unpack_u4_bytes_to_u8(&bytes, nx, ny * sz);
Some(Ok(VoxelBlock {
offset: [0, 0, start],
shape: [nx, ny, sz],
data,
}))
}));
}
if self.mode() != Mode::Uint16 {
return Box::new(std::iter::once(Err(Error::ModeMismatch {
file_mode: self.mode(),
requested_mode: Mode::Uint16,
})));
}
let k = k.max(1);
Box::new(self.slabs::<u16>(k).map(|b| {
let b = b?;
Ok(VoxelBlock {
offset: b.offset,
shape: b.shape,
data: crate::engine::convert::convert_u16_slice_to_u8(&b.data)?,
})
}))
}
pub fn slices_mode0(&self, interp: M0Interpretation) -> VoxelIter<'_, f32> {
if self.mode() != Mode::Int8 {
return Box::new(std::iter::once(Err(Error::ModeMismatch {
file_mode: self.mode(),
requested_mode: Mode::Int8,
})));
}
let volume_shape = self.shape();
Box::new((0..volume_shape.nz).map(move |z| {
let bytes =
self.vs_read_block_bytes([0, 0, z], [volume_shape.nx, volume_shape.ny, 1])?;
let data = crate::engine::convert::reinterpret_m0(&bytes, interp);
Ok(VoxelBlock {
offset: [0, 0, z],
shape: [volume_shape.nx, volume_shape.ny, 1],
data,
})
}))
}
pub fn slabs_mode0(&self, k: usize, interp: M0Interpretation) -> VoxelIter<'_, f32> {
if self.mode() != Mode::Int8 {
return Box::new(std::iter::once(Err(Error::ModeMismatch {
file_mode: self.mode(),
requested_mode: Mode::Int8,
})));
}
let volume_shape = self.shape();
let k = k.max(1);
let mut z = 0usize;
Box::new(std::iter::from_fn(move || {
if z >= volume_shape.nz {
return None;
}
let start = z;
let sz = k.min(volume_shape.nz - z);
z += sz;
let bytes = match self
.vs_read_block_bytes([0, 0, start], [volume_shape.nx, volume_shape.ny, sz])
{
Ok(b) => b,
Err(e) => return Some(Err(e)),
};
let data = crate::engine::convert::reinterpret_m0(&bytes, interp);
Some(Ok(VoxelBlock {
offset: [0, 0, start],
shape: [volume_shape.nx, volume_shape.ny, sz],
data,
}))
}))
}
}
};
}
impl_inherent_reader_methods!(crate::Reader);
impl_convert_method!(crate::Reader);
#[cfg(feature = "mmap")]
impl_inherent_reader_methods!(crate::MmapReader);
#[cfg(feature = "mmap")]
impl_convert_method!(crate::MmapReader);
pub(crate) fn validate_block_bounds(
volume_shape: VolumeShape,
mode: Mode,
data_len: usize,
offset: [usize; 3],
block_shape: [usize; 3],
) -> Result<usize, Error> {
let [nx, ny, nz] = [volume_shape.nx, volume_shape.ny, volume_shape.nz];
let [ox, oy, oz] = offset;
let [sx, sy, sz] = block_shape;
if ox.checked_add(sx).is_none_or(|end| end > nx)
|| oy.checked_add(sy).is_none_or(|end| end > ny)
|| oz.checked_add(sz).is_none_or(|end| end > nz)
{
return Err(Error::BoundsError);
}
let count = sx
.checked_mul(sy)
.and_then(|v| v.checked_mul(sz))
.ok_or(Error::BoundsError)?;
let block_row_bytes = sx.div_ceil(2);
let byte_len = if mode == Mode::Packed4Bit {
block_row_bytes
.checked_mul(sy)
.and_then(|v| v.checked_mul(sz))
.ok_or(Error::BoundsError)?
} else {
mode.byte_size_for_count(count)
};
if count == 0 {
return Ok(0);
}
if mode == Mode::Packed4Bit {
if ox % 2 != 0 {
return Err(Error::BoundsError);
}
let vol_row_bytes = nx.div_ceil(2);
let start_byte_in_row = ox / 2;
let last_vol_row = (oz + sz - 1) * ny + (oy + sy - 1);
let last_byte = last_vol_row
.checked_mul(vol_row_bytes)
.and_then(|b| b.checked_add(start_byte_in_row + block_row_bytes))
.ok_or(Error::BoundsError)?;
if last_byte > data_len {
return Err(Error::BoundsError);
}
} else {
let last_row_start = volume_shape
.checked_linear_index([ox, oy + sy - 1, oz + sz - 1])
.ok_or(Error::BoundsError)?;
let last_byte = last_row_start
.checked_add(sx)
.map(|end| mode.byte_size_for_count(end))
.ok_or(Error::BoundsError)?;
if last_byte > data_len {
return Err(Error::BoundsError);
}
}
Ok(byte_len)
}
pub(crate) fn gather_block_bytes(
data: &[u8],
volume_shape: VolumeShape,
mode: Mode,
offset: [usize; 3],
block_shape: [usize; 3],
) -> Vec<u8> {
let [nx, ny, _nz] = [volume_shape.nx, volume_shape.ny, volume_shape.nz];
let [ox, oy, oz] = offset;
let [sx, sy, sz] = block_shape;
if mode == Mode::Packed4Bit {
let vol_row_bytes = nx.div_ceil(2);
let block_row_bytes = sx.div_ceil(2);
let byte_len = block_row_bytes * sy * sz;
let mut dst = vec![0u8; byte_len];
if ox == 0 && sx == nx && oy == 0 && sy == ny {
let slice_bytes = ny * vol_row_bytes;
let start = oz * slice_bytes;
let byte_len = sz * slice_bytes;
return data[start..start + byte_len].to_vec();
}
for z in 0..sz {
for y in 0..sy {
let vol_row = (oz + z) * ny + (oy + y);
let src_start = vol_row * vol_row_bytes + ox / 2;
let dst_start = (y + z * sy) * block_row_bytes;
dst[dst_start..dst_start + block_row_bytes]
.copy_from_slice(&data[src_start..src_start + block_row_bytes]);
}
}
return dst;
}
let b = mode.byte_size();
let voxel_count = sx * sy * sz;
let byte_len = voxel_count * b;
let mut dst = vec![0u8; byte_len];
if ox == 0 && sx == nx && oy == 0 && sy == ny {
let linear = oz * nx * ny;
let start = linear * b;
let byte_len = sx * sy * sz * b;
return data[start..start + byte_len].to_vec();
}
for z in 0..sz {
for y in 0..sy {
let src_linear = ox + (oy + y) * nx + (oz + z) * nx * ny;
let src_start = src_linear * b;
let dst_linear = y * sx + z * sx * sy;
let dst_start = dst_linear * b;
dst[dst_start..dst_start + sx * b]
.copy_from_slice(&data[src_start..src_start + sx * b]);
}
}
dst
}
pub(crate) fn encode_block_to_buf<T: EndianCodec + Sync>(
block: &VoxelBlock<T>,
volume_shape: VolumeShape,
bytes_per_voxel: usize,
file_endian: FileEndian,
data_offset: usize,
buf: &mut [u8],
) -> Result<(), Error> {
let [nx, ny, _nz] = [volume_shape.nx, volume_shape.ny, volume_shape.nz];
let [ox, oy, oz] = block.offset;
let [sx, sy, sz] = block.shape;
let b = bytes_per_voxel;
if ox == 0 && sx == nx && oy == 0 && sy == ny {
let linear = oz * nx * ny;
let start_byte = data_offset + linear * b;
let byte_len = sx * sy * sz * b;
let end_byte = start_byte + byte_len;
if end_byte > buf.len() {
return Err(Error::BoundsError);
}
encode_slice(&block.data, &mut buf[start_byte..end_byte], file_endian)?;
return Ok(());
}
for z in 0..sz {
for y in 0..sy {
let file_linear = ox + (oy + y) * nx + (oz + z) * nx * ny;
let file_start = data_offset + file_linear * b;
let block_idx = y * sx + z * sx * sy;
if block_idx + sx > block.data.len() {
return Err(Error::BoundsError);
}
let row_values = &block.data[block_idx..block_idx + sx];
let row_end = file_start + sx * b;
if row_end > buf.len() {
return Err(Error::BoundsError);
}
encode_slice(row_values, &mut buf[file_start..row_end], file_endian)?;
}
}
Ok(())
}
pub(crate) fn write_block_bytes(
packed: &[u8],
volume_shape: VolumeShape,
block_offset: [usize; 3],
block_shape: [usize; 3],
data_offset: usize,
buf: &mut [u8],
) -> Result<(), Error> {
let [nx, ny, _nz] = [volume_shape.nx, volume_shape.ny, volume_shape.nz];
let [ox, oy, oz] = block_offset;
let [sx, sy, sz] = block_shape;
let file_row_bytes = nx.div_ceil(2); let block_row_bytes = sx.div_ceil(2);
assert!(ox == 0, "write_block_bytes requires ox == 0");
if sx == nx && oy == 0 && sy == ny {
let slice_bytes = ny * file_row_bytes;
let start_byte = data_offset + oz * slice_bytes;
let byte_len = sz * slice_bytes;
let end_byte = start_byte + byte_len;
if end_byte > buf.len() {
return Err(Error::BoundsError);
}
buf[start_byte..end_byte].copy_from_slice(&packed[..byte_len]);
return Ok(());
}
for z in 0..sz {
for y in 0..sy {
let vol_row = (oz + z) * ny + (oy + y);
let file_start = data_offset + vol_row * file_row_bytes;
let file_end = file_start + block_row_bytes;
if file_end > buf.len() {
return Err(Error::BoundsError);
}
let packed_start = (y + z * sy) * block_row_bytes;
let packed_end = packed_start + block_row_bytes;
if packed_end > packed.len() {
return Err(Error::BoundsError);
}
buf[file_start..file_end].copy_from_slice(&packed[packed_start..packed_end]);
}
}
Ok(())
}
pub(crate) fn decode_block<T: Voxel>(
bytes: &[u8],
file_mode: Mode,
endian: FileEndian,
) -> Result<Vec<T>, Error> {
if T::MODE != file_mode {
return Err(Error::ModeMismatch {
file_mode,
requested_mode: T::MODE,
});
}
if endian == FileEndian::native() {
decode_native_endian(bytes)
} else {
decode_slice(bytes, endian)
}
}
pub(crate) fn decode_native_endian<T: EndianCodec + Copy>(bytes: &[u8]) -> Result<Vec<T>, Error> {
let n = bytes.len() / T::BYTE_SIZE;
debug_assert_eq!(
bytes.len() % T::BYTE_SIZE,
0,
"decode_native_endian: bytes.len() ({}) must be a multiple of T::BYTE_SIZE ({})",
bytes.len(),
T::BYTE_SIZE
);
let mut result = Vec::with_capacity(n);
unsafe {
core::ptr::copy_nonoverlapping(bytes.as_ptr(), result.as_mut_ptr() as *mut u8, bytes.len());
result.set_len(n);
}
Ok(result)
}
pub(crate) fn parse_header(
header_bytes: &[u8; 1024],
permissive: bool,
) -> Result<(crate::Header, Vec<String>, crate::FileEndian, usize), crate::Error> {
let (header, endian_warning) = crate::Header::decode_from_bytes_with_info(header_bytes);
let mut warnings = if permissive {
header
.validate_permissive()
.map_err(crate::Error::InvalidHeaderDetailed)?
} else {
header
.validate_detailed()
.map_err(crate::Error::InvalidHeaderDetailed)?;
Vec::new()
};
if let Some(msg) = endian_warning {
warnings.push(msg.to_string());
}
let data_size = header.data_size().ok_or(crate::Error::InvalidHeader)?;
let endian = header.detect_endian();
Ok((header, warnings, endian, data_size))
}
pub const DEFAULT_MAX_DECOMPRESSED_BYTES: u64 = 256 * 1024 * 1024 * 1024;
pub(crate) struct DecompressedMrc {
pub header: crate::Header,
pub ext_header: Vec<u8>,
pub data: Vec<u8>,
pub warnings: Vec<String>,
pub endian: crate::FileEndian,
pub shape: VolumeShape,
}
pub(crate) fn open_compressed<D: std::io::Read>(
mut decoder: D,
permissive: bool,
max_bytes: u64,
) -> Result<DecompressedMrc, crate::Error> {
let limit = max_bytes + 1;
let mut buf = Vec::with_capacity(limit.min(1024 * 1024) as usize);
decoder.by_ref().take(limit).read_to_end(&mut buf)?;
if buf.len() > max_bytes as usize {
return Err(crate::Error::Io(std::io::Error::other(format!(
"Decompressed data exceeds safety limit of {max_bytes} bytes \
({} GiB). Refusing to allocate. \
Use Reader::open_gzip_with_limit() or Reader::open_bzip2_with_limit() \
with a larger max_bytes if you trust this file.",
max_bytes / (1024 * 1024 * 1024),
))));
}
if buf.len() < 1024 {
return Err(crate::Error::InvalidHeader);
}
let mut header_bytes = [0u8; 1024];
header_bytes.copy_from_slice(&buf[..1024]);
let (header, mut warnings, endian, data_size) = parse_header(&header_bytes, permissive)?;
let ext_size = header.nsymbt as usize;
if !permissive {
if buf.len() != 1024 + ext_size + data_size {
return Err(crate::Error::FileSizeMismatch {
expected: 1024 + ext_size + data_size,
actual: buf.len(),
});
}
} else if buf.len() != 1024 + ext_size + data_size {
warnings.push(format!(
"File size mismatch: expected {} bytes, got {}",
1024 + ext_size + data_size,
buf.len()
));
}
let ext_end = (1024 + ext_size).min(buf.len());
let ext_header = buf[1024..ext_end].to_vec();
let data = if ext_end < buf.len() {
buf[ext_end..].to_vec()
} else {
Vec::new()
};
let shape = VolumeShape::new(header.nx as usize, header.ny as usize, header.nz as usize);
Ok(DecompressedMrc {
header,
ext_header,
data,
warnings,
endian,
shape,
})
}
impl private::Sealed for crate::Reader {}
impl VoxelSource for crate::Reader {
fn vs_read_block_bytes<'a>(
&'a self,
offset: [usize; 3],
shape: [usize; 3],
) -> Result<Cow<'a, [u8]>, Error> {
self.read_block_bytes(offset, shape).map(Cow::Owned)
}
fn vs_decode_block<T: Voxel>(&self, bytes: &[u8]) -> Result<Vec<T>, Error> {
self.decode_block(bytes)
}
}
impl ReaderCore for crate::Reader {
fn shape(&self) -> VolumeShape {
self.shape()
}
fn mode(&self) -> Mode {
self.mode()
}
fn endian(&self) -> FileEndian {
self.endian
}
fn header(&self) -> &Header {
&self.header
}
fn ext_header_bytes(&self) -> &[u8] {
&self.ext_header
}
}
#[cfg(feature = "mmap")]
impl private::Sealed for crate::MmapReader {}
#[cfg(feature = "mmap")]
impl VoxelSource for crate::MmapReader {
fn vs_read_block_bytes<'a>(
&'a self,
offset: [usize; 3],
shape: [usize; 3],
) -> Result<Cow<'a, [u8]>, Error> {
self.read_block_bytes_cow(offset, shape)
}
fn vs_decode_block<T: Voxel>(&self, bytes: &[u8]) -> Result<Vec<T>, Error> {
self.decode_block(bytes)
}
}
#[cfg(feature = "mmap")]
impl ReaderCore for crate::MmapReader {
fn shape(&self) -> VolumeShape {
self.shape()
}
fn mode(&self) -> Mode {
self.mode()
}
fn endian(&self) -> FileEndian {
self.endian()
}
fn header(&self) -> &Header {
self.header()
}
fn ext_header_bytes(&self) -> &[u8] {
self.ext_header_bytes()
}
}