#![forbid(unsafe_code)]
#![cfg_attr(not(feature = "std"), no_std)]
const BASE: u32 = 65521;
const NMAX: usize = 5552;
#[inline(always)]
fn do1(adler: &mut u32, sum2: &mut u32, buf: &[u8]) {
*adler += u32::from(buf[0]);
*sum2 += *adler;
}
#[inline(always)]
fn do2(adler: &mut u32, sum2: &mut u32, buf: &[u8]) {
do1(adler, sum2, &buf[0..1]);
do1(adler, sum2, &buf[1..2]);
}
#[inline(always)]
fn do4(adler: &mut u32, sum2: &mut u32, buf: &[u8]) {
do2(adler, sum2, &buf[0..2]);
do2(adler, sum2, &buf[2..4]);
}
#[inline(always)]
fn do8(adler: &mut u32, sum2: &mut u32, buf: &[u8]) {
do4(adler, sum2, &buf[0..4]);
do4(adler, sum2, &buf[4..8]);
}
#[inline(always)]
fn do16(adler: &mut u32, sum2: &mut u32, buf: &[u8]) {
do8(adler, sum2, &buf[0..8]);
do8(adler, sum2, &buf[8..16]);
}
#[derive(Clone)]
pub struct RollingAdler32 {
a: u32,
b: u32,
}
impl Default for RollingAdler32 {
fn default() -> RollingAdler32 {
RollingAdler32::new()
}
}
impl RollingAdler32 {
pub fn new() -> RollingAdler32 {
Self::from_value(1)
}
pub fn from_value(adler32: u32) -> RollingAdler32 {
let a = adler32 & 0xFFFF;
let b = adler32 >> 16;
RollingAdler32 { a, b }
}
pub fn from_buffer(buffer: &[u8]) -> RollingAdler32 {
let mut hash = RollingAdler32::new();
hash.update_buffer(buffer);
hash
}
pub fn hash(&self) -> u32 {
(self.b << 16) | self.a
}
pub fn remove(&mut self, size: usize, byte: u8) {
let byte = u32::from(byte);
self.a = (self.a + BASE - byte) % BASE;
self.b = ((self.b + BASE - 1)
.wrapping_add(BASE.wrapping_sub(size as u32).wrapping_mul(byte)))
% BASE;
}
pub fn update(&mut self, byte: u8) {
let byte = u32::from(byte);
self.a = (self.a + byte) % BASE;
self.b = (self.b + self.a) % BASE;
}
pub fn update_buffer(&mut self, buffer: &[u8]) {
let len = buffer.len();
if len == 1 {
self.update(buffer[0]);
return;
}
if len < 16 {
for byte in buffer.iter().take(len) {
self.a += u32::from(*byte);
self.b += self.a;
}
if self.a >= BASE {
self.a -= BASE;
}
self.b %= BASE;
return;
}
let mut pos = 0;
while pos + NMAX <= len {
let end = pos + NMAX;
while pos < end {
do16(&mut self.a, &mut self.b, &buffer[pos..pos + 16]);
pos += 16;
}
self.a %= BASE;
self.b %= BASE;
}
if pos < len {
while len - pos >= 16 {
do16(&mut self.a, &mut self.b, &buffer[pos..pos + 16]);
pos += 16;
}
while len - pos > 0 {
self.a += u32::from(buffer[pos]);
self.b += self.a;
pos += 1;
}
self.a %= BASE;
self.b %= BASE;
}
}
}
#[cfg(feature = "std")]
pub fn adler32<R: std::io::Read>(mut reader: R) -> std::io::Result<u32> {
let mut hash = RollingAdler32::new();
let mut buffer = [0u8; NMAX];
let mut read = reader.read(&mut buffer)?;
while read > 0 {
hash.update_buffer(&buffer[..read]);
read = reader.read(&mut buffer)?;
}
Ok(hash.hash())
}
#[cfg(test)]
mod test {
use rand::Rng;
use std::io;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test;
use super::{adler32, RollingAdler32, BASE};
fn adler32_slow<R: io::Read>(reader: R) -> io::Result<u32> {
let mut a: u32 = 1;
let mut b: u32 = 0;
for byte in reader.bytes() {
let byte = byte? as u32;
a = (a + byte) % BASE;
b = (b + a) % BASE;
}
Ok((b << 16) | a)
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn testvectors() {
fn do_test(v: u32, bytes: &[u8]) {
let mut hash = RollingAdler32::new();
hash.update_buffer(&bytes);
assert_eq!(hash.hash(), v);
let r = io::Cursor::new(bytes);
assert_eq!(adler32(r).unwrap(), v);
}
do_test(0x00000001, b"");
do_test(0x00620062, b"a");
do_test(0x024d0127, b"abc");
do_test(0x29750586, b"message digest");
do_test(0x90860b20, b"abcdefghijklmnopqrstuvwxyz");
do_test(
0x8adb150c,
b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789",
);
do_test(
0x97b61069,
b"1234567890123456789012345678901234567890\
1234567890123456789012345678901234567890",
);
do_test(0xD6251498, &[255; 64000]);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn compare() {
let mut rng = rand::thread_rng();
let mut data = vec![0u8; 5589];
for size in [
0, 1, 3, 4, 5, 31, 32, 33, 67, 5550, 5552, 5553, 5568, 5584, 5589,
]
.iter()
.cloned()
{
rng.fill(&mut data[..size]);
let r1 = io::Cursor::new(&data[..size]);
let r2 = r1.clone();
if adler32_slow(r1).unwrap() != adler32(r2).unwrap() {
panic!("Comparison failed, size={}", size);
}
}
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn rolling() {
assert_eq!(RollingAdler32::from_value(0x01020304).hash(), 0x01020304);
fn do_test(a: &[u8], b: &[u8]) {
let mut total = Vec::with_capacity(a.len() + b.len());
total.extend(a);
total.extend(b);
let mut h = RollingAdler32::from_buffer(&total[..(b.len())]);
for i in 0..(a.len()) {
h.remove(b.len(), a[i]);
h.update(total[b.len() + i]);
}
assert_eq!(h.hash(), adler32(b).unwrap());
}
do_test(b"a", b"b");
do_test(b"", b"this a test");
do_test(b"th", b"is a test");
do_test(b"this a ", b"test");
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn long_window_remove() {
let mut hash = RollingAdler32::new();
let w = 65536;
assert!(w as u32 > BASE);
let mut bytes = vec![0; w * 3];
for (i, b) in bytes.iter_mut().enumerate() {
*b = i as u8;
}
for (i, b) in bytes.iter().enumerate() {
if i >= w {
hash.remove(w, bytes[i - w]);
}
hash.update(*b);
if i > 0 && i % w == 0 {
assert_eq!(hash.hash(), 0x433a8772);
}
}
assert_eq!(hash.hash(), 0xbbba8772);
}
}