const SIPHASH_BLOCK_BITS: usize = 6;
const SIPHASH_BLOCK_SIZE: usize = 1usize << SIPHASH_BLOCK_BITS;
const SIPHASH_BLOCK_MASK: u64 = (SIPHASH_BLOCK_SIZE as u64) - 1;
pub fn siphash24(v: &[u64; 4], nonce: u64) -> u64 {
let mut siphash = SipHash24::new(v);
siphash.hash(nonce, 21); siphash.digest()
}
#[allow(clippy::indexing_slicing)]
#[allow(clippy::cast_possible_truncation)]
pub fn siphash_block(v: &[u64; 4], nonce: u64, rot_e: u8) -> u64 {
let edge0 = nonce & !SIPHASH_BLOCK_MASK;
let mut nonce_hash = vec![0u64; SIPHASH_BLOCK_SIZE];
let mut siphash = SipHash24::new(v);
for (i, item) in nonce_hash.iter_mut().enumerate() {
siphash.hash(edge0 + i as u64, rot_e);
*item = siphash.digest();
}
let last = nonce_hash[SIPHASH_BLOCK_SIZE - 1];
let last_nonce = nonce & SIPHASH_BLOCK_MASK;
for item in nonce_hash.iter_mut().take(SIPHASH_BLOCK_MASK as usize) {
*item ^= last;
}
nonce_hash[last_nonce as usize]
}
pub struct SipHash24(u64, u64, u64, u64);
impl SipHash24 {
pub fn new(v: &[u64; 4]) -> SipHash24 {
SipHash24(v[0], v[1], v[2], v[3])
}
pub fn hash(&mut self, nonce: u64, rot_e: u8) {
self.3 ^= nonce;
self.round(rot_e);
self.round(rot_e);
self.0 ^= nonce;
self.2 ^= 0xff;
for _ in 0..4 {
self.round(rot_e);
}
}
pub fn digest(&self) -> u64 {
(self.0 ^ self.1) ^ (self.2 ^ self.3)
}
fn round(&mut self, rot_e: u8) {
self.0 = self.0.wrapping_add(self.1);
self.2 = self.2.wrapping_add(self.3);
self.1 = self.1.rotate_left(13);
self.3 = self.3.rotate_left(16);
self.1 ^= self.0;
self.3 ^= self.2;
self.0 = self.0.rotate_left(32);
self.2 = self.2.wrapping_add(self.1);
self.0 = self.0.wrapping_add(self.3);
self.1 = self.1.rotate_left(17);
self.3 = self.3.rotate_left(rot_e.into());
self.1 ^= self.2;
self.3 ^= self.0;
self.2 = self.2.rotate_left(32);
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn hash_some() {
assert_eq!(siphash24(&[1, 2, 3, 4], 10), 928382149599306901);
assert_eq!(siphash24(&[1, 2, 3, 4], 111), 10524991083049122233);
assert_eq!(siphash24(&[9, 7, 6, 7], 12), 1305683875471634734);
assert_eq!(siphash24(&[9, 7, 6, 7], 10), 11589833042187638814);
}
#[test]
fn hash_block() {
assert_eq!(siphash_block(&[1, 2, 3, 4], 10, 21), 1182162244994096396);
assert_eq!(siphash_block(&[1, 2, 3, 4], 123, 21), 11303676240481718781);
assert_eq!(siphash_block(&[9, 7, 6, 7], 12, 21), 4886136884237259030);
}
}