use std::fs::{File, OpenOptions, create_dir_all};
use std::io::{Read, Write};
use packed_simd::*;
const PASSES: usize = 5;
const MAP_BITS: usize = 22;
const MAP_LENGTH: usize = 4194304;
const HASH_SIZE_BYTES: usize = 32;
const SEED: usize = 0xFAFAECECFAFAECEC;
const FIRSTB: usize = 4631534797403582785;
const FIRSTV: usize = 3523455478921636871;
const FIRSTRAND: usize = 2458719153079158768;
#[derive(Copy, Clone, Default, Debug)]
pub struct State {
s1: usize,
s2: usize,
s3: usize,
a_s: usize,
hs: [usize; HASH_SIZE_BYTES],
first_idx: usize
}
#[derive(Copy, Clone, Default, Debug)]
pub struct SimdState {
s1: usizex4,
s2: usizex4,
s3: usizex4,
a_s: usizex4,
hs: [usizex4; HASH_SIZE_BYTES],
first_idx: usizex4
}
#[derive(Copy, Clone, Default, Debug)]
pub struct Simd8State {
s1: usizex8,
s2: usizex8,
s3: usizex8,
a_s: usizex8,
hs: [usizex8; HASH_SIZE_BYTES],
first_idx: usizex8
}
fn simd8_b(bytemap: &Vec<u8>, v: usizex8) -> u8x8 {
let idx = v & (MAP_LENGTH - 1);
u8x8::new(bytemap[idx.extract(0)],
bytemap[idx.extract(1)],
bytemap[idx.extract(2)],
bytemap[idx.extract(3)],
bytemap[idx.extract(4)],
bytemap[idx.extract(5)],
bytemap[idx.extract(6)],
bytemap[idx.extract(7)])
}
fn simd8_big_b(bytemap: &Vec<u8>, v : usizex8) -> usizex8 {
let idx = v & (MAP_LENGTH - 1);
usizex8::new(bytemap[idx.extract(0)] as usize,
bytemap[idx.extract(1)] as usize,
bytemap[idx.extract(2)] as usize,
bytemap[idx.extract(3)] as usize,
bytemap[idx.extract(4)] as usize,
bytemap[idx.extract(5)] as usize,
bytemap[idx.extract(6)] as usize,
bytemap[idx.extract(7)] as usize)
}
pub fn simd8_hash(bytemap: &Vec<u8>, src: &[u8x8])-> [u8x8; HASH_SIZE_BYTES] {
let hs = [usizex8::splat(0); HASH_SIZE_BYTES];
let mut state = Simd8State{hs,
a_s: usizex8::splat(SEED),
..Default::default()};
let mut idx: usize = 0;
for v2 in src.iter(){
if idx >= HASH_SIZE_BYTES {
idx = 0;
}
let v2_usize: usizex8 = (*v2).cast();
state = simd8_faststep(bytemap, state, v2_usize, idx);
idx += 1;
}
idx = 0;
for (i, v2) in src.iter().enumerate(){
if idx as usize >= HASH_SIZE_BYTES {
idx = 0;
}
if i == 0 {
let v2_usize: usizex8 = (*v2).cast();
state.first_idx = (state.a_s >> 5 ^ v2_usize) & (MAP_LENGTH - 1)
}
let v2_usize: usizex8 = (*v2).cast();
state= simd8_step(bytemap, state, v2_usize, idx);
idx += 1;
}
let u8_splat = u8x8::splat(0);
let mut digest = [u8_splat; HASH_SIZE_BYTES];
for i in (0..HASH_SIZE_BYTES).rev() {
let hs_int = state.hs[i];
state = simd8_step(bytemap, state, hs_int, i);
let bas = simd8_b(bytemap, state.a_s);
let bhs = simd8_b(bytemap, state.hs[i]);
let xor = bas ^ bhs;
digest[i] = xor;
}
digest
}
fn simd8_faststep(bytemap: &Vec<u8>,
state: Simd8State,
v2: usizex8,
idx: usize)
-> Simd8State {
let mut a_s = state.a_s;
let mut s1 = state.s1;
let hs = state.hs;
let b = simd8_big_b(bytemap, a_s ^ v2);
a_s = a_s<<7 ^ a_s>>5 ^ v2<<20 ^ v2<<16 ^ v2 ^ b<<20 ^ b<<12 ^ b<<4;
s1 = s1<<9 ^ s1>>3 ^ hs[idx];
let mut state = Simd8State{hs:hs,
a_s,
s1:state.s3,
s2:s1,
s3: state.s2,
first_idx: state.first_idx};
state.hs[idx] = s1 ^ a_s;
state
}
fn simd8_step(bytemap: &Vec<u8>, state: Simd8State, v2: usizex8, idx: usize) -> Simd8State {
let mut s1 = state.s1;
let mut s2 = state.s2;
let mut a_s =state.a_s;
let mut hs = state.hs;
s1 = s1 << 9 ^ s1 >> 1 ^ a_s ^ simd8_big_b(&bytemap, a_s>>5^v2) <<3 ;
s1 = s1 << 5 ^ s1 >> 3 ^ simd8_big_b(&bytemap, s1^v2) <<7;
s1 = s1 << 7 ^ s1 >> 7 ^ simd8_big_b(&bytemap, a_s^s1>>7)<<5;
s1 = s1 << 11 ^ s1 >> 5 ^ simd8_big_b(&bytemap, v2^a_s>>11^s1)<<27 ;
let hs_idx: usizex8 = hs[idx];
hs[idx] = s1 ^ a_s ^ hs_idx << 7 ^ hs_idx >> 13;
a_s = a_s<<17 ^ a_s>>5 ^ s1 ^ simd8_big_b(&bytemap, a_s^s1>>27^v2)<<3;
a_s = a_s<<13 ^ a_s>>3 ^ simd8_big_b(&bytemap, a_s^s1)<<7;
a_s = a_s<<15 ^ a_s>>7 ^ simd8_big_b(&bytemap, a_s>>7^s1)<<11;
a_s = a_s<<9 ^ a_s>>11 ^ simd8_big_b(&bytemap, v2^a_s^s1)<<3;
s1 = s1<<7 ^ s1>>27 ^ a_s ^ simd8_big_b(&bytemap, a_s>>3)<<13;
s1 = s1<<3 ^ s1>>13 ^ simd8_big_b(&bytemap, s1^v2)<<11;
s1 = s1<<8 ^ s1>>11 ^ simd8_big_b(&bytemap, a_s^s1>>11)<<9;
s1 = s1<<6 ^ s1>>9 ^ simd8_big_b(&bytemap, v2^a_s^s1)<<3;
a_s = a_s<<23 ^ a_s>>3 ^ s1 ^ simd8_big_b(&bytemap, a_s^v2^s1>>3)<<7;
a_s = a_s<<17 ^ a_s>>7 ^ simd8_big_b(&bytemap, a_s^s1>>3)<<5;
a_s = a_s<<13 ^ a_s>>5 ^ simd8_big_b(&bytemap, a_s>>5^s1)<<1;
a_s = a_s<<11 ^ a_s>>1 ^ simd8_big_b(&bytemap, v2^a_s^s1)<<7;
s1 = s1<<5 ^ s1>>3 ^ a_s ^ simd8_big_b(&bytemap, a_s>>7^s1>>3)<<6;
s1 = s1<<8 ^ s1>> 6 ^ simd8_big_b(&bytemap, s1^v2)<<11;
s1 = s1<<11 ^ s1>>11 ^ simd8_big_b(&bytemap, a_s^s1>>11)<<5;
s1 = s1<<7 ^ s1>>5 ^ simd8_big_b(&bytemap, v2^a_s>>7^a_s^s1)<<17;
s2 = s2<<3 ^ s2>>17 ^ s1 ^ simd8_big_b(&bytemap, a_s^s2>>5^v2)<<13;
s2 = s2<<6 ^ s2>>13 ^ simd8_big_b(&bytemap, s2)<<11;
s2 = s2<<11 ^ s2>>11 ^ simd8_big_b(&bytemap, a_s^s1^s2>>11)<<23;
s2 = s2<<4 ^ s2>>23 ^ simd8_big_b(&bytemap, v2^a_s>>8^a_s^s2>>10)<<1;
let hs_int = hs[idx];
s1 = s2<<3 ^ s2>>1 ^ hs_int ^ v2;
a_s = a_s<<9 ^ a_s>>7 ^ s1>>1 ^ simd8_big_b(&bytemap, s2>>1^ hs_int)<<5;
Simd8State{
s1: state.s3,
s2: s1,
s3: s2,
a_s: a_s,
hs: hs,
first_idx: state.first_idx
}
}
fn b(bytemap: &Vec<u8>, v: usize) -> u8 {
let idx = v & (MAP_LENGTH - 1);
bytemap[idx]
}
fn big_b(bytemap: &Vec<u8>, v : usize) -> usize {
b(bytemap, v) as usize
}
pub fn load_bytemap() -> Vec<u8> {
let mut pegnet_path = match dirs::home_dir() {
Some(path) => path,
None => panic!("Home directory permission. "),
};
pegnet_path.push(".pegnet");
create_dir_all(&pegnet_path).expect("Creating Pegnet Dir");
let filename = format!("lxrhash-seed-{:x}-passes-{}-size-{}.dat",
SEED, PASSES, MAP_BITS);
pegnet_path.push(filename);
match &mut File::open(pegnet_path.clone()) {
Ok(file) => {
let mut bytemap = vec!(0u8; MAP_LENGTH);
file.read(&mut bytemap).expect("Reading lxrhash file");
bytemap
},
Err(_) => {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&pegnet_path)
.expect("Creating lxrhash file");
let bytemap = generate_table();
file.write_all(&bytemap).expect("Writing lxrhash file");
bytemap}
}
}
fn generate_table() -> Vec<u8> {
let mut offset = SEED ^ FIRSTRAND;
let mut b = SEED ^ FIRSTB;
let mut v = FIRSTV;
let map_mask = MAP_LENGTH - 1;
let mut bytemap = vec!(0u8; MAP_LENGTH);
for i in 0..bytemap.len() {
bytemap[i] = (i % 256) as u8
}
for _ in 0..PASSES {
for i in 0..bytemap.len() {
offset = offset << 9 ^ offset >> 1 ^ offset >> 7 ^ b;
v = (bytemap[(offset ^ b) & map_mask]) as usize ^ v << 8 ^ v >> 1;
b = v<<7 ^ v<<13 ^ v<<33 ^ v<<52 ^ b<<9 ^ b>>1;
let j = (offset & map_mask) as i64;
bytemap.swap(i, j as usize);
}
}
bytemap
}
fn simd_b(bytemap: &Vec<u8>, v: usizex4) -> u8x4 {
let idx = v & (MAP_LENGTH - 1);
u8x4::new(bytemap[idx.extract(0)],
bytemap[idx.extract(1)],
bytemap[idx.extract(2)],
bytemap[idx.extract(3)])
}
fn simd_big_b(bytemap: &Vec<u8>, v : usizex4) -> usizex4 {
let idx = v & (MAP_LENGTH - 1);
usizex4::new(bytemap[idx.extract(0)] as usize,
bytemap[idx.extract(1)] as usize,
bytemap[idx.extract(2)] as usize,
bytemap[idx.extract(3)] as usize)
}
pub fn simd_hash(bytemap: &Vec<u8>, src: &[u8x4])-> [u8x4; HASH_SIZE_BYTES] {
let hs = [usizex4::splat(0); HASH_SIZE_BYTES];
let mut state = SimdState{hs, a_s: usizex4::splat(SEED), ..Default::default()};
let mut idx: usize = 0;
for v2 in src.iter(){
if idx >= HASH_SIZE_BYTES {
idx = 0;
}
let v2_usize: usizex4 = (*v2).cast();
state = simd_faststep(bytemap, state, v2_usize, idx);
idx += 1;
}
idx = 0;
for (i, v2) in src.iter().enumerate(){
if idx as usize >= HASH_SIZE_BYTES {
idx = 0;
}
if i == 0 {
let v2_usize: usizex4 = (*v2).cast();
state.first_idx = (state.a_s >> 5 ^ v2_usize) & (MAP_LENGTH - 1)
}
let v2_usize: usizex4 = (*v2).cast();
state= simd_step(bytemap, state, v2_usize, idx);
idx += 1;
}
let u8_splat = u8x4::splat(0);
let mut digest = [u8_splat; HASH_SIZE_BYTES];
for i in (0..HASH_SIZE_BYTES).rev() {
let hs_int = state.hs[i];
state = simd_step(bytemap, state, hs_int, i);
let bas = simd_b(bytemap, state.a_s);
let bhs = simd_b(bytemap, state.hs[i]);
let xor = bas ^ bhs;
digest[i] = xor;
}
digest
}
fn simd_faststep(bytemap: &Vec<u8>,
state: SimdState,
v2: usizex4,
idx: usize)
-> SimdState {
let mut a_s = state.a_s;
let mut s1 = state.s1;
let hs = state.hs;
let b = simd_big_b(bytemap, a_s ^ v2);
a_s = a_s<<7 ^ a_s>>5 ^ v2<<20 ^ v2<<16 ^ v2 ^ b<<20 ^ b<<12 ^ b<<4;
s1 = s1<<9 ^ s1>>3 ^ hs[idx];
let mut state = SimdState{hs:hs,
a_s,
s1:state.s3,
s2:s1,
s3: state.s2,
first_idx: state.first_idx};
state.hs[idx] = s1 ^ a_s;
state
}
fn simd_step(bytemap: &Vec<u8>, state: SimdState, v2: usizex4, idx: usize) -> SimdState {
let mut s1 = state.s1;
let mut s2 = state.s2;
let mut a_s =state.a_s;
let mut hs = state.hs;
s1 = s1 << 9 ^ s1 >> 1 ^ a_s ^ simd_big_b(&bytemap, a_s>>5^v2) <<3 ;
s1 = s1 << 5 ^ s1 >> 3 ^ simd_big_b(&bytemap, s1^v2) <<7;
s1 = s1 << 7 ^ s1 >> 7 ^ simd_big_b(&bytemap, a_s^s1>>7)<<5;
s1 = s1 << 11 ^ s1 >> 5 ^ simd_big_b(&bytemap, v2^a_s>>11^s1)<<27 ;
let hs_idx: usizex4 = hs[idx];
hs[idx] = s1 ^ a_s ^ hs_idx << 7 ^ hs_idx >> 13;
a_s = a_s<<17 ^ a_s>>5 ^ s1 ^ simd_big_b(&bytemap, a_s^s1>>27^v2)<<3;
a_s = a_s<<13 ^ a_s>>3 ^ simd_big_b(&bytemap, a_s^s1)<<7;
a_s = a_s<<15 ^ a_s>>7 ^ simd_big_b(&bytemap, a_s>>7^s1)<<11;
a_s = a_s<<9 ^ a_s>>11 ^ simd_big_b(&bytemap, v2^a_s^s1)<<3;
s1 = s1<<7 ^ s1>>27 ^ a_s ^ simd_big_b(&bytemap, a_s>>3)<<13;
s1 = s1<<3 ^ s1>>13 ^ simd_big_b(&bytemap, s1^v2)<<11;
s1 = s1<<8 ^ s1>>11 ^ simd_big_b(&bytemap, a_s^s1>>11)<<9;
s1 = s1<<6 ^ s1>>9 ^ simd_big_b(&bytemap, v2^a_s^s1)<<3;
a_s = a_s<<23 ^ a_s>>3 ^ s1 ^ simd_big_b(&bytemap, a_s^v2^s1>>3)<<7;
a_s = a_s<<17 ^ a_s>>7 ^ simd_big_b(&bytemap, a_s^s1>>3)<<5;
a_s = a_s<<13 ^ a_s>>5 ^ simd_big_b(&bytemap, a_s>>5^s1)<<1;
a_s = a_s<<11 ^ a_s>>1 ^ simd_big_b(&bytemap, v2^a_s^s1)<<7;
s1 = s1<<5 ^ s1>>3 ^ a_s ^ simd_big_b(&bytemap, a_s>>7^s1>>3)<<6;
s1 = s1<<8 ^ s1>> 6 ^ simd_big_b(&bytemap, s1^v2)<<11;
s1 = s1<<11 ^ s1>>11 ^ simd_big_b(&bytemap, a_s^s1>>11)<<5;
s1 = s1<<7 ^ s1>>5 ^ simd_big_b(&bytemap, v2^a_s>>7^a_s^s1)<<17;
s2 = s2<<3 ^ s2>>17 ^ s1 ^ simd_big_b(&bytemap, a_s^s2>>5^v2)<<13;
s2 = s2<<6 ^ s2>>13 ^ simd_big_b(&bytemap, s2)<<11;
s2 = s2<<11 ^ s2>>11 ^ simd_big_b(&bytemap, a_s^s1^s2>>11)<<23;
s2 = s2<<4 ^ s2>>23 ^ simd_big_b(&bytemap, v2^a_s>>8^a_s^s2>>10)<<1;
let hs_int = hs[idx];
s1 = s2<<3 ^ s2>>1 ^ hs_int ^ v2;
a_s = a_s<<9 ^ a_s>>7 ^ s1>>1 ^ simd_big_b(&bytemap, s2>>1^ hs_int)<<5;
SimdState{
s1: state.s3,
s2: s1,
s3: s2,
a_s: a_s,
hs: hs,
first_idx: state.first_idx
}
}
pub fn hash(bytemap: &Vec<u8>, src: &[u8])-> [u8; HASH_SIZE_BYTES] {
let hs = [0usize; HASH_SIZE_BYTES];
let mut state = State{hs, a_s: SEED, ..Default::default()};
let mut idx: usize = 0;
for v2 in src.iter(){
if idx >= HASH_SIZE_BYTES {
idx = 0;
}
state = faststep(bytemap, state, *v2 as usize, idx);
idx += 1;
}
idx = 0;
for (i, v2) in src.iter().enumerate(){
if idx as usize >= HASH_SIZE_BYTES {
idx = 0;
}
if i == 0 {
state.first_idx = (state.a_s >> 5 ^ (*v2 as usize)) & (MAP_LENGTH - 1)
}
state= step(bytemap, state, *v2 as usize, idx);
idx += 1;
}
let mut digest = [0u8; HASH_SIZE_BYTES];
for i in (0..HASH_SIZE_BYTES).rev() {
let hs_int = state.hs[i];
state = step(bytemap, state, hs_int, i);
let bas = b(bytemap, state.a_s);
let bhs = b(bytemap, state.hs[i]);
let xor = bas ^ bhs;
digest[i] = xor;
}
digest
}
fn faststep(bytemap: &Vec<u8>, state: State, v2: usize, idx: usize)-> State {
let mut a_s = state.a_s;
let mut s1 = state.s1;
let hs = state.hs;
let b = big_b(bytemap, a_s ^ v2);
a_s = a_s<<7 ^ a_s>>5 ^ v2<<20 ^ v2<<16 ^ v2 ^ b<<20 ^ b<<12 ^ b<<4;
s1 = s1<<9 ^ s1>>3 ^ hs[idx];
let mut state = State{hs:hs,
a_s,
s1:state.s3,
s2:s1,
s3: state.s2,
first_idx: state.first_idx};
state.hs[idx] = s1 ^ a_s;
state
}
fn step(bytemap: &Vec<u8>, state: State, v2: usize, idx: usize) -> State {
let mut s1 = state.s1;
let mut s2 = state.s2;
let mut a_s =state.a_s;
let mut hs = state.hs;
s1 = s1 << 9 ^ s1 >> 1 ^ a_s ^ big_b(&bytemap, a_s>>5^v2) <<3 ;
s1 = s1 << 5 ^ s1 >> 3 ^ big_b(&bytemap, s1^v2) <<7;
s1 = s1 << 7 ^ s1 >> 7 ^ big_b(&bytemap, a_s^s1>>7)<<5;
s1 = s1 << 11 ^ s1 >> 5 ^ big_b(&bytemap, v2^a_s>>11^s1)<<27 ;
hs[idx] = s1 ^ a_s ^ (hs[idx] as usize) << 7 ^ (hs[idx] as usize) >> 13;
a_s = a_s<<17 ^ a_s>>5 ^ s1 ^ big_b(&bytemap, a_s^s1>>27^v2)<<3;
a_s = a_s<<13 ^ a_s>>3 ^ big_b(&bytemap, a_s^s1)<<7;
a_s = a_s<<15 ^ a_s>>7 ^ big_b(&bytemap, a_s>>7^s1)<<11;
a_s = a_s<<9 ^ a_s>>11 ^ big_b(&bytemap, v2^a_s^s1)<<3;
s1 = s1<<7 ^ s1>>27 ^ a_s ^ big_b(&bytemap, a_s>>3)<<13;
s1 = s1<<3 ^ s1>>13 ^ big_b(&bytemap, s1^v2)<<11;
s1 = s1<<8 ^ s1>>11 ^ big_b(&bytemap, a_s^s1>>11)<<9;
s1 = s1<<6 ^ s1>>9 ^ big_b(&bytemap, v2^a_s^s1)<<3;
a_s = a_s<<23 ^ a_s>>3 ^ s1 ^ big_b(&bytemap, a_s^v2^s1>>3)<<7;
a_s = a_s<<17 ^ a_s>>7 ^ big_b(&bytemap, a_s^s1>>3)<<5;
a_s = a_s<<13 ^ a_s>>5 ^ big_b(&bytemap, a_s>>5^s1)<<1;
a_s = a_s<<11 ^ a_s>>1 ^ big_b(&bytemap, v2^a_s^s1)<<7;
s1 = s1<<5 ^ s1>>3 ^ a_s ^ big_b(&bytemap, a_s>>7^s1>>3)<<6;
s1 = s1<<8 ^ s1>> 6 ^ big_b(&bytemap, s1^v2)<<11;
s1 = s1<<11 ^ s1>>11 ^ big_b(&bytemap, a_s^s1>>11)<<5;
s1 = s1<<7 ^ s1>>5 ^ big_b(&bytemap, v2^a_s>>7^a_s^s1)<<17;
s2 = s2<<3 ^ s2>>17 ^ s1 ^ big_b(&bytemap, a_s^s2>>5^v2)<<13;
s2 = s2<<6 ^ s2>>13 ^ big_b(&bytemap, s2)<<11;
s2 = s2<<11 ^ s2>>11 ^ big_b(&bytemap, a_s^s1^s2>>11)<<23;
s2 = s2<<4 ^ s2>>23 ^ big_b(&bytemap, v2^a_s>>8^a_s^s2>>10)<<1;
let hs_int = hs[idx] as usize;
s1 = s2<<3 ^ s2>>1 ^ hs_int ^ v2;
a_s = a_s<<9 ^ a_s>>7 ^ s1>>1 ^ big_b(&bytemap, s2>>1^ hs_int)<<5;
State{
s1: state.s3,
s2: s1,
s3: s2,
a_s: a_s,
hs: hs,
first_idx: state.first_idx
}
}
#[cfg(test)]
mod tests {
use super::*;
const KNOWN_ANSWERS: [(&[u8], &str); 5] =
[(&[0u8], "45fe0c90ad90e60a23add5083b9b52c116fa04760b0e44190ce4feee5ad2196a"),
(&[42u8], "6e76b1383579aa5d4720e977c9398f4d84ad662fabfb97af24ead4943ba09fa9"),
(&[42u8; 8], "4dd2a4ad9a4426174d1434cc6fa3d34ca39f41a1ddae1c91a52315b9a26298ca"),
(&[42u8; 32], "8cd7b4a2a44eee34f950a94c0269244de7b90ff9746c3454f5b5f5c5bde65894"),
(&[42u8; 700], "c8ae7dece8eb7720547b336c723c6577043f4cf49fe1075c653a04d06feeae7d"),
];
pub fn hex(bytes: &[u8]) -> String {
let strs: Vec<String> = bytes.iter()
.map(|b| format!("{:02X}", b))
.collect();
strs.join("")
}
#[test]
fn known_answers() {
let bytemap = load_bytemap();
for kat in KNOWN_ANSWERS.into_iter() {
let digest = hash(&bytemap, kat.0);
let hex_digest = hex(&digest);
dbg!(&hex_digest);
dbg!(&kat.1.to_uppercase());
assert_eq!(hex_digest, kat.1.to_uppercase());
}
}
#[test]
fn simd_known_answers() {
let bytemap = load_bytemap();
for kat in KNOWN_ANSWERS.into_iter() {
let oprx4: Vec<u8x4> = kat.0.iter()
.map(|b| u8x4::splat(*b))
.collect();
let digest = simd_hash(&bytemap, &oprx4);
let hex_digest: Vec<u8> = digest.iter()
.map(|b| b.extract(0))
.collect::<_>();
dbg!(&hex_digest);
dbg!(&kat.1.to_uppercase());
assert_eq!(hex(&hex_digest), kat.1.to_uppercase());
}
}
}