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
10pub 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
134pub 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
144pub 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
152pub 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
167pub 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
183pub 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}