sane_array/
write.rs

1use std::io::prelude::Write;
2use std::mem::size_of;
3use std::slice::from_raw_parts;
4use std::error::Error;
5
6use ndarray::{Dimension, ArrayBase, Data};
7
8use crate::{data::{SaneData, data_type_code}, Sane};
9
10/// To be able to write SANE data we need to be able to
11/// convert an element to a byte sequence
12pub trait WriteSane: SaneData {
13    fn to_le_bytes(elem: Self) -> Vec<u8>;
14}
15
16impl WriteSane for f32 {
17    fn to_le_bytes(elem: f32) -> Vec<u8> {
18        f32::to_le_bytes(elem).to_vec()
19    }
20}
21
22impl WriteSane for i32 {
23    fn to_le_bytes(elem: i32) -> Vec<u8> {
24        i32::to_le_bytes(elem).to_vec()
25    }
26}
27
28impl WriteSane for u32 {
29    fn to_le_bytes(elem: u32) -> Vec<u8> {
30        u32::to_le_bytes(elem).to_vec()
31    }
32}
33
34impl WriteSane for f64 {
35    fn to_le_bytes(elem: f64) -> Vec<u8> {
36        f64::to_le_bytes(elem).to_vec()
37    }
38}
39
40impl WriteSane for i64 {
41    fn to_le_bytes(elem: i64) -> Vec<u8> {
42        i64::to_le_bytes(elem).to_vec()
43    }
44}
45
46impl WriteSane for u64 {
47    fn to_le_bytes(elem: u64) -> Vec<u8> {
48        u64::to_le_bytes(elem).to_vec()
49    }
50}
51
52impl WriteSane for i8 {
53    fn to_le_bytes(elem: i8) -> Vec<u8> {
54        i8::to_le_bytes(elem).to_vec()
55    }
56}
57
58impl WriteSane for u8 {
59    fn to_le_bytes(elem: u8) -> Vec<u8> {
60        vec![elem]
61    }
62}
63
64#[derive(Debug)]
65pub enum WriteError {
66    Failed(std::io::Error),
67    ShapeTooLong(<u32 as TryFrom<usize>>::Error),
68    DimTooLarge(<u64 as TryFrom<usize>>::Error),
69    TooMuchData(<u64 as TryFrom<usize>>::Error),
70}
71
72impl std::fmt::Display for WriteError {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        use WriteError::*;
75        match self {
76            Failed(e) => write!(f, "Failed to write {}", e),
77            ShapeTooLong(e) => write!(f, "Shape length doesn't fit in 32 bits {}", e),
78            DimTooLarge(e) => write!(f, "Dimension size doesn't fit in 64 bits {}", e),
79            TooMuchData(e) => write!(f, "Length of array doesn't fit in 64 bits {}", e),
80        }
81    }
82}
83
84impl Error for WriteError {
85    fn source(&self) -> Option<&(dyn Error + 'static)> {
86        None
87    }
88}
89
90fn write_header<F: Write, A: SaneData, D: Dimension, Repr>(file: &mut F, array: &ArrayBase<Repr, D>)  -> Result<(), WriteError>
91where
92    Repr: Data<Elem = A>
93{
94    let shape = array.shape();
95    let data_type = A::sane_data_type();
96    let magic = "SANE".as_bytes();
97    file.write_all(magic).map_err(WriteError::Failed)?;
98    let shape_length = u32::try_from(shape.len()).map_err(WriteError::ShapeTooLong)?;
99    let shape_length_bytes = shape_length.to_le_bytes();
100    file.write_all(&shape_length_bytes).map_err(WriteError::Failed)?;
101    for &dim in shape.iter().rev() {
102        let dimension = u64::try_from(dim).map_err(WriteError::DimTooLarge)?;
103        let dim_bytes = dimension.to_le_bytes();
104        file.write_all(&dim_bytes).map_err(WriteError::Failed)?
105    }
106    let code = data_type_code(data_type);
107    file.write_all(&[code]).map_err(WriteError::Failed)?;
108    let byte_length = array.len() * size_of::<A>();
109    let data_length = u64::try_from(byte_length).map_err(WriteError::TooMuchData)?;
110    let data_length_bytes = data_length.to_le_bytes();
111    file.write_all(&data_length_bytes).map_err(WriteError::Failed)?;
112    Ok(())
113}
114
115fn write_data<F: Write, A: WriteSane, D: Dimension, Repr>(file: &mut F, array: &ArrayBase<Repr, D>) -> Result<(), WriteError>
116where
117    Repr: Data<Elem = A>
118{
119    let data_ptr = array.as_ptr();
120    let byte_length = array.len() * size_of::<A>();
121    if cfg!(endianness = "little") {
122        let data_ptr_bytes = data_ptr.cast::<u8>();
123        let data_bytes = unsafe { from_raw_parts(data_ptr_bytes, byte_length) };
124        file.write_all(data_bytes).map_err(WriteError::Failed)?;
125    } else {
126        for &elem in array.iter() {
127            let elem_bytes = WriteSane::to_le_bytes(elem);
128            file.write_all(&elem_bytes).map_err(WriteError::Failed)?;
129        }
130    }
131    Ok(())
132}
133
134/// Write array into a SANE-encoded file
135pub fn write_sane<F: Write, A: WriteSane, D: Dimension, Repr>(file: &mut F, array: &ArrayBase<Repr, D>) -> Result<(), WriteError>
136where
137    Repr: Data<Elem = A>
138{
139    write_header(file, &array)?;
140    write_data(file, &array)?;
141    Ok(())
142}
143
144/// Write array into SANE-encoded file, returning [`std::io::Error`]s
145pub fn write_sane_io<F: Write, A: WriteSane, D: Dimension, Repr>(file: &mut F, array: &ArrayBase<Repr, D>) -> Result<(), std::io::Error>
146where
147    Repr: Data<Elem = A>
148{
149    write_sane(file, array).map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))
150}
151
152/// Write multiple SANE-encoded arrays to a file
153pub fn write_sane_arrays<'a, F: Write, A: WriteSane + 'a, D: Dimension + 'a, Arrays, Repr>(
154    mut file: F,
155    arrays: Arrays,
156) -> Result<(), WriteError>
157where
158    Repr: Data<Elem = A> + 'a,
159    Arrays: IntoIterator<Item = &'a ArrayBase<Repr,D>>
160{
161    for array in arrays.into_iter() {
162        write_sane(&mut file, array)?;
163    }
164    Ok(())
165}
166
167/// Write multiple SANE-encoded arrays to a file, returning [`std::io::Error`]s
168pub fn write_sane_arrays_io<'a, F: Write, A: WriteSane + 'a, D: Dimension + 'a, Arrays, Repr>(
169    mut file: F,
170    arrays: Arrays,
171) -> Result<(), std::io::Error>
172where
173    Repr: Data<Elem = A> + 'a,
174    Arrays: IntoIterator<Item = &'a ArrayBase<Repr,D>>
175{
176    for array in arrays.into_iter() {
177        write_sane_io(&mut file, array)?;
178    }
179    Ok(())
180}
181
182
183/// Write multiple SANE-encoded arrays to a file, each with a dynamic shape and data type
184pub fn write_sane_arrays_dyn<'a, F: Write, Arrays>(
185    mut file: F,
186    arrays: Arrays,
187) -> Result<(), WriteError>
188where
189    Arrays: IntoIterator<Item = &'a Sane>
190{
191    use Sane::*;
192    for sane in arrays.into_iter() {
193        match sane {
194           ArrayF32(array) => write_sane(&mut file, array)?,
195           ArrayI32(array) => write_sane(&mut file, array)?,
196           ArrayU32(array) => write_sane(&mut file, array)?,
197           ArrayF64(array) => write_sane(&mut file, array)?,
198           ArrayI64(array) => write_sane(&mut file, array)?,
199           ArrayU64(array) => write_sane(&mut file, array)?,
200           ArrayI8(array) => write_sane(&mut file, array)?,
201           ArrayU8(array) => write_sane(&mut file, array)?,
202        }
203    }
204    Ok(())
205}