digest 0.7.4

Traits for cryptographic hash functions
Documentation
use super::{Digest, Input, VariableOutput, ExtendableOutput, XofReader};
use core::fmt::Debug;

pub struct Test {
    pub name: &'static str,
    pub input: &'static [u8],
    pub output: &'static [u8],
}

#[macro_export]
macro_rules! new_tests {
    ( $( $name:expr ),*  ) => {
        [$(
            Test {
                name: $name,
                input: include_bytes!(concat!("data/", $name, ".input.bin")),
                output: include_bytes!(concat!("data/", $name, ".output.bin")),
            },
        )*]
    };
    ( $( $name:expr ),+, ) => (new_tests!($($name),+))
}

pub fn main_test<D: Digest + Debug + Clone>(tests: &[Test]) {
    // Test that it works when accepting the message all at once
    for t in tests.iter() {
        let mut sh = D::default();
        sh.input(t.input);

        let out = sh.result();

        assert_eq!(out[..], t.output[..]);
    }

    // Test that it works when accepting the message in pieces
    for t in tests.iter() {
        let mut sh = D::default();
        let len = t.input.len();
        let mut left = len;
        while left > 0 {
            let take = (left + 1) / 2;
            sh.input(&t.input[len - left..take + len - left]);
            left = left - take;
        }

        let out = sh.result();

        assert_eq!(out[..], t.output[..]);
    }
}

pub fn variable_test<D>(tests: &[Test])
    where D: Input + VariableOutput + Clone + Debug
{
    let mut buf = [0u8; 1024];
    // Test that it works when accepting the message all at once
    for t in tests.iter() {
        let mut sh = D::new(t.output.len()).unwrap();
        sh.process(t.input);

        let out = sh.variable_result(&mut buf[..t.output.len()]).unwrap();

        assert_eq!(out[..], t.output[..]);
    }

    // Test that it works when accepting the message in pieces
    for t in tests.iter() {
        let mut sh = D::new(t.output.len()).unwrap();
        let len = t.input.len();
        let mut left = len;
        while left > 0 {
            let take = (left + 1) / 2;
            sh.process(&t.input[len - left..take + len - left]);
            left = left - take;
        }

        let out = sh.variable_result(&mut buf[..t.output.len()]).unwrap();

        assert_eq!(out[..], t.output[..]);
    }
}


pub fn xof_test<D>(tests: &[Test])
    where D: Input + ExtendableOutput + Default + Debug + Clone
{
    let mut buf = [0u8; 1024];
    // Test that it works when accepting the message all at once
    for t in tests.iter() {
        let mut sh = D::default();
        sh.process(t.input);

        let out = &mut buf[..t.output.len()];
        sh.xof_result().read(out);

        assert_eq!(out[..], t.output[..]);
    }

    // Test that it works when accepting the message in pieces
    for t in tests.iter() {
        let mut sh = D::default();
        let len = t.input.len();
        let mut left = len;
        while left > 0 {
            let take = (left + 1) / 2;
            sh.process(&t.input[len - left..take + len - left]);
            left = left - take;
        }

        let out = &mut buf[..t.output.len()];
        sh.xof_result().read(out);

        assert_eq!(out[..], t.output[..]);
    }

    // Test reeading from reader byte by byte
    for t in tests.iter() {
        let mut sh = D::default();
        sh.process(t.input);

        let mut reader = sh.xof_result();
        let out = &mut buf[..t.output.len()];
        for chunk in out.chunks_mut(1) {
            reader.read(chunk);
        }

        assert_eq!(out[..], t.output[..]);
    }
}

pub fn one_million_a<D: Digest + Default + Debug + Clone>(expected: &[u8]) {
    let mut sh = D::default();
    for _ in 0..50000 {
        sh.input(&[b'a'; 10]);
    }
    sh.input(&[b'a'; 500000]);
    let out = sh.result();
    assert_eq!(out[..], expected[..]);
}


#[macro_export]
macro_rules! bench_digest {
    ($name:ident, $engine:path, $bs:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut d = <$engine>::default();
            let data = [0; $bs];

            b.iter(|| {
                d.input(&data);
            });

            b.bytes = $bs;
        }
    };

    ($engine:path) => {
        extern crate test;

        use test::Bencher;
        use digest::Digest;

        bench_digest!(bench1_16, $engine, 1<<4);
        bench_digest!(bench2_64, $engine, 1<<6);
        bench_digest!(bench3_256, $engine, 1<<8);
        bench_digest!(bench4_1k, $engine, 1<<10);
        bench_digest!(bench5_4k, $engine, 1<<12);
        bench_digest!(bench6_16k, $engine, 1<<14);
    }
}