use crate::constants::IV;
use crate::padding::{BLOCK_SIZE, pad_message_in_place, MAX_PADDED_SIZE};
#[cfg(feature = "simd")]
use crate::simd::process_block_simd;
#[cfg(not(feature = "simd"))]
use crate::clockmix::clock_mix;
#[cfg(not(feature = "simd"))]
use crate::clockpermute::clock_permute;
#[cfg(not(feature = "simd"))]
use crate::utils::rotr64;
pub struct ClockHasher {
state: [u64; 8],
buffer: [u8; BLOCK_SIZE],
buffer_len: usize,
message_len: u64,
}
impl ClockHasher {
#[inline]
pub fn new() -> Self {
Self {
state: IV,
buffer: [0u8; BLOCK_SIZE],
buffer_len: 0,
message_len: 0,
}
}
#[inline]
fn process_block(&mut self, block: &[u8; BLOCK_SIZE]) {
#[cfg(feature = "simd")]
{
process_block_simd(block, &mut self.state);
}
#[cfg(not(feature = "simd"))]
{
let mut words = [0u64; 16];
for i in 0..16 {
let offset = i * 8;
words[i] = u64::from_le_bytes([
block[offset],
block[offset + 1],
block[offset + 2],
block[offset + 3],
block[offset + 4],
block[offset + 5],
block[offset + 6],
block[offset + 7],
]);
}
clock_mix(&mut words);
for i in 0..8 {
self.state[i] = self.state[i].wrapping_add(words[i]);
let rot_idx = (i + 4) % 8;
self.state[i] ^= rotr64(self.state[rot_idx], 17);
}
clock_permute(&mut self.state);
}
}
pub fn update(&mut self, data: &[u8]) {
self.message_len = self.message_len.wrapping_add(data.len() as u64);
let mut data_offset = 0;
if self.buffer_len > 0 {
let needed = BLOCK_SIZE - self.buffer_len;
let to_copy = core::cmp::min(needed, data.len());
self.buffer[self.buffer_len..self.buffer_len + to_copy]
.copy_from_slice(&data[0..to_copy]);
self.buffer_len += to_copy;
data_offset = to_copy;
if self.buffer_len == BLOCK_SIZE {
let block: [u8; BLOCK_SIZE] = self.buffer;
self.process_block(&block);
self.buffer_len = 0;
}
}
while data_offset + BLOCK_SIZE <= data.len() {
let mut block = [0u8; BLOCK_SIZE];
block.copy_from_slice(&data[data_offset..data_offset + BLOCK_SIZE]);
self.process_block(&block);
data_offset += BLOCK_SIZE;
}
if data_offset < data.len() {
let remaining = data.len() - data_offset;
self.buffer[0..remaining].copy_from_slice(&data[data_offset..]);
self.buffer_len = remaining;
}
}
pub fn finalize(mut self) -> [u8; 32] {
use crate::padding::{MAX_PADDED_SIZE, BLOCK_SIZE};
let mut final_block = [0u8; MAX_PADDED_SIZE];
final_block[0..self.buffer_len].copy_from_slice(&self.buffer[0..self.buffer_len]);
pad_message_in_place(
&mut final_block,
self.buffer_len,
0,
self.message_len as usize,
);
let mut offset = 0;
while offset + BLOCK_SIZE <= MAX_PADDED_SIZE {
let mut block = [0u8; BLOCK_SIZE];
block.copy_from_slice(&final_block[offset..offset + BLOCK_SIZE]);
self.process_block(&block);
offset += BLOCK_SIZE;
}
for i in 0..8 {
self.state[i] ^= IV[i];
}
let h0 = self.state[0] ^ self.state[4];
let h1 = self.state[1] ^ self.state[5];
let h2 = self.state[2] ^ self.state[6];
let h3 = self.state[3] ^ self.state[7];
let mut result = [0u8; 32];
result[0..8].copy_from_slice(&h0.to_le_bytes());
result[8..16].copy_from_slice(&h1.to_le_bytes());
result[16..24].copy_from_slice(&h2.to_le_bytes());
result[24..32].copy_from_slice(&h3.to_le_bytes());
result
}
}
impl Default for ClockHasher {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hasher_empty() {
let hasher = ClockHasher::new();
let hash = hasher.finalize();
assert_eq!(hash.len(), 32);
}
#[test]
fn test_hasher_small() {
let mut hasher = ClockHasher::new();
hasher.update(b"abc");
let hash = hasher.finalize();
assert_eq!(hash.len(), 32);
}
#[test]
fn test_hasher_incremental() {
let mut hasher1 = ClockHasher::new();
hasher1.update(b"hello");
hasher1.update(b", ");
hasher1.update(b"world");
let hash1 = hasher1.finalize();
let mut hasher2 = ClockHasher::new();
hasher2.update(b"hello, world");
let hash2 = hasher2.finalize();
assert_eq!(hash1, hash2);
}
#[test]
fn test_hasher_large() {
let mut hasher = ClockHasher::new();
let data = [0u8; 10000];
hasher.update(&data);
let hash = hasher.finalize();
assert_eq!(hash.len(), 32);
}
}