use std::fs::File;
use std::io::{BufReader, Read};
use crate::util::mix::{rapid_mix, rapid_mum};
use crate::util::read::{read_u32_combined, read_u64};
use crate::v1::rapid_const::{RAPID_SEED, RAPID_SECRET, rapidhash_finish, rapidhash_seed};
#[inline]
pub fn rapidhash_v1_file(data: &mut File) -> std::io::Result<u64> {
rapidhash_v1_file_inline::<false>(data, RAPID_SEED)
}
#[inline]
pub fn rapidhash_v1_file_seeded(data: &mut File, seed: u64) -> std::io::Result<u64> {
rapidhash_v1_file_inline::<false>(data, seed)
}
#[inline(always)]
pub fn rapidhash_v1_file_inline<const PROTECTED: bool>(data: &mut File, mut seed: u64) -> std::io::Result<u64> {
let len = data.metadata()?.len();
let mut reader = BufReader::new(data);
seed = rapidhash_seed(seed) ^ len;
let (a, b, _) = rapidhash_file_core::<PROTECTED>(0, 0, seed, len as usize, &mut reader)?;
Ok(rapidhash_finish::<PROTECTED>(a, b, len))
}
#[inline(always)]
fn rapidhash_file_core<const PROTECTED: bool>(mut a: u64, mut b: u64, mut seed: u64, len: usize, iter: &mut BufReader<&mut File>) -> std::io::Result<(u64, u64, u64)> {
if len <= 16 {
let mut data = [0u8; 16];
iter.read_exact(&mut data[0..len])?;
if len >= 8 {
let plast = len - 4;
let delta = 4;
a ^= read_u32_combined(&data, 0, plast);
b ^= read_u32_combined(&data, delta, plast - delta);
} else if len >= 4 {
let plast = len - 4;
let delta = 0;
a ^= read_u32_combined(&data, 0, plast);
b ^= read_u32_combined(&data, delta, plast - delta);
} else if len > 0 {
a ^= ((data[0] as u64) << 56) | ((data[len >> 1] as u64) << 32) | data[len - 1] as u64;
}
} else {
let mut remaining = len;
let mut buf = [0u8; 192];
let mut slice = &mut buf[..96];
let end;
if remaining > 48 {
let mut see1 = seed;
let mut see2 = seed;
while remaining >= 96 {
iter.read_exact(slice)?;
seed = rapid_mix::<PROTECTED>(read_u64(slice, 0) ^ RAPID_SECRET[0], read_u64(slice, 8) ^ seed);
see1 = rapid_mix::<PROTECTED>(read_u64(slice, 16) ^ RAPID_SECRET[1], read_u64(slice, 24) ^ see1);
see2 = rapid_mix::<PROTECTED>(read_u64(slice, 32) ^ RAPID_SECRET[2], read_u64(slice, 40) ^ see2);
seed = rapid_mix::<PROTECTED>(read_u64(slice, 48) ^ RAPID_SECRET[0], read_u64(slice, 56) ^ seed);
see1 = rapid_mix::<PROTECTED>(read_u64(slice, 64) ^ RAPID_SECRET[1], read_u64(slice, 72) ^ see1);
see2 = rapid_mix::<PROTECTED>(read_u64(slice, 80) ^ RAPID_SECRET[2], read_u64(slice, 88) ^ see2);
remaining -= 96;
}
slice = &mut buf[96..96 + remaining];
iter.read_exact(slice)?;
end = 96 + remaining;
if remaining >= 48 {
seed = rapid_mix::<PROTECTED>(read_u64(slice, 0) ^ RAPID_SECRET[0], read_u64(slice, 8) ^ seed);
see1 = rapid_mix::<PROTECTED>(read_u64(slice, 16) ^ RAPID_SECRET[1], read_u64(slice, 24) ^ see1);
see2 = rapid_mix::<PROTECTED>(read_u64(slice, 32) ^ RAPID_SECRET[2], read_u64(slice, 40) ^ see2);
slice = &mut buf[96 + 48..96 + remaining];
remaining -= 48;
}
seed ^= see1 ^ see2;
} else {
end = remaining;
slice = &mut buf[..remaining];
iter.read_exact(slice)?;
}
if remaining > 16 {
seed = rapid_mix::<PROTECTED>(read_u64(slice, 0) ^ RAPID_SECRET[2], read_u64(slice, 8) ^ seed ^ RAPID_SECRET[1]);
if remaining > 32 {
seed = rapid_mix::<PROTECTED>(read_u64(slice, 16) ^ RAPID_SECRET[2], read_u64(slice, 24) ^ seed);
}
}
a ^= read_u64(&buf, end - 16);
b ^= read_u64(&buf, end - 8);
}
a ^= RAPID_SECRET[1];
b ^= seed;
(a, b) = rapid_mum::<PROTECTED>(a, b);
Ok((a, b, seed))
}
#[cfg(test)]
mod tests {
use std::io::{Seek, SeekFrom, Write};
use crate::util::macros::compare_rapidhash_file;
use crate::v1::rapidhash_v1_inline;
use super::*;
compare_rapidhash_file!(compare_rapidhash_v1_file, rapidhash_v1_inline::<false, false, false>, rapidhash_v1_file_inline::<false>);
}