use crate::generator::NextRandom;
use crate::util::fold;
use anyhow as ah;
use std::sync::OnceLock;
const CRC64_ECMA_POLY: u64 = 0xC96C_5795_D787_0F42;
static CRC64_ECMA_LUT: OnceLock<[u64; 256]> = OnceLock::new();
fn crc64_gen_lut(p: u64) -> [u64; 256] {
let mut lut = [0_u64; 256];
for (i, item) in lut.iter_mut().enumerate() {
let mut d: u64 = i.try_into().unwrap();
for _ in 0..8 {
if d & 1 == 0 {
d >>= 1;
} else {
d = (d >> 1) ^ p;
}
}
*item = d;
}
lut
}
#[inline(always)]
#[allow(clippy::inline_always)]
#[allow(clippy::cast_possible_truncation)]
fn crc64(lut: &[u64; 256], mut crc: u64, data: &[u8]) -> u64 {
for d in data {
crc = lut[((crc as u8) ^ *d) as usize] ^ (crc >> 8);
}
crc
}
pub struct GeneratorCrc {
folded_seed: [u8; GeneratorCrc::FOLDED_SEED_SIZE],
counter: u64,
}
impl GeneratorCrc {
pub const BASE_SIZE: usize = 256 * GeneratorCrc::CRC_SIZE;
pub const DEFAULT_CHUNK_FACTOR: usize = 1024 + 512;
const CRC_SIZE: usize = 64 / 8;
const FOLDED_SEED_SIZE: usize = 64 / 8;
pub fn new(seed: &[u8]) -> GeneratorCrc {
let _ = CRC64_ECMA_LUT.get_or_init(|| crc64_gen_lut(CRC64_ECMA_POLY));
assert!(!seed.is_empty());
let folded_seed = fold(seed, GeneratorCrc::FOLDED_SEED_SIZE)
.try_into()
.unwrap();
GeneratorCrc {
folded_seed,
counter: 0,
}
}
}
impl NextRandom for GeneratorCrc {
fn get_base_size(&self) -> usize {
GeneratorCrc::BASE_SIZE
}
#[allow(clippy::cast_possible_truncation)]
fn next(&mut self, buf: &mut [u8], count: usize) {
debug_assert!(buf.len() == GeneratorCrc::BASE_SIZE * count);
let lut = CRC64_ECMA_LUT.get().unwrap();
for i in 0..count {
let chunk_offs = i * GeneratorCrc::BASE_SIZE;
let mut crc = !0_u64;
crc = crc64(lut, crc, &self.folded_seed);
crc = crc64(lut, crc, &self.counter.to_le_bytes());
self.counter += 1;
for offs in 0..(GeneratorCrc::BASE_SIZE / GeneratorCrc::CRC_SIZE) {
debug_assert!(offs <= 0xFF);
crc = crc64(lut, crc, &(offs as u8).to_le_bytes());
let crc_bytes = (!crc).to_le_bytes();
let begin = chunk_offs + (offs * GeneratorCrc::CRC_SIZE);
let end = chunk_offs + ((offs + 1) * GeneratorCrc::CRC_SIZE);
buf[begin..end].copy_from_slice(&crc_bytes);
}
}
}
fn seek(&mut self, byte_offset: u64) -> ah::Result<()> {
if byte_offset % GeneratorCrc::BASE_SIZE as u64 != 0 {
return Err(ah::format_err!(
"CRC seek: Byte offset is not a \
multiple of the base size ({} bytes).",
GeneratorCrc::BASE_SIZE
));
}
self.counter = byte_offset / GeneratorCrc::BASE_SIZE as u64;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cmp_result() {
fn reduce(acc: u32, (i, x): (usize, &u8)) -> u32 {
acc.rotate_left(u32::try_from(i).unwrap()) ^ (u32::from(*x))
}
let mut a = GeneratorCrc::new(&[1, 2, 3]);
let mut buf = vec![0_u8; GeneratorCrc::BASE_SIZE * 3];
a.next(&mut buf[0..GeneratorCrc::BASE_SIZE], 1);
assert_eq!(buf.iter().enumerate().fold(0, reduce), 2_183_862_535);
a.next(&mut buf[0..GeneratorCrc::BASE_SIZE], 1);
assert_eq!(buf.iter().enumerate().fold(0, reduce), 2_200_729_683);
a.next(&mut buf[0..GeneratorCrc::BASE_SIZE * 2], 2);
assert_eq!(buf.iter().enumerate().fold(0, reduce), 17_260_884);
a.next(&mut buf[0..GeneratorCrc::BASE_SIZE * 3], 3);
assert_eq!(buf.iter().enumerate().fold(0, reduce), 581_162_875);
}
#[test]
fn test_seed_equal() {
let mut a = GeneratorCrc::new(&[1, 2, 3]);
let mut b = GeneratorCrc::new(&[1, 2, 3]);
let mut res_a: Vec<Vec<u8>> = vec![];
let mut res_b: Vec<Vec<u8>> = vec![];
for _ in 0..2 {
let mut buf = vec![0_u8; GeneratorCrc::BASE_SIZE];
a.next(&mut buf, 1);
res_a.push(buf);
let mut buf = vec![0_u8; GeneratorCrc::BASE_SIZE];
b.next(&mut buf, 1);
res_b.push(buf);
}
assert_eq!(res_a[0], res_b[0]);
assert_eq!(res_a[1], res_b[1]);
assert_ne!(res_a[0], res_a[1]);
assert_ne!(res_b[0], res_b[1]);
}
#[test]
fn test_seed_diff() {
let mut a = GeneratorCrc::new(&[1, 2, 3]);
let mut b = GeneratorCrc::new(&[1, 2, 4]);
let mut res_a: Vec<Vec<u8>> = vec![];
let mut res_b: Vec<Vec<u8>> = vec![];
for _ in 0..2 {
let mut buf = vec![0_u8; GeneratorCrc::BASE_SIZE];
a.next(&mut buf, 1);
res_a.push(buf);
let mut buf = vec![0_u8; GeneratorCrc::BASE_SIZE];
b.next(&mut buf, 1);
res_b.push(buf);
}
assert_ne!(res_a[0], res_b[0]);
assert_ne!(res_a[1], res_b[1]);
assert_ne!(res_a[0], res_a[1]);
assert_ne!(res_b[0], res_b[1]);
}
#[test]
fn test_concat_equal() {
let mut a = GeneratorCrc::new(&[1, 2, 3]);
let mut b = GeneratorCrc::new(&[1, 2, 3]);
let mut buf_a = vec![0_u8; GeneratorCrc::BASE_SIZE * 2];
a.next(&mut buf_a[0..GeneratorCrc::BASE_SIZE], 1);
a.next(
&mut buf_a[GeneratorCrc::BASE_SIZE..GeneratorCrc::BASE_SIZE * 2],
1,
);
let mut buf_b = vec![0_u8; GeneratorCrc::BASE_SIZE * 2];
b.next(&mut buf_b, 2);
assert_eq!(buf_a, buf_b);
}
#[test]
fn test_seek() {
let mut a = GeneratorCrc::new(&[1, 2, 3]);
let mut b = GeneratorCrc::new(&[1, 2, 3]);
b.seek(GeneratorCrc::BASE_SIZE as u64 * 2).unwrap();
let mut bdata = vec![0_u8; GeneratorCrc::BASE_SIZE];
b.next(&mut bdata, 1);
let mut adata = vec![0_u8; GeneratorCrc::BASE_SIZE];
a.next(&mut adata, 1);
assert_ne!(adata, bdata);
a.next(&mut adata, 1);
assert_ne!(adata, bdata);
a.next(&mut adata, 1);
assert_eq!(adata, bdata);
a.next(&mut adata, 1);
assert_ne!(adata, bdata);
}
}