exif/
endian.rs

1//
2// Copyright (c) 2016 KAMADA Ken'ichi.
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions
7// are met:
8// 1. Redistributions of source code must retain the above copyright
9//    notice, this list of conditions and the following disclaimer.
10// 2. Redistributions in binary form must reproduce the above copyright
11//    notice, this list of conditions and the following disclaimer in the
12//    documentation and/or other materials provided with the distribution.
13//
14// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24// SUCH DAMAGE.
25//
26
27use std::io;
28use std::mem;
29
30// This is a module to select endianess by using generics
31// in order to avoid run-time dispatching penalty at the cost of
32// increased object size.
33
34pub trait Endian {
35    fn loadu16(buf: &[u8], from: usize) -> u16;
36    fn loadu32(buf: &[u8], from: usize) -> u32;
37    fn loadu64(buf: &[u8], from: usize) -> u64;
38    fn writeu16<W>(w: &mut W, num: u16) -> io::Result<()> where W: io::Write;
39    fn writeu32<W>(w: &mut W, num: u32) -> io::Result<()> where W: io::Write;
40    fn writeu64<W>(w: &mut W, num: u64) -> io::Result<()> where W: io::Write;
41}
42
43pub struct BigEndian;
44pub struct LittleEndian;
45
46macro_rules! generate_load {
47    ($name:ident, $int_type:ident, $from_func:ident) => (
48        fn $name(buf: &[u8], offset: usize) -> $int_type {
49            let mut num = [0u8; mem::size_of::<$int_type>()];
50            num.copy_from_slice(
51                &buf[offset .. offset + mem::size_of::<$int_type>()]);
52            $int_type::$from_func(num)
53        }
54    )
55}
56
57macro_rules! generate_write {
58    ($name:ident, $int_type:ident, $to_func:ident) => (
59        fn $name<W>(w: &mut W, num: $int_type)
60                    -> io::Result<()> where W: io::Write {
61            let buf = num.$to_func();
62            w.write_all(&buf)
63        }
64    )
65}
66
67impl Endian for BigEndian {
68    generate_load!(loadu16, u16, from_be_bytes);
69    generate_load!(loadu32, u32, from_be_bytes);
70    generate_load!(loadu64, u64, from_be_bytes);
71    generate_write!(writeu16, u16, to_be_bytes);
72    generate_write!(writeu32, u32, to_be_bytes);
73    generate_write!(writeu64, u64, to_be_bytes);
74}
75
76impl Endian for LittleEndian {
77    generate_load!(loadu16, u16, from_le_bytes);
78    generate_load!(loadu32, u32, from_le_bytes);
79    generate_load!(loadu64, u64, from_le_bytes);
80    generate_write!(writeu16, u16, to_le_bytes);
81    generate_write!(writeu32, u32, to_le_bytes);
82    generate_write!(writeu64, u64, to_le_bytes);
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn loadu16() {
91        assert_eq!(BigEndian::loadu16(&[0x01, 0x02], 0), 0x0102);
92        assert_eq!(BigEndian::loadu16(&[0x01, 0x02, 0x03], 1), 0x0203);
93        assert_eq!(LittleEndian::loadu16(&[0x01, 0x02], 0), 0x0201);
94        assert_eq!(LittleEndian::loadu16(&[0x01, 0x02, 0x03], 1), 0x0302);
95    }
96
97    #[test]
98    fn loadu32() {
99        assert_eq!(BigEndian::loadu32(&[0x01, 0x02, 0x03, 0x04], 0),
100                   0x01020304);
101        assert_eq!(BigEndian::loadu32(&[0x01, 0x02, 0x03, 0x04, 0x05], 1),
102                   0x02030405);
103        assert_eq!(LittleEndian::loadu32(&[0x01, 0x02, 0x03, 0x04], 0),
104                   0x04030201);
105        assert_eq!(LittleEndian::loadu32(&[0x01, 0x02, 0x03, 0x04, 0x05], 1),
106                   0x05040302);
107    }
108
109    #[test]
110    fn loadu64() {
111        assert_eq!(BigEndian::loadu64(&[0x01, 0x02, 0x03, 0x04,
112                                        0x05, 0x06, 0x07, 0x08], 0),
113                   0x0102030405060708);
114        assert_eq!(BigEndian::loadu64(&[0x01, 0x02, 0x03, 0x04, 0x05,
115                                        0x06, 0x07, 0x08, 0x09], 1),
116                   0x0203040506070809);
117        assert_eq!(LittleEndian::loadu64(&[0x01, 0x02, 0x03, 0x04,
118                                           0x05, 0x06, 0x07, 0x08], 0),
119                   0x0807060504030201);
120        assert_eq!(LittleEndian::loadu64(&[0x01, 0x02, 0x03, 0x04, 0x05,
121                                           0x06, 0x07, 0x08, 0x09], 1),
122                   0x0908070605040302);
123    }
124
125    #[test]
126    fn writeu16() {
127        let mut buf = Vec::new();
128        BigEndian::writeu16(&mut buf, 0x0102).unwrap();
129        LittleEndian::writeu16(&mut buf, 0x0304).unwrap();
130        assert_eq!(buf, b"\x01\x02\x04\x03");
131    }
132
133    #[test]
134    fn writeu32() {
135        let mut buf = Vec::new();
136        BigEndian::writeu32(&mut buf, 0x01020304).unwrap();
137        LittleEndian::writeu32(&mut buf, 0x05060708).unwrap();
138        assert_eq!(buf, b"\x01\x02\x03\x04\x08\x07\x06\x05");
139    }
140
141    #[test]
142    fn writeu64() {
143        let mut buf = Vec::new();
144        BigEndian::writeu64(&mut buf, 0x0102030405060708).unwrap();
145        LittleEndian::writeu64(&mut buf, 0x090a0b0c0d0e0f10).unwrap();
146        assert_eq!(buf, b"\x01\x02\x03\x04\x05\x06\x07\x08\
147                          \x10\x0f\x0e\x0d\x0c\x0b\x0a\x09");
148    }
149
150    #[test]
151    fn dispatch() {
152        fn dispatch_sub<E>(data: &[u8]) -> u16 where E: Endian {
153            E::loadu16(data, 0)
154        }
155        assert_eq!(dispatch_sub::<BigEndian>(&[0x01, 0x02]), 0x0102);
156        assert_eq!(dispatch_sub::<LittleEndian>(&[0x01, 0x02]), 0x0201);
157    }
158
159    #[test]
160    fn static_dispatch() {
161        fn dispatch_sub<E>(data: &[u8]) -> u16 where E: Endian {
162            E::loadu16(data, 0)
163        }
164        assert_eq!(dispatch_sub::<BigEndian> as *const (),
165                   dispatch_sub::<BigEndian> as *const ());
166        assert_ne!(dispatch_sub::<BigEndian> as *const (),
167                   dispatch_sub::<LittleEndian> as *const ());
168    }
169
170    #[test]
171    #[should_panic(expected = "index 3 out of range for slice of length 2")]
172    fn out_of_range() {
173        BigEndian::loadu16(&[0x01, 0x02], 1);
174    }
175
176    // "attempt to add with overflow" with the arithmetic overflow
177    // check, and "slice index starts at 18446744073709551615 but ends
178    // at 1" without it.
179    #[test]
180    #[should_panic(expected = "at")]
181    fn wrap_around() {
182        BigEndian::loadu16(&[0x01, 0x02], (-1isize) as usize);
183    }
184}