use crate::v1::functions::{read32, read64, P0, P1};
#[cfg(feature = "mum32bit")]
#[inline]
fn wyrotate(x: u64) -> u64 {
(x >> 32) | (x << 32)
}
#[cfg(feature = "mum32bit")]
#[inline]
fn wymum(a: u64, b: u64) -> u64 {
let hh = (a >> 32) * (b >> 32);
let hl = (a >> 32) * (b & 0xFFFF_FFFF);
let lh = (a & 0xFFFF_FFFF) * (b >> 32);
let ll = (a & 0xFFFF_FFFF) * (b & 0xFFFF_FFFF);
let a = wyrotate(hl) ^ hh;
let b = wyrotate(lh) ^ ll;
a ^ b
}
#[cfg(not(feature = "mum32bit"))]
#[inline]
fn wymum(a: u64, b: u64) -> u64 {
let r = u128::from(a) * u128::from(b);
((r >> 64) ^ r) as u64
}
#[inline]
fn read_up_to_24(bytes: &[u8]) -> u64 {
u64::from(bytes[0]) << 16
| u64::from(bytes[bytes.len() >> 1]) << 8
| u64::from(bytes[bytes.len() - 1])
}
pub fn wyhash(bytes: &[u8], seed: u64, secret: [u64; 4]) -> u64 {
let seed = seed ^ secret[0];
let (a, b, seed) = wyhash_core(bytes, seed, secret);
wyhash_finish(a, b, seed, bytes.len() as u64, secret[1])
}
#[inline]
pub fn wyhash_core(bytes: &[u8], seed: u64, secret: [u64; 4]) -> (u64, u64, u64) {
let (mut a, mut b) = (0, 0);
let mut seed = seed;
let length = bytes.len();
if length <= 16 {
if length >= 4 {
a = read32(bytes) << 32 | read32(&bytes[((length >> 3) << 2)..]);
b = read32(&bytes[(length - 4)..]) << 32
| read32(&bytes[(length - 4 - ((length >> 3) << 2))..]);
} else if length > 0 {
a = read_up_to_24(bytes);
}
} else {
let mut index = length;
let mut start = 0;
if length > 48 {
let mut see1 = seed;
let mut see2 = seed;
while index > 48 {
seed = wymum(
read64(&bytes[start..]) ^ secret[1],
read64(&bytes[start + 8..]) ^ seed,
);
see1 = wymum(
read64(&bytes[start + 16..]) ^ secret[2],
read64(&bytes[start + 24..]) ^ see1,
);
see2 = wymum(
read64(&bytes[start + 32..]) ^ secret[3],
read64(&bytes[start + 40..]) ^ see2,
);
index -= 48;
start += 48;
}
seed ^= see1 ^ see2;
}
while index > 16 {
seed = wymum(
read64(&bytes[start..]) ^ secret[1],
read64(&bytes[start + 8..]) ^ seed,
);
index -= 16;
start += 16
}
a = read64(&bytes[(length - 16)..]);
b = read64(&bytes[(length - 8)..]);
}
(a, b, seed)
}
#[inline]
pub fn wyhash_finish(a: u64, b: u64, seed: u64, length: u64, secret1: u64) -> u64 {
wymum(secret1 ^ length, wymum(a ^ secret1, b ^ seed))
}
pub fn make_secret(seed: u64) -> [u64; 4] {
let c = [
15_u8, 23, 27, 29, 30, 39, 43, 45, 46, 51, 53, 54, 57, 58, 60, 71, 75, 77, 78, 83, 85, 86,
89, 90, 92, 99, 101, 102, 105, 106, 108, 113, 114, 116, 120, 135, 139, 141, 142, 147, 149,
150, 153, 154, 156, 163, 165, 166, 169, 170, 172, 177, 178, 180, 184, 195, 197, 198, 201,
202, 204, 209, 210, 212, 216, 225, 226, 228, 232, 240,
];
let mut secret = [0_u64; 4];
let mut seed = seed;
for i in 0..secret.len() {
loop {
secret[i] = 0;
for j in (0..64).step_by(8) {
secret[i] |= u64::from(c[(wyrng(&mut seed) as usize) % c.len()]) << j;
}
if secret[i] % 2 == 0 {
continue;
}
let incorrect_number_of_ones_found = (0..i)
.step_by(1)
.find(|j| (secret[*j] ^ secret[i]).count_ones() != 32);
if incorrect_number_of_ones_found.is_none() {
break;
}
}
}
secret
}
pub fn wyrng(seed: &mut u64) -> u64 {
*seed = seed.wrapping_add(P0);
wymum(*seed, *seed ^ P1)
}
#[cfg(test)]
mod tests {
use super::make_secret;
#[test]
fn generates_secret_with_32_ones() {
let secret = make_secret(0);
for s in &secret {
assert_eq!(32, s.count_ones());
}
}
}