1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//
// Copyright (c) 2017 KAMADA Ken'ichi.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//

use std::collections::HashMap;
use std::io;
use std::io::Read;
use std::mem;

use error::Error;
use jpeg;
use tag_priv::Tag;
use tiff;
use tiff::Field;

/// The `Reader` struct reads a JPEG or TIFF image,
/// parses the Exif attributes in it, and holds the results.
//
// The struct Reader is self-contained, which means that it does not
// have any external reference.  The `fields` field actually refers to
// the `buf` field.  The type system of the current Rust (as of 1.15)
// cannot represent this, so the static lifetime is used to trick it.
//
// This struct can be moved because the contents of a Vec are allocated
// in the heap and do not change their addresses by the move.
//
// The static lifetime is a lie and it must be kept secret in this struct.
// - This struct must not be destructured by the users.
// - The `fields` must be adjusted to refer the struct itself when
//   returned to the outside world.
pub struct Reader {
    // TIFF data.
    buf: Vec<u8>,
    // Exif fields.
    fields: Vec<Field<'static>>,
    // True if the TIFF data is little endian.
    little_endian: bool,
    // HashMap to find a field quickly.
    field_map: HashMap<(Tag, bool), &'static Field<'static>>,
}

impl Reader {
    /// Reads a JPEG or TIFF image and parses the Exif attributes in it.
    /// If an error occurred, `exif::Error` is returned.
    pub fn new<R>(mut reader: &mut R)
                  -> Result<Reader, Error> where R: io::BufRead {
        let mut buf = Vec::new();
        try!(reader.by_ref().take(4).read_to_end(&mut buf));
        if jpeg::is_jpeg(&buf) {
            let exif_buf = try!(jpeg::get_exif_attr(
                &mut buf.as_mut_slice().chain(reader)));
            buf = exif_buf;
        } else if tiff::is_tiff(&buf) {
            try!(reader.read_to_end(&mut buf));
        } else {
            return Err(Error::InvalidFormat("Unknown image format"));
        }

        // Cheat on the type system and erase the lifetime by transmute().
        // The scope releases the inner `v` to unborrow `buf`.
        let (fields, le) = {
            let (v, le) = try!(tiff::parse_exif(&buf));
            (unsafe { mem::transmute::<Vec<Field>, Vec<Field>>(v) }, le) };

        // Initialize the HashMap of all fields.
        let mut field_map = HashMap::new();
        for f in &fields {
            field_map.insert((f.tag, f.thumbnail),
                             unsafe { mem::transmute::<&Field, &Field>(f) });
        }

        Ok(Reader {
            buf: buf,
            fields: fields,
            little_endian: le,
            field_map: field_map
        })
    }

    /// Returns the slice that contains the TIFF data.
    #[inline]
    pub fn buf(&self) -> &[u8] {
        &self.buf[..]
    }

    /// Returns a slice of Exif fields.
    #[inline]
    pub fn fields<'a>(&'a self) -> &[Field<'a>] {
        &self.fields
    }

    /// Returns true if the TIFF data is in the little-endian byte order.
    #[inline]
    pub fn little_endian(&self) -> bool {
        self.little_endian
    }

    /// Returns a reference to the Exif field specified by the tag
    /// and the thumbnail flag.
    #[inline]
    pub fn get_field(&self, tag: Tag, thumbnail: bool) -> Option<&Field> {
        self.field_map.get(&(tag, thumbnail)).map(|&f| f)
    }
}

#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::io::BufReader;
    use value::Value;
    use tag_priv::constants as tag;
    use super::*;

    static TIFF_ASCII: &'static [u8] =
        b"MM\0\x2a\0\0\0\x08\0\x01\x01\x0e\0\x02\0\0\0\x04ABC\0\0\0\0\0";

    // Test if moving a `Reader` does not invalidate the references in it.
    // The referer is in the heap and does not move, so this test is not
    // so meaningful.
    #[test]
    fn move_reader() {
        let r1 = Reader::new(&mut BufReader::new(TIFF_ASCII)).unwrap();
        let ptr = &r1 as *const _;
        move_in_and_drop(r1, ptr);

        let (r2, ptr) = move_out_and_drop();
        assert!(ptr != &r2 as *const _, "not moved");
        check_abc(r2.fields());

        box_and_drop();
    }

    #[inline(never)]
    fn move_in_and_drop(r1: Reader, ptr: *const Reader) {
        assert!(ptr != &r1 as *const _, "not moved");
        check_abc(r1.fields());
    }

    #[inline(never)]
    fn move_out_and_drop() -> (Reader, *const Reader) {
        let r2 = Reader::new(&mut BufReader::new(TIFF_ASCII)).unwrap();
        let ptr = &r2 as *const _;
        (r2, ptr)
    }

    fn box_and_drop() {
        let r = Reader::new(&mut BufReader::new(TIFF_ASCII)).unwrap();
        let ptr = &r as *const _;
        let b = Box::new(r);
        assert!(ptr != &*b as *const _, "not moved");
        check_abc(b.fields());
    }

    fn check_abc(fields: &[Field]) {
        if let Value::Ascii(ref v) = fields[0].value {
            assert_eq!(*v, vec![b"ABC"]);
        } else {
            panic!("TIFF ASCII field is expected");
        }
    }

    #[test]
    fn get_field() {
        let file = File::open("tests/exif.jpg").unwrap();
        let reader = Reader::new(&mut BufReader::new(&file)).unwrap();
        assert_pat!(reader.get_field(tag::ExifVersion, false).unwrap().value,
                    Value::Undefined(b"0230"));
    }
}