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
pub const CASTAGNOLI: u32 = 0x82f63b78;
pub const IEEE: u32 = 0xedb88320;
pub const KOOPMAN: u32 = 0xeb31d82e;

lazy_static! {
    static ref IEEE_TABLE: [u32; 256] = make_table(IEEE);
    static ref CASTAGNOLI_TABLE: [u32; 256] = make_table(CASTAGNOLI);
    static ref KOOPMAN_TABLE: [u32; 256] = make_table(KOOPMAN);
}

pub struct Digest {
    table: [u32; 256],
    value: u32
}

pub trait Hasher32 {
    fn reset(&mut self);
    fn write(&mut self, bytes: &[u8]);
    fn sum32(&self) -> u32;
}

pub fn make_table(poly: u32) -> [u32; 256] {
    let mut table = [0u32; 256];
    for i in 0..256 {
        let mut value = i as u32;
        for _ in 0..8 {
            value = if (value & 1) == 1 {
                (value >> 1) ^ poly
            } else {
                value >> 1
            }
        }
        table[i] = value;
    }
    table
}

pub fn update(mut value: u32, table: &[u32; 256], bytes: &[u8]) -> u32 {
    value = !value;
    for &i in bytes.iter() {
        value = table[((value as u8) ^ i) as usize] ^ (value >> 8)
    }
    !value
}

pub fn checksum_ieee(bytes: &[u8]) -> u32 {
    return update(0, &IEEE_TABLE, bytes);
}

pub fn checksum_castagnoli(bytes: &[u8]) -> u32 {
    return update(0, &CASTAGNOLI_TABLE, bytes);
}

pub fn checksum_koopman(bytes: &[u8]) -> u32 {
    return update(0, &KOOPMAN_TABLE, bytes);
}

impl Digest {
    pub fn new(poly: u32) -> Digest {
        Digest {
            table: make_table(poly),
            value: 0,
        }
    }
}

impl Hasher32 for Digest {
    fn reset(&mut self) {
        self.value = 0;
    }
    fn write(&mut self, bytes: &[u8]) {
        self.value = update(self.value, &self.table, bytes);
    }
    fn sum32(&self) -> u32 {
        self.value
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use test::Bencher;

    const CASTAGNOLI_CHECK_VALUE: u32 = 0xe3069283;
    const IEEE_CHECK_VALUE: u32 = 0xcbf43926;
    const KOOPMAN_CHECK_VALUE: u32 = 0x2d3dd0ae;

    #[test]
    fn test_checksum_castagnoli() {
        assert_eq!(checksum_castagnoli(b"123456789"), CASTAGNOLI_CHECK_VALUE)
    }

    #[test]
    fn test_checksum_ieee() {
        assert_eq!(checksum_ieee(b"123456789"), IEEE_CHECK_VALUE)
    }

    #[test]
    fn test_checksum_koopman() {
        assert_eq!(checksum_koopman(b"123456789"), KOOPMAN_CHECK_VALUE)
    }

    #[test]
    fn test_digest_castagnoli() {
        verify_checksum(CASTAGNOLI, CASTAGNOLI_CHECK_VALUE);
    }

    #[test]
    fn test_digest_ieee() {
        verify_checksum(IEEE, IEEE_CHECK_VALUE);
    }

    #[test]
    fn test_digest_koopman() {
        verify_checksum(KOOPMAN, KOOPMAN_CHECK_VALUE);
    }


    #[bench]
    fn bench_make_table(b: &mut Bencher) {
        b.iter(|| make_table(IEEE));
    }

    #[bench]
    fn bench_update_megabytes(b: &mut Bencher) {
        let table = make_table(IEEE);
        let bytes = Box::new([0u8; 1_000_000]);
        b.iter(|| update(0, &table, &*bytes));
    }

    fn verify_checksum(poly: u32, check_value: u32) {
        let mut digest = Digest::new(poly);
        digest.write(b"123456789");
        assert_eq!(digest.sum32(), check_value);
        digest.reset();
        for i in 1..10 {
            digest.write(i.to_string().as_bytes());
        }
        assert_eq!(digest.sum32(), check_value);
    }
}