1use byteorder;
10use byteorder::ByteOrder;
11use digest;
12use digest::generic_array::GenericArray;
13
14use std::fmt;
15use std::fmt::Debug;
16use std::marker;
17use std::mem;
18
19macro_rules! endian_methods {
20 (
21 $t:ty,
22 $size:expr,
23 $input_method:ident,
24 $chain_method:ident,
25 $bo_func:ident
26 ) => {
27 fn $input_method(&mut self, n: $t) {
28 let mut buf: [u8; $size]
29 = unsafe { mem::uninitialized() };
30 <Self::ByteOrder>::$bo_func(&mut buf, n);
31 self.input(&buf);
32 }
33
34 fn $chain_method(mut self, n: $t) -> Self
35 where Self: Sized {
36 self.$input_method(n);
37 self
38 }
39 }
40}
41
42pub trait EndianInput: digest::Input {
48 type ByteOrder: ByteOrder;
54
55 fn input_u8(&mut self, n: u8) {
60 self.input(&[n]);
61 }
62
63 fn input_i8(&mut self, n: i8) {
68 self.input(&[n as u8]);
69 }
70
71 fn chain_u8(self, n: u8) -> Self
76 where
77 Self: Sized,
78 {
79 self.chain(&[n])
80 }
81
82 fn chain_i8(self, n: i8) -> Self
87 where
88 Self: Sized,
89 {
90 self.chain(&[n as u8])
91 }
92
93 for_all_mi_words!(endian_methods!);
94}
95
96#[derive(Clone)]
98pub struct Endian<D, Bo> {
99 inner: D,
100 phantom: marker::PhantomData<Bo>,
101}
102
103pub type BigEndian<D> = Endian<D, byteorder::BigEndian>;
105
106pub type LittleEndian<D> = Endian<D, byteorder::LittleEndian>;
108
109pub type NetworkEndian<D> = BigEndian<D>;
117
118impl<D, Bo> Endian<D, Bo>
119where
120 Bo: ByteOrder,
121{
122 pub fn byte_order_str() -> &'static str {
128 let mut buf = [0u8; 4];
130 Bo::write_u32(&mut buf, 0x01020304);
131 let le = byteorder::LittleEndian::read_u32(&buf);
132 match le {
133 0x01020304 => "LittleEndian",
134 0x04030201 => "BigEndian",
135 _ => "unknown byte order",
136 }
137 }
138}
139
140impl<D, Bo> Debug for Endian<D, Bo>
141where
142 D: Debug,
143 Bo: ByteOrder,
144{
145 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
146 f.debug_struct(Self::byte_order_str())
147 .field("digest", &self.inner)
148 .finish()
149 }
150}
151
152impl<D, Bo> EndianInput for Endian<D, Bo>
153where
154 D: digest::Input,
155 Bo: ByteOrder,
156{
157 type ByteOrder = Bo;
158}
159
160impl<D, Bo> digest::Input for Endian<D, Bo>
161where
162 D: digest::Input,
163{
164 fn input<B: AsRef<[u8]>>(&mut self, data: B) {
165 self.inner.input(data)
166 }
167}
168
169impl<D, Bo> digest::BlockInput for Endian<D, Bo>
170where
171 D: digest::BlockInput,
172{
173 type BlockSize = D::BlockSize;
174}
175
176impl<D, Bo> digest::FixedOutput for Endian<D, Bo>
177where
178 D: digest::FixedOutput,
179{
180 type OutputSize = D::OutputSize;
181
182 fn fixed_result(self) -> GenericArray<u8, Self::OutputSize> {
183 self.inner.fixed_result()
184 }
185}
186
187impl<D, Bo> digest::Reset for Endian<D, Bo>
188where
189 D: digest::Reset,
190{
191 fn reset(&mut self) {
192 self.inner.reset()
193 }
194}
195
196impl<D, Bo> Endian<D, Bo>
197where
198 D: digest::Input,
199 D: Default,
200 Bo: ByteOrder,
201{
202 pub fn new() -> Self {
215 Endian {
216 inner: D::default(),
217 phantom: marker::PhantomData,
218 }
219 }
220}
221
222impl<D, Bo> Default for Endian<D, Bo>
223where
224 D: digest::Input,
225 D: Default,
226 Bo: ByteOrder,
227{
228 fn default() -> Self {
229 Self::new()
230 }
231}
232
233impl<D, Bo> From<D> for Endian<D, Bo>
234where
235 D: digest::Input,
236 Bo: ByteOrder,
237{
238 fn from(digest: D) -> Self {
239 Endian {
240 inner: digest,
241 phantom: marker::PhantomData,
242 }
243 }
244}
245
246impl<D, Bo> Endian<D, Bo> {
247 pub fn into_inner(self) -> D {
249 self.inner
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use EndianInput;
256 use {BigEndian, LittleEndian, NetworkEndian};
257
258 use testmocks::conv_with;
259 use testmocks::MockDigest;
260
261 use std::mem;
262 use std::{f32, f64};
263
264 #[test]
265 fn default_works() {
266 let _ = NetworkEndian::<MockDigest>::default();
267 }
268
269 macro_rules! test_endian_debug {
270 (
271 $test:ident,
272 $Endian:ident
273 ) => {
274 #[test]
275 fn $test() {
276 assert_eq!($Endian::<MockDigest>::byte_order_str(), stringify!($Endian));
277 let hasher = $Endian::<MockDigest>::new();
278 let repr = format!("{:?}", hasher);
279 assert!(repr.starts_with(stringify!($Endian)));
280 assert!(repr.contains("MockDigest"));
281 assert!(!repr.contains("PhantomData"));
282 }
283 };
284 }
285
286 test_endian_debug!(debug_be, BigEndian);
287 test_endian_debug!(debug_le, LittleEndian);
288
289 macro_rules! test_endian_input {
290 (
291 $test:ident,
292 $Endian:ident,
293 $input_method:ident,
294 $chain_method:ident,
295 $val:expr,
296 $to_endian_bits:expr
297 ) => {
298 #[test]
299 fn $test() {
300 let val_bits = conv_with($val, $to_endian_bits);
301 let expected = bytes_from_endian!(val_bits);
302
303 let mut hasher = $Endian::<MockDigest>::new();
304 hasher.$input_method($val);
305 let output = hasher.into_inner().bytes;
306 assert_eq!(output, expected);
307
308 let hasher = $Endian::<MockDigest>::new();
309 let output = hasher.$chain_method($val).into_inner().bytes;
310 assert_eq!(output, expected);
311 }
312 };
313 }
314
315 macro_rules! test_byte_input {
316 (
317 $be_test:ident,
318 $le_test:ident,
319 $input_method:ident,
320 $chain_method:ident,
321 $val:expr
322 ) => {
323 test_endian_input!(
324 $be_test,
325 BigEndian,
326 $input_method,
327 $chain_method,
328 $val,
329 |v| v
330 );
331 test_endian_input!(
332 $le_test,
333 LittleEndian,
334 $input_method,
335 $chain_method,
336 $val,
337 |v| v
338 );
339 };
340 }
341
342 macro_rules! test_word_input {
343 (
344 $be_test:ident,
345 $le_test:ident,
346 $input_method:ident,
347 $chain_method:ident,
348 $val:expr
349 ) => {
350 test_word_input!(
351 $be_test,
352 $le_test,
353 $input_method,
354 $chain_method,
355 $val,
356 |v| v
357 );
358 };
359
360 (
361 $be_test:ident,
362 $le_test:ident,
363 $input_method:ident,
364 $chain_method:ident,
365 $val:expr,
366 $conv:expr
367 ) => {
368 test_endian_input!(
369 $be_test,
370 BigEndian,
371 $input_method,
372 $chain_method,
373 $val,
374 |v| conv_with(v, $conv).to_be()
375 );
376 test_endian_input!(
377 $le_test,
378 LittleEndian,
379 $input_method,
380 $chain_method,
381 $val,
382 |v| conv_with(v, $conv).to_le()
383 );
384 };
385 }
386
387 macro_rules! test_float_input {
388 (
389 $be_test:ident,
390 $le_test:ident,
391 $input_method:ident,
392 $chain_method:ident,
393 $val:expr
394 ) => {
395 test_word_input!(
396 $be_test,
397 $le_test,
398 $input_method,
399 $chain_method,
400 $val,
401 |v| v.to_bits()
402 );
403 };
404 }
405
406 test_byte_input!(u8_be_input, u8_le_input, input_u8, chain_u8, 0xA5u8);
407 test_byte_input!(i8_be_input, i8_le_input, input_i8, chain_i8, -128i8);
408 test_word_input!(u16_be_input, u16_le_input, input_u16, chain_u16, 0xA55Au16);
409 test_word_input!(i16_be_input, i16_le_input, input_i16, chain_i16, -0x7FFEi16);
410 test_word_input!(
411 u32_be_input,
412 u32_le_input,
413 input_u32,
414 chain_u32,
415 0xA0B0_C0D0u32
416 );
417 test_word_input!(
418 i32_be_input,
419 i32_le_input,
420 input_i32,
421 chain_i32,
422 -0x7F01_02FDi32
423 );
424 test_word_input!(
425 u64_be_input,
426 u64_le_input,
427 input_u64,
428 chain_u64,
429 0xA0B0_C0D0_0102_0304u64
430 );
431 test_word_input!(
432 i64_be_input,
433 i64_le_input,
434 input_i64,
435 chain_i64,
436 -0x7F01_0203_0405_FFFDi64
437 );
438 test_float_input!(
439 f32_be_input,
440 f32_le_input,
441 input_f32,
442 chain_f32,
443 f32::consts::PI
444 );
445 test_float_input!(
446 f64_be_input,
447 f64_le_input,
448 input_f64,
449 chain_f64,
450 f64::consts::PI
451 );
452}