1use super::error::NiftiError;
3use super::typedef::NiftiType;
4use crate::error::Result;
5use crate::NiftiHeader;
6use byteordered::Endian;
7use either::Either;
8use flate2::bufread::GzDecoder;
9use std::borrow::Cow;
10use std::fs::File;
11use std::io::{BufReader, Result as IoResult};
12use std::mem;
13use std::path::{Path, PathBuf};
14
15pub fn convert_bytes_to<T, E>(mut a: Vec<u8>, e: E) -> Vec<T>
16where
17 T: bytemuck::Pod,
18 E: Endian,
19{
20 adapt_bytes_inline::<T, _>(&mut a, e);
21 match bytemuck::allocation::try_cast_vec(a) {
22 Ok(v) => v,
23 Err((_, v)) => bytemuck::allocation::pod_collect_to_vec(&v),
24 }
25}
26
27pub fn adapt_bytes_inline<T, E>(a: &mut [u8], e: E)
30where
31 E: Endian,
32{
33 let nb_bytes = mem::size_of::<T>();
34 if !e.is_native() && nb_bytes > 1 {
35 let split_at = nb_bytes / 2;
37 for c in a.chunks_mut(nb_bytes) {
38 let (a, b) = c.split_at_mut(split_at);
39 for (l, r) in a.iter_mut().zip(b.iter_mut().rev()) {
40 mem::swap(l, r);
41 }
42 }
43 }
44}
45
46#[cfg_attr(not(feature = "ndarray_volumes"), allow(dead_code))]
50pub fn adapt_bytes<T, E>(bytes: &[u8], e: E) -> Cow<[u8]>
51where
52 E: Endian,
53{
54 let nb_bytes = mem::size_of::<T>();
55 if !e.is_native() && nb_bytes > 1 {
56 let mut a = bytes.to_vec();
58 adapt_bytes_inline::<T, E>(&mut a, e);
59 a.into()
60 } else {
61 bytes.into()
62 }
63}
64
65pub fn validate_dim(raw_dim: &[u16; 8]) -> Result<&[u16]> {
73 let ndim = validate_dimensionality(raw_dim)?;
74 let o = &raw_dim[1..=ndim];
75 if let Some(i) = o.iter().position(|&x| x == 0) {
76 return Err(NiftiError::InconsistentDim(i as u8, raw_dim[i]));
77 }
78 Ok(o)
79}
80
81pub fn validate_dimensionality(raw_dim: &[u16; 8]) -> Result<usize> {
88 if raw_dim[0] == 0 || raw_dim[0] > 7 {
89 return Err(NiftiError::InconsistentDim(0, raw_dim[0]));
90 }
91 Ok(usize::from(raw_dim[0]))
92}
93
94pub fn nb_bytes_for_data(header: &NiftiHeader) -> Result<usize> {
95 let resolution = nb_values_for_dims(header.dim()?);
96 resolution
97 .and_then(|r| r.checked_mul(header.bitpix as usize / 8))
98 .ok_or(NiftiError::BadVolumeSize)
99}
100
101pub fn nb_values_for_dims(dim: &[u16]) -> Option<usize> {
102 dim.iter()
103 .cloned()
104 .try_fold(1usize, |acc, v| acc.checked_mul(v as usize))
105}
106
107pub fn nb_bytes_for_dim_datatype(dim: &[u16], datatype: NiftiType) -> Option<usize> {
108 let resolution = nb_values_for_dims(dim);
109 resolution.and_then(|r| r.checked_mul(datatype.size_of()))
110}
111
112#[cfg(feature = "ndarray_volumes")]
113pub fn is_hdr_file<P>(path: P) -> bool
114where
115 P: AsRef<Path>,
116{
117 path.as_ref()
118 .file_name()
119 .map(|a| {
120 let s = a.to_string_lossy();
121 s.ends_with(".hdr") || s.ends_with(".hdr.gz")
122 })
123 .unwrap_or(false)
124}
125
126pub fn is_gz_file<P>(path: P) -> bool
127where
128 P: AsRef<Path>,
129{
130 path.as_ref()
131 .file_name()
132 .map(|a| a.to_string_lossy().ends_with(".gz"))
133 .unwrap_or(false)
134}
135
136pub fn into_img_file_gz(mut path: PathBuf) -> PathBuf {
143 if is_gz_file(&path) {
144 let _ = path.set_extension("");
146 }
147 path.with_extension("img.gz")
148}
149
150pub type GzDecodedFile = GzDecoder<BufReader<File>>;
152
153pub type MaybeGzDecoded<T> = Either<T, GzDecoder<T>>;
155
156pub type MaybeGzDecodedFile = MaybeGzDecoded<BufReader<File>>;
159
160pub fn open_file_maybe_gz<P>(path: P) -> IoResult<MaybeGzDecodedFile>
163where
164 P: AsRef<Path>,
165{
166 let path = path.as_ref();
167 let file = BufReader::new(File::open(path)?);
168 if is_gz_file(path) {
169 Ok(Either::Right(GzDecoder::new(file)))
170 } else {
171 Ok(Either::Left(file))
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 #[cfg(feature = "ndarray_volumes")]
178 use super::is_hdr_file;
179 use super::{into_img_file_gz, is_gz_file, nb_bytes_for_dim_datatype};
180 use crate::typedef::NiftiType;
181 use std::path::PathBuf;
182
183 #[test]
184 fn test_nbytes() {
185 assert_eq!(
186 nb_bytes_for_dim_datatype(&[2, 3, 2], NiftiType::Uint8),
187 Some(12),
188 );
189 assert_eq!(
190 nb_bytes_for_dim_datatype(&[2, 3], NiftiType::Uint8),
191 Some(6),
192 );
193 assert_eq!(
194 nb_bytes_for_dim_datatype(&[2, 3], NiftiType::Uint16),
195 Some(12),
196 );
197 assert_eq!(
198 nb_bytes_for_dim_datatype(&[0x4000, 0x4000, 0x4000, 0x4000, 0x4000], NiftiType::Uint32),
199 None,
200 );
201 }
202
203 #[test]
204 fn filenames() {
205 assert!(!is_gz_file("/path/to/something.nii"));
206 assert!(is_gz_file("/path/to/something.nii.gz"));
207 assert!(!is_gz_file("volume.não"));
208 assert!(is_gz_file("1.2.3.nii.gz"));
209 assert!(!is_gz_file("não_é_gz.hdr"));
210
211 let path = "/path/to/image.hdr";
212 #[cfg(feature = "ndarray_volumes")]
213 assert!(is_hdr_file(path));
214 assert!(!is_gz_file(path));
215 assert_eq!(
216 into_img_file_gz(PathBuf::from(path)),
217 PathBuf::from("/path/to/image.img.gz")
218 );
219
220 let path = "/path/to/image.hdr.gz";
221 #[cfg(feature = "ndarray_volumes")]
222 assert!(is_hdr_file(path));
223 assert!(is_gz_file(path));
224 assert_eq!(
225 into_img_file_gz(PathBuf::from(path)),
226 PathBuf::from("/path/to/image.img.gz")
227 );
228
229 let path = "my_ct_scan.1.hdr.gz";
230 #[cfg(feature = "ndarray_volumes")]
231 assert!(is_hdr_file(path));
232 assert!(is_gz_file(path));
233 assert_eq!(
234 into_img_file_gz(PathBuf::from(path)),
235 PathBuf::from("my_ct_scan.1.img.gz")
236 );
237
238 assert_eq!(
239 into_img_file_gz(PathBuf::from("../you.cant.fool.me.hdr.gz")),
240 PathBuf::from("../you.cant.fool.me.img.gz")
241 );
242 }
243}
244
245#[cfg(feature = "ndarray_volumes")]
246#[cfg(test)]
247mod test_nd_array {
248 use super::convert_bytes_to;
249 use byteordered::Endianness;
250
251 #[test]
252 fn test_convert_vec_i8() {
253 assert_eq!(
254 convert_bytes_to::<i8, _>(vec![0x01, 0x11, 0xff], Endianness::Big),
255 vec![1, 17, -1]
256 );
257 assert_eq!(
258 convert_bytes_to::<i8, _>(vec![0x01, 0x11, 0xfe], Endianness::Little),
259 vec![1, 17, -2]
260 );
261 }
262
263 #[test]
264 fn test_convert_vec_u8() {
265 assert_eq!(
266 convert_bytes_to::<u8, _>(vec![0x01, 0x11, 0xff], Endianness::Big),
267 vec![1, 17, 255]
268 );
269 assert_eq!(
270 convert_bytes_to::<u8, _>(vec![0x01, 0x11, 0xfe], Endianness::Little),
271 vec![1, 17, 254]
272 );
273 }
274
275 #[test]
276 fn test_convert_vec_i16() {
277 assert_eq!(
278 convert_bytes_to::<i16, _>(vec![0x00, 0x01, 0x01, 0x00, 0xff, 0xfe], Endianness::Big),
279 vec![1, 256, -2]
280 );
281 assert_eq!(
282 convert_bytes_to::<i16, _>(
283 vec![0x01, 0x00, 0x00, 0x01, 0xfe, 0xff],
284 Endianness::Little
285 ),
286 vec![1, 256, -2]
287 );
288 }
289
290 #[test]
291 fn test_convert_vec_u16() {
292 assert_eq!(
293 convert_bytes_to::<u16, _>(vec![0x00, 0x01, 0x01, 0x00, 0xff, 0xfe], Endianness::Big),
294 vec![1, 256, 65534]
295 );
296 assert_eq!(
297 convert_bytes_to::<u16, _>(
298 vec![0x01, 0x00, 0x00, 0x01, 0xfe, 0xff],
299 Endianness::Little
300 ),
301 vec![1, 256, 65534]
302 );
303 }
304
305 #[test]
306 fn test_convert_vec_i32() {
307 assert_eq!(
308 convert_bytes_to::<i32, _>(
309 vec![0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xf0, 0xf1, 0xf2, 0xf3],
310 Endianness::Big
311 ),
312 vec![1, 16777216, -252_579_085]
313 );
314 assert_eq!(
315 convert_bytes_to::<i32, _>(
316 vec![0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf3, 0xf2, 0xf1, 0xf0],
317 Endianness::Little
318 ),
319 vec![1, 16777216, -252_579_085]
320 );
321 }
322
323 #[test]
324 fn test_convert_vec_u32() {
325 assert_eq!(
326 convert_bytes_to::<u32, _>(
327 vec![0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0xf0, 0xf1, 0xf2, 0xf3],
328 Endianness::Big
329 ),
330 vec![1, 0x01000000, 4_042_388_211]
331 );
332 assert_eq!(
333 convert_bytes_to::<u32, _>(
334 vec![0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf3, 0xf2, 0xf1, 0xf0],
335 Endianness::Little
336 ),
337 vec![1, 0x01000000, 4_042_388_211]
338 );
339 }
340
341 #[test]
342 fn test_convert_vec_i64() {
343 assert_eq!(
344 convert_bytes_to::<i64, _>(
345 vec![
346 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
347 0x00, 0x00, 0x00, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
348 ],
349 Endianness::Big
350 ),
351 vec![1, 0x0100000000000000, -1_084_818_905_618_843_913]
352 );
353 assert_eq!(
354 convert_bytes_to::<i64, _>(
355 vec![
356 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
357 0x00, 0x00, 0x01, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
358 ],
359 Endianness::Little
360 ),
361 vec![1, 0x0100000000000000, -1_084_818_905_618_843_913]
362 );
363 }
364
365 #[test]
366 fn test_convert_vec_u64() {
367 assert_eq!(
368 convert_bytes_to::<u64, _>(
369 vec![
370 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
371 0x00, 0x00, 0x00, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
372 ],
373 Endianness::Big
374 ),
375 vec![1, 0x100000000000000, 0xf0f1f2f3f4f5f6f7]
376 );
377 assert_eq!(
378 convert_bytes_to::<u64, _>(
379 vec![
380 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
381 0x00, 0x00, 0x01, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
382 ],
383 Endianness::Little
384 ),
385 vec![1, 0x100000000000000, 0xf0f1f2f3f4f5f6f7]
386 );
387 }
388
389 #[test]
390 fn test_convert_vec_f32() {
391 let v: Vec<f32> = convert_bytes_to(
392 vec![0x42, 0x28, 0x00, 0x00, 0x42, 0x2A, 0x00, 0x00],
393 Endianness::Big,
394 );
395 assert_eq!(v, vec![42., 42.5]);
396
397 let v: Vec<f32> = convert_bytes_to(
398 vec![0x00, 0x00, 0x28, 0x42, 0x00, 0x00, 0x2A, 0x42],
399 Endianness::Little,
400 );
401 assert_eq!(v, vec![42., 42.5]);
402 }
403
404 #[test]
405 fn test_convert_vec_f64() {
406 let v: Vec<f64> = convert_bytes_to(
407 vec![
408 0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x45, 0x40, 0x00, 0x00, 0x00,
409 0x00, 0x00,
410 ],
411 Endianness::Big,
412 );
413 assert_eq!(v, vec![42.0, 42.5]);
414
415 let v: Vec<f64> = convert_bytes_to(
416 vec![
417 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
418 0x45, 0x40,
419 ],
420 Endianness::Little,
421 );
422 assert_eq!(v, vec![42.0, 42.5]);
423 }
424}