ilda_idtf/
lib.rs

1//! A complete implementation of the ILDA Image Data Transfer Format Specification, Revision 011,
2//! 2014-11-16.
3//!
4//! This library provides efficient reading and writing of IDTF. The reader implementation uses a
5//! zero-copy approach where structures are mapped directly to the memory from which they are read.
6//!
7//! ## Usage
8//!
9//! The [**SectionReader**][1] type can be used to read IDTF sections from any type implementing
10//! `std::io::Read`. This allows for efficiently reading the format from any byte source (e.g.
11//! file, memory, socket, etc).
12//!
13//! ```rust,no_run
14//! # let mut reader = &[0u8][..];
15//! let mut reader = ilda_idtf::SectionReader::new(reader);
16//! ```
17//!
18//! The [**open**][2] function is provided as a convenience for opening a buffered IDTF
19//! **SectionReader** for the file at the given path.
20//!
21//! ```rust,no_run
22//! # let path = "/foo/bar";
23//! let mut reader = ilda_idtf::open(path).unwrap();
24//! ```
25//!
26//! The [**SectionReader::read_next**][3] method allows for iteration over the sections contained
27//! within.
28//!
29//! ```rust,no_run
30//! # let mut reader = ilda_idtf::open("/foo/bar").unwrap();
31//! while let Ok(Some(section)) = reader.read_next() {
32//!     // ...
33//! }
34//! ```
35//!
36//! Each yielded [**Section**][4] provides access to the [**Header**][5] and the inner `reader`.
37//! The exact `reader` kind is determined via the [**Format**][6] specified within the header. The
38//! user must pattern match on the section's `reader` field in order to retrieve an instance of the
39//! correct subsection reader type. The user may then read the associated subsection data.
40//!
41//! ```rust,no_run
42//! # let mut reader = ilda_idtf::open("/foo/bar").unwrap();
43//! # while let Ok(Some(section)) = reader.read_next() {
44//! match section.reader {
45//!     ilda_idtf::SubsectionReaderKind::Coords3dIndexedColor(mut r) => {
46//!         while let Some(point) = r.read_next().unwrap() {
47//!             // ...
48//!         }
49//!     }
50//!     ilda_idtf::SubsectionReaderKind::Coords2dIndexedColor(mut r) => {
51//!         while let Some(point) = r.read_next().unwrap() {
52//!             // ...
53//!         }
54//!     }
55//!     ilda_idtf::SubsectionReaderKind::ColorPalette(mut r) => {
56//!         while let Some(palette) = r.read_next().unwrap() {
57//!             // ...
58//!         }
59//!     }
60//!     ilda_idtf::SubsectionReaderKind::Coords3dTrueColor(mut r) => {
61//!         while let Some(point) = r.read_next().unwrap() {
62//!             // ...
63//!         }
64//!     }
65//!     ilda_idtf::SubsectionReaderKind::Coords2dTrueColor(mut r) => {
66//!         while let Some(point) = r.read_next().unwrap() {
67//!             // ...
68//!         }
69//!     }
70//! }
71//! # }
72//! ```
73//!
74//! In order to interpret the indexed color data formats, a color palette must be used. A color
75//! palette is an ordered list of RGB colors. While the palette *should* be specified by a
76//! preceding `Section`, this is not always the case. The ILDA IDTF specification recommends a
77//! default palette. This palette is provided via the [**DEFAULT_PALETTE**][7] constant.
78//!
79//! [1]: struct.SectionReader.html
80//! [2]: fn.open.html
81//! [3]: struct.SectionReader.html#method.read_next
82//! [4]: struct.Section.html
83//! [5]: layout/struct.Header.html
84//! [6]: layout/struct.Format.html
85//! [7]: constant.DEFAULT_PALETTE.html
86
87#[macro_use]
88extern crate bitflags;
89
90use std::{
91    io::{self, Read},
92    mem,
93    path::Path,
94};
95
96pub mod layout;
97
98/// A helper trait for producing and working with precisely sized buffers for IDTF layout.
99pub trait LayoutBuffer: zerocopy::FromBytes {
100    type Buffer;
101    fn empty() -> Self::Buffer;
102    fn slice(buffer: &Self::Buffer) -> &[u8];
103    fn slice_mut(buffer: &mut Self::Buffer) -> &mut [u8];
104}
105
106/// Reads a sequence of frames from the ILDA IDTF spec from a stream of bytes.
107///
108/// Reads `Section`s.
109pub struct SectionReader<R> {
110    reader: R,
111    buffer: [u8; mem::size_of::<layout::Header>()],
112}
113
114/// Contains a verified **Header** and a reader for the section contents.
115pub struct Section<'a, R>
116where
117    R: Read,
118{
119    pub header: &'a layout::Header,
120    pub reader: SubsectionReaderKind<R>,
121}
122
123/// Reads `len` consecutive subsections of type `T`.
124pub struct SubsectionReader<R, T>
125where
126    R: Read,
127    T: LayoutBuffer,
128{
129    reader: R,
130    len: u16,
131    buffer: T::Buffer,
132    subsection_layout: std::marker::PhantomData<T>,
133}
134
135pub type Coords3dIndexedColorReader<R> = SubsectionReader<R, layout::Coords3dIndexedColor>;
136pub type Coords2dIndexedColorReader<R> = SubsectionReader<R, layout::Coords2dIndexedColor>;
137pub type ColorPaletteReader<R> = SubsectionReader<R, layout::ColorPalette>;
138pub type Coords3dTrueColorReader<R> = SubsectionReader<R, layout::Coords3dTrueColor>;
139pub type Coords2dTrueColorReader<R> = SubsectionReader<R, layout::Coords2dTrueColor>;
140
141/// The subsection reader kind determined via the header's `format` field.
142pub enum SubsectionReaderKind<R>
143where
144    R: Read,
145{
146    Coords3dIndexedColor(Coords3dIndexedColorReader<R>),
147    Coords2dIndexedColor(Coords2dIndexedColorReader<R>),
148    ColorPalette(ColorPaletteReader<R>),
149    Coords3dTrueColor(Coords3dTrueColorReader<R>),
150    Coords2dTrueColor(Coords2dTrueColorReader<R>),
151}
152
153impl<R> SectionReader<R>
154where
155    R: Read,
156{
157    /// Read ILDA IDTF sections from the given reader.
158    pub fn new(reader: R) -> Self {
159        let buffer = [0u8; mem::size_of::<layout::Header>()];
160        SectionReader { reader, buffer }
161    }
162
163    /// Begin reading the next **Section**.
164    ///
165    /// A successfully read **Section** contains a verified **Header** and a reader for the section
166    /// contents.
167    pub fn read_next(&mut self) -> io::Result<Option<Section<&mut R>>> {
168        let SectionReader {
169            ref mut buffer,
170            ref mut reader,
171        } = *self;
172
173        // Buffer the header bytes.
174        if let Err(err) = reader.read_exact(buffer) {
175            if let io::ErrorKind::UnexpectedEof = err.kind() {
176                return Ok(None);
177            }
178        }
179
180        // Verify the header layout.
181        let header: &layout::Header = zerocopy::LayoutVerified::new(&buffer[..])
182            .map(zerocopy::LayoutVerified::into_ref)
183            .ok_or_else(|| {
184                let err_msg = "could not verify the layout of `Header`";
185                io::Error::new(io::ErrorKind::InvalidData, err_msg)
186            })?;
187
188        // Validate header by ascii "ILDA".
189        if header.ilda != layout::Header::ILDA {
190            let err_msg = "could not verify `Header` due to invalid ILDA ascii";
191            return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
192        }
193
194        // Determine the format.
195        let len = header.num_records.get();
196        let reader = match header.format {
197            layout::Format::COORDS_3D_INDEXED_COLOR => {
198                Coords3dIndexedColorReader::new(reader, len).into()
199            }
200            layout::Format::COORDS_2D_INDEXED_COLOR => {
201                Coords2dIndexedColorReader::new(reader, len).into()
202            }
203            layout::Format::COLOR_PALETTE => ColorPaletteReader::new(reader, len).into(),
204            layout::Format::COORDS_3D_TRUE_COLOR => {
205                Coords3dTrueColorReader::new(reader, len).into()
206            }
207            layout::Format::COORDS_2D_TRUE_COLOR => {
208                Coords2dTrueColorReader::new(reader, len).into()
209            }
210            _ => {
211                let err_msg = "could not verify the layout of `Header`";
212                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
213            }
214        };
215
216        Ok(Some(Section { header, reader }))
217    }
218}
219
220impl<R, T> SubsectionReader<R, T>
221where
222    R: Read,
223    T: LayoutBuffer,
224{
225    fn new(reader: R, len: u16) -> Self {
226        let buffer = T::empty();
227        let subsection_layout = std::marker::PhantomData;
228        Self {
229            reader,
230            len,
231            buffer,
232            subsection_layout,
233        }
234    }
235
236    /// The number of remaining subsections expected.
237    pub fn len(&self) -> u16 {
238        self.len
239    }
240
241    /// Read the next subsection.
242    pub fn read_next(&mut self) -> io::Result<Option<&T>> {
243        match self.len {
244            0 => return Ok(None),
245            ref mut n => *n -= 1,
246        }
247        self.reader.read_exact(T::slice_mut(&mut self.buffer))?;
248        let subsection = zerocopy::LayoutVerified::new(T::slice(&self.buffer))
249            .map(zerocopy::LayoutVerified::into_ref)
250            .ok_or_else(|| {
251                let err_msg = "could not verify the layout of `Header`";
252                io::Error::new(io::ErrorKind::InvalidData, err_msg)
253            })?;
254        Ok(Some(subsection))
255    }
256}
257
258impl<R, T> Drop for SubsectionReader<R, T>
259where
260    R: Read,
261    T: LayoutBuffer,
262{
263    fn drop(&mut self) {
264        while let Ok(Some(_)) = self.read_next() {}
265    }
266}
267
268impl LayoutBuffer for layout::Coords3dIndexedColor {
269    type Buffer = [u8; mem::size_of::<Self>()];
270    fn empty() -> Self::Buffer {
271        [0u8; mem::size_of::<Self>()]
272    }
273    fn slice(buffer: &Self::Buffer) -> &[u8] {
274        &buffer[..]
275    }
276    fn slice_mut(buffer: &mut Self::Buffer) -> &mut [u8] {
277        &mut buffer[..]
278    }
279}
280
281impl LayoutBuffer for layout::Coords2dIndexedColor {
282    type Buffer = [u8; mem::size_of::<Self>()];
283    fn empty() -> Self::Buffer {
284        [0u8; mem::size_of::<Self>()]
285    }
286    fn slice(buffer: &Self::Buffer) -> &[u8] {
287        &buffer[..]
288    }
289    fn slice_mut(buffer: &mut Self::Buffer) -> &mut [u8] {
290        &mut buffer[..]
291    }
292}
293
294impl LayoutBuffer for layout::ColorPalette {
295    type Buffer = [u8; mem::size_of::<Self>()];
296    fn empty() -> Self::Buffer {
297        [0u8; mem::size_of::<Self>()]
298    }
299    fn slice(buffer: &Self::Buffer) -> &[u8] {
300        &buffer[..]
301    }
302    fn slice_mut(buffer: &mut Self::Buffer) -> &mut [u8] {
303        &mut buffer[..]
304    }
305}
306
307impl LayoutBuffer for layout::Coords3dTrueColor {
308    type Buffer = [u8; mem::size_of::<Self>()];
309    fn empty() -> Self::Buffer {
310        [0u8; mem::size_of::<Self>()]
311    }
312    fn slice(buffer: &Self::Buffer) -> &[u8] {
313        &buffer[..]
314    }
315    fn slice_mut(buffer: &mut Self::Buffer) -> &mut [u8] {
316        &mut buffer[..]
317    }
318}
319
320impl LayoutBuffer for layout::Coords2dTrueColor {
321    type Buffer = [u8; mem::size_of::<Self>()];
322    fn empty() -> Self::Buffer {
323        [0u8; mem::size_of::<Self>()]
324    }
325    fn slice(buffer: &Self::Buffer) -> &[u8] {
326        &buffer[..]
327    }
328    fn slice_mut(buffer: &mut Self::Buffer) -> &mut [u8] {
329        &mut buffer[..]
330    }
331}
332
333impl<R> From<Coords3dIndexedColorReader<R>> for SubsectionReaderKind<R>
334where
335    R: Read,
336{
337    fn from(r: Coords3dIndexedColorReader<R>) -> Self {
338        Self::Coords3dIndexedColor(r)
339    }
340}
341
342impl<R> From<Coords2dIndexedColorReader<R>> for SubsectionReaderKind<R>
343where
344    R: Read,
345{
346    fn from(r: Coords2dIndexedColorReader<R>) -> Self {
347        Self::Coords2dIndexedColor(r)
348    }
349}
350
351impl<R> From<ColorPaletteReader<R>> for SubsectionReaderKind<R>
352where
353    R: Read,
354{
355    fn from(r: ColorPaletteReader<R>) -> Self {
356        Self::ColorPalette(r)
357    }
358}
359
360impl<R> From<Coords3dTrueColorReader<R>> for SubsectionReaderKind<R>
361where
362    R: Read,
363{
364    fn from(r: Coords3dTrueColorReader<R>) -> Self {
365        Self::Coords3dTrueColor(r)
366    }
367}
368
369impl<R> From<Coords2dTrueColorReader<R>> for SubsectionReaderKind<R>
370where
371    R: Read,
372{
373    fn from(r: Coords2dTrueColorReader<R>) -> Self {
374        Self::Coords2dTrueColor(r)
375    }
376}
377
378/// A `SectionReader` that reads from a buffered file.
379pub type BufFileSectionReader = SectionReader<io::BufReader<std::fs::File>>;
380
381/// Open the file at the given path as a `SectionReader`.
382///
383/// Returns a `SectionReader` that performs buffered reads on the file at the given path.
384pub fn open<P>(path: P) -> io::Result<BufFileSectionReader>
385where
386    P: AsRef<Path>,
387{
388    open_path(path.as_ref())
389}
390
391fn open_path(path: &Path) -> io::Result<BufFileSectionReader> {
392    let file = std::fs::File::open(path).unwrap();
393    let buf_reader = std::io::BufReader::new(file);
394    Ok(SectionReader::new(buf_reader))
395}
396
397/// As recommended in the specification appendix.
398pub const DEFAULT_PALETTE: [layout::Color; 64] = [
399    layout::Color {
400        red: 255,
401        green: 0,
402        blue: 0,
403    },
404    layout::Color {
405        red: 255,
406        green: 16,
407        blue: 0,
408    },
409    layout::Color {
410        red: 255,
411        green: 32,
412        blue: 0,
413    },
414    layout::Color {
415        red: 255,
416        green: 48,
417        blue: 0,
418    },
419    layout::Color {
420        red: 255,
421        green: 64,
422        blue: 0,
423    },
424    layout::Color {
425        red: 255,
426        green: 80,
427        blue: 0,
428    },
429    layout::Color {
430        red: 255,
431        green: 96,
432        blue: 0,
433    },
434    layout::Color {
435        red: 255,
436        green: 112,
437        blue: 0,
438    },
439    layout::Color {
440        red: 255,
441        green: 128,
442        blue: 0,
443    },
444    layout::Color {
445        red: 255,
446        green: 144,
447        blue: 0,
448    },
449    layout::Color {
450        red: 255,
451        green: 160,
452        blue: 0,
453    },
454    layout::Color {
455        red: 255,
456        green: 176,
457        blue: 0,
458    },
459    layout::Color {
460        red: 255,
461        green: 192,
462        blue: 0,
463    },
464    layout::Color {
465        red: 255,
466        green: 208,
467        blue: 0,
468    },
469    layout::Color {
470        red: 255,
471        green: 224,
472        blue: 0,
473    },
474    layout::Color {
475        red: 255,
476        green: 240,
477        blue: 0,
478    },
479    layout::Color {
480        red: 255,
481        green: 255,
482        blue: 0,
483    },
484    layout::Color {
485        red: 224,
486        green: 255,
487        blue: 0,
488    },
489    layout::Color {
490        red: 192,
491        green: 255,
492        blue: 0,
493    },
494    layout::Color {
495        red: 160,
496        green: 255,
497        blue: 0,
498    },
499    layout::Color {
500        red: 128,
501        green: 255,
502        blue: 0,
503    },
504    layout::Color {
505        red: 96,
506        green: 255,
507        blue: 0,
508    },
509    layout::Color {
510        red: 64,
511        green: 255,
512        blue: 0,
513    },
514    layout::Color {
515        red: 32,
516        green: 255,
517        blue: 0,
518    },
519    layout::Color {
520        red: 0,
521        green: 255,
522        blue: 0,
523    },
524    layout::Color {
525        red: 0,
526        green: 255,
527        blue: 36,
528    },
529    layout::Color {
530        red: 0,
531        green: 255,
532        blue: 73,
533    },
534    layout::Color {
535        red: 0,
536        green: 255,
537        blue: 109,
538    },
539    layout::Color {
540        red: 0,
541        green: 255,
542        blue: 146,
543    },
544    layout::Color {
545        red: 0,
546        green: 255,
547        blue: 182,
548    },
549    layout::Color {
550        red: 0,
551        green: 255,
552        blue: 219,
553    },
554    layout::Color {
555        red: 0,
556        green: 255,
557        blue: 255,
558    },
559    layout::Color {
560        red: 0,
561        green: 227,
562        blue: 255,
563    },
564    layout::Color {
565        red: 0,
566        green: 198,
567        blue: 255,
568    },
569    layout::Color {
570        red: 0,
571        green: 170,
572        blue: 255,
573    },
574    layout::Color {
575        red: 0,
576        green: 142,
577        blue: 255,
578    },
579    layout::Color {
580        red: 0,
581        green: 113,
582        blue: 255,
583    },
584    layout::Color {
585        red: 0,
586        green: 85,
587        blue: 255,
588    },
589    layout::Color {
590        red: 0,
591        green: 56,
592        blue: 255,
593    },
594    layout::Color {
595        red: 0,
596        green: 28,
597        blue: 255,
598    },
599    layout::Color {
600        red: 0,
601        green: 0,
602        blue: 255,
603    },
604    layout::Color {
605        red: 32,
606        green: 0,
607        blue: 255,
608    },
609    layout::Color {
610        red: 64,
611        green: 0,
612        blue: 255,
613    },
614    layout::Color {
615        red: 96,
616        green: 0,
617        blue: 255,
618    },
619    layout::Color {
620        red: 128,
621        green: 0,
622        blue: 255,
623    },
624    layout::Color {
625        red: 160,
626        green: 0,
627        blue: 255,
628    },
629    layout::Color {
630        red: 192,
631        green: 0,
632        blue: 255,
633    },
634    layout::Color {
635        red: 224,
636        green: 0,
637        blue: 255,
638    },
639    layout::Color {
640        red: 255,
641        green: 0,
642        blue: 255,
643    },
644    layout::Color {
645        red: 255,
646        green: 32,
647        blue: 255,
648    },
649    layout::Color {
650        red: 255,
651        green: 64,
652        blue: 255,
653    },
654    layout::Color {
655        red: 255,
656        green: 96,
657        blue: 255,
658    },
659    layout::Color {
660        red: 255,
661        green: 128,
662        blue: 255,
663    },
664    layout::Color {
665        red: 255,
666        green: 160,
667        blue: 255,
668    },
669    layout::Color {
670        red: 255,
671        green: 192,
672        blue: 255,
673    },
674    layout::Color {
675        red: 255,
676        green: 224,
677        blue: 255,
678    },
679    layout::Color {
680        red: 255,
681        green: 255,
682        blue: 255,
683    },
684    layout::Color {
685        red: 255,
686        green: 224,
687        blue: 224,
688    },
689    layout::Color {
690        red: 255,
691        green: 192,
692        blue: 192,
693    },
694    layout::Color {
695        red: 255,
696        green: 160,
697        blue: 160,
698    },
699    layout::Color {
700        red: 255,
701        green: 128,
702        blue: 128,
703    },
704    layout::Color {
705        red: 255,
706        green: 96,
707        blue: 96,
708    },
709    layout::Color {
710        red: 255,
711        green: 64,
712        blue: 64,
713    },
714    layout::Color {
715        red: 255,
716        green: 32,
717        blue: 32,
718    },
719];