1use crate::io::{ReadExt as _, SeekExt as _};
2use crate::{Error, ErrorKind, Result};
3use ndarray;
4use ndarray::ArrayD;
5use std;
6use std::convert::TryFrom;
7use std::io::{Read, Seek};
8
9#[derive(Debug)]
11pub enum DataObject {
12    Float(ArrayD<f64>),
14}
15
16#[derive(Debug, Clone)]
19pub struct ObjectHeader {
20    prefix: ObjectHeaderPrefix,
21}
22impl ObjectHeader {
23    pub fn from_reader<R: Read>(mut reader: R) -> Result<Self> {
24        let prefix = track!(ObjectHeaderPrefix::from_reader(&mut reader))?;
25        Ok(Self { prefix })
26    }
27
28    pub fn get_data_object<R: Read + Seek>(&self, mut reader: R) -> Result<DataObject> {
29        let bytes = track!(self.get_data_bytes(&mut reader))?;
30        let dimensions = track!(self.dimensions())?
31            .iter()
32            .map(|&d| d as usize)
33            .collect::<Vec<_>>();
34        let datatype = track!(self.datatype())?;
35
36        let count = dimensions.iter().cloned().product::<usize>();
37        let mut reader = &bytes[..];
38        match datatype {
39            DatatypeMessage::FloatingPoint(t) => {
40                let items = (0..count)
41                    .map(|i| track!(t.decode(&mut reader); i))
42                    .collect::<Result<Vec<_>>>()?;
43                track_assert_eq!(reader, b"", ErrorKind::InvalidFile);
44
45                let items = track!(ndarray::aview1(&items)
46                    .into_shape(dimensions)
47                    .map_err(Error::from))?;
48                Ok(DataObject::Float(items.to_owned()))
49            }
50            _ => track_panic!(ErrorKind::Unsupported),
51        }
52    }
53
54    fn dimensions(&self) -> Result<&[u64]> {
55        for m in &self.prefix.messages {
56            if let Message::Dataspace(m) = &m.message {
57                return Ok(&m.dimension_sizes);
58            }
59        }
60        track_panic!(ErrorKind::Other);
61    }
62
63    fn datatype(&self) -> Result<DatatypeMessage> {
64        for m in &self.prefix.messages {
65            if let Message::Datatype(m) = &m.message {
66                return Ok(m.clone());
67            }
68        }
69        track_panic!(ErrorKind::Other);
70    }
71
72    pub fn get_data_bytes<R: Read + Seek>(&self, mut reader: R) -> Result<Vec<u8>> {
73        for m in &self.prefix.messages {
74            if let Message::DataLayout(m) = &m.message {
75                let Layout::Contiguous { address, size } = m.layout;
76                track!(reader.seek_to(address))?;
77                return track!(reader.read_vec(size as usize));
78            }
79        }
80        track_panic!(ErrorKind::Other, "Not a data object");
81    }
82}
83
84#[derive(Debug, Clone)]
85pub struct ObjectHeaderPrefix {
86    messages: Vec<HeaderMessage>,
87    object_reference_count: u32,
88    object_header_size: u32,
89}
90impl ObjectHeaderPrefix {
91    pub fn from_reader<R: Read>(mut reader: R) -> Result<Self> {
92        let version = track!(reader.read_u8())?;
93        track_assert_eq!(version, 1, ErrorKind::InvalidFile);
94
95        let _reserved = track!(reader.read_u8())?;
96        track_assert_eq!(_reserved, 0, ErrorKind::InvalidFile);
97
98        let header_message_count = track!(reader.read_u16())?;
99        let object_reference_count = track!(reader.read_u32())?;
100        let object_header_size = track!(reader.read_u32())?;
101
102        track!(reader.skip(4))?;
104
105        let mut reader = reader.take(u64::from(object_header_size));
106        let messages = (0..header_message_count)
107            .map(|_| track!(HeaderMessage::from_reader(&mut reader)))
108            .collect::<Result<_>>()?;
109        track_assert_eq!(reader.limit(), 0, ErrorKind::Other; object_header_size, messages);
110
111        Ok(Self {
112            messages,
113            object_reference_count,
114            object_header_size,
115        })
116    }
117}
118
119bitflags! {
120    struct HeaderMessageFlags: u8 {
121        const CONSTANT = 0b0000_0001;
122        const SHARED = 0b0000_0010;
123        const UNSHARABLE = 0b0000_0100;
124        const CANNOT_WRITE_IF_UNKNOWN = 0b0000_1000;
125        const SET_5_BIT_IF_UNKNOWN = 0b0001_0000;
126        const UNKNOWN_BUT_MODIFIED = 0b0010_0000;
127        const SHARABLE = 0b0100_0000;
128        const FAIL_IF_UNKNOWN = 0b0100_0000;
129    }
130}
131
132#[derive(Debug, Clone)]
133pub struct HeaderMessage {
134    flags: HeaderMessageFlags,
135    message: Message,
136}
137impl HeaderMessage {
138    pub fn from_reader<R: Read>(mut reader: R) -> Result<Self> {
139        let kind = track!(reader.read_u16())?;
140        let data_len = track!(reader.read_u16())?;
141        let flags = HeaderMessageFlags::from_bits_truncate(track!(reader.read_u8())?);
142        track!(reader.skip(3))?;
143        let mut reader = reader.take(u64::from(data_len));
144        let message = match kind {
145            0x00 => track!(NilMessage::from_reader(&mut reader)).map(Message::Nil)?,
146            0x01 => track!(DataspaceMessage::from_reader(&mut reader)).map(Message::Dataspace)?,
147            0x03 => track!(DatatypeMessage::from_reader(&mut reader)).map(Message::Datatype)?,
148            0x05 => track!(FillValueMessage::from_reader(&mut reader)).map(Message::FillValue)?,
149            0x08 => track!(DataLayoutMessage::from_reader(&mut reader)).map(Message::DataLayout)?,
150            0x11 => {
151                track!(SymbolTableMessage::from_reader(&mut reader)).map(Message::SymbolTable)?
152            }
153            0x12 => track!(ObjectModificationTimeMessage::from_reader(&mut reader))
154                .map(Message::ObjectModificationTime)?,
155            _ => track_panic!(ErrorKind::Unsupported, "Message type: {}", kind),
156        };
157        track_assert_eq!(reader.limit(), 0, ErrorKind::Other);
158
159        Ok(Self { flags, message })
160    }
161}
162
163#[derive(Debug, Clone)]
165pub struct NilMessage {}
166impl NilMessage {
167    pub fn from_reader<R: Read>(mut reader: R) -> Result<Self> {
168        let _ = track!(reader.read_all())?;
169        Ok(Self {})
170    }
171}
172
173#[derive(Debug, Clone)]
175pub struct DataspaceMessage {
176    dimension_sizes: Vec<u64>,
177    dimension_max_sizes: Option<Vec<u64>>,
178}
179impl DataspaceMessage {
180    pub fn from_reader<R: Read>(mut reader: R) -> Result<Self> {
181        let version = track!(reader.read_u8())?;
182        track_assert_eq!(version, 1, ErrorKind::Unsupported);
183
184        let dimensionality = track!(reader.read_u8())?;
185        let flags = track!(reader.read_u8())?; track!(reader.skip(5))?;
187
188        let dimension_sizes = (0..dimensionality)
189            .map(|_| track!(reader.read_u64()))
190            .collect::<Result<Vec<_>>>()?;
191
192        let dimension_max_sizes = if (flags & 0b0000_0001) != 0 {
193            Some(
194                (0..dimensionality)
195                    .map(|_| track!(reader.read_u64()))
196                    .collect::<Result<Vec<_>>>()?,
197            )
198        } else {
199            None
200        };
201
202        if (flags & 0b0000_0010) != 0 {
203            track_panic!(ErrorKind::Unsupported);
204        }
205
206        Ok(Self {
207            dimension_sizes,
208            dimension_max_sizes,
209        })
210    }
211}
212
213#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
214pub enum DatatypeClass {
215    FixedPoint,
216    FloatingPoint,
217    Time,
218    String,
219    BitField,
220    Opaque,
221    Compound,
222    Reference,
223    Enumerated,
224    VariableLength,
225    Array,
226}
227impl TryFrom<u8> for DatatypeClass {
228    type Error = Error;
229
230    fn try_from(f: u8) -> Result<Self> {
231        Ok(match f {
232            0 => DatatypeClass::FixedPoint,
233            1 => DatatypeClass::FloatingPoint,
234            2 => DatatypeClass::Time,
235            3 => DatatypeClass::String,
236            4 => DatatypeClass::BitField,
237            5 => DatatypeClass::Opaque,
238            6 => DatatypeClass::Compound,
239            7 => DatatypeClass::Reference,
240            8 => DatatypeClass::Enumerated,
241            9 => DatatypeClass::VariableLength,
242            10 => DatatypeClass::Array,
243            _ => track_panic!(ErrorKind::InvalidFile, "Unknown datatype class: {}", f),
244        })
245    }
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
249pub enum MantissaNorm {
250    None,
251    AlwaysSet,
252    ImpliedToBeSet,
253}
254impl TryFrom<u8> for MantissaNorm {
255    type Error = Error;
256
257    fn try_from(f: u8) -> Result<Self> {
258        match f {
259            0 => Ok(MantissaNorm::None),
260            1 => Ok(MantissaNorm::AlwaysSet),
261            2 => Ok(MantissaNorm::ImpliedToBeSet),
262            3 => track_panic!(ErrorKind::InvalidFile, "Reserved value"),
263            _ => track_panic!(ErrorKind::InvalidInput),
264        }
265    }
266}
267
268#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
269pub enum Endian {
270    Little,
271    Big,
272    Vax,
273}
274impl TryFrom<u8> for Endian {
275    type Error = Error;
276
277    fn try_from(f: u8) -> Result<Self> {
278        match f {
279            0b0000_0000 => Ok(Endian::Little),
280            0b0000_0001 => Ok(Endian::Big),
281            0b0100_0000 => track_panic!(ErrorKind::InvalidFile, "Reserved endian bits"),
282            0b0100_0001 => Ok(Endian::Vax),
283            _ => track_panic!(ErrorKind::InvalidInput),
284        }
285    }
286}
287
288#[derive(Debug, Clone)]
289pub struct FloatingPointDatatype {
290    size: u32,
291
292    endian: Endian,
293    low_padding_bit: u8,
294    high_padding_bit: u8,
295    internal_padding_bit: u8,
296    mantissa_norm: MantissaNorm,
297    sign_location: u8,
298
299    bit_offset: u16,
300    bit_precision: u16,
301    exponent_location: u8,
302    exponent_size: u8,
303    mantissa_location: u8,
304    mantissa_size: u8,
305    exponent_bias: u32,
306}
307impl FloatingPointDatatype {
308    pub fn decode<R: Read>(&self, mut reader: R) -> Result<f64> {
309        track_assert_eq!(self.endian, Endian::Little, ErrorKind::Unsupported);
310        track_assert_eq!(self.low_padding_bit, 0, ErrorKind::Unsupported);
311        track_assert_eq!(self.high_padding_bit, 0, ErrorKind::Unsupported);
312        track_assert_eq!(self.internal_padding_bit, 0, ErrorKind::Unsupported);
313        track_assert_eq!(
314            self.mantissa_norm,
315            MantissaNorm::ImpliedToBeSet,
316            ErrorKind::Unsupported
317        );
318        track_assert_eq!(self.sign_location, 31, ErrorKind::Unsupported);
319
320        track_assert_eq!(self.bit_offset, 0, ErrorKind::Unsupported);
321        track_assert_eq!(self.bit_precision, 32, ErrorKind::Unsupported);
322        track_assert_eq!(self.exponent_location, 23, ErrorKind::Unsupported);
323        track_assert_eq!(self.exponent_size, 8, ErrorKind::Unsupported);
324        track_assert_eq!(self.mantissa_location, 0, ErrorKind::Unsupported);
325        track_assert_eq!(self.mantissa_size, 23, ErrorKind::Unsupported);
326        track_assert_eq!(self.exponent_bias, 127, ErrorKind::Unsupported);
327
328        track!(reader.read_f32()).map(f64::from)
329    }
330
331    pub fn from_reader<R: Read>(bit_field: u32, size: u32, mut reader: R) -> Result<Self> {
332        let bit_offset = track!(reader.read_u16())?;
333        let bit_precision = track!(reader.read_u16())?;
334        let exponent_location = track!(reader.read_u8())?;
335        let exponent_size = track!(reader.read_u8())?;
336        let mantissa_location = track!(reader.read_u8())?;
337        let mantissa_size = track!(reader.read_u8())?;
338        let exponent_bias = track!(reader.read_u32())?;
339        track!(reader.skip(4))?;
340
341        Ok(Self {
342            size,
343
344            endian: track!(Endian::try_from((bit_field & 0b0100_0001) as u8))?,
345            low_padding_bit: ((bit_field >> 1) & 1) as u8,
346            high_padding_bit: ((bit_field >> 2) & 1) as u8,
347            internal_padding_bit: ((bit_field >> 3) & 1) as u8,
348            mantissa_norm: track!(MantissaNorm::try_from(((bit_field >> 4) & 0b11) as u8))?,
349            sign_location: (bit_field >> 8) as u8,
350
351            bit_offset,
352            bit_precision,
353            exponent_location,
354            exponent_size,
355            mantissa_location,
356            mantissa_size,
357            exponent_bias,
358        })
359    }
360}
361
362#[derive(Debug, Clone)]
363pub struct FixedPointDatatype {
364    bit_field: u32,
365    size: u32,
366
367    bit_offset: u16,
368    bit_precision: u16,
369}
370impl FixedPointDatatype {
371    pub fn from_reader<R: Read>(bit_field: u32, size: u32, mut reader: R) -> Result<Self> {
372        let bit_offset = track!(reader.read_u16())?;
373        let bit_precision = track!(reader.read_u16())?;
374        track!(reader.skip(4))?;
375
376        Ok(Self {
377            bit_field,
378            size,
379
380            bit_offset,
381            bit_precision,
382        })
383    }
384}
385
386#[derive(Debug, Clone)]
388pub enum DatatypeMessage {
389    FixedPoint(FixedPointDatatype),
390    FloatingPoint(FloatingPointDatatype),
391    }
401impl DatatypeMessage {
402    pub fn from_reader<R: Read>(mut reader: R) -> Result<Self> {
410        let class_and_version = track!(reader.read_u8())?;
411        let version = class_and_version >> 4;
412        let class = track!(DatatypeClass::try_from(class_and_version & 0b0000_1111))?;
413        track_assert_eq!(version, 1, ErrorKind::Unsupported);
414
415        let bit_field = track!(reader.read_u24())?;
416        let size = track!(reader.read_u32())?;
417
418        match class {
419            DatatypeClass::FixedPoint => {
420                track!(FixedPointDatatype::from_reader(bit_field, size, reader))
421                    .map(DatatypeMessage::FixedPoint)
422            }
423            DatatypeClass::FloatingPoint => {
424                track!(FloatingPointDatatype::from_reader(bit_field, size, reader))
425                    .map(DatatypeMessage::FloatingPoint)
426            }
427            _ => track_panic!(ErrorKind::Unsupported; class),
428        }
429    }
430}
431
432#[derive(Debug, Clone)]
434pub struct FillValueMessage {
435    space_allocation_time: u8,
436    fill_value_write_time: u8,
437    fill_value: Option<Vec<u8>>,
438}
439impl FillValueMessage {
440    pub fn from_reader<R: Read>(mut reader: R) -> Result<Self> {
441        let version = track!(reader.read_u8())?;
442        track_assert_eq!(version, 2, ErrorKind::Unsupported);
443
444        let space_allocation_time = track!(reader.read_u8())?;
445        let fill_value_write_time = track!(reader.read_u8())?;
446        let fill_value_defined = track!(reader.read_u8())?;
447        let fill_value = if fill_value_defined == 1 {
448            let size = track!(reader.read_u32())?;
449            let fill_value = track!(reader.read_vec(size as usize))?;
450            Some(fill_value)
451        } else {
452            None
453        };
454        Ok(Self {
455            space_allocation_time,
456            fill_value_write_time,
457            fill_value,
458        })
459    }
460}
461
462#[derive(Debug, Clone)]
463pub enum Layout {
464    Contiguous { address: u64, size: u64 },
465}
466impl Layout {
467    pub fn from_reader<R: Read>(class: u8, mut reader: R) -> Result<Self> {
468        match class {
469            0 => track_panic!(ErrorKind::Unsupported),
470            1 => {
471                let address = track!(reader.read_u64())?;
472                let size = track!(reader.read_u64())?;
473                Ok(Layout::Contiguous { address, size })
474            }
475            2 => track_panic!(ErrorKind::Unsupported),
476            _ => track_panic!(ErrorKind::InvalidFile, "Unknown layout class: {}", class),
477        }
478    }
479}
480
481#[derive(Debug, Clone)]
483pub struct DataLayoutMessage {
484    layout: Layout,
485}
486impl DataLayoutMessage {
487    pub fn from_reader<R: Read>(mut reader: R) -> Result<Self> {
488        let version = track!(reader.read_u8())?;
489        track_assert_eq!(version, 3, ErrorKind::Unsupported);
490
491        let layout_class = track!(reader.read_u8())?;
492        let layout = track!(Layout::from_reader(layout_class, &mut reader))?;
493        let _padding = track!(reader.read_all())?;
494        Ok(Self { layout })
495    }
496}
497
498#[derive(Debug, Clone)]
500pub struct SymbolTableMessage {
501    pub b_tree_address: u64,
502    pub local_heap_address: u64,
503}
504impl SymbolTableMessage {
505    pub fn from_reader<R: Read>(mut reader: R) -> Result<Self> {
506        Ok(Self {
507            b_tree_address: track!(reader.read_u64())?,
508            local_heap_address: track!(reader.read_u64())?,
509        })
510    }
511}
512
513#[derive(Debug, Clone)]
515pub struct ObjectModificationTimeMessage {
516    unixtime_seconds: u32,
517}
518impl ObjectModificationTimeMessage {
519    pub fn from_reader<R: Read>(mut reader: R) -> Result<Self> {
520        let version = track!(reader.read_u8())?;
521        track_assert_eq!(version, 1, ErrorKind::Unsupported);
522        track!(reader.skip(3))?;
523
524        let unixtime_seconds = track!(reader.read_u32())?;
525        Ok(Self { unixtime_seconds })
526    }
527}
528
529#[derive(Debug, Clone)]
530pub enum Message {
531    Nil(NilMessage),
532    Dataspace(DataspaceMessage),
533    Datatype(DatatypeMessage),
535    FillValue(FillValueMessage),
537    DataLayout(DataLayoutMessage),
540    SymbolTable(SymbolTableMessage),
549    ObjectModificationTime(ObjectModificationTimeMessage),
550    }
555
556#[cfg(test)]
557mod tests {
558    use super::*;
559    use trackable::result::TopLevelResult;
560
561    #[test]
562    fn floating_point_decode_works() -> TopLevelResult {
563        let datatype = FloatingPointDatatype {
564            size: 4,
565            endian: Endian::Little,
566            low_padding_bit: 0,
567            high_padding_bit: 0,
568            internal_padding_bit: 0,
569            mantissa_norm: MantissaNorm::ImpliedToBeSet,
570            sign_location: 31,
571            bit_offset: 0,
572            bit_precision: 32,
573            exponent_location: 23,
574            exponent_size: 8,
575            mantissa_location: 0,
576            mantissa_size: 23,
577            exponent_bias: 127,
578        };
579        let bytes = [166, 73, 90, 67];
580
581        let item = track!(datatype.decode(&bytes[..]))?;
582        assert_eq!(item, 218.28768920898438);
583        Ok(())
584    }
585}