use byteordered::{ByteOrdered, Endianness};
use flate2::bufread::GzDecoder;
use flate2::Compression;
use ndarray::{array, Array, Array1, Array2, Array4, Dim};
use std::fmt;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::path::Path;
use crate::error::{NeuroformatsError, Result};
use crate::util::{checked_mul_dims, validate_finite_f32_slice};
use crate::config;
const MGH_VERSION_CODE: i32 = 1;
pub const MRI_UCHAR: i32 = 0;
pub const MRI_INT: i32 = 1;
pub const MRI_FLOAT: i32 = 3;
pub const MRI_SHORT: i32 = 4;
const MGH_DATA_START: i32 = 284;
#[derive(Debug, Clone, PartialEq)]
pub struct FsMghHeader {
pub mgh_format_version: i32,
pub dim1len: i32,
pub dim2len: i32,
pub dim3len: i32,
pub dim4len: i32, pub dtype: i32,
pub dof: i32,
pub is_ras_good: i16,
pub delta: [f32; 3],
pub mdc_raw: [f32; 9],
pub p_xyz_c: [f32; 3],
}
#[derive(Debug, Clone, PartialEq)]
pub struct FsMghData {
pub mri_uchar: Option<Array4<u8>>,
pub mri_float: Option<Array4<f32>>,
pub mri_int: Option<Array4<i32>>,
pub mri_short: Option<Array4<i16>>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FsMgh {
pub header: FsMghHeader,
pub data: FsMghData,
}
impl Default for FsMghHeader {
fn default() -> FsMghHeader {
FsMghHeader {
mgh_format_version: 1 as i32,
dim1len: 0 as i32,
dim2len: 0 as i32,
dim3len: 0 as i32,
dim4len: 0 as i32,
dtype: MRI_INT,
dof: 0 as i32,
is_ras_good: 0 as i16,
delta: [0.; 3],
mdc_raw: [0.; 9],
p_xyz_c: [0.; 3],
}
}
}
impl FsMghHeader {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<FsMghHeader> {
let gz = is_mgz_file(&path);
let mut file = BufReader::new(File::open(path)?);
if gz {
FsMghHeader::from_reader(&mut BufReader::new(GzDecoder::new(file)))
} else {
FsMghHeader::from_reader(&mut file)
}
}
pub fn from_reader<S>(input: &mut S) -> Result<FsMghHeader>
where
S: BufRead,
{
let mut hdr = FsMghHeader::default();
let mut input = ByteOrdered::be(input);
hdr.mgh_format_version = input.read_i32()?;
if hdr.mgh_format_version != MGH_VERSION_CODE {
return Err(NeuroformatsError::InvalidFsMghFormat);
}
hdr.dim1len = input.read_i32()?;
hdr.dim2len = input.read_i32()?;
hdr.dim3len = input.read_i32()?;
hdr.dim4len = input.read_i32()?;
hdr.dtype = input.read_i32()?;
hdr.dof = input.read_i32()?;
hdr.is_ras_good = input.read_i16()?;
hdr.delta = [f32::NAN; 3];
hdr.mdc_raw = [f32::NAN; 9];
hdr.p_xyz_c = [f32::NAN; 3];
if hdr.is_ras_good == 1 as i16 {
for idx in 0..3 {
hdr.delta[idx] = input.read_f32()?;
}
for idx in 0..9 {
hdr.mdc_raw[idx] = input.read_f32()?;
}
for idx in 0..3 {
hdr.p_xyz_c[idx] = input.read_f32()?;
}
validate_finite_f32_slice(&hdr.delta, "delta")?;
validate_finite_f32_slice(&hdr.mdc_raw, "mdc_raw")?;
validate_finite_f32_slice(&hdr.p_xyz_c, "p_xyz_c")?;
}
Ok(hdr)
}
pub fn dim(&self) -> [usize; 4] {
[
self.dim1len as usize,
self.dim2len as usize,
self.dim3len as usize,
self.dim4len as usize,
]
}
pub fn vox2ras(&self) -> Result<Array2<f32>> {
if self.is_ras_good != 1 as i16 {
return Err(NeuroformatsError::NoRasInformationInHeader);
}
let mut d: Array2<f32> = Array::zeros((3, 3));
d[[0, 0]] = self.delta[0]; d[[1, 1]] = self.delta[1];
d[[2, 2]] = self.delta[2];
let mdc_mat = Array2::from_shape_vec((3, 3), self.mdc_raw.to_vec()).unwrap();
let mdc_scaled: Array2<f32> = d.dot(&mdc_mat);
let p_crs_c: Array1<f32> = array![
(self.dim1len / 2) as f32,
(self.dim2len / 2) as f32,
(self.dim3len / 2) as f32
];
let p_xyz_c: Array1<f32> = array![self.p_xyz_c[0], self.p_xyz_c[1], self.p_xyz_c[2]];
let p_xyz_0: Array1<f32> = p_xyz_c - (mdc_scaled.t().dot(&p_crs_c));
let mut m: Array2<f32> = Array::zeros((4, 4));
for i in 0..3 {
for j in 0..3 {
m[[i, j]] = mdc_scaled[[i, j]];
}
}
m[[3, 0]] = p_xyz_0[0]; m[[3, 1]] = p_xyz_0[1];
m[[3, 2]] = p_xyz_0[2];
m[[3, 3]] = 1.;
let v2r = m.t().into_owned();
Ok(v2r)
}
}
impl FsMgh {
pub fn from_file<P: AsRef<Path> + Copy>(path: P) -> Result<FsMgh> {
let hdr: FsMghHeader = FsMghHeader::from_file(path)?;
let gz = is_mgz_file(&path);
let mut file = BufReader::new(File::open(path)?);
let data = if gz {
FsMgh::data_from_reader(&mut BufReader::new(GzDecoder::new(file)), &hdr)?
} else {
FsMgh::data_from_reader(&mut file, &hdr)?
};
let mgh = FsMgh {
header: hdr,
data: data,
};
Ok(mgh)
}
pub fn data_from_reader<S>(file: &mut S, hdr: &FsMghHeader) -> Result<FsMghData>
where
S: BufRead,
{
let total_elements =
checked_mul_dims(&[hdr.dim1len, hdr.dim2len, hdr.dim3len, hdr.dim4len])?;
let vol_dim = Dim([
hdr.dim1len as usize,
hdr.dim2len as usize,
hdr.dim3len as usize,
hdr.dim4len as usize,
]);
let bytes_per_element: usize = match hdr.dtype {
MRI_UCHAR => 1,
MRI_INT => 4,
MRI_FLOAT => 4,
MRI_SHORT => 2,
_ => return Err(NeuroformatsError::UnsupportedMriDataTypeInMgh),
};
let total_bytes = total_elements
.checked_mul(bytes_per_element)
.ok_or(NeuroformatsError::IntegerOverflow)?;
if total_bytes > config::max_bytes_per_file() {
return Err(NeuroformatsError::AllocationTooLarge);
}
let mut file = ByteOrdered::be(file);
for _ in 0..MGH_DATA_START {
let _discarded = file.read_u8()?;
}
let mut data_mri_uchar = None;
let mut data_mri_int = None;
let mut data_mri_float = None;
let mut data_mri_short = None;
let num_voxels = total_elements;
if hdr.dtype == MRI_UCHAR {
let mut mgh_data: Vec<u8> = Vec::with_capacity(num_voxels);
for _ in 0..num_voxels {
mgh_data.push(file.read_u8()?);
}
data_mri_uchar = Some(
Array::from_shape_vec(vol_dim, mgh_data)
.map_err(|e| NeuroformatsError::Io(std::io::Error::new(std::io::ErrorKind::InvalidData, e)))?,
);
} else if hdr.dtype == MRI_INT {
let mut mgh_data: Vec<i32> = Vec::with_capacity(num_voxels);
for _ in 0..num_voxels {
mgh_data.push(file.read_i32()?);
}
data_mri_int = Some(
Array::from_shape_vec(vol_dim, mgh_data)
.map_err(|e| NeuroformatsError::Io(std::io::Error::new(std::io::ErrorKind::InvalidData, e)))?,
);
} else if hdr.dtype == MRI_FLOAT {
let mut mgh_data: Vec<f32> = Vec::with_capacity(num_voxels);
for _ in 0..num_voxels {
mgh_data.push(file.read_f32()?);
}
data_mri_float = Some(
Array::from_shape_vec(vol_dim, mgh_data)
.map_err(|e| NeuroformatsError::Io(std::io::Error::new(std::io::ErrorKind::InvalidData, e)))?,
);
} else if hdr.dtype == MRI_SHORT {
let mut mgh_data: Vec<i16> = Vec::with_capacity(num_voxels);
for _ in 0..num_voxels {
mgh_data.push(file.read_i16()?);
}
data_mri_short = Some(
Array::from_shape_vec(vol_dim, mgh_data)
.map_err(|e| NeuroformatsError::Io(std::io::Error::new(std::io::ErrorKind::InvalidData, e)))?,
);
} else {
return Err(NeuroformatsError::UnsupportedMriDataTypeInMgh);
}
let mgh_data = FsMghData {
mri_uchar: data_mri_uchar,
mri_int: data_mri_int,
mri_float: data_mri_float,
mri_short: data_mri_short,
};
Ok(mgh_data)
}
pub fn dim(&self) -> [usize; 4] {
self.header.dim()
}
pub fn vox2ras(&self) -> Result<Array2<f32>> {
self.header.vox2ras()
}
}
pub fn is_mgz_file<P>(path: P) -> bool
where
P: AsRef<Path>,
{
path.as_ref()
.file_name()
.map(|a| a.to_string_lossy().ends_with(".mgz"))
.unwrap_or(false)
}
pub fn read_mgh<P: AsRef<Path> + Copy>(path: P) -> Result<FsMgh> {
FsMgh::from_file(path)
}
impl fmt::Display for FsMgh {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"FreeSurfer 4D MRI data with dim {}, {}, {}, {} and RAS flag: {}.",
self.header.dim1len,
self.header.dim2len,
self.header.dim3len,
self.header.dim4len,
self.header.is_ras_good
)
}
}
pub fn write_mgh<P: AsRef<Path> + Copy>(path: P, mgh: &FsMgh) -> std::io::Result<()> {
if is_mgz_file(path) {
write_mgz_compressed(path, mgh)
} else {
write_mgh_uncompressed(path, mgh)
}
}
fn write_mgh_uncompressed<P: AsRef<Path> + Copy>(path: P, mgh: &FsMgh) -> std::io::Result<()> {
let f = File::create(path)?;
let writer = BufWriter::new(&f);
write_mgh_to(writer, mgh)
}
fn write_mgz_compressed<P: AsRef<Path> + Copy>(path: P, mgh: &FsMgh) -> std::io::Result<()> {
let f = File::create(path)?;
let writer = BufWriter::new(flate2::write::GzEncoder::new(&f, Compression::default()));
write_mgh_to(writer, mgh)
}
fn write_mgh_to<W>(f: W, mgh: &FsMgh) -> std::io::Result<()>
where
W: Write,
{
let mut f = ByteOrdered::runtime(f, Endianness::Big);
f.write_i32(mgh.header.mgh_format_version as i32)?;
f.write_i32(mgh.header.dim1len)?;
f.write_i32(mgh.header.dim2len)?;
f.write_i32(mgh.header.dim3len)?;
f.write_i32(mgh.header.dim4len)?;
f.write_i32(mgh.header.dtype)?;
f.write_i32(mgh.header.dof)?;
f.write_i16(mgh.header.is_ras_good)?;
for v in mgh.header.delta.iter() {
f.write_f32(*v)?;
}
for v in mgh.header.mdc_raw.iter() {
f.write_f32(*v)?;
}
for v in mgh.header.p_xyz_c.iter() {
f.write_f32(*v)?;
}
let header_space_left: usize = 194;
for _v in 0..header_space_left {
f.write_u8(0 as u8)?;
}
if mgh.header.dtype == MRI_UCHAR {
for v in mgh.data.mri_uchar.as_ref().unwrap().iter() {
f.write_u8(*v)?;
}
} else if mgh.header.dtype == MRI_INT {
for v in mgh.data.mri_int.as_ref().unwrap().iter() {
f.write_i32(*v)?;
}
} else if mgh.header.dtype == MRI_FLOAT {
for v in mgh.data.mri_float.as_ref().unwrap().iter() {
f.write_f32(*v)?;
}
} else if mgh.header.dtype == MRI_SHORT {
for v in mgh.data.mri_short.as_ref().unwrap().iter() {
f.write_i16(*v)?;
}
} else {
panic!("Unsupported MRI data type.");
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
use tempfile::tempdir;
#[test]
fn the_brain_mgz_file_can_be_read() {
const MGZ_FILE: &str = "resources/subjects_dir/subject1/mri/brain.mgz";
let mgh = read_mgh(MGZ_FILE).unwrap();
assert_eq!(mgh.header.dim1len, 256);
assert_eq!(mgh.header.dim2len, 256);
assert_eq!(mgh.header.dim3len, 256);
assert_eq!(mgh.header.dim4len, 1);
assert_eq!(mgh.header.dtype, MRI_UCHAR);
assert_eq!(mgh.header.is_ras_good, 1);
let expected_delta: Array1<f32> = array![1.0, 1.0, 1.0];
let expected_mdc: Array2<f32> =
Array2::from_shape_vec((3, 3), [-1., 0., 0., 0., 0., -1., 0., 1., 0.].to_vec())
.unwrap();
let expected_p_xyz_c: Array1<f32> = array![-0.49995422, 29.372742, -48.90473];
let delta: Array1<f32> = Array1::from(mgh.header.delta.to_vec());
let mdc: Array2<f32> = Array2::from_shape_vec((3, 3), mgh.header.mdc_raw.to_vec()).unwrap();
let p_xyz_c: Array1<f32> = Array1::from(mgh.header.p_xyz_c.to_vec());
assert!(delta.abs_diff_eq(&expected_delta, 1e-5));
assert!(mdc.abs_diff_eq(&expected_mdc, 1e-5));
assert!(p_xyz_c.abs_diff_eq(&expected_p_xyz_c, 1e-5));
let data = mgh.data.mri_uchar.unwrap();
assert_eq!(data.ndim(), 4);
assert_eq!(data[[99, 99, 99, 0]], 77); assert_eq!(data[[109, 109, 109, 0]], 71);
assert_eq!(data[[0, 0, 0, 0]], 0);
assert_eq!(data.mapv(|a| a as i32).sum(), 121035479);
}
#[test]
fn the_vox2ras_matrix_can_be_computed() {
const MGZ_FILE: &str = "resources/subjects_dir/subject1/mri/brain.mgz";
let mgh = read_mgh(MGZ_FILE).unwrap();
let vox2ras = mgh.header.vox2ras().unwrap();
assert_eq!(vox2ras.len(), 16);
let expected_vox2ras_ar: Vec<f32> = [
-1., 0., 0., 0., 0., 0., -1., 0., 0., 1., 0., 0., 127.5, -98.6273, 79.0953, 1.,
]
.to_vec();
let expected_vox2ras = Array2::from_shape_vec((4, 4), expected_vox2ras_ar)
.unwrap()
.t()
.into_owned();
assert!(vox2ras.abs_diff_eq(&expected_vox2ras, 1e-2));
let my_voxel_ijk: Array1<f32> = Array1::from([32.0, 32.0, 32.0, 1.0].to_vec()); let my_voxel_ras = vox2ras.dot(&my_voxel_ijk);
let expected_voxel_ras: Array1<f32> =
Array1::from([95.500046, -66.62726, 47.09527, 1.0].to_vec());
assert!(my_voxel_ras.abs_diff_eq(&expected_voxel_ras, 1e-2));
}
#[test]
fn the_demo_mgh_file_can_be_read() {
const MGH_FILE: &str = "resources/mgh/tiny.mgh";
let mgh = read_mgh(MGH_FILE).unwrap();
assert_eq!(mgh.header.dim1len, 3);
assert_eq!(mgh.header.dim2len, 3);
assert_eq!(mgh.header.dim3len, 3);
assert_eq!(mgh.header.dim4len, 1);
assert_eq!(mgh.dim(), [3 as usize, 3 as usize, 3 as usize, 1 as usize]);
assert_eq!(
mgh.header.dim(),
[3 as usize, 3 as usize, 3 as usize, 1 as usize]
);
assert_eq!(mgh.header.is_ras_good, -1);
}
#[test]
fn an_mgh_file_can_be_written_as_mgh_and_reread() {
const MGZ_FILE: &str = "resources/subjects_dir/subject1/mri/brain.mgz";
let mgh = read_mgh(MGZ_FILE).unwrap();
let dir = tempdir().unwrap();
let tfile_path = dir.path().join("temp-file.mgh");
let tfile_path = tfile_path.to_str().unwrap();
write_mgh(tfile_path, &mgh).unwrap();
let mgh_re = read_mgh(tfile_path).unwrap();
let vox2ras = mgh_re.header.vox2ras().unwrap();
assert_eq!(vox2ras.len(), 16);
let expected_vox2ras_ar: Vec<f32> = [
-1., 0., 0., 0., 0., 0., -1., 0., 0., 1., 0., 0., 127.5, -98.6273, 79.0953, 1.,
]
.to_vec();
let expected_vox2ras = Array2::from_shape_vec((4, 4), expected_vox2ras_ar)
.unwrap()
.t()
.into_owned();
assert!(vox2ras.abs_diff_eq(&expected_vox2ras, 1e-2));
let my_voxel_ijk: Array1<f32> = Array1::from([32.0, 32.0, 32.0, 1.0].to_vec()); let my_voxel_ras = vox2ras.dot(&my_voxel_ijk);
let expected_voxel_ras: Array1<f32> =
Array1::from([95.500046, -66.62726, 47.09527, 1.0].to_vec());
assert!(my_voxel_ras.abs_diff_eq(&expected_voxel_ras, 1e-2));
assert_eq!(mgh_re.header.dim1len, 256);
assert_eq!(mgh_re.header.dim2len, 256);
assert_eq!(mgh_re.header.dim3len, 256);
assert_eq!(mgh_re.header.dim4len, 1);
assert_eq!(mgh_re.header.dtype, MRI_UCHAR);
assert_eq!(mgh_re.header.is_ras_good, 1);
let expected_delta: Array1<f32> = array![1.0, 1.0, 1.0];
let expected_mdc: Array2<f32> =
Array2::from_shape_vec((3, 3), [-1., 0., 0., 0., 0., -1., 0., 1., 0.].to_vec())
.unwrap();
let expected_p_xyz_c: Array1<f32> = array![-0.49995422, 29.372742, -48.90473];
let delta: Array1<f32> = Array1::from(mgh_re.header.delta.to_vec());
let mdc: Array2<f32> =
Array2::from_shape_vec((3, 3), mgh_re.header.mdc_raw.to_vec()).unwrap();
let p_xyz_c: Array1<f32> = Array1::from(mgh_re.header.p_xyz_c.to_vec());
assert!(delta.abs_diff_eq(&expected_delta, 1e-5));
assert!(mdc.abs_diff_eq(&expected_mdc, 1e-5));
assert!(p_xyz_c.abs_diff_eq(&expected_p_xyz_c, 1e-5));
let data = mgh_re.data.mri_uchar.unwrap();
assert_eq!(data.ndim(), 4);
assert_eq!(data[[99, 99, 99, 0]], 77); assert_eq!(data[[109, 109, 109, 0]], 71);
assert_eq!(data[[0, 0, 0, 0]], 0);
assert_eq!(data.mapv(|a| a as i32).sum(), 121035479);
}
#[test]
fn an_mgh_file_can_be_written_as_mgz_and_reread() {
const MGZ_FILE: &str = "resources/subjects_dir/subject1/mri/brain.mgz";
let mgh = read_mgh(MGZ_FILE).unwrap();
let dir = tempdir().unwrap();
let tfile_path = dir.path().join("temp-file.mgz");
let tfile_path = tfile_path.to_str().unwrap();
write_mgh(tfile_path, &mgh).unwrap();
let mgh_re = read_mgh(tfile_path).unwrap();
let vox2ras = mgh_re.header.vox2ras().unwrap();
assert_eq!(vox2ras.len(), 16);
let expected_vox2ras_ar: Vec<f32> = [
-1., 0., 0., 0., 0., 0., -1., 0., 0., 1., 0., 0., 127.5, -98.6273, 79.0953, 1.,
]
.to_vec();
let expected_vox2ras = Array2::from_shape_vec((4, 4), expected_vox2ras_ar)
.unwrap()
.t()
.into_owned();
assert!(vox2ras.abs_diff_eq(&expected_vox2ras, 1e-2));
let my_voxel_ijk: Array1<f32> = Array1::from([32.0, 32.0, 32.0, 1.0].to_vec()); let my_voxel_ras = vox2ras.dot(&my_voxel_ijk);
let expected_voxel_ras: Array1<f32> =
Array1::from([95.500046, -66.62726, 47.09527, 1.0].to_vec());
assert!(my_voxel_ras.abs_diff_eq(&expected_voxel_ras, 1e-2));
assert_eq!(mgh_re.header.dim1len, 256);
assert_eq!(mgh_re.header.dim2len, 256);
assert_eq!(mgh_re.header.dim3len, 256);
assert_eq!(mgh_re.header.dim4len, 1);
assert_eq!(mgh_re.header.dtype, MRI_UCHAR);
assert_eq!(mgh_re.header.is_ras_good, 1);
let expected_delta: Array1<f32> = array![1.0, 1.0, 1.0];
let expected_mdc: Array2<f32> =
Array2::from_shape_vec((3, 3), [-1., 0., 0., 0., 0., -1., 0., 1., 0.].to_vec())
.unwrap();
let expected_p_xyz_c: Array1<f32> = array![-0.49995422, 29.372742, -48.90473];
let delta: Array1<f32> = Array1::from(mgh_re.header.delta.to_vec());
let mdc: Array2<f32> =
Array2::from_shape_vec((3, 3), mgh_re.header.mdc_raw.to_vec()).unwrap();
let p_xyz_c: Array1<f32> = Array1::from(mgh_re.header.p_xyz_c.to_vec());
assert!(delta.abs_diff_eq(&expected_delta, 1e-5));
assert!(mdc.abs_diff_eq(&expected_mdc, 1e-5));
assert!(p_xyz_c.abs_diff_eq(&expected_p_xyz_c, 1e-5));
let data = mgh_re.data.mri_uchar.unwrap();
assert_eq!(data.ndim(), 4);
assert_eq!(data[[99, 99, 99, 0]], 77); assert_eq!(data[[109, 109, 109, 0]], 71);
assert_eq!(data[[0, 0, 0, 0]], 0);
assert_eq!(data.mapv(|a| a as i32).sum(), 121035479);
}
#[test]
fn mgh_header_rejects_negative_dimension() {
use std::io::Cursor;
use byteordered::byteorder::WriteBytesExt;
let mut buf = Cursor::new(Vec::new());
buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(-1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(256).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(256).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); for _ in 0..(MGH_DATA_START - 5 * 4) {
buf.write_u8(0).unwrap();
}
buf.set_position(0);
let hdr = FsMghHeader::from_reader(&mut buf).unwrap();
let result = FsMgh::data_from_reader(&mut buf, &hdr);
assert!(result.is_err());
}
#[test]
fn mgh_header_rejects_overflow_dimensions() {
use std::io::Cursor;
use byteordered::byteorder::WriteBytesExt;
let mut buf = Cursor::new(Vec::new());
buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(i32::MAX).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(2).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(2).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(2).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(MRI_UCHAR).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(0).unwrap(); buf.write_i16::<byteordered::byteorder::BigEndian>(-1).unwrap(); for _ in 0..(MGH_DATA_START - 7 * 4 - 2) {
buf.write_u8(0).unwrap();
}
buf.set_position(0);
let hdr = FsMghHeader::from_reader(&mut buf).unwrap();
let result = FsMgh::data_from_reader(&mut buf, &hdr);
assert!(result.is_err());
}
#[test]
fn mgh_header_rejects_nan_in_ras_fields() {
use std::io::Cursor;
use byteordered::byteorder::WriteBytesExt;
let mut buf = Cursor::new(Vec::new());
buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(MRI_UCHAR).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(0).unwrap(); buf.write_i16::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_f32::<byteordered::byteorder::BigEndian>(f32::NAN).unwrap();
buf.write_f32::<byteordered::byteorder::BigEndian>(1.0).unwrap();
buf.write_f32::<byteordered::byteorder::BigEndian>(1.0).unwrap();
for _ in 0..12 {
buf.write_f32::<byteordered::byteorder::BigEndian>(0.0).unwrap();
}
for _ in 0..(MGH_DATA_START - 7 * 4 - 2 - 15 * 4) {
buf.write_u8(0).unwrap();
}
buf.set_position(0);
let result = FsMghHeader::from_reader(&mut buf);
assert!(result.is_err());
}
#[test]
fn mgh_header_rejects_inf_in_ras_fields() {
use std::io::Cursor;
use byteordered::byteorder::WriteBytesExt;
let mut buf = Cursor::new(Vec::new());
buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(1).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(MRI_UCHAR).unwrap(); buf.write_i32::<byteordered::byteorder::BigEndian>(0).unwrap(); buf.write_i16::<byteordered::byteorder::BigEndian>(1).unwrap(); for _ in 0..3 {
buf.write_f32::<byteordered::byteorder::BigEndian>(1.0).unwrap();
}
buf.write_f32::<byteordered::byteorder::BigEndian>(f32::INFINITY).unwrap();
for _ in 0..8 {
buf.write_f32::<byteordered::byteorder::BigEndian>(0.0).unwrap();
}
for _ in 0..3 {
buf.write_f32::<byteordered::byteorder::BigEndian>(0.0).unwrap();
}
for _ in 0..(MGH_DATA_START - 7 * 4 - 2 - 15 * 4) {
buf.write_u8(0).unwrap();
}
buf.set_position(0);
let result = FsMghHeader::from_reader(&mut buf);
assert!(result.is_err());
}
}