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
// SPDX-License-Identifier: Apache-2.0 OR MIT-0

use std::io::{Read, Result, Write};

use digest::Digest;

#[derive(Clone)]
pub struct CalculatedDigest {
    pub bytes_read: u64,
    pub algorithm_name: String,
    pub digest: String,
}

//
#[derive(Copy, Clone)]
pub struct Algorithm<'a> {
    pub digest_bit_size: u16,
    pub name: &'a str,
    digest_fn: fn(&Algorithm, &mut dyn Read) -> Result<CalculatedDigest>,
}

impl<'a> Algorithm<'a> {
    pub fn digest(&'a self, read: &mut dyn Read) -> Result<CalculatedDigest> {
        (self.digest_fn)(&self, read)
    }
}

fn calculated_digest<D: Digest + Write>(a: &Algorithm, read: &mut dyn Read) -> Result<CalculatedDigest> {
    let mut digest = D::new();
    let bytes_read = std::io::copy(read, &mut digest);
    bytes_read.map(|br| CalculatedDigest {
        algorithm_name: String::from(a.name).clone(),
        bytes_read: br,
        digest: hex::encode(digest.finalize().as_slice()),
    })
}

fn to_calculated_digest(a: &Algorithm, bytes_read: Result<u64>, dr: &[u8]) -> Result<CalculatedDigest> {
    bytes_read.map(|br| CalculatedDigest {
        algorithm_name: String::from(a.name).clone(),
        bytes_read: br,
        digest: hex::encode(dr),
    })
}

#[cfg(test)]
mod tests {
    use crate::Algorithm;

    pub fn test_algorithm(
        algorithm: &Algorithm,
        data: &mut dyn std::io::Read,
        expected_length: u64,
        expected_value: &str,
    ) {
        let calculated = algorithm.digest(data).unwrap();
        assert_eq!(calculated.bytes_read, expected_length);
        assert_eq!(calculated.algorithm_name, algorithm.name);
        assert_eq!(
            calculated.digest, expected_value,
            "Expected {}, got {}",
            expected_value, calculated.digest
        );
    }

    #[macro_export]
    macro_rules! test_algorithm {
        ($algorithm:ident, $name:ident, $input:expr, $expected_digest:expr) => {
            #[cfg(test)]
            #[test]
            fn $name() {
                println!("'{:?}'", $input);
                crate::tests::test_algorithm(
                    &(<$algorithm>::new()),
                    &mut std::io::Cursor::new($input),
                    $input.len() as u64,
                    $expected_digest,
                )
            }
        };
    }

    #[macro_export]
    macro_rules! test_algorithm_s {
        ($algorithm:ident, $name:ident, $str:expr, $expected_digest:expr) => {
            #[test]
            fn $name() {
                crate::tests::test_algorithm(
                    &(<$algorithm>::new()),
                    &mut ($str).as_bytes(),
                    $str.len() as u64,
                    $expected_digest,
                )
            }
        };
    }

    pub const U8_EMPTY: [u8; 0] = [0x00; 0];
    pub const U8_32_ALL_ZEROS: [u8; 32] = [0x00; 32];
    pub const U8_32_HALF_ONES: [u8; 32] = [0x0f; 32];
    pub const U8_32_ALL_ONES: [u8; 32] = [0xff; 32];
    pub const U8_32_ASCENDING: [u8;32] = *b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x20";
    pub const U8_32_DESCENDING: [u8;32] = *b"\x20\x1F\x1E\x1D\x1C\x1B\x1A\x19\x18\x17\x16\x15\x14\x13\x12\x11\x10\x0F\x0E\x0D\x0C\x0B\x0A\x09\x08\x07\x06\x05\x04\x03\x02\x01";
    pub const U8_1_BYTE: [u8; 1] = [0x65];
    pub const U8_2_BYTES: [u8; 2] = [0xac, 0x23];
    pub const U8_3_BYTES: [u8; 3] = [0xbc, 0x33, 0xe2];
    pub const U8_4_BYTES: [u8; 4] = [0xe2, 0xf3, 0xa1, 0xdf];
    pub const S_HI: &str = "hi";
    pub const S_QUICK_BROWN_FOX: &str = "The quick brown fox jumps over 13 lazy dogs.";
    pub const S_QUICK_BROWN_FOX_W_EOL: &str = "The quick brown fox jumps over 13 lazy dogs.\n";
    pub const S_QEOIC_SMOUAU_POSWUC: &str = "Qeoic-Smouau-Poswuc";
}

pub mod crc32;
pub mod md5;
pub mod sha1;
pub mod sha256;
pub mod sha512;