exifsd/scan_segment.rs
1use crate::*;
2use byteorder::{BigEndian, WriteBytesExt};
3use combine::{parser::*, *};
4use std::io;
5
6/// A Scan Segment is contains a segment of the JPEG data.
7#[derive(Copy, Clone, Debug, PartialEq, Eq)]
8pub struct ScanSegment<'a> {
9 /// Specifies which segment is encoded following this specifier.
10 pub specifier: &'a [u8],
11 /// The actual entropy-encoded data that makes up the image segment.
12 pub data: &'a [u8],
13}
14
15impl<'a> ScanSegment<'a> {
16 /// Parses out a Scan Segment, including the entropy-encoded data.
17 ///
18 /// ```
19 /// use exifsd::*;
20 /// use combine::*;
21 ///
22 /// let input = &[0xFF, 0xDA, 0x00, 0x02, 0x01, 0xFF, 0x00, 0x02, 0xFF, 0xFF, 0xD9][..];
23 /// let result = ScanSegment::parser().parse(input);
24 /// let expected = ScanSegment { specifier: &[], data: &[0x01, 0xFF, 0x00, 0x02, 0xFF] };
25 ///
26 /// // Note that the marker `[0xFF, 0xD9]` is not consumed.
27 /// assert_eq!(result, Ok((expected, &[0xFF, 0xD9][..])));
28 /// ```
29 pub fn parser<I: 'a>() -> impl Parser<Input = I, Output = ScanSegment<'a>> + 'a
30 where
31 I: RangeStream<Item = u8, Range = &'a [u8]>,
32 I::Error: ParseError<I::Item, I::Range, I::Position>,
33 {
34 MarkedData::parser(token(0xDA).expected("Start of Scan marker")).then(|md| {
35 segment_data().map(move |data| ScanSegment {
36 specifier: md.data,
37 data,
38 })
39 })
40 }
41
42 /// Writes the binary representation of the `ScanSegment` out to a file.
43 ///
44 /// ```
45 /// use exifsd::*;
46 /// use combine::*;
47 ///
48 /// let input = &[0xFF, 0xDA, 0x00, 0x02, 0x01, 0xFF, 0x00, 0x02][..];
49 /// let scan_segment = ScanSegment::parser().parse(input).unwrap().0;
50 /// let mut written = vec![];
51 /// scan_segment.write(&mut written).unwrap();
52 /// assert_eq!(input, &written[..]);
53 /// ```
54 pub fn write<W: WriteBytesExt>(&self, writer: &mut W) -> io::Result<()> {
55 // Start of Segment marker.
56 writer.write_u16::<BigEndian>(0xFFDA)?;
57 // Include the size of the data field in its own size.
58 writer.write_u16::<BigEndian>((self.specifier.len() + 2) as u16)?;
59 writer.write_all(self.specifier)?;
60 writer.write_all(self.data)
61 }
62}
63
64/// Parses out an entropy-encoded data section, including `0xFF` padding.
65///
66/// ```
67/// use exifsd::*;
68/// use combine::*;
69///
70/// let result = segment_data().parse(&[0x01, 0xFF, 0x00, 0x02, 0xFF, 0xFF, 0xD9][..]);
71///
72/// // Note that the marker `[0xFF, 0xD9]` is not consumed.
73/// assert_eq!(result, Ok(((&[0x01, 0xFF, 0x00, 0x02, 0xFF][..]), &[0xFF, 0xD9][..])));
74///
75/// let result = segment_data().parse(&[0x01, 0xFF, 0x00, 0x02, 0xFF, 0xFF, 0x00][..]);
76///
77/// // Note that the marker `[0xFF, 0x00]` is not consumed because it follows the padding.
78/// assert_eq!(result, Ok(((&[0x01, 0xFF, 0x00, 0x02, 0xFF][..]), &[0xFF, 0x00][..])));
79/// ```
80pub fn segment_data<'a, I: 'a>() -> impl Parser<Input = I, Output = &'a [u8]> + 'a
81where
82 I: RangeStream<Item = u8, Range = &'a [u8]>,
83 I::Error: ParseError<I::Item, I::Range, I::Position>,
84{
85 let marker = |m| range::recognize(token(0xFF)).skip(look_ahead(m));
86 let escape = marker(token(0x00));
87 let padding = marker(token(0xFF));
88 let unescaped_data = range::recognize(none_of(std::iter::once(0xFF)));
89 range::recognize(
90 skip_many(choice((
91 attempt(unescaped_data),
92 attempt(escape),
93 attempt(rst()),
94 )))
95 .skip(skip_many(attempt(padding))),
96 )
97}