#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]
use core::fmt;
use core::hash;
mod baseline;
mod combine;
mod specialized;
mod table;
pub fn hash(buf: &[u8]) -> u32 {
let mut h = Hasher::new();
h.update(buf);
h.finalize()
}
#[derive(Clone)]
pub struct Hasher {
amount: u64,
state: u32,
spec: Option<specialized::State>,
}
const DEFAULT_INIT_STATE: u32 = 0;
impl Hasher {
pub fn new() -> Self {
Self::new_with_initial(DEFAULT_INIT_STATE)
}
pub fn new_with_initial(init: u32) -> Self {
Self::new_with_initial_len(init, 0)
}
pub fn new_with_initial_len(init: u32, amount: u64) -> Self {
Self::internal_new_specialized(init, amount)
.unwrap_or_else(|| Self::internal_new_baseline(init, amount))
}
#[doc(hidden)]
pub fn internal_new_baseline(init: u32, amount: u64) -> Self {
Hasher {
amount,
state: init,
spec: None,
}
}
#[doc(hidden)]
pub fn internal_new_specialized(init: u32, amount: u64) -> Option<Self> {
Some(Hasher {
amount,
state: init,
spec: Some(specialized::State::new()?),
})
}
pub fn update(&mut self, buf: &[u8]) {
self.amount += buf.len() as u64;
self.state = match &self.spec {
None => baseline::update_fast_16(self.state, buf),
Some(spec) => spec.update(self.state, buf),
};
}
#[inline]
pub fn total_len(&self) -> u64 {
self.amount
}
#[inline]
pub fn finalize(self) -> u32 {
self.state
}
#[inline]
pub fn reset(&mut self) {
self.state = 0;
self.amount = 0;
}
pub fn combine(&mut self, other: &Self) {
self.amount += other.amount;
self.state = combine::combine(self.state, other.state, other.amount);
}
}
impl fmt::Debug for Hasher {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("crc32fast::Hasher").finish()
}
}
impl Default for Hasher {
fn default() -> Self {
Self::new()
}
}
impl hash::Hasher for Hasher {
fn write(&mut self, bytes: &[u8]) {
self.update(bytes)
}
fn finish(&self) -> u64 {
u64::from(self.clone().finalize())
}
}
#[cfg(test)]
mod test {
use super::Hasher;
#[test]
fn total_len_tracks_resumed_and_combined_blocks() {
let prefix = b"hello";
let suffix = b" world";
let mut hasher = Hasher::new_with_initial_len(super::hash(prefix), prefix.len() as u64);
assert_eq!(hasher.total_len(), 5);
hasher.update(b" ");
assert_eq!(hasher.total_len(), 6);
let mut remaining = Hasher::new();
remaining.update(&suffix[1..]);
hasher.combine(&remaining);
assert_eq!(hasher.total_len(), 11);
assert_eq!(hasher.clone().finalize(), super::hash(b"hello world"));
assert_eq!(remaining.total_len(), 5);
hasher.reset();
assert_eq!(hasher.total_len(), 0);
hasher.update(b"again");
assert_eq!(hasher.total_len(), 5);
assert_eq!(hasher.finalize(), super::hash(b"again"));
}
#[test]
fn total_len_for_empty_and_unknown_initial_length() {
let mut hasher = Hasher::new();
assert_eq!(hasher.total_len(), 0);
hasher.update(b"");
hasher.combine(&Hasher::new());
assert_eq!(hasher.total_len(), 0);
assert_eq!(
Hasher::new_with_initial(super::hash(b"prefix")).total_len(),
0
);
assert_eq!(
Hasher::new_with_initial_len(0, u64::MAX).total_len(),
u64::MAX
);
}
quickcheck::quickcheck! {
fn combine(bytes_1: Vec<u8>, bytes_2: Vec<u8>) -> bool {
let mut hash_a = Hasher::new();
hash_a.update(&bytes_1);
hash_a.update(&bytes_2);
let mut hash_b = Hasher::new();
hash_b.update(&bytes_2);
let mut hash_c = Hasher::new();
hash_c.update(&bytes_1);
hash_c.combine(&hash_b);
hash_a.finalize() == hash_c.finalize()
}
fn combine_from_len(bytes_1: Vec<u8>, bytes_2: Vec<u8>) -> bool {
let mut hash_a = Hasher::new();
hash_a.update(&bytes_1);
let mut hash_b = Hasher::new();
hash_b.update(&bytes_2);
let mut hash_ab = Hasher::new();
hash_ab.update(&bytes_1);
hash_ab.update(&bytes_2);
let ab = hash_ab.finalize();
hash_a.combine(&hash_b);
hash_a.finalize() == ab
}
}
}