1#![doc(html_root_url = "https://docs.rs/matfile/0.5.0")]
2
3#[macro_use]
63extern crate enum_primitive_derive;
64
65#[cfg(feature = "ndarray")]
66pub mod ndarray;
67mod parse;
68
69#[derive(Clone, Debug)]
80pub struct MatFile {
81 arrays: Vec<Array>,
82}
83
84#[derive(Clone, Debug)]
103pub struct Array {
104 name: String,
105 size: Vec<usize>,
106 data: NumericData,
107}
108
109#[derive(Clone, Debug)]
117pub enum NumericData {
118 Int8 {
119 real: Vec<i8>,
120 imag: Option<Vec<i8>>,
121 },
122 UInt8 {
123 real: Vec<u8>,
124 imag: Option<Vec<u8>>,
125 },
126 Int16 {
127 real: Vec<i16>,
128 imag: Option<Vec<i16>>,
129 },
130 UInt16 {
131 real: Vec<u16>,
132 imag: Option<Vec<u16>>,
133 },
134 Int32 {
135 real: Vec<i32>,
136 imag: Option<Vec<i32>>,
137 },
138 UInt32 {
139 real: Vec<u32>,
140 imag: Option<Vec<u32>>,
141 },
142 Int64 {
143 real: Vec<i64>,
144 imag: Option<Vec<i64>>,
145 },
146 UInt64 {
147 real: Vec<u64>,
148 imag: Option<Vec<u64>>,
149 },
150 Single {
151 real: Vec<f32>,
152 imag: Option<Vec<f32>>,
153 },
154 Double {
155 real: Vec<f64>,
156 imag: Option<Vec<f64>>,
157 },
158}
159
160fn try_convert_number_format(
161 target_type: parse::ArrayType,
162 data: parse::NumericData,
163) -> Result<parse::NumericData, Error> {
164 match target_type {
165 parse::ArrayType::Double => match data {
166 parse::NumericData::UInt8(data) => Ok(parse::NumericData::Double(
167 data.into_iter().map(|x| x as f64).collect(),
168 )),
169 parse::NumericData::Int16(data) => Ok(parse::NumericData::Double(
170 data.into_iter().map(|x| x as f64).collect(),
171 )),
172 parse::NumericData::UInt16(data) => Ok(parse::NumericData::Double(
173 data.into_iter().map(|x| x as f64).collect(),
174 )),
175 parse::NumericData::Int32(data) => Ok(parse::NumericData::Double(
176 data.into_iter().map(|x| x as f64).collect(),
177 )),
178 parse::NumericData::Double(data) => Ok(parse::NumericData::Double(data)),
179 _ => Err(Error::ConversionError),
180 },
181 parse::ArrayType::Single => match data {
182 parse::NumericData::UInt8(data) => Ok(parse::NumericData::Single(
183 data.into_iter().map(|x| x as f32).collect(),
184 )),
185 parse::NumericData::Int16(data) => Ok(parse::NumericData::Single(
186 data.into_iter().map(|x| x as f32).collect(),
187 )),
188 parse::NumericData::UInt16(data) => Ok(parse::NumericData::Single(
189 data.into_iter().map(|x| x as f32).collect(),
190 )),
191 parse::NumericData::Int32(data) => Ok(parse::NumericData::Single(
192 data.into_iter().map(|x| x as f32).collect(),
193 )),
194 parse::NumericData::Single(data) => Ok(parse::NumericData::Single(data)),
195 _ => Err(Error::ConversionError),
196 },
197 parse::ArrayType::UInt64 => match data {
198 parse::NumericData::UInt8(data) => Ok(parse::NumericData::UInt64(
199 data.into_iter().map(|x| x as u64).collect(),
200 )),
201 parse::NumericData::Int16(data) => Ok(parse::NumericData::UInt64(
202 data.into_iter().map(|x| x as u64).collect(),
203 )),
204 parse::NumericData::UInt16(data) => Ok(parse::NumericData::UInt64(
205 data.into_iter().map(|x| x as u64).collect(),
206 )),
207 parse::NumericData::Int32(data) => Ok(parse::NumericData::UInt64(
208 data.into_iter().map(|x| x as u64).collect(),
209 )),
210 parse::NumericData::UInt64(data) => Ok(parse::NumericData::UInt64(data)),
211 _ => Err(Error::ConversionError),
212 },
213 parse::ArrayType::Int64 => match data {
214 parse::NumericData::UInt8(data) => Ok(parse::NumericData::Int64(
215 data.into_iter().map(|x| x as i64).collect(),
216 )),
217 parse::NumericData::Int16(data) => Ok(parse::NumericData::Int64(
218 data.into_iter().map(|x| x as i64).collect(),
219 )),
220 parse::NumericData::UInt16(data) => Ok(parse::NumericData::Int64(
221 data.into_iter().map(|x| x as i64).collect(),
222 )),
223 parse::NumericData::Int32(data) => Ok(parse::NumericData::Int64(
224 data.into_iter().map(|x| x as i64).collect(),
225 )),
226 parse::NumericData::Int64(data) => Ok(parse::NumericData::Int64(data)),
227 _ => Err(Error::ConversionError),
228 },
229 parse::ArrayType::UInt32 => match data {
230 parse::NumericData::UInt8(data) => Ok(parse::NumericData::UInt32(
231 data.into_iter().map(|x| x as u32).collect(),
232 )),
233 parse::NumericData::Int16(data) => Ok(parse::NumericData::UInt32(
234 data.into_iter().map(|x| x as u32).collect(),
235 )),
236 parse::NumericData::UInt16(data) => Ok(parse::NumericData::UInt32(
237 data.into_iter().map(|x| x as u32).collect(),
238 )),
239 parse::NumericData::UInt32(data) => Ok(parse::NumericData::UInt32(data)),
240 _ => Err(Error::ConversionError),
241 },
242 parse::ArrayType::Int32 => match data {
243 parse::NumericData::UInt8(data) => Ok(parse::NumericData::Int32(
244 data.into_iter().map(|x| x as i32).collect(),
245 )),
246 parse::NumericData::Int16(data) => Ok(parse::NumericData::Int32(
247 data.into_iter().map(|x| x as i32).collect(),
248 )),
249 parse::NumericData::UInt16(data) => Ok(parse::NumericData::Int32(
250 data.into_iter().map(|x| x as i32).collect(),
251 )),
252 parse::NumericData::Int32(data) => Ok(parse::NumericData::Int32(data)),
253 _ => Err(Error::ConversionError),
254 },
255 parse::ArrayType::UInt16 => match data {
256 parse::NumericData::UInt8(data) => Ok(parse::NumericData::UInt16(
257 data.into_iter().map(|x| x as u16).collect(),
258 )),
259 parse::NumericData::UInt16(data) => Ok(parse::NumericData::UInt16(data)),
260 _ => Err(Error::ConversionError),
261 },
262 parse::ArrayType::Int16 => match data {
263 parse::NumericData::UInt8(data) => Ok(parse::NumericData::Int16(
264 data.into_iter().map(|x| x as i16).collect(),
265 )),
266 parse::NumericData::Int16(data) => Ok(parse::NumericData::Int16(data)),
267 _ => Err(Error::ConversionError),
268 },
269 parse::ArrayType::UInt8 => match data {
270 parse::NumericData::UInt8(data) => Ok(parse::NumericData::UInt8(data)),
271 _ => Err(Error::ConversionError),
272 },
273 parse::ArrayType::Int8 => match data {
274 parse::NumericData::Int8(data) => Ok(parse::NumericData::Int8(data)),
275 _ => Err(Error::ConversionError),
276 },
277 _ => Err(Error::ConversionError),
278 }
279}
280
281impl NumericData {
282 fn try_from(
283 target_type: parse::ArrayType,
284 real: parse::NumericData,
285 imag: Option<parse::NumericData>,
286 ) -> Result<Self, Error> {
287 let real = try_convert_number_format(target_type, real)?;
288 let imag = match imag {
289 Some(imag) => Some(try_convert_number_format(target_type, imag)?),
290 None => None,
291 };
292 match (real, imag) {
294 (parse::NumericData::Double(real), None) => Ok(NumericData::Double {
295 real: real,
296 imag: None,
297 }),
298 (parse::NumericData::Double(real), Some(parse::NumericData::Double(imag))) => {
299 Ok(NumericData::Double {
300 real: real,
301 imag: Some(imag),
302 })
303 }
304 (parse::NumericData::Single(real), None) => Ok(NumericData::Single {
305 real: real,
306 imag: None,
307 }),
308 (parse::NumericData::Single(real), Some(parse::NumericData::Single(imag))) => {
309 Ok(NumericData::Single {
310 real: real,
311 imag: Some(imag),
312 })
313 }
314 (parse::NumericData::UInt64(real), None) => Ok(NumericData::UInt64 {
315 real: real,
316 imag: None,
317 }),
318 (parse::NumericData::UInt64(real), Some(parse::NumericData::UInt64(imag))) => {
319 Ok(NumericData::UInt64 {
320 real: real,
321 imag: Some(imag),
322 })
323 }
324 (parse::NumericData::Int64(real), None) => Ok(NumericData::Int64 {
325 real: real,
326 imag: None,
327 }),
328 (parse::NumericData::Int64(real), Some(parse::NumericData::Int64(imag))) => {
329 Ok(NumericData::Int64 {
330 real: real,
331 imag: Some(imag),
332 })
333 }
334 (parse::NumericData::UInt32(real), None) => Ok(NumericData::UInt32 {
335 real: real,
336 imag: None,
337 }),
338 (parse::NumericData::UInt32(real), Some(parse::NumericData::UInt32(imag))) => {
339 Ok(NumericData::UInt32 {
340 real: real,
341 imag: Some(imag),
342 })
343 }
344 (parse::NumericData::Int32(real), None) => Ok(NumericData::Int32 {
345 real: real,
346 imag: None,
347 }),
348 (parse::NumericData::Int32(real), Some(parse::NumericData::Int32(imag))) => {
349 Ok(NumericData::Int32 {
350 real: real,
351 imag: Some(imag),
352 })
353 }
354 (parse::NumericData::UInt16(real), None) => Ok(NumericData::UInt16 {
355 real: real,
356 imag: None,
357 }),
358 (parse::NumericData::UInt16(real), Some(parse::NumericData::UInt16(imag))) => {
359 Ok(NumericData::UInt16 {
360 real: real,
361 imag: Some(imag),
362 })
363 }
364 (parse::NumericData::Int16(real), None) => Ok(NumericData::Int16 {
365 real: real,
366 imag: None,
367 }),
368 (parse::NumericData::Int16(real), Some(parse::NumericData::Int16(imag))) => {
369 Ok(NumericData::Int16 {
370 real: real,
371 imag: Some(imag),
372 })
373 }
374 (parse::NumericData::UInt8(real), None) => Ok(NumericData::UInt8 {
375 real: real,
376 imag: None,
377 }),
378 (parse::NumericData::UInt8(real), Some(parse::NumericData::UInt8(imag))) => {
379 Ok(NumericData::UInt8 {
380 real: real,
381 imag: Some(imag),
382 })
383 }
384 (parse::NumericData::Int8(real), None) => Ok(NumericData::Int8 {
385 real: real,
386 imag: None,
387 }),
388 (parse::NumericData::Int8(real), Some(parse::NumericData::Int8(imag))) => {
389 Ok(NumericData::Int8 {
390 real: real,
391 imag: Some(imag),
392 })
393 }
394 _ => return Err(Error::InternalError),
395 }
396 }
397}
398
399#[derive(Debug)]
400pub enum Error {
401 IOError(std::io::Error),
402 ParseError(nom::Err<nom::error::Error<&'static [u8]>>),
403 ConversionError,
404 InternalError,
405}
406
407impl std::fmt::Display for Error {
408 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
409 match self {
410 Error::IOError(_) => write!(f, "An I/O error occurred"),
411 Error::ParseError(_) => write!(f, "An error occurred while parsing the file"),
412 Error::ConversionError => {
413 write!(f, "An error occurred while converting number formats")
414 }
415 Error::InternalError => write!(f, "An internal error occurred, this is a bug"),
416 }
417 }
418}
419
420impl std::error::Error for Error {
421 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
422 match self {
423 Error::IOError(ref err) => Some(err),
424 _ => None,
425 }
426 }
427}
428
429impl Array {
430 pub fn name(&self) -> &str {
432 &self.name
433 }
434
435 pub fn size(&self) -> &Vec<usize> {
442 &self.size
443 }
444
445 pub fn ndims(&self) -> usize {
447 self.size.len()
448 }
449
450 pub fn data(&self) -> &NumericData {
467 &self.data
468 }
469}
470
471impl MatFile {
472 pub fn parse<R: std::io::Read>(mut reader: R) -> Result<Self, Error> {
474 let mut buf = Vec::new();
475 reader
476 .read_to_end(&mut buf)
477 .map_err(|err| Error::IOError(err))?;
478 let (_remaining, parse_result) = parse::parse_all(&buf)
479 .map_err(|err| Error::ParseError(parse::replace_err_slice(err, &[])))?;
480 let arrays: Result<Vec<Array>, Error> = parse_result
481 .data_elements
482 .into_iter()
483 .filter_map(|data_element| match data_element {
484 parse::DataElement::NumericMatrix(flags, dims, name, real, imag) => {
485 let size = dims.into_iter().map(|d| d as usize).collect();
486 let numeric_data = match NumericData::try_from(flags.class, real, imag) {
487 Ok(numeric_data) => numeric_data,
488 Err(err) => return Some(Err(err)),
489 };
490 Some(Ok(Array {
491 size: size,
492 name: name,
493 data: numeric_data,
494 }))
495 }
496 _ => None,
497 })
498 .collect();
499 let arrays = arrays?;
500 Ok(MatFile { arrays: arrays })
501 }
502
503 pub fn arrays(&self) -> &Vec<Array> {
509 &self.arrays
510 }
511
512 pub fn find_by_name<'me>(&'me self, name: &'_ str) -> Option<&'me Array> {
518 for array in &self.arrays {
519 if array.name == name {
520 return Some(array);
521 }
522 }
523 None
524 }
525}
526
527#[cfg(test)]
532mod tests {
533 use super::*;
534
535 #[test]
536 fn double_array() {
537 let data = include_bytes!("../tests/double.mat");
538 let _mat_file = MatFile::parse(data.as_ref()).unwrap();
539 }
540
541 #[test]
542 fn double_as_int16_array() {
543 let data = include_bytes!("../tests/double_as_int16.mat");
544 let _mat_file = MatFile::parse(data.as_ref()).unwrap();
545 }
546
547 #[test]
548 fn double_as_uint8_array() {
549 let data = include_bytes!("../tests/double_as_uint8.mat");
550 let _mat_file = MatFile::parse(data.as_ref()).unwrap();
551 }
552
553 #[test]
554 fn single_complex_array() {
555 let data = include_bytes!("../tests/single_complex.mat");
556 let _mat_file = MatFile::parse(data.as_ref()).unwrap();
557 }
558
559 #[test]
560 fn two_arrays() {
561 let data = include_bytes!("../tests/two_arrays.mat");
562 let _mat_file = MatFile::parse(data.as_ref()).unwrap();
563 }
564
565 #[test]
566 fn multidimensional_array() {
567 let data = include_bytes!("../tests/multidimensional.mat");
568 let _mat_file = MatFile::parse(data.as_ref()).unwrap();
569 }
570
571 #[test]
572 fn long_name() {
573 let data = include_bytes!("../tests/long_name.mat");
574 let _mat_file = MatFile::parse(data.as_ref()).unwrap();
575 }
576}