#![cfg_attr(not(test), no_std)]
#![deny(unsafe_code)]
#![doc(test(attr(deny(warnings))))]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(unreachable_pub)]
#![warn(unused_qualifications)]
mod buffer;
use crate::buffer::Buffer;
use core::cmp::min;
use core::error;
use core::fmt;
use core::hash::Hash;
use core::hash::Hasher;
use core::ops::BitOr;
use core::ops::BitOrAssign;
#[inline]
#[must_use]
const fn bytes_to_u64(bytes: &[u8]) -> u64 {
let len = bytes.len();
let mut array = [0u8; 8];
array.split_at_mut(len).0.copy_from_slice(bytes);
u64::from_le_bytes(array)
}
#[derive(Clone, PartialEq, Eq, Debug)]
struct State {
v0: u64,
v1: u64,
v2: u64,
v3: u64,
}
impl State {
#[inline]
#[must_use]
const fn zeroed() -> Self {
Self {
v0: 0,
v1: 0,
v2: 0,
v3: 0,
}
}
#[inline]
#[must_use]
const fn from_keys(k0: u64, k1: u64) -> Self {
Self {
v0: k0 ^ 0x736f6d6570736575,
v1: k1 ^ 0x646f72616e646f6d,
v2: k0 ^ 0x6c7967656e657261,
v3: k1 ^ 0x7465646279746573,
}
}
#[inline]
const fn round(&mut self) {
self.v0 = self.v0.wrapping_add(self.v1);
self.v1 = self.v1.rotate_left(13);
self.v1 ^= self.v0;
self.v0 = self.v0.rotate_left(32);
self.v2 = self.v2.wrapping_add(self.v3);
self.v3 = self.v3.rotate_left(16);
self.v3 ^= self.v2;
self.v0 = self.v0.wrapping_add(self.v3);
self.v3 = self.v3.rotate_left(21);
self.v3 ^= self.v0;
self.v2 = self.v2.wrapping_add(self.v1);
self.v1 = self.v1.rotate_left(17);
self.v1 ^= self.v2;
self.v2 = self.v2.rotate_left(32);
}
const fn compress(&mut self, m: u64) {
self.v3 ^= m;
self.round();
self.round();
self.v0 ^= m;
}
const fn add(&mut self, other: &Self) {
self.v0 = self.v0.wrapping_add(other.v0);
self.v1 = self.v1.wrapping_add(other.v1);
self.v2 = self.v2.wrapping_add(other.v2);
self.v3 = self.v3.wrapping_add(other.v3);
}
#[must_use]
const fn finalize(&mut self) -> u64 {
self.v2 ^= 0xff;
self.round();
self.round();
self.round();
self.round();
self.v0 ^ self.v1 ^ self.v2 ^ self.v3
}
}
#[derive(Clone, Debug)]
pub struct SoupElementHasher {
state: State,
buf: Buffer,
count: u8,
}
impl SoupElementHasher {
#[inline]
#[must_use]
const fn from_state(state: State) -> Self {
Self {
state,
buf: Buffer::new(),
count: 0,
}
}
#[inline]
const fn write_uint<const N: usize>(&mut self, x: u64) {
self.count = self.count.wrapping_add(N as u8);
if let Some(m) = self.buf.write(x, N) {
self.state.compress(m);
}
}
fn write_bytes(&mut self, mut bytes: &[u8]) {
self.count = self.count.wrapping_add(bytes.len() as u8);
if !self.buf.is_empty() {
let n = min(self.buf.available(), bytes.len());
let (head, remaining) = bytes.split_at(n);
if let Some(m) = self.buf.write_bytes(head) {
self.state.compress(m);
}
bytes = remaining;
}
let (chunks, tail) = bytes.as_chunks::<8>();
for c in chunks {
let m = u64::from_le_bytes(*c);
self.state.compress(m);
}
let _ = self.buf.write_bytes(tail);
}
#[must_use]
const fn flush(mut self) -> State {
let m = self.buf.take() | ((self.count as u64) << 56);
self.state.compress(m);
self.state
}
}
impl Hasher for SoupElementHasher {
fn write(&mut self, bytes: &[u8]) {
self.write_bytes(bytes)
}
fn write_u8(&mut self, x: u8) {
self.write_uint::<1>(x as u64)
}
fn write_u16(&mut self, x: u16) {
self.write_uint::<2>(x as u64)
}
fn write_u32(&mut self, x: u32) {
self.write_uint::<4>(x as u64)
}
fn write_u64(&mut self, x: u64) {
self.write_uint::<8>(x)
}
fn write_usize(&mut self, x: usize) {
const N: usize = size_of::<usize>();
self.write_uint::<N>(x as u64)
}
fn finish(&self) -> u64 {
self.clone().flush().finalize()
}
}
#[derive(Clone, Debug)]
pub struct SoupHasher {
state: State,
count: u64,
elem_hasher_state: State,
}
impl SoupHasher {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self::with_keys(0, 0)
}
#[inline]
#[must_use]
pub const fn with_keys(k0: u64, k1: u64) -> Self {
Self {
state: State::zeroed(),
count: 0,
elem_hasher_state: State::from_keys(k0, k1),
}
}
#[inline]
#[must_use]
pub const fn with_key(k: [u8; 16]) -> Self {
let (k0, k1) = k.split_at(8);
debug_assert!(k0.len() == 8);
debug_assert!(k1.len() == 8);
let k0 = bytes_to_u64(k0);
let k1 = bytes_to_u64(k1);
Self::with_keys(k0, k1)
}
#[inline]
pub fn add<T: Hash>(&mut self, elem: T) {
self.add_with_hasher(move |hasher| elem.hash(hasher));
}
pub fn add_with_hasher<F>(&mut self, f: F)
where
F: FnOnce(&mut SoupElementHasher),
{
let mut elem_hasher = SoupElementHasher::from_state(self.elem_hasher_state.clone());
f(&mut elem_hasher);
let mut elem_state = elem_hasher.flush();
let _ = elem_state.finalize();
self.state.add(&elem_state);
self.count = self.count.wrapping_add(1);
}
#[must_use]
pub fn finish(&self) -> u64 {
let mut state = self.state.clone();
state.add(&self.elem_hasher_state);
state.compress(self.count);
state.finalize()
}
#[must_use]
pub fn combine(&self, other: &Self) -> Self {
self.try_combine(other).unwrap()
}
pub fn try_combine(&self, other: &Self) -> Result<Self, KeyMismatch> {
if self.elem_hasher_state != other.elem_hasher_state {
return Err(KeyMismatch);
}
let mut result = self.clone();
result.state.add(&other.state);
result.count = self.count.wrapping_add(other.count);
Ok(result)
}
}
impl Default for SoupHasher {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: Hash> Extend<T> for SoupHasher {
fn extend<I>(&mut self, it: I)
where
I: IntoIterator<Item = T>,
{
for elem in it.into_iter() {
self.add(elem);
}
}
}
impl BitOr<SoupHasher> for SoupHasher {
type Output = SoupHasher;
#[inline]
fn bitor(self, other: SoupHasher) -> Self::Output {
self.combine(&other)
}
}
impl BitOr<&SoupHasher> for SoupHasher {
type Output = SoupHasher;
#[inline]
fn bitor(self, other: &SoupHasher) -> Self::Output {
self.combine(other)
}
}
impl BitOr<SoupHasher> for &SoupHasher {
type Output = SoupHasher;
#[inline]
fn bitor(self, other: SoupHasher) -> Self::Output {
self.combine(&other)
}
}
impl BitOr<&SoupHasher> for &SoupHasher {
type Output = SoupHasher;
#[inline]
fn bitor(self, other: &SoupHasher) -> Self::Output {
self.combine(other)
}
}
impl BitOrAssign<Self> for SoupHasher {
#[inline]
fn bitor_assign(&mut self, other: Self) {
*self = self.combine(&other);
}
}
impl BitOrAssign<&Self> for SoupHasher {
#[inline]
fn bitor_assign(&mut self, other: &Self) {
*self = self.combine(other);
}
}
#[derive(Debug)]
pub struct KeyMismatch;
impl fmt::Display for KeyMismatch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"keys don't match".fmt(f)
}
}
impl error::Error for KeyMismatch {}
#[cfg(test)]
mod tests {
use crate::SoupElementHasher;
use crate::SoupHasher;
use crate::State;
use std::collections::HashSet;
use std::hash::Hasher;
#[test]
fn siphash_vectors() {
let s = State::from_keys(0x0706050403020100, 0x0f0e0d0c0b0a0908);
let mut h = SoupElementHasher::from_state(s);
assert_eq!(h.state.v0, 0x7469686173716475);
assert_eq!(h.state.v1, 0x6b617f6d656e6665);
assert_eq!(h.state.v2, 0x6b7f62616d677361);
assert_eq!(h.state.v3, 0x7b6b696e727e6c7b);
h.write(&[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e,
]);
assert_eq!(h.finish(), 0xa129ca6149be45e5);
}
macro_rules! hash {
[] => {
SoupHasher::new().finish()
};
[ $( $elem:expr ),+ $(,)? ] => {{
let mut h = SoupHasher::new();
$( h.add($elem); )+
h.finish()
}}
}
macro_rules! assert_hash_eq {
( [ $( $elem:expr ),* $(,)? ] => $expected:expr ) => {{
let actual = hash![ $( $elem ),* ];
let expected = $expected;
assert_eq!(actual, expected, "hash mismatch: 0x{actual:x} != 0x{expected:x}");
}}
}
#[test]
fn vectors() {
assert_hash_eq!([] => 0x1e924b9d737700d7);
assert_hash_eq!([()] => 0x6a93544f73268a1d);
assert_hash_eq!([(), ()] => 0xe932b92cdd6952c);
assert_hash_eq!([(), (), ()] => 0xe808f7c71defaf9e);
assert_hash_eq!([(), (), (), ()] => 0xfbb7281c4b1a99ba);
assert_hash_eq!([(), (), (), (), ()] => 0xa7f4e0cf94fce9dd);
assert_hash_eq!([0] => 0x1cf09c9565dfbff4);
assert_hash_eq!([0, 0] => 0x4e341d5d8eacf71c);
assert_hash_eq!([0, 0, 0] => 0x76671729dfb6e001);
assert_hash_eq!([0, 0, 0, 0] => 0xc3b488b49f3b7aa7);
assert_hash_eq!([0, 0, 0, 0, 0] => 0xb890466d03badf45);
assert_hash_eq!([""] => 0x9d15b53333498468);
assert_hash_eq!([b""] => 0xf11858ac1f1c713f);
assert_hash_eq!([123] => 0xfa1d43c971e02556);
assert_hash_eq!(["abc"] => 0xa5b4dab870ae41a8);
assert_hash_eq!([123, "abc"] => 0x29400d763b6fa6c2);
assert_hash_eq!(["abc", 123] => 0x29400d763b6fa6c2);
assert_hash_eq!([(), 0, ""] => 0x7a82eb8ad5f1c23e);
assert_hash_eq!([(), "", 0] => 0x7a82eb8ad5f1c23e);
assert_hash_eq!([0, "", ()] => 0x7a82eb8ad5f1c23e);
assert_hash_eq!([0, (), ""] => 0x7a82eb8ad5f1c23e);
assert_hash_eq!(["", (), 0] => 0x7a82eb8ad5f1c23e);
assert_hash_eq!(["", 0, ()] => 0x7a82eb8ad5f1c23e);
}
#[test]
fn combine() {
let mut a = SoupHasher::new();
let mut b = SoupHasher::new();
a.add("hello");
b.add(1234567);
assert_eq!(a.finish(), 0x83675ec42551b23d);
assert_eq!(b.finish(), 0xab8b024a3c2bde71);
assert_eq!(a.combine(&b).finish(), 0x9986fcb17df59b52);
assert_eq!(b.combine(&a).finish(), 0x9986fcb17df59b52);
assert_eq!((&a | &b).finish(), 0x9986fcb17df59b52);
assert_eq!((&b | &a).finish(), 0x9986fcb17df59b52);
let mut c = a.clone();
c |= &b;
assert_eq!(c.finish(), 0x9986fcb17df59b52);
let mut c = b.clone();
c |= &a;
assert_eq!(c.finish(), 0x9986fcb17df59b52);
}
#[test]
fn uniqueness() {
let mut hasher = SoupHasher::new();
let mut seen = HashSet::new();
seen.insert(hasher.finish());
for i in 0..100_000 {
hasher.add(i);
let hash = hasher.finish();
assert!(seen.insert(hash), "0x{hash:x} repeated at step {i}");
}
}
#[test]
fn key_dependance() {
let mut seen = HashSet::new();
for k0 in 0..100 {
for k1 in 0..100 {
let mut hasher = SoupHasher::with_keys(k0, k1);
let hash = hasher.finish();
assert!(
seen.insert(hash),
"0x{hash:x} repeated with k0={k0}, k1={k1} (no input)"
);
for i in 0..100 {
hasher.add(i);
let hash = hasher.finish();
assert!(
seen.insert(hash),
"0x{hash:x} repeated at step {i} with k0={k0}"
);
}
}
}
}
}