xxhash-rust-0.8.2/.cargo_vcs_info.json0000644000000001120000000000000133320ustar { "git": { "sha1": "4c08d10707ccb770138bb8289ba1ee8b8569fca0" } } xxhash-rust-0.8.2/Cargo.toml0000644000000024320000000000000113370ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "xxhash-rust" version = "0.8.2" authors = ["Douman "] include = ["**/*.rs", "Cargo.toml", "README.md"] description = "Implementation of xxhash" readme = "README.md" keywords = ["hash", "xxhash"] categories = ["algorithms"] license = "BSL-1.0" repository = "https://github.com/DoumanAsh/xxhash-rust" [package.metadata.docs.rs] features = ["xxh32", "const_xxh32", "xxh64", "const_xxh64", "xxh3", "const_xxh3"] [[bench]] name = "compare_c" harness = false [dev-dependencies.criterion] version = "0.3" [dev-dependencies.getrandom] version = "0" [dev-dependencies.twox-hash] version = "1" [dev-dependencies.xxhash-c-sys] version = "0.8.1" [features] const_xxh3 = [] const_xxh32 = [] const_xxh64 = [] xxh3 = [] xxh32 = [] xxh64 = [] xxhash-rust-0.8.2/Cargo.toml.orig000064400000000000000000000016630000000000000150030ustar 00000000000000[package] name = "xxhash-rust" version = "0.8.2" authors = ["Douman "] edition = "2018" description = "Implementation of xxhash" readme = "README.md" repository = "https://github.com/DoumanAsh/xxhash-rust" license = "BSL-1.0" keywords = ["hash", "xxhash"] categories = ["algorithms"] include = [ "**/*.rs", "Cargo.toml", "README.md" ] [[bench]] name = "compare_c" harness = false [features] # XXH32 makes sense only on 32bit platforms xxh32 = [] # Enable xxh32 implementation const_xxh32 = [] # Enable const xxh32 implementation xxh64 = [] # Enable xxh64 implementation const_xxh64 = [] # Enable const xxh64 implementation xxh3 = [] # Enable xxh3 implementation const_xxh3 = [] # Enable const xxh3 implementation [dev-dependencies] twox-hash = "1" xxhash-c-sys = "0.8.1" criterion = "0.3" getrandom = "0" [package.metadata.docs.rs] features = ["xxh32", "const_xxh32", "xxh64", "const_xxh64", "xxh3", "const_xxh3"] xxhash-rust-0.8.2/README.md000064400000000000000000000035650000000000000133760ustar 00000000000000# xxhash-rust ![Rust](https://github.com/DoumanAsh/xxhash-rust/workflows/Rust/badge.svg?branch=master) [![Crates.io](https://img.shields.io/crates/v/xxhash-rust.svg)](https://crates.io/crates/xxhash-rust) [![Documentation](https://docs.rs/xxhash-rust/badge.svg)](https://docs.rs/crate/xxhash-rust/) Implementation of [xxHash](https://github.com/Cyan4973/xxHash) in Rust Version corresponds to xxHash [releases](https://github.com/Cyan4973/xxHash/releases) Each algorithm is implemented via feature, allowing precise control over code size. ## Features: - `xxh32` - Enables 32bit algorithm. Suitable for x86 targets - `const_xxh32` - `const fn` version of `xxh32` algorithm - `xxh64` - Enables 64 algorithm. Suitable for x86_64 targets - `const_xxh64` - `const fn` version of `xxh64` algorithm - `xxh3` - Enables `xxh3` family of algorithms, superior to `xxh32` and `xxh64` in terms of performance. - `const_xxh3` - `const fn` version of `xxh3` algorithm ## HW acceleration Similar to reference implementation, crate implements various SIMDs in `xxh3` depending on provided flags. All checks are performed only at compile time, hence user is encouraged to enable these accelerations (for example via `-C target_cpu=native`) Used SIMD acceleration: - SSE2 - widely available, can be safely enabled in 99% of cases. Enabled by default in `x86_64` targets. - AVX2; ## Version note - Crate `0.8.0` contains invalid API and hence new increment was required. - Crate `0.8.1` contains mistake in xxh3 algorithm, resulting in invalid input at input length equal to multiple of internal buffer + 1. In addition to that when total length reaches 1025 and more - As `0.8.0` and `0.8.1` are yanked, I consider it non-existing and hence `0.8.2` is the only version with stable API In order to keep up with original implementation version I'm not planning to bump major/minor until C implementation does so. xxhash-rust-0.8.2/benches/compare_c.rs000064400000000000000000000224160000000000000160200ustar 00000000000000use criterion::{criterion_group, criterion_main, Criterion}; const DATA: [&str; 34] = [ "waifulandshigtgsqwetyuop[]asbnm,./", "waifulandshigtgsqwetyuop[]asbnm,.", "waifulandshigtgsqwetyuop[]asbnm,", "waifulandshigtgsqwetyuop[]asbnm", "waifulandshigtgsqwetyuop[]asbn", "waifulandshigtgsqwetyuop[]asb", "waifulandshigtgsqwetyuop[]as", "waifulandshigtgsqwetyuop[]a", "waifulandshigtgsqwetyuop[]", "waifulandshigtgsqwetyuop[", "waifulandshigtgsqwetyuop", "waifulandshigtgsqwetyuo", "waifulandshigtgsqwetyu", "waifulandshigtgsqwety", "waifulandshigtgsqwet", "waifulandshigtgsqwe", "waifulandshigtgsqw", "waifulandshigtgsq", "waifulandshigtgs", "waifulandshigtg", "waifulandshigt", "waifulandshig", "waifulandshi", "waifulandsh", "waifulands", "waifuland", "waifulan", "waifula", "waiful", "lolka", "lolk", "lol", "lo", "l", ]; fn define(c: &mut Criterion) { #[cfg(feature = "xxh32")] c.bench_function("xxh32 Rust", |b| b.iter_batched(|| &DATA, |data| for input in data { xxhash_rust::xxh32::xxh32(input.as_bytes(), 0); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "const_xxh32")] c.bench_function("const_xxh32 Rust", |b| b.iter_batched(|| &DATA, |data| for input in data { xxhash_rust::const_xxh32::xxh32(input.as_bytes(), 0); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh32")] c.bench_function("xxh32 Rust Stateful", |b| b.iter_batched(|| &DATA, |data| for input in data { let mut hasher = xxhash_rust::xxh32::Xxh32::new(0); hasher.update(input.as_bytes()); hasher.digest(); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh64")] c.bench_function("xxh64 Rust", |b| b.iter_batched(|| &DATA, |data| for input in data { xxhash_rust::xxh64::xxh64(input.as_bytes(), 0); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh64")] c.bench_function("xxh64 Rust Stateful", |b| b.iter_batched(|| &DATA, |data| for input in data { let mut hasher = xxhash_rust::xxh64::Xxh64::new(0); hasher.update(input.as_bytes()); hasher.digest(); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "const_xxh64")] c.bench_function("const_xxh64 Rust", |b| b.iter_batched(|| &DATA, |data| for input in data { xxhash_rust::const_xxh64::xxh64(input.as_bytes(), 0); }, criterion::BatchSize::SmallInput)); c.bench_function("twox-hash32 Rust", |b| b.iter_batched(|| &DATA, |data| for input in data { use core::hash::Hasher; let mut hasher = twox_hash::XxHash32::with_seed(0); hasher.write(input.as_bytes()); hasher.finish(); }, criterion::BatchSize::SmallInput)); c.bench_function("twox-hash64 Rust", |b| b.iter_batched(|| &DATA, |data| for input in data { use core::hash::Hasher; let mut hasher = twox_hash::XxHash64::with_seed(0); hasher.write(input.as_bytes()); hasher.finish(); }, criterion::BatchSize::SmallInput)); c.bench_function("xxh32 C", |b| b.iter_batched(|| &DATA, |data| for input in data { unsafe { xxhash_c_sys::XXH32(input.as_ptr() as _, input.len(), 0); } }, criterion::BatchSize::SmallInput)); c.bench_function("xxh32 C Stateful", |b| b.iter_batched(|| &DATA, |data| for input in data { use xxhash_c_sys as sys; let mut state = core::mem::MaybeUninit::::uninit(); unsafe { sys::XXH32_reset(state.as_mut_ptr(), 0); sys::XXH32_update(state.as_mut_ptr(), input.as_ptr() as _, input.len()); sys::XXH32_digest(state.as_mut_ptr()); } }, criterion::BatchSize::SmallInput)); c.bench_function("xxh64 C", |b| b.iter_batched(|| &DATA, |data| for input in data { unsafe { xxhash_c_sys::XXH64(input.as_ptr() as _, input.len(), 0); } }, criterion::BatchSize::SmallInput)); c.bench_function("xxh64 C Stateful", |b| b.iter_batched(|| &DATA, |data| for input in data { use xxhash_c_sys as sys; let mut state = core::mem::MaybeUninit::::uninit(); unsafe { sys::XXH64_reset(state.as_mut_ptr(), 0); sys::XXH64_update(state.as_mut_ptr(), input.as_ptr() as _, input.len()); sys::XXH64_digest(state.as_mut_ptr()); } }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh3")] c.bench_function("xxh3_64 Rust", |b| b.iter_batched(|| &DATA, |data| for input in data { xxhash_rust::xxh3::xxh3_64(input.as_bytes()); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh3")] c.bench_function("xxh3_64 Rust Stateful", move |b| b.iter_batched(move || &DATA, |data| for input in data { let mut hasher = xxhash_rust::xxh3::Xxh3::new(); hasher.update(input.as_bytes()); hasher.digest(); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh3")] c.bench_function("xxh3_128 Rust", |b| b.iter_batched(|| &DATA, |data| for input in data { xxhash_rust::xxh3::xxh3_128(input.as_bytes()); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "const_xxh3")] c.bench_function("const_xxh3 64 Rust", |b| b.iter_batched(|| &DATA, |data| for input in data { xxhash_rust::const_xxh3::xxh3_64(input.as_bytes()); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "const_xxh3")] c.bench_function("const_xxh3 128 Rust", |b| b.iter_batched(|| &DATA, |data| for input in data { xxhash_rust::const_xxh3::xxh3_128(input.as_bytes()); }, criterion::BatchSize::SmallInput)); c.bench_function("xxh3_64 C Stateful", |b| b.iter_batched(|| &DATA, |data| for input in data { use xxhash_c_sys as sys; let mut state = core::mem::MaybeUninit::::uninit(); unsafe { sys::XXH3_64bits_reset(state.as_mut_ptr()); sys::XXH3_64bits_update(state.as_mut_ptr(), input.as_ptr() as _, input.len()); sys::XXH3_64bits_digest(state.as_mut_ptr()); } }, criterion::BatchSize::SmallInput)); c.bench_function("xxh3_64 C", |b| b.iter_batched(|| &DATA, |data| for input in data { unsafe { xxhash_c_sys::XXH3_64bits(input.as_ptr() as _, input.len()); } }, criterion::BatchSize::SmallInput)); c.bench_function("xxh3_128 C", |b| b.iter_batched(|| &DATA, |data| for input in data { unsafe { xxhash_c_sys::XXH3_128bits(input.as_ptr() as _, input.len()); } }, criterion::BatchSize::SmallInput)); let mut rand_230_bytes = [0u8; 260]; let _ = getrandom::getrandom(&mut rand_230_bytes); c.bench_function("xxh3_64 C 230b", |b| b.iter_batched(|| rand_230_bytes, |input| { unsafe { xxhash_c_sys::XXH3_64bits(input.as_ptr() as _, input.len()); } }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh3")] c.bench_function("xxh3_64 Rust 230b", |b| b.iter_batched(|| rand_230_bytes, |input| { xxhash_rust::xxh3::xxh3_64(&input); }, criterion::BatchSize::SmallInput)); c.bench_function("xxh3_128 C 230b", |b| b.iter_batched(|| rand_230_bytes, |input| { unsafe { xxhash_c_sys::XXH3_128bits(input.as_ptr() as _, input.len()); } }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh3")] c.bench_function("xxh3_128 Rust 230b", |b| b.iter_batched(|| rand_230_bytes, |input| { xxhash_rust::xxh3::xxh3_128(&input); }, criterion::BatchSize::SmallInput)); let mut rand_260_bytes = [0u8; 260]; let _ = getrandom::getrandom(&mut rand_260_bytes); c.bench_function("xxh3_64 C 260b", |b| b.iter_batched(|| rand_260_bytes, |input| { unsafe { xxhash_c_sys::XXH3_64bits(input.as_ptr() as _, input.len()); } }, criterion::BatchSize::SmallInput)); c.bench_function("xxh3_64 C Stateful 260b", |b| b.iter_batched(|| rand_260_bytes, |input| { use xxhash_c_sys as sys; let mut state = core::mem::MaybeUninit::::uninit(); unsafe { sys::XXH3_64bits_reset(state.as_mut_ptr()); sys::XXH3_64bits_update(state.as_mut_ptr(), input.as_ptr() as _, input.len()); sys::XXH3_64bits_digest(state.as_mut_ptr()); } }, criterion::BatchSize::SmallInput)); c.bench_function("xxh3_128 C 260b", |b| b.iter_batched(|| rand_260_bytes, |input| { unsafe { xxhash_c_sys::XXH3_128bits(input.as_ptr() as _, input.len()); } }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh3")] c.bench_function("xxh3_64 Rust 260b", |b| b.iter_batched(|| rand_260_bytes, |input| { xxhash_rust::xxh3::xxh3_64(&input); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh3")] c.bench_function("xxh3_64 Rust Stateful 260b", |b| b.iter_batched(|| rand_260_bytes, |input| { let mut hasher = xxhash_rust::xxh3::Xxh3::new(); hasher.update(&input); hasher.digest(); }, criterion::BatchSize::SmallInput)); #[cfg(feature = "xxh3")] c.bench_function("xxh3_128 Rust 260b", |b| b.iter_batched(|| rand_260_bytes, |input| { xxhash_rust::xxh3::xxh3_128(&input); }, criterion::BatchSize::SmallInput)); } criterion_group!(benches, define); criterion_main!(benches); xxhash-rust-0.8.2/src/const_xxh3.rs000064400000000000000000000450660000000000000153560ustar 00000000000000//!Xxh3 `const fn` implementation //! //!This module is efficient only when hashes are guaranteed to be executed at compile time. //!At runtime algorithm is written in fairly inefficient code, although still fast enough. use core::mem; use crate::xxh32_common as xxh32; use crate::xxh64_common as xxh64; use crate::xxh3_common::*; const INITIAL_ACC: [u64; ACC_NB] = [ xxh32::PRIME_3 as u64, xxh64::PRIME_1, xxh64::PRIME_2, xxh64::PRIME_3, xxh64::PRIME_4, xxh32::PRIME_2 as u64, xxh64::PRIME_5, xxh32::PRIME_1 as u64 ]; #[inline(always)] const fn read_u32(input: &[u8], cursor: usize) -> u32 { input[cursor] as u32 | (input[cursor + 1] as u32) << 8 | (input[cursor + 2] as u32) << 16 | (input[cursor + 3] as u32) << 24 } #[inline(always)] const fn read_u64(input: &[u8], cursor: usize) -> u64 { input[cursor] as u64 | (input[cursor + 1] as u64) << 8 | (input[cursor + 2] as u64) << 16 | (input[cursor + 3] as u64) << 24 | (input[cursor + 4] as u64) << 32 | (input[cursor + 5] as u64) << 40 | (input[cursor + 6] as u64) << 48 | (input[cursor + 7] as u64) << 56 } #[inline(always)] const fn mult32_to64(left: u32, right: u32) -> u64 { (left as u64).wrapping_mul(right as u64) } #[inline] const fn mix16_b(input: &[u8], input_offset: usize, secret: &[u8], secret_offset: usize, seed: u64) -> u64 { let mut input_lo = read_u64(input, input_offset); let mut input_hi = read_u64(input, input_offset + 8); input_lo ^= read_u64(secret, secret_offset).wrapping_add(seed); input_hi ^= read_u64(secret, secret_offset + 8).wrapping_sub(seed); mul128_fold64(input_lo, input_hi) } #[inline] const fn mix32_b(mut acc: (u64, u64), input_1: &[u8], input_1_off: usize, input_2: &[u8], input_2_off: usize, secret: &[u8], secret_offset: usize, seed: u64) -> (u64, u64) { acc.0 = acc.0.wrapping_add(mix16_b(input_1, input_1_off, secret, secret_offset, seed)); acc.0 ^= read_u64(input_2, input_2_off).wrapping_add(read_u64(input_2, input_2_off + 8)); acc.1 = acc.1.wrapping_add(mix16_b(input_2, input_2_off, secret, secret_offset + 16, seed)); acc.1 ^= read_u64(input_1, input_1_off).wrapping_add(read_u64(input_1, input_1_off + 8)); acc } #[inline(always)] const fn xxh3_64_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 { let flip1 = (read_u64(secret, 24) ^ read_u64(secret, 32)).wrapping_add(seed); let flip2 = (read_u64(secret, 40) ^ read_u64(secret, 48)).wrapping_sub(seed); let input_lo = read_u64(input, 0) ^ flip1; let input_hi = read_u64(input, input.len() - 8) ^ flip2; let acc = (input.len() as u64).wrapping_add(input_lo.swap_bytes()) .wrapping_add(input_hi) .wrapping_add(mul128_fold64(input_lo, input_hi)); avalanche(acc) } #[inline(always)] const fn xxh3_64_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u64 { seed ^= ((seed as u32).swap_bytes() as u64) << 32; let input1 = read_u32(input, 0); let input2 = read_u32(input, input.len() - 4); let flip = (read_u64(secret, 8) ^ read_u64(secret, 16)).wrapping_sub(seed); let input64 = (input2 as u64).wrapping_add((input1 as u64) << 32); let keyed = input64 ^ flip; strong_avalanche(keyed, input.len() as u64) } #[inline(always)] const fn xxh3_64_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u64 { let combo = ((input[0] as u32) << 16) | ((input[input.len() >> 1] as u32) << 24) | (input[input.len() - 1] as u32) | ((input.len() as u32) << 8); let flip = ((read_u32(secret, 0) ^ read_u32(secret, 4)) as u64).wrapping_add(seed); xxh64::avalanche((combo as u64) ^ flip) } #[inline(always)] const fn xxh3_64_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 { if input.len() > 8 { xxh3_64_9to16(input, seed, secret) } else if input.len() >= 4 { xxh3_64_4to8(input, seed, secret) } else if input.len() > 0 { xxh3_64_1to3(input, seed, secret) } else { xxh64::avalanche(seed ^ read_u64(secret, 56) ^ read_u64(secret, 64)) } } #[inline(always)] const fn xxh3_64_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u64 { let mut acc = (input.len() as u64).wrapping_mul(xxh64::PRIME_1); if input.len() > 32 { if input.len() > 64 { if input.len() > 96 { acc = acc.wrapping_add(mix16_b(input, 48, secret, 96, seed)); acc = acc.wrapping_add(mix16_b(input, input.len()-64, secret, 112, seed)); } acc = acc.wrapping_add(mix16_b(input, 32, secret, 64, seed)); acc = acc.wrapping_add(mix16_b(input, input.len()-48, secret, 80, seed)); } acc = acc.wrapping_add(mix16_b(input, 16, secret, 32, seed)); acc = acc.wrapping_add(mix16_b(input, input.len()-32, secret, 48, seed)); } acc = acc.wrapping_add(mix16_b(input, 0, secret, 0, seed)); acc = acc.wrapping_add(mix16_b(input, input.len()-16, secret, 16, seed)); avalanche(acc) } const fn xxh3_64_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u64 { const START_OFFSET: usize = 3; const LAST_OFFSET: usize = 17; let mut acc = (input.len() as u64).wrapping_mul(xxh64::PRIME_1); let nb_rounds = input.len() / 16; let mut idx = 0; while idx < 8 { acc = acc.wrapping_add(mix16_b(input, 16*idx, secret, 16*idx, seed)); idx += 1; } acc = avalanche(acc); while idx < nb_rounds { acc = acc.wrapping_add(mix16_b(input, 16*idx, secret, 16*(idx-8)+START_OFFSET, seed)); idx += 1; } acc = acc.wrapping_add(mix16_b(input, input.len()-16, secret, SECRET_SIZE_MIN-LAST_OFFSET, seed)); avalanche(acc) } #[inline(always)] const fn mix_two_accs(acc: &[u64], acc_offset: usize, secret: &[u8], secret_offset: usize) -> u64 { mul128_fold64(acc[acc_offset] ^ read_u64(secret, secret_offset), acc[acc_offset + 1] ^ read_u64(secret, secret_offset + 8)) } #[inline] const fn merge_accs(acc: &[u64], secret: &[u8], secret_offset: usize, mut result: u64) -> u64 { let mut idx = 0; while idx < 4 { result = result.wrapping_add(mix_two_accs(acc, idx * 2, secret, secret_offset + idx * 16)); idx += 1; } avalanche(result) } const fn scramble_acc(mut acc: [u64; ACC_NB], secret: &[u8], secret_offset: usize) -> [u64; ACC_NB] { let mut idx = 0; while idx < ACC_NB { let key = read_u64(secret, secret_offset + 8 * idx); let mut acc_val = xorshift64(acc[idx], 47); acc_val ^= key; acc[idx] = acc_val.wrapping_mul(xxh32::PRIME_1 as u64); idx += 1; } acc } const fn accumulate_512(mut acc: [u64; ACC_NB], input: &[u8], input_offset: usize, secret: &[u8], secret_offset: usize) -> [u64; ACC_NB] { let mut idx = 0; while idx < ACC_NB { let data_val = read_u64(input, input_offset + 8 * idx); let data_key = data_val ^ read_u64(secret, secret_offset + 8 * idx); acc[idx ^ 1] = acc[idx ^ 1].wrapping_add(data_val); acc[idx] = acc[idx].wrapping_add(mult32_to64((data_key & 0xFFFFFFFF) as u32, (data_key >> 32) as u32)); idx += 1; } acc } #[inline(always)] const fn accumulate_loop(mut acc: [u64; ACC_NB], input: &[u8], input_offset: usize, secret: &[u8], secret_offset: usize, nb_stripes: usize) -> [u64; ACC_NB] { let mut idx = 0; while idx < nb_stripes { acc = accumulate_512(acc, input, input_offset + idx * STRIPE_LEN, secret, secret_offset + idx * SECRET_CONSUME_RATE); idx += 1; } acc } #[inline] const fn hash_long_internal_loop(input: &[u8], secret: &[u8]) -> [u64; ACC_NB] { let mut acc = INITIAL_ACC; let nb_stripes = (secret.len() - STRIPE_LEN) / SECRET_CONSUME_RATE; let block_len = STRIPE_LEN * nb_stripes; let nb_blocks = (input.len() - 1) / block_len; let mut idx = 0; while idx < nb_blocks { acc = accumulate_loop(acc, input, idx * block_len, secret, 0, nb_stripes); acc = scramble_acc(acc, secret, secret.len() - STRIPE_LEN); idx += 1; } let nb_stripes = ((input.len() - 1) - (block_len * nb_blocks)) / STRIPE_LEN; acc = accumulate_loop(acc, input, nb_blocks * block_len, secret, 0, nb_stripes); accumulate_512(acc, input, input.len() - STRIPE_LEN, secret, secret.len() - STRIPE_LEN - SECRET_LASTACC_START) } const fn xxh3_64_long_impl(input: &[u8], secret: &[u8]) -> u64 { let acc = hash_long_internal_loop(input, secret); merge_accs(&acc, secret, SECRET_MERGEACCS_START, (input.len() as u64).wrapping_mul(xxh64::PRIME_1)) } #[inline(always)] ///Returns 64bit hash for provided input. pub const fn xxh3_64(input: &[u8]) -> u64 { xxh3_64_with_seed(input, 0) } ///Returns 64bit hash for provided input using seed. pub const fn xxh3_64_with_seed(input: &[u8], seed: u64) -> u64 { if input.len() <= 16 { xxh3_64_0to16(input, seed, &DEFAULT_SECRET) } else if input.len() <= 128 { xxh3_64_7to128(input, seed, &DEFAULT_SECRET) } else if input.len() <= MID_SIZE_MAX { xxh3_64_129to240(input, seed, &DEFAULT_SECRET) } else { xxh3_64_long_impl(input, &const_custom_default_secret(seed)) } } ///Returns 64bit hash for provided input using custom secret. pub const fn xxh3_64_with_secret(input: &[u8], secret: &[u8; DEFAULT_SECRET_SIZE]) -> u64 { if input.len() <= 16 { xxh3_64_0to16(input, 0, secret) } else if input.len() <= 128 { xxh3_64_7to128(input, 0, secret) } else if input.len() <= MID_SIZE_MAX { xxh3_64_129to240(input, 0, secret) } else { xxh3_64_long_impl(input, secret) } } // //128bit // #[inline(always)] const fn xxh3_128_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u128 { let c1 = input[0]; let c2 = input[input.len() >> 1]; let c3 = input[input.len() - 1]; let input_lo = (c1 as u32) << 16 | (c2 as u32) << 24 | c3 as u32 | (input.len() as u32) << 8; let input_hi = input_lo.swap_bytes().rotate_left(13); let flip_lo = (read_u32(secret, 0) as u64 ^ read_u32(secret, 4) as u64).wrapping_add(seed); let flip_hi = (read_u32(secret, 8) as u64 ^ read_u32(secret, 12) as u64).wrapping_sub(seed); let keyed_lo = input_lo as u64 ^ flip_lo; let keyed_hi = input_hi as u64 ^ flip_hi; xxh64::avalanche(keyed_lo) as u128 | (xxh64::avalanche(keyed_hi) as u128) << 64 } #[inline(always)] const fn xxh3_128_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u128 { seed ^= ((seed as u32).swap_bytes() as u64) << 32; let lo = read_u32(input, 0); let hi = read_u32(input, input.len() - 4); let input_64 = (lo as u64).wrapping_add((hi as u64) << 32); let flip = (read_u64(secret, 16) ^ read_u64(secret, 24)).wrapping_add(seed); let keyed = input_64 ^ flip; let (mut lo, mut hi) = mul64_to128(keyed, xxh64::PRIME_1.wrapping_add((input.len() as u64) << 2)); hi = hi.wrapping_add(lo << 1); lo ^= hi >> 3; lo = xorshift64(lo, 35).wrapping_mul(0x9FB21C651E98DF25); lo = xorshift64(lo, 28); hi = avalanche(hi); lo as u128 | (hi as u128) << 64 } #[inline(always)] const fn xxh3_128_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 { let flip_lo = (read_u64(secret, 32) ^ read_u64(secret, 40)).wrapping_sub(seed); let flip_hi = (read_u64(secret, 48) ^ read_u64(secret, 56)).wrapping_add(seed); let input_lo = read_u64(input, 0); let mut input_hi = read_u64(input, input.len() - 8); let (mut mul_low, mut mul_high) = mul64_to128(input_lo ^ input_hi ^ flip_lo, xxh64::PRIME_1); mul_low = mul_low.wrapping_add((input.len() as u64 - 1) << 54); input_hi ^= flip_hi; //Separate code for 32bit and bigger targets //It only makes sense when width is multiple of 2, but if necessary change to simple if //Not to mention I'm not sure you can even compile u128 on something like 16bit? #[cfg(any(target_pointer_width = "32", target_pointer_width = "16", target_pointer_width = "8"))] { mul_high = mul_high.wrapping_add( (input_hi & 0xFFFFFFFF00000000).wrapping_add(mult32_to64(input_hi as u32, xxh32::PRIME_2)) ); } #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "16", target_pointer_width = "8")))] { mul_high = mul_high.wrapping_add( input_hi.wrapping_add(mult32_to64(input_hi as u32, xxh32::PRIME_2 - 1)) ) } mul_low ^= mul_high.swap_bytes(); let (result_low, mut result_hi) = mul64_to128(mul_low, xxh64::PRIME_2); result_hi = result_hi.wrapping_add( mul_high.wrapping_mul(xxh64::PRIME_2) ); avalanche(result_low) as u128 | (avalanche(result_hi) as u128) << 64 } #[inline(always)] const fn xxh3_128_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 { if input.len() > 8 { xxh3_128_9to16(input, seed, secret) } else if input.len() >= 4 { xxh3_128_4to8(input, seed, secret) } else if input.len() > 0 { xxh3_128_1to3(input, seed, secret) } else { let flip_lo = read_u64(secret, 64) ^ read_u64(secret, 72); let flip_hi = read_u64(secret, 80) ^ read_u64(secret, 88); xxh64::avalanche(seed ^ flip_lo) as u128 | (xxh64::avalanche(seed ^ flip_hi) as u128) << 64 } } #[inline(always)] const fn xxh3_128_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u128 { let mut acc = ((input.len() as u64).wrapping_mul(xxh64::PRIME_1), 0u64); if input.len() > 32 { if input.len() > 64 { if input.len() > 96 { acc = mix32_b(acc, input, 48, input, input.len() - 64, secret, 96, seed); } acc = mix32_b(acc, input, 32, input, input.len() - 48, secret, 64, seed); } acc = mix32_b(acc, input, 16, input, input.len() - 32, secret, 32, seed); } acc = mix32_b(acc, input, 0, input, input.len() - 16, secret, 0, seed); let result_lo = acc.0.wrapping_add(acc.1); let result_hi = acc.0.wrapping_mul(xxh64::PRIME_1) .wrapping_add(acc.1.wrapping_mul(xxh64::PRIME_4)) .wrapping_add((input.len() as u64).wrapping_sub(seed).wrapping_mul(xxh64::PRIME_2)); avalanche(result_lo) as u128 | (0u64.wrapping_sub(avalanche(result_hi)) as u128) << 64 } #[inline(never)] const fn xxh3_128_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u128 { const START_OFFSET: usize = 3; const LAST_OFFSET: usize = 17; let nb_rounds = input.len() / 32; let mut acc = ((input.len() as u64).wrapping_mul(xxh64::PRIME_1), 0u64); let mut idx = 0; while idx < 4 { acc = mix32_b(acc, input, 32 * idx, input, (32 * idx) + 16, secret, 32 * idx, seed); idx += 1; } acc.0 = avalanche(acc.0); acc.1 = avalanche(acc.1); while idx < nb_rounds { acc = mix32_b(acc, input, 32 * idx, input, (32 * idx) + 16, secret, START_OFFSET.wrapping_add(32 * (idx - 4)), seed); idx += 1; } acc = mix32_b(acc, input, input.len() - 16, input, input.len() - 32, secret, SECRET_SIZE_MIN - LAST_OFFSET - 16, 0u64.wrapping_sub(seed)); let result_lo = acc.0.wrapping_add(acc.1); let result_hi = acc.0.wrapping_mul(xxh64::PRIME_1) .wrapping_add(acc.1.wrapping_mul(xxh64::PRIME_4)) .wrapping_add((input.len() as u64).wrapping_sub(seed).wrapping_mul(xxh64::PRIME_2)); avalanche(result_lo) as u128 | 0u128.wrapping_sub(avalanche(result_hi) as u128) << 64 } const fn xxh3_128_long_impl(input: &[u8], secret: &[u8]) -> u128 { let acc = hash_long_internal_loop(input, secret); let lo = merge_accs(&acc, secret, SECRET_MERGEACCS_START, (input.len() as u64).wrapping_mul(xxh64::PRIME_1)); let hi = merge_accs(&acc, secret, secret.len() - ACC_NB * mem::size_of::() - SECRET_MERGEACCS_START, !(input.len() as u64).wrapping_mul(xxh64::PRIME_2)); lo as u128 | (hi as u128) << 64 } #[inline(always)] ///Returns 128 hash for provided input. pub const fn xxh3_128(input: &[u8]) -> u128 { xxh3_128_with_seed(input, 0) } ///Returns 128 hash for provided input using seed. pub const fn xxh3_128_with_seed(input: &[u8], seed: u64) -> u128 { if input.len() <= 16 { xxh3_128_0to16(input, seed, &DEFAULT_SECRET) } else if input.len() <= 128 { xxh3_128_7to128(input, seed, &DEFAULT_SECRET) } else if input.len() <= MID_SIZE_MAX { xxh3_128_129to240(input, seed, &DEFAULT_SECRET) } else { xxh3_128_long_impl(input, &const_custom_default_secret(seed)) } } ///Returns 128 hash for provided input using custom secret. pub const fn xxh3_128_with_secret(input: &[u8], secret: &[u8; DEFAULT_SECRET_SIZE]) -> u128 { if input.len() <= 16 { xxh3_128_0to16(input, 0, secret) } else if input.len() <= 128 { xxh3_128_7to128(input, 0, secret) } else if input.len() <= MID_SIZE_MAX { xxh3_128_129to240(input, 0, secret) } else { xxh3_128_long_impl(input, secret) } } //Const version is only efficient when it is actually executed at compile time #[inline(always)] ///Generates secret derived from provided seed and default secret. /// ///Efficient when executed at compile time as alternative to using version of algorithm with custom `seed` pub const fn const_custom_default_secret(seed: u64) -> [u8; DEFAULT_SECRET_SIZE] { if seed == 0 { return DEFAULT_SECRET; } #[inline(always)] const fn read_u64(input: &[u8], cursor: usize) -> u64 { input[cursor] as u64 | (input[cursor + 1] as u64) << 8 | (input[cursor + 2] as u64) << 16 | (input[cursor + 3] as u64) << 24 | (input[cursor + 4] as u64) << 32 | (input[cursor + 5] as u64) << 40 | (input[cursor + 6] as u64) << 48 | (input[cursor + 7] as u64) << 56 } let mut idx = 0; let mut result = [0; DEFAULT_SECRET_SIZE]; const NB_ROUNDS: usize = DEFAULT_SECRET_SIZE / 16; while idx < NB_ROUNDS { let lo = read_u64(&DEFAULT_SECRET, idx * 16).wrapping_add(seed).to_le_bytes(); let hi = read_u64(&DEFAULT_SECRET, idx * 16 + 8).wrapping_sub(seed).to_le_bytes(); result[idx * 16] = lo[0]; result[idx * 16 + 1] = lo[1]; result[idx * 16 + 2] = lo[2]; result[idx * 16 + 3] = lo[3]; result[idx * 16 + 4] = lo[4]; result[idx * 16 + 5] = lo[5]; result[idx * 16 + 6] = lo[6]; result[idx * 16 + 7] = lo[7]; result[idx * 16 + 8] = hi[0]; result[idx * 16 + 8 + 1] = hi[1]; result[idx * 16 + 8 + 2] = hi[2]; result[idx * 16 + 8 + 3] = hi[3]; result[idx * 16 + 8 + 4] = hi[4]; result[idx * 16 + 8 + 5] = hi[5]; result[idx * 16 + 8 + 6] = hi[6]; result[idx * 16 + 8 + 7] = hi[7]; idx += 1; } result } xxhash-rust-0.8.2/src/const_xxh32.rs000064400000000000000000000043270000000000000154330ustar 00000000000000//!Const eval friendly xxh32 implementation. use core::mem; use crate::xxh32_common::*; #[inline(always)] const fn read_u32(input: &[u8], cursor: usize) -> u32 { input[cursor] as u32 | (input[cursor + 1] as u32) << 8 | (input[cursor + 2] as u32) << 16 | (input[cursor + 3] as u32) << 24 } const fn finalize(mut input: u32, data: &[u8], mut cursor: usize) -> u32 { let mut len = data.len() - cursor; while len >= 4 { input = input.wrapping_add( read_u32(data, cursor).wrapping_mul(PRIME_3) ); cursor += mem::size_of::(); len -= mem::size_of::(); input = input.rotate_left(17).wrapping_mul(PRIME_4); } while len > 0 { input = input.wrapping_add((data[cursor] as u32).wrapping_mul(PRIME_5)); cursor += mem::size_of::(); len -= mem::size_of::(); input = input.rotate_left(11).wrapping_mul(PRIME_1); } avalanche(input) } ///Const variant of xxh32 hashing pub const fn xxh32(input: &[u8], seed: u32) -> u32 { let mut result = input.len() as u32; let mut cursor = 0; if input.len() >= CHUNK_SIZE { let mut v1 = seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2); let mut v2 = seed.wrapping_add(PRIME_2); let mut v3 = seed; let mut v4 = seed.wrapping_sub(PRIME_1); loop { v1 = round(v1, read_u32(input, cursor)); cursor += mem::size_of::(); v2 = round(v2, read_u32(input, cursor)); cursor += mem::size_of::(); v3 = round(v3, read_u32(input, cursor)); cursor += mem::size_of::(); v4 = round(v4, read_u32(input, cursor)); cursor += mem::size_of::(); if (input.len() - cursor) < CHUNK_SIZE { break; } } result = result.wrapping_add( v1.rotate_left(1).wrapping_add( v2.rotate_left(7).wrapping_add( v3.rotate_left(12).wrapping_add( v4.rotate_left(18) ) ) ) ); } else { result = result.wrapping_add(seed.wrapping_add(PRIME_5)); } finalize(result, input, cursor) } xxhash-rust-0.8.2/src/const_xxh64.rs000064400000000000000000000056730000000000000154450ustar 00000000000000//!Const 64 bit version of xxhash algorithm use core::mem; use crate::xxh64_common::*; #[inline(always)] const fn read_u32(input: &[u8], cursor: usize) -> u32 { input[cursor] as u32 | (input[cursor + 1] as u32) << 8 | (input[cursor + 2] as u32) << 16 | (input[cursor + 3] as u32) << 24 } #[inline(always)] const fn read_u64(input: &[u8], cursor: usize) -> u64 { input[cursor] as u64 | (input[cursor + 1] as u64) << 8 | (input[cursor + 2] as u64) << 16 | (input[cursor + 3] as u64) << 24 | (input[cursor + 4] as u64) << 32 | (input[cursor + 5] as u64) << 40 | (input[cursor + 6] as u64) << 48 | (input[cursor + 7] as u64) << 56 } const fn finalize(mut input: u64, data: &[u8], mut cursor: usize) -> u64 { let mut len = data.len() - cursor; while len >= 8 { input ^= round(0, read_u64(data, cursor)); cursor += mem::size_of::(); len -= mem::size_of::(); input = input.rotate_left(27).wrapping_mul(PRIME_1).wrapping_add(PRIME_4) } if len >= 4 { input ^= (read_u32(data, cursor) as u64).wrapping_mul(PRIME_1); cursor += mem::size_of::(); len -= mem::size_of::(); input = input.rotate_left(23).wrapping_mul(PRIME_2).wrapping_add(PRIME_3); } while len > 0 { input ^= (data[cursor] as u64).wrapping_mul(PRIME_5); cursor += mem::size_of::(); len -= mem::size_of::(); input = input.rotate_left(11).wrapping_mul(PRIME_1); } avalanche(input) } ///Returns hash for the provided input. pub const fn xxh64(input: &[u8], seed: u64) -> u64 { let input_len = input.len() as u64; let mut cursor = 0; let mut result; if input.len() >= CHUNK_SIZE { let mut v1 = seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2); let mut v2 = seed.wrapping_add(PRIME_2); let mut v3 = seed; let mut v4 = seed.wrapping_sub(PRIME_1); loop { v1 = round(v1, read_u64(input, cursor)); cursor += mem::size_of::(); v2 = round(v2, read_u64(input, cursor)); cursor += mem::size_of::(); v3 = round(v3, read_u64(input, cursor)); cursor += mem::size_of::(); v4 = round(v4, read_u64(input, cursor)); cursor += mem::size_of::(); if (input.len() - cursor) < CHUNK_SIZE { break; } } result = v1.rotate_left(1).wrapping_add(v2.rotate_left(7)) .wrapping_add(v3.rotate_left(12)) .wrapping_add(v4.rotate_left(18)); result = merge_round(result, v1); result = merge_round(result, v2); result = merge_round(result, v3); result = merge_round(result, v4); } else { result = seed.wrapping_add(PRIME_5) } result = result.wrapping_add(input_len); finalize(result, input, cursor) } xxhash-rust-0.8.2/src/lib.rs000064400000000000000000000025450000000000000140170ustar 00000000000000//!Implementation of [xxHash](https://github.com/Cyan4973/xxHash) in Rust //! //!Version corresponds to xxHash [releases](https://github.com/Cyan4973/xxHash/releases) //! //!Each algorithm is implemented via feature, allowing precise control over code size. //! //!## Features: //! //!- `xxh32` - Enables 32bit algorithm. Suitable for x86 targets //!- `const_xxh32` - `const fn` version of `xxh32` algorithm //!- `xxh64` - Enables 64 algorithm. Suitable for x86_64 targets //!- `const_xxh64` - `const fn` version of `xxh64` algorithm //!- `xxh3` - Enables `xxh3` family of algorithms, superior to `xxh32` and `xxh64` in terms of performance. //!- `const_xxh3` - `const fn` version of `xxh3` algorithm #![no_std] #![warn(missing_docs)] #![cfg_attr(feature = "cargo-clippy", allow(clippy::style))] #[cfg(any(feature = "xxh32", feature = "const_xxh32", feature = "xxh3", feature = "const_xxh3"))] mod xxh32_common; #[cfg(feature = "xxh32")] pub mod xxh32; #[cfg(feature = "const_xxh32")] pub mod const_xxh32; #[cfg(any(feature = "xxh64", feature = "const_xxh64", feature = "xxh3", feature = "const_xxh3"))] mod xxh64_common; #[cfg(feature = "xxh64")] pub mod xxh64; #[cfg(feature = "const_xxh64")] pub mod const_xxh64; #[cfg(any(feature = "xxh3", feature = "const_xxh3"))] mod xxh3_common; #[cfg(feature = "xxh3")] pub mod xxh3; #[cfg(feature = "const_xxh3")] pub mod const_xxh3; xxhash-rust-0.8.2/src/xxh3.rs000064400000000000000000001025240000000000000141410ustar 00000000000000//!XXH3 implementation //! //!Provides `Hasher` only for 64bit as 128bit variant would not be much different due to trait //!being limited to `u64` outputs. use core::{ptr, mem}; use crate::xxh32_common as xxh32; use crate::xxh64_common as xxh64; use crate::xxh3_common::*; // Code is as close to original C implementation as possible // It does make it look ugly, but it is fast and easy to update once xxhash gets new version. #[cfg(all(target_feature = "sse2", not(target_feature = "avx2")))] #[repr(align(16))] #[derive(Clone)] struct Acc([u64; ACC_NB]); #[cfg(target_feature = "avx2")] #[repr(align(32))] #[derive(Clone)] struct Acc([u64; ACC_NB]); #[cfg(not(any(target_feature = "avx2", target_feature = "sse2")))] #[repr(align(8))] #[derive(Clone)] struct Acc([u64; ACC_NB]); const INITIAL_ACC: Acc = Acc([ xxh32::PRIME_3 as u64, xxh64::PRIME_1, xxh64::PRIME_2, xxh64::PRIME_3, xxh64::PRIME_4, xxh32::PRIME_2 as u64, xxh64::PRIME_5, xxh32::PRIME_1 as u64 ]); type LongHashFn = fn(&[u8], u64, &[u8]) -> u64; type LongHashFn128 = fn(&[u8], u64, &[u8]) -> u128; #[cfg(any(target_feature = "sse2", target_feature = "avx2"))] #[inline] const fn _mm_shuffle(z: u32, y: u32, x: u32, w: u32) -> i32 { ((z << 6) | (y << 4) | (x << 2) | w) as i32 } #[inline(always)] const fn mult32_to64(left: u32, right: u32) -> u64 { (left as u64).wrapping_mul(right as u64) } #[inline(always)] fn _mm_prefetch(ptr: *const i8, offset: isize) { #[cfg(target_arch = "x86")] unsafe { core::arch::x86::_mm_prefetch(ptr.offset(offset), core::arch::x86::_MM_HINT_T0); } #[cfg(target_arch = "x86_64")] unsafe { core::arch::x86_64::_mm_prefetch(ptr.offset(offset), core::arch::x86_64::_MM_HINT_T0); } } #[inline(always)] ///It is faster to use unsafe offset than wasting time to slice fn slice_offset_ptr(slice: &[u8], offset: usize) -> *const u8 { debug_assert!(slice.len() >= offset); unsafe { slice.as_ptr().add(offset) } } #[inline(always)] fn read_32le_unaligned(data: *const u8) -> u32 { let mut result = mem::MaybeUninit::::uninit(); unsafe { ptr::copy_nonoverlapping(data, result.as_mut_ptr() as _, mem::size_of::()); result.assume_init().to_le() } } #[inline(always)] fn read_64le_unaligned(data: *const u8) -> u64 { let mut result = mem::MaybeUninit::::uninit(); unsafe { ptr::copy_nonoverlapping(data, result.as_mut_ptr() as _, mem::size_of::()); result.assume_init().to_le() } } #[inline(always)] fn mix_two_accs(acc: &mut [u64], secret: *const u8) -> u64 { mul128_fold64(acc[0] ^ read_64le_unaligned(secret), acc[1] ^ read_64le_unaligned(unsafe { secret.offset(8) })) } #[inline] fn merge_accs(acc: &mut Acc, secret: *const u8, mut result: u64) -> u64 { for idx in 0..4 { result = result.wrapping_add(mix_two_accs(&mut acc.0[idx * 2..], unsafe { secret.add(idx * 16) })); } avalanche(result) } #[inline] fn mix16_b(input: *const u8, secret: *const u8, seed: u64) -> u64 { let mut input_lo = read_64le_unaligned(input); let mut input_hi = read_64le_unaligned(unsafe { input.offset(8) }); input_lo ^= read_64le_unaligned(secret).wrapping_add(seed); input_hi ^= read_64le_unaligned(unsafe { secret.offset(8) }).wrapping_sub(seed); mul128_fold64(input_lo, input_hi) } #[inline] fn mix32_b(lo: &mut u64, hi: &mut u64, input_1: *const u8, input_2: *const u8, secret: *const u8, seed: u64) { *lo = lo.wrapping_add(mix16_b(input_1, secret, seed)); *lo ^= read_64le_unaligned(input_2).wrapping_add(read_64le_unaligned(unsafe { input_2.offset(8) })); *hi = hi.wrapping_add(mix16_b(input_2, unsafe { secret.offset(16) }, seed)); *hi ^= read_64le_unaligned(input_1).wrapping_add(read_64le_unaligned(unsafe { input_1.offset(8) })); } #[inline(always)] fn custom_default_secret(seed: u64) -> [u8; DEFAULT_SECRET_SIZE] { let mut result = mem::MaybeUninit::<[u8; DEFAULT_SECRET_SIZE]>::uninit(); let nb_rounds = DEFAULT_SECRET_SIZE / 16; for idx in 0..nb_rounds { let low = read_64le_unaligned(slice_offset_ptr(&DEFAULT_SECRET, idx * 16)).wrapping_add(seed); let hi = read_64le_unaligned(slice_offset_ptr(&DEFAULT_SECRET, idx * 16 + 8)).wrapping_sub(seed); unsafe { ptr::copy_nonoverlapping(low.to_le_bytes().as_ptr(), (result.as_mut_ptr() as *mut u8).add(idx * 16), mem::size_of::()); ptr::copy_nonoverlapping(hi.to_le_bytes().as_ptr(), (result.as_mut_ptr() as *mut u8).add(idx * 16 + 8), mem::size_of::()); } } unsafe { result.assume_init() } } //TODO: Should we add AVX? // SSE is safe cuz it is available everywhere, but avx should probably be optional fn accumulate_512(acc: &mut Acc, input: *const u8, secret: *const u8) { #[cfg(all(target_feature = "sse2", not(target_feature = "avx2")))] unsafe { #[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::*; let xacc = acc.0.as_mut_ptr() as *mut __m128i; let xinput = input as *const __m128i; let xsecret = secret as *const __m128i; for idx in 0..STRIPE_LEN / mem::size_of::<__m128i>() { let data_vec = _mm_loadu_si128(xinput.add(idx)); let key_vec = _mm_loadu_si128(xsecret.add(idx)); let data_key = _mm_xor_si128(data_vec, key_vec); let data_key_lo = _mm_shuffle_epi32(data_key, _mm_shuffle(0, 3, 0, 1)); let product = _mm_mul_epu32(data_key, data_key_lo); let data_swap = _mm_shuffle_epi32(data_vec, _mm_shuffle(1,0,3,2)); let sum = _mm_add_epi64(*xacc.add(idx), data_swap); xacc.add(idx).write(_mm_add_epi64(product, sum)); } } #[cfg(target_feature = "avx2")] unsafe { #[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::*; let xacc = acc.0.as_mut_ptr() as *mut __m256i; let xinput = input as *const __m256i; let xsecret = secret as *const __m256i; for idx in 0..STRIPE_LEN / mem::size_of::<__m256i>() { let data_vec = _mm256_loadu_si256(xinput.add(idx)); let key_vec = _mm256_loadu_si256(xsecret.add(idx)); let data_key = _mm256_xor_si256(data_vec, key_vec); let data_key_lo = _mm256_shuffle_epi32(data_key, _mm_shuffle(0, 3, 0, 1)); let product = _mm256_mul_epu32(data_key, data_key_lo); let data_swap = _mm256_shuffle_epi32(data_vec, _mm_shuffle(1,0,3,2)); let sum = _mm256_add_epi64(*xacc.add(idx), data_swap); xacc.add(idx).write(_mm256_add_epi64(product, sum)); } } #[cfg(not(any(target_feature = "avx2", target_feature = "sse2")))] { for idx in 0..ACC_NB { let data_val = read_64le_unaligned(unsafe { input.add(8 * idx) }); let data_key = data_val ^ read_64le_unaligned(unsafe { secret.add(8 * idx) }); acc.0[idx ^ 1] = acc.0[idx ^ 1].wrapping_add(data_val); acc.0[idx] = acc.0[idx].wrapping_add(mult32_to64((data_key & 0xFFFFFFFF) as u32, (data_key >> 32) as u32)); } } } fn scramble_acc(acc: &mut Acc, secret: *const u8) { #[cfg(all(target_feature = "sse2", not(target_feature = "avx2")))] unsafe { #[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::*; let xacc = acc.0.as_mut_ptr() as *mut __m128i; let xsecret = secret as *const __m128i; let prime32 = _mm_set1_epi32(xxh32::PRIME_1 as i32); for idx in 0..STRIPE_LEN / mem::size_of::<__m128i>() { let acc_vec = *xacc.add(idx); let shifted = _mm_srli_epi64(acc_vec, 47); let data_vec = _mm_xor_si128(acc_vec, shifted); let key_vec = _mm_loadu_si128(xsecret.add(idx)); let data_key = _mm_xor_si128(data_vec, key_vec); let data_key_hi = _mm_shuffle_epi32(data_key, _mm_shuffle(0, 3, 0, 1)); let prod_lo = _mm_mul_epu32(data_key, prime32); let prod_hi = _mm_mul_epu32(data_key_hi, prime32); xacc.add(idx).write(_mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32))); } } #[cfg(target_feature = "avx2")] unsafe { #[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::*; let xacc = acc.0.as_mut_ptr() as *mut __m256i; let xsecret = secret as *const __m256i; let prime32 = _mm256_set1_epi32(xxh32::PRIME_1 as i32); for idx in 0..STRIPE_LEN / mem::size_of::<__m256i>() { let acc_vec = *xacc.add(idx); let shifted = _mm256_srli_epi64(acc_vec, 47); let data_vec = _mm256_xor_si256(acc_vec, shifted); let key_vec = _mm256_loadu_si256(xsecret.add(idx)); let data_key = _mm256_xor_si256(data_vec, key_vec); let data_key_hi = _mm256_shuffle_epi32(data_key, _mm_shuffle(0, 3, 0, 1)); let prod_lo = _mm256_mul_epu32(data_key, prime32); let prod_hi = _mm256_mul_epu32(data_key_hi, prime32); xacc.add(idx).write(_mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32))); } } #[cfg(not(any(target_feature = "avx2", target_feature = "sse2")))] { for idx in 0..ACC_NB { let key = read_64le_unaligned(unsafe { secret.add(8 * idx) }); let mut acc_val = xorshift64(acc.0[idx], 47); acc_val ^= key; acc.0[idx] = acc_val.wrapping_mul(xxh32::PRIME_1 as u64); } } } #[inline(always)] fn accumulate_loop(acc: &mut Acc, input: *const u8, secret: *const u8, nb_stripes: usize) { for idx in 0..nb_stripes { _mm_prefetch(input as _, 320); accumulate_512(acc, unsafe { input.add(idx * STRIPE_LEN) }, unsafe { secret.add(idx * SECRET_CONSUME_RATE) }); } } #[inline] fn hash_long_internal_loop(acc: &mut Acc, input: &[u8], secret: &[u8]) { let nb_stripes = (secret.len() - STRIPE_LEN) / SECRET_CONSUME_RATE; let block_len = STRIPE_LEN * nb_stripes; let nb_blocks = (input.len() - 1) / block_len; for idx in 0..nb_blocks { accumulate_loop(acc, slice_offset_ptr(input, idx * block_len), secret.as_ptr(), nb_stripes); scramble_acc(acc, slice_offset_ptr(secret, secret.len() - STRIPE_LEN)); } //last partial block debug_assert!(input.len() > STRIPE_LEN); let nb_stripes = ((input.len() - 1) - (block_len * nb_blocks)) / STRIPE_LEN; debug_assert!(nb_stripes <= (secret.len() / SECRET_CONSUME_RATE)); accumulate_loop(acc, slice_offset_ptr(input, nb_blocks * block_len), secret.as_ptr(), nb_stripes); //last stripe accumulate_512(acc, slice_offset_ptr(input, input.len() - STRIPE_LEN), slice_offset_ptr(secret, secret.len() - STRIPE_LEN - SECRET_LASTACC_START)); } #[inline(always)] fn xxh3_64_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u64 { debug_assert!(input.len() >= 1 && input.len() <= 3); let combo = ((input[0] as u32) << 16) | ((input[input.len() >> 1] as u32) << 24) | (input[input.len() - 1] as u32) | ((input.len() as u32) << 8); let flip = ((read_32le_unaligned(secret.as_ptr()) ^ read_32le_unaligned(slice_offset_ptr(secret, 4))) as u64).wrapping_add(seed); xxh64::avalanche((combo as u64) ^ flip) } #[inline(always)] fn xxh3_64_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u64 { debug_assert!(input.len() >= 4 && input.len() <= 8); seed ^= ((seed as u32).swap_bytes() as u64) << 32; let input1 = read_32le_unaligned(input.as_ptr()); let input2 = read_32le_unaligned(slice_offset_ptr(input, input.len() - 4)); let flip = (read_64le_unaligned(slice_offset_ptr(secret, 8)) ^ read_64le_unaligned(slice_offset_ptr(secret, 16))).wrapping_sub(seed); let input64 = (input2 as u64).wrapping_add((input1 as u64) << 32); let keyed = input64 ^ flip; strong_avalanche(keyed, input.len() as u64) } #[inline(always)] fn xxh3_64_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 { debug_assert!(input.len() >= 9 && input.len() <= 16); let flip1 = (read_64le_unaligned(slice_offset_ptr(secret, 24)) ^ read_64le_unaligned(slice_offset_ptr(secret, 32))).wrapping_add(seed); let flip2 = (read_64le_unaligned(slice_offset_ptr(secret, 40)) ^ read_64le_unaligned(slice_offset_ptr(secret, 48))).wrapping_sub(seed); let input_lo = read_64le_unaligned(input.as_ptr()) ^ flip1; let input_hi = read_64le_unaligned(slice_offset_ptr(input, input.len() - 8)) ^ flip2; let acc = (input.len() as u64).wrapping_add(input_lo.swap_bytes()) .wrapping_add(input_hi) .wrapping_add(mul128_fold64(input_lo, input_hi)); avalanche(acc) } #[inline(always)] fn xxh3_64_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u64 { if input.len() > 8 { xxh3_64_9to16(input, seed, secret) } else if input.len() >= 4 { xxh3_64_4to8(input, seed, secret) } else if input.len() > 0 { xxh3_64_1to3(input, seed, secret) } else { xxh64::avalanche(seed ^ (read_64le_unaligned(slice_offset_ptr(secret, 56)) ^ read_64le_unaligned(slice_offset_ptr(secret, 64)))) } } #[inline(always)] fn xxh3_64_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u64 { let mut acc = (input.len() as u64).wrapping_mul(xxh64::PRIME_1); if input.len() > 32 { if input.len() > 64 { if input.len() > 96 { acc = acc.wrapping_add(mix16_b(slice_offset_ptr(input, 48), slice_offset_ptr(secret, 96), seed)); acc = acc.wrapping_add(mix16_b(slice_offset_ptr(input, input.len()-64), slice_offset_ptr(secret, 112), seed)); } acc = acc.wrapping_add(mix16_b(slice_offset_ptr(input, 32), slice_offset_ptr(secret, 64), seed)); acc = acc.wrapping_add(mix16_b(slice_offset_ptr(input, input.len()-48), slice_offset_ptr(secret, 80), seed)); } acc = acc.wrapping_add(mix16_b(slice_offset_ptr(input, 16), slice_offset_ptr(secret, 32), seed)); acc = acc.wrapping_add(mix16_b(slice_offset_ptr(input, input.len()-32), slice_offset_ptr(secret, 48), seed)); } acc = acc.wrapping_add(mix16_b(input.as_ptr(), secret.as_ptr(), seed)); acc = acc.wrapping_add(mix16_b(slice_offset_ptr(input, input.len()-16), slice_offset_ptr(secret, 16), seed)); avalanche(acc) } #[inline(never)] fn xxh3_64_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u64 { const START_OFFSET: usize = 3; const LAST_OFFSET: usize = 17; let mut acc = (input.len() as u64).wrapping_mul(xxh64::PRIME_1); let nb_rounds = input.len() / 16; for idx in 0..8 { acc = acc.wrapping_add(mix16_b(slice_offset_ptr(input, 16*idx), slice_offset_ptr(secret, 16*idx), seed)); } acc = avalanche(acc); for idx in 8..nb_rounds { acc = acc.wrapping_add(mix16_b(slice_offset_ptr(input, 16*idx), slice_offset_ptr(secret, 16*(idx-8) + START_OFFSET), seed)); } acc = acc.wrapping_add(mix16_b(slice_offset_ptr(input, input.len()-16), slice_offset_ptr(secret, SECRET_SIZE_MIN-LAST_OFFSET), seed)); avalanche(acc) } fn xxh3_64_internal(input: &[u8], seed: u64, secret: &[u8], long_hash_fn: LongHashFn) -> u64 { debug_assert!(secret.len() >= SECRET_SIZE_MIN); if input.len() <= 16 { xxh3_64_0to16(input, seed, secret) } else if input.len() <= 128 { xxh3_64_7to128(input, seed, secret) } else if input.len() <= MID_SIZE_MAX { xxh3_64_129to240(input, seed, secret) } else { long_hash_fn(input, seed, secret) } } #[inline] fn xxh3_64_long_impl(input: &[u8], secret: &[u8]) -> u64 { let mut acc = INITIAL_ACC; hash_long_internal_loop(&mut acc, input, secret); merge_accs(&mut acc, slice_offset_ptr(secret, SECRET_MERGEACCS_START), (input.len() as u64).wrapping_mul(xxh64::PRIME_1)) } fn xxh3_64_long_with_seed(input: &[u8], seed: u64, _secret: &[u8]) -> u64 { match seed { 0 => xxh3_64_long_impl(input, &DEFAULT_SECRET), seed => xxh3_64_long_impl(input, &custom_default_secret(seed)), } } fn xxh3_64_long_default(input: &[u8], _seed: u64, _secret: &[u8]) -> u64 { xxh3_64_long_impl(input, &DEFAULT_SECRET) } fn xxh3_64_long_with_secret(input: &[u8], _seed: u64, secret: &[u8]) -> u64 { xxh3_64_long_impl(input, secret) } #[inline] ///Returns 64bit hash for provided input. pub fn xxh3_64(input: &[u8]) -> u64 { xxh3_64_internal(input, 0, &DEFAULT_SECRET, xxh3_64_long_default) } #[inline] ///Returns 64bit hash for provided input using seed. /// ///Note: While overhead of deriving new secret from provided seed is low, ///it would more efficient to generate secret at compile time using special function ///`const_custom_default_secret` from `const_xxh3` pub fn xxh3_64_with_seed(input: &[u8], seed: u64) -> u64 { xxh3_64_internal(input, seed, &DEFAULT_SECRET, xxh3_64_long_with_seed) } #[inline] ///Returns 64bit hash for provided input using custom secret. pub fn xxh3_64_with_secret(input: &[u8], secret: &[u8]) -> u64 { xxh3_64_internal(input, 0, secret, xxh3_64_long_with_secret) } const INTERNAL_BUFFER_SIZE: usize = 256; #[derive(Clone)] #[repr(align(64))] struct Aligned64(T); #[derive(Clone)] ///XXH3 Streaming algorithm /// ///Internal state uses rather large buffers, therefore it might be beneficial ///to store hasher on heap rather than stack. ///Implementation makes no attempts at that, leaving choice entirely to user. pub struct Xxh3 { acc: Acc, custom_secret: Aligned64<[u8; DEFAULT_SECRET_SIZE]>, buffer: Aligned64<[u8; INTERNAL_BUFFER_SIZE]>, buffered_size: u16, nb_stripes_acc: usize, total_len: u64, seed: u64, } impl Xxh3 { #[inline(always)] ///Creates new hasher with default settings pub const fn new() -> Self { Self::with_custom_ops(0, DEFAULT_SECRET) } #[inline] ///Creates new hasher with all options. const fn with_custom_ops(seed: u64, secret: [u8; DEFAULT_SECRET_SIZE]) -> Self { Self { acc: INITIAL_ACC, custom_secret: Aligned64(secret), buffer: Aligned64([0; INTERNAL_BUFFER_SIZE]), buffered_size: 0, nb_stripes_acc: 0, total_len: 0, seed, } } #[inline(always)] ///Creates new hasher with custom seed. pub const fn with_secret(secret: [u8; DEFAULT_SECRET_SIZE]) -> Self { Self::with_custom_ops(0, secret) } #[inline(always)] ///Creates new hasher with custom seed. pub fn with_seed(seed: u64) -> Self { Self::with_custom_ops(seed, custom_default_secret(seed)) } #[inline(always)] ///Resets state pub fn reset(&mut self) { self.acc = INITIAL_ACC; self.total_len = 0; self.buffered_size = 0; self.nb_stripes_acc = 0; } #[inline] fn consume_stripes(acc: &mut Acc, nb_stripes: usize, nb_stripes_acc: usize, input: *const u8, secret: &[u8; DEFAULT_SECRET_SIZE]) -> usize { const STRIPES_PER_BLOCK: usize = (DEFAULT_SECRET_SIZE - STRIPE_LEN) / SECRET_CONSUME_RATE; if (STRIPES_PER_BLOCK - nb_stripes_acc) <= nb_stripes { let stripes_to_end = STRIPES_PER_BLOCK - nb_stripes_acc; let stripes_after_end = nb_stripes - stripes_to_end; accumulate_loop(acc, input, slice_offset_ptr(secret, nb_stripes_acc * SECRET_CONSUME_RATE), stripes_to_end); scramble_acc(acc, slice_offset_ptr(secret, DEFAULT_SECRET_SIZE - STRIPE_LEN)); accumulate_loop(acc, unsafe { input.add(stripes_to_end * STRIPE_LEN) }, secret.as_ptr(), stripes_after_end); stripes_after_end } else { accumulate_loop(acc, input, slice_offset_ptr(secret, nb_stripes_acc * SECRET_CONSUME_RATE), nb_stripes); nb_stripes_acc.wrapping_add(nb_stripes) } } ///Hashes provided chunk pub fn update(&mut self, mut input: &[u8]) { const INTERNAL_BUFFER_STRIPES: usize = INTERNAL_BUFFER_SIZE / STRIPE_LEN; self.total_len = self.total_len.wrapping_add(input.len() as u64); if (input.len() + self.buffered_size as usize) <= INTERNAL_BUFFER_SIZE { unsafe { ptr::copy_nonoverlapping(input.as_ptr(), (self.buffer.0.as_mut_ptr() as *mut u8).offset(self.buffered_size as isize), input.len()) } self.buffered_size += input.len() as u16; return; } if self.buffered_size > 0 { let fill_len = INTERNAL_BUFFER_SIZE - self.buffered_size as usize; unsafe { ptr::copy_nonoverlapping(input.as_ptr(), (self.buffer.0.as_mut_ptr() as *mut u8).offset(self.buffered_size as isize), fill_len) } self.nb_stripes_acc = Self::consume_stripes(&mut self.acc, INTERNAL_BUFFER_STRIPES, self.nb_stripes_acc, self.buffer.0.as_ptr(), &self.custom_secret.0); input = &input[fill_len..]; self.buffered_size = 0; } debug_assert_ne!(input.len(), 0); if input.len() > INTERNAL_BUFFER_SIZE { loop { self.nb_stripes_acc = Self::consume_stripes(&mut self.acc, INTERNAL_BUFFER_STRIPES, self.nb_stripes_acc, input.as_ptr(), &self.custom_secret.0); input = &input[INTERNAL_BUFFER_SIZE..]; if input.len() <= INTERNAL_BUFFER_SIZE { break; } } unsafe { ptr::copy_nonoverlapping(input.as_ptr().offset(-(STRIPE_LEN as isize)), (self.buffer.0.as_mut_ptr() as *mut u8).add(self.buffer.0.len() - STRIPE_LEN), STRIPE_LEN) } } debug_assert_ne!(input.len(), 0); debug_assert_eq!(self.buffered_size, 0); unsafe { ptr::copy_nonoverlapping(input.as_ptr(), self.buffer.0.as_mut_ptr() as *mut u8, input.len()) } self.buffered_size = input.len() as u16; } #[inline] fn digest_internal(&self, acc: &mut Acc) { if self.buffered_size as usize >= STRIPE_LEN { let nb_stripes = (self.buffered_size as usize - 1) / STRIPE_LEN; Self::consume_stripes(acc, nb_stripes, self.nb_stripes_acc, self.buffer.0.as_ptr(), &self.custom_secret.0); accumulate_512(acc, slice_offset_ptr(&self.buffer.0, self.buffered_size as usize - STRIPE_LEN), slice_offset_ptr(&self.custom_secret.0, self.custom_secret.0.len() - STRIPE_LEN - SECRET_LASTACC_START) ); } else { let mut last_stripe = mem::MaybeUninit::<[u8; STRIPE_LEN]>::uninit(); let catchup_size = STRIPE_LEN - self.buffered_size as usize; debug_assert!(self.buffered_size > 0); unsafe { ptr::copy_nonoverlapping(slice_offset_ptr(&self.buffer.0, self.buffer.0.len() - catchup_size), last_stripe.as_mut_ptr() as _, catchup_size); ptr::copy_nonoverlapping(self.buffer.0.as_ptr(), (last_stripe.as_mut_ptr() as *mut u8).add(catchup_size), self.buffered_size as usize); } accumulate_512(acc, last_stripe.as_ptr() as _, slice_offset_ptr(&self.custom_secret.0, self.custom_secret.0.len() - STRIPE_LEN - SECRET_LASTACC_START)); } } ///Computes hash. pub fn digest(&self) -> u64 { if self.total_len > MID_SIZE_MAX as u64 { let mut acc = self.acc.clone(); self.digest_internal(&mut acc); merge_accs(&mut acc, slice_offset_ptr(&self.custom_secret.0, SECRET_MERGEACCS_START), self.total_len.wrapping_mul(xxh64::PRIME_1)) } else if self.seed > 0 { //Technically we should not need to use it. //But in all actuality original xxh3 implementation uses default secret for input with size less or equal to MID_SIZE_MAX xxh3_64_internal(&self.buffer.0[..self.buffered_size as usize], self.seed, &DEFAULT_SECRET, xxh3_64_long_with_seed) } else { xxh3_64_internal(&self.buffer.0[..self.buffered_size as usize], self.seed, &self.custom_secret.0, xxh3_64_long_with_secret) } } } impl Default for Xxh3 { #[inline(always)] fn default() -> Self { Self::new() } } impl core::hash::Hasher for Xxh3 { #[inline(always)] fn finish(&self) -> u64 { self.digest() } #[inline(always)] fn write(&mut self, input: &[u8]) { self.update(input) } } // //128bit // #[inline] fn xxh3_128_long_impl(input: &[u8], secret: &[u8]) -> u128 { let mut acc = INITIAL_ACC; hash_long_internal_loop(&mut acc, input, secret); debug_assert!(secret.len() >= mem::size_of::() + SECRET_MERGEACCS_START); let lo = merge_accs(&mut acc, slice_offset_ptr(secret, SECRET_MERGEACCS_START), (input.len() as u64).wrapping_mul(xxh64::PRIME_1)); let hi = merge_accs(&mut acc, slice_offset_ptr(secret, secret.len() - mem::size_of::() - SECRET_MERGEACCS_START), !(input.len() as u64).wrapping_mul(xxh64::PRIME_2)); lo as u128 | (hi as u128) << 64 } #[inline(always)] fn xxh3_128_9to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 { let flip_lo = (read_64le_unaligned(slice_offset_ptr(secret, 32)) ^ read_64le_unaligned(slice_offset_ptr(secret, 40))).wrapping_sub(seed); let flip_hi = (read_64le_unaligned(slice_offset_ptr(secret, 48)) ^ read_64le_unaligned(slice_offset_ptr(secret, 56))).wrapping_add(seed); let input_lo = read_64le_unaligned(input.as_ptr()); let mut input_hi = read_64le_unaligned(slice_offset_ptr(input, input.len() - 8)); let (mut mul_low, mut mul_high) = mul64_to128(input_lo ^ input_hi ^ flip_lo, xxh64::PRIME_1); mul_low = mul_low.wrapping_add((input.len() as u64 - 1) << 54); input_hi ^= flip_hi; //Separate code for 32bit and bigger targets //It only makes sense when width is multiple of 2, but if necessary change to simple if //Not to mention I'm not sure you can even compile u128 on something like 16bit? #[cfg(any(target_pointer_width = "32", target_pointer_width = "16", target_pointer_width = "8"))] { mul_high = mul_high.wrapping_add( (input_hi & 0xFFFFFFFF00000000).wrapping_add(mult32_to64(input_hi as u32, xxh32::PRIME_2)) ); } #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "16", target_pointer_width = "8")))] { mul_high = mul_high.wrapping_add( input_hi.wrapping_add(mult32_to64(input_hi as u32, xxh32::PRIME_2 - 1)) ) } mul_low ^= mul_high.swap_bytes(); let (result_low, mut result_hi) = mul64_to128(mul_low, xxh64::PRIME_2); result_hi = result_hi.wrapping_add( mul_high.wrapping_mul(xxh64::PRIME_2) ); avalanche(result_low) as u128 | (avalanche(result_hi) as u128) << 64 } #[inline(always)] fn xxh3_128_4to8(input: &[u8], mut seed: u64, secret: &[u8]) -> u128 { seed ^= ((seed as u32).swap_bytes() as u64) << 32; let lo = read_32le_unaligned(input.as_ptr()); let hi = read_32le_unaligned(slice_offset_ptr(input, input.len() - 4)); let input_64 = (lo as u64).wrapping_add((hi as u64) << 32); let flip = (read_64le_unaligned(slice_offset_ptr(secret, 16)) ^ read_64le_unaligned(slice_offset_ptr(secret, 24))).wrapping_add(seed); let keyed = input_64 ^ flip; let (mut lo, mut hi) = mul64_to128(keyed, xxh64::PRIME_1.wrapping_add((input.len() as u64) << 2)); hi = hi.wrapping_add(lo << 1); lo ^= hi >> 3; lo = xorshift64(lo, 35).wrapping_mul(0x9FB21C651E98DF25); lo = xorshift64(lo, 28); hi = avalanche(hi); lo as u128 | (hi as u128) << 64 } #[inline(always)] fn xxh3_128_1to3(input: &[u8], seed: u64, secret: &[u8]) -> u128 { let c1 = input[0]; let c2 = input[input.len() >> 1]; let c3 = input[input.len() - 1]; let input_lo = (c1 as u32) << 16 | (c2 as u32) << 24 | c3 as u32 | (input.len() as u32) << 8; let input_hi = input_lo.swap_bytes().rotate_left(13); let flip_lo = (read_32le_unaligned(slice_offset_ptr(secret, 0)) as u64 ^ read_32le_unaligned(slice_offset_ptr(secret, 4)) as u64).wrapping_add(seed); let flip_hi = (read_32le_unaligned(slice_offset_ptr(secret, 8)) as u64 ^ read_32le_unaligned(slice_offset_ptr(secret, 12)) as u64).wrapping_sub(seed); let keyed_lo = input_lo as u64 ^ flip_lo; let keyed_hi = input_hi as u64 ^ flip_hi; xxh64::avalanche(keyed_lo) as u128 | (xxh64::avalanche(keyed_hi) as u128) << 64 } #[inline(always)] fn xxh3_128_0to16(input: &[u8], seed: u64, secret: &[u8]) -> u128 { if input.len() > 8 { xxh3_128_9to16(input, seed, secret) } else if input.len() >= 4 { xxh3_128_4to8(input, seed, secret) } else if input.len() > 0 { xxh3_128_1to3(input, seed, secret) } else { let flip_lo = read_64le_unaligned(slice_offset_ptr(secret, 64)) ^ read_64le_unaligned(slice_offset_ptr(secret, 72)); let flip_hi = read_64le_unaligned(slice_offset_ptr(secret, 80)) ^ read_64le_unaligned(slice_offset_ptr(secret, 88)); xxh64::avalanche(seed ^ flip_lo) as u128 | (xxh64::avalanche(seed ^ flip_hi) as u128) << 64 } } #[inline(always)] fn xxh3_128_7to128(input: &[u8], seed: u64, secret: &[u8]) -> u128 { let mut lo = (input.len() as u64).wrapping_mul(xxh64::PRIME_1); let mut hi = 0; if input.len() > 32 { if input.len() > 64 { if input.len() > 96 { mix32_b(&mut lo, &mut hi, slice_offset_ptr(input, 48), slice_offset_ptr(input, input.len() - 64), slice_offset_ptr(secret, 96), seed); } mix32_b(&mut lo, &mut hi, slice_offset_ptr(input, 32), slice_offset_ptr(input, input.len() - 48), slice_offset_ptr(secret, 64), seed); } mix32_b(&mut lo, &mut hi, slice_offset_ptr(input, 16), slice_offset_ptr(input, input.len() - 32), slice_offset_ptr(secret, 32), seed); } mix32_b(&mut lo, &mut hi, input.as_ptr(), slice_offset_ptr(input, input.len() - 16), secret.as_ptr(), seed); let result_lo = lo.wrapping_add(hi); let result_hi = lo.wrapping_mul(xxh64::PRIME_1) .wrapping_add(hi.wrapping_mul(xxh64::PRIME_4)) .wrapping_add((input.len() as u64).wrapping_sub(seed).wrapping_mul(xxh64::PRIME_2)); avalanche(result_lo) as u128 | (0u64.wrapping_sub(avalanche(result_hi)) as u128) << 64 } #[inline(never)] fn xxh3_128_129to240(input: &[u8], seed: u64, secret: &[u8]) -> u128 { const START_OFFSET: usize = 3; const LAST_OFFSET: usize = 17; let nb_rounds = input.len() / 32; debug_assert!(nb_rounds >= 4); let mut lo = (input.len() as u64).wrapping_mul(xxh64::PRIME_1); let mut hi = 0; for idx in 0..4 { let idx = 32 * idx; mix32_b(&mut lo, &mut hi, slice_offset_ptr(input, idx), slice_offset_ptr(input, idx + 16), slice_offset_ptr(secret, idx), seed); } lo = avalanche(lo); hi = avalanche(hi); for idx in 4..nb_rounds { mix32_b(&mut lo, &mut hi, slice_offset_ptr(input, 32 * idx), slice_offset_ptr(input, (32 * idx) + 16), slice_offset_ptr(secret, START_OFFSET.wrapping_add(32 * (idx - 4))), seed); } mix32_b(&mut lo, &mut hi, slice_offset_ptr(input, input.len() - 16), slice_offset_ptr(input, input.len() - 32), slice_offset_ptr(secret, SECRET_SIZE_MIN - LAST_OFFSET - 16), 0u64.wrapping_sub(seed)); let result_lo = lo.wrapping_add(hi); let result_hi = lo.wrapping_mul(xxh64::PRIME_1) .wrapping_add(hi.wrapping_mul(xxh64::PRIME_4)) .wrapping_add((input.len() as u64).wrapping_sub(seed).wrapping_mul(xxh64::PRIME_2)); avalanche(result_lo) as u128 | 0u128.wrapping_sub(avalanche(result_hi) as u128) << 64 } fn xxh3_128_internal(input: &[u8], seed: u64, secret: &[u8], long_hash_fn: LongHashFn128) -> u128 { debug_assert!(secret.len() >= SECRET_SIZE_MIN); if input.len() <= 16 { xxh3_128_0to16(input, seed, secret) } else if input.len() <= 128 { xxh3_128_7to128(input, seed, secret) } else if input.len() <= MID_SIZE_MAX { xxh3_128_129to240(input, seed, secret) } else { long_hash_fn(input, seed, secret) } } fn xxh3_128_long_default(input: &[u8], _seed: u64, _secret: &[u8]) -> u128 { xxh3_128_long_impl(input, &DEFAULT_SECRET) } fn xxh3_128_long_with_seed(input: &[u8], seed: u64, _secret: &[u8]) -> u128 { match seed { 0 => xxh3_128_long_impl(input, &DEFAULT_SECRET), seed => xxh3_128_long_impl(input, &custom_default_secret(seed)), } } fn xxh3_128_long_with_secret(input: &[u8], _seed: u64, secret: &[u8]) -> u128 { xxh3_128_long_impl(input, secret) } #[inline] ///Returns 128bit hash for provided input. pub fn xxh3_128(input: &[u8]) -> u128 { xxh3_128_internal(input, 0, &DEFAULT_SECRET, xxh3_128_long_default) } #[inline] ///Returns 128 hash for provided input using seed. /// ///Note: While overhead of deriving new secret from provided seed is low, ///it would more efficient to generate secret at compile time using special function ///`const_custom_default_secret` from `const_xxh3` pub fn xxh3_128_with_seed(input: &[u8], seed: u64) -> u128 { xxh3_128_internal(input, seed, &DEFAULT_SECRET, xxh3_128_long_with_seed) } #[inline] ///Returns 128 hash for provided input using custom secret. pub fn xxh3_128_with_secret(input: &[u8], secret: &[u8]) -> u128 { xxh3_128_internal(input, 0, secret, xxh3_128_long_with_secret) } xxhash-rust-0.8.2/src/xxh32.rs000064400000000000000000000150060000000000000142210ustar 00000000000000//!32 bit version of xxhash algorithm //! //!Written using C implementation as reference. use core::{ptr, mem, slice}; use crate::xxh32_common::*; #[inline(always)] fn read_le_unaligned(data: *const u8) -> u32 { let mut result = mem::MaybeUninit::::uninit(); unsafe { ptr::copy_nonoverlapping(data, result.as_mut_ptr() as _, mem::size_of::()); result.assume_init().to_le() } } #[inline(always)] fn read_le_is_align(data: *const u8, is_aligned: bool) -> u32 { match is_aligned { true => unsafe { (*(data as *const u32)).to_le() }, false => read_le_unaligned(data), } } fn finalize(mut input: u32, mut data: &[u8], is_aligned: bool) -> u32 { while data.len() >= 4 { input = input.wrapping_add( read_le_is_align(data.as_ptr(), is_aligned).wrapping_mul(PRIME_3) ); data = &data[4..]; input = input.rotate_left(17).wrapping_mul(PRIME_4); } for byte in data.iter() { input = input.wrapping_add((*byte as u32).wrapping_mul(PRIME_5)); input = input.rotate_left(11).wrapping_mul(PRIME_1); } avalanche(input) } ///Returns hash for the provided input pub fn xxh32(mut input: &[u8], seed: u32) -> u32 { let mut result = input.len() as u32; if input.len() >= CHUNK_SIZE { let mut v1 = seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2); let mut v2 = seed.wrapping_add(PRIME_2); let mut v3 = seed; let mut v4 = seed.wrapping_sub(PRIME_1); loop { v1 = round(v1, read_le_is_align(input.as_ptr(), false)); input = &input[4..]; v2 = round(v2, read_le_is_align(input.as_ptr(), false)); input = &input[4..]; v3 = round(v3, read_le_is_align(input.as_ptr(), false)); input = &input[4..]; v4 = round(v4, read_le_is_align(input.as_ptr(), false)); input = &input[4..]; if input.len() < CHUNK_SIZE { break; } } result = result.wrapping_add( v1.rotate_left(1).wrapping_add( v2.rotate_left(7).wrapping_add( v3.rotate_left(12).wrapping_add( v4.rotate_left(18) ) ) ) ); } else { result = result.wrapping_add(seed.wrapping_add(PRIME_5)); } return finalize(result, input, false); } ///XXH32 Streaming algorithm pub struct Xxh32 { total_len: u32, is_large_len: bool, v1: u32, v2: u32, v3: u32, v4: u32, mem: [u32; 4], mem_size: u32, } impl Xxh32 { #[inline] ///Creates new hasher with specified seed. pub const fn new(seed: u32) -> Self { Self { total_len: 0, is_large_len: false, v1: seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2), v2: seed.wrapping_add(PRIME_2), v3: seed, v4: seed.wrapping_sub(PRIME_1), mem: [0, 0, 0, 0], mem_size: 0, } } ///Hashes provided input. pub fn update(&mut self, mut input: &[u8]) { self.total_len = self.total_len.wrapping_add(input.len() as u32); self.is_large_len |= (input.len() as u32 >= CHUNK_SIZE as u32) | (self.total_len >= CHUNK_SIZE as u32); if (self.mem_size + input.len() as u32) < CHUNK_SIZE as u32 { unsafe { ptr::copy_nonoverlapping(input.as_ptr(), (self.mem.as_mut_ptr() as *mut u8).offset(self.mem_size as isize), input.len()) } self.mem_size += input.len() as u32; return } if self.mem_size > 0 { //previous if can fail only when we do not have enough space in buffer for input. //hence fill_len >= input.len() let fill_len = CHUNK_SIZE - self.mem_size as usize; unsafe { ptr::copy_nonoverlapping(input.as_ptr(), (self.mem.as_mut_ptr() as *mut u8).offset(self.mem_size as isize), fill_len) } self.v1 = round(self.v1, self.mem[0].to_le()); self.v2 = round(self.v2, self.mem[1].to_le()); self.v3 = round(self.v3, self.mem[2].to_le()); self.v4 = round(self.v4, self.mem[3].to_le()); input = &input[fill_len..]; self.mem_size = 0; } if input.len() >= CHUNK_SIZE { //In general this loop is not that long running on small input //So it is questionable whether we want to allocate local vars here. //Streaming version is likely to be used with relatively small chunks anyway. loop { self.v1 = round(self.v1, read_le_unaligned(input.as_ptr())); input = &input[4..]; self.v2 = round(self.v2, read_le_unaligned(input.as_ptr())); input = &input[4..]; self.v3 = round(self.v3, read_le_unaligned(input.as_ptr())); input = &input[4..]; self.v4 = round(self.v4, read_le_unaligned(input.as_ptr())); input = &input[4..]; if input.len() < CHUNK_SIZE { break; } } } if input.len() > 0 { unsafe { ptr::copy_nonoverlapping(input.as_ptr(), self.mem.as_mut_ptr() as *mut u8, input.len()) } self.mem_size = input.len() as u32; } } ///Finalize hashing. pub fn digest(&self) -> u32 { let mut result = self.total_len; if self.is_large_len { result = result.wrapping_add( self.v1.rotate_left(1).wrapping_add( self.v2.rotate_left(7).wrapping_add( self.v3.rotate_left(12).wrapping_add( self.v4.rotate_left(18) ) ) ) ); } else { result = result.wrapping_add(self.v3.wrapping_add(PRIME_5)); } let input = unsafe { slice::from_raw_parts(self.mem.as_ptr() as *const u8, self.mem_size as usize) }; return finalize(result, input, true); } #[inline] ///Resets the state with specified seed. pub fn reset(&mut self, seed: u32) { self.total_len = 0; self.is_large_len = false; self.v1 = seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2); self.v2 = seed.wrapping_add(PRIME_2); self.v3 = seed; self.v4 = seed.wrapping_sub(PRIME_1); self.mem_size = 0; } } xxhash-rust-0.8.2/src/xxh32_common.rs000064400000000000000000000012430000000000000155670ustar 00000000000000#![allow(unused)] use core::mem; pub const CHUNK_SIZE: usize = mem::size_of::() * 4; pub const PRIME_1: u32 = 0x9E3779B1; pub const PRIME_2: u32 = 0x85EBCA77; pub const PRIME_3: u32 = 0xC2B2AE3D; pub const PRIME_4: u32 = 0x27D4EB2F; pub const PRIME_5: u32 = 0x165667B1; #[inline] pub const fn round(acc: u32, input: u32) -> u32 { acc.wrapping_add(input.wrapping_mul(PRIME_2)) .rotate_left(13) .wrapping_mul(PRIME_1) } #[inline] pub const fn avalanche(mut input: u32) -> u32 { input ^= input >> 15; input = input.wrapping_mul(PRIME_2); input ^= input >> 13; input = input.wrapping_mul(PRIME_3); input ^= input >> 16; input } xxhash-rust-0.8.2/src/xxh3_common.rs000064400000000000000000000050400000000000000155040ustar 00000000000000use core::mem; pub const STRIPE_LEN: usize = 64; pub const SECRET_CONSUME_RATE: usize = 8; pub const ACC_NB: usize = STRIPE_LEN / mem::size_of::(); pub const SECRET_MERGEACCS_START: usize = 11; pub const SECRET_LASTACC_START: usize = 7; //not aligned on 8, last secret is different from acc & scrambler pub const MID_SIZE_MAX: usize = 240; pub const SECRET_SIZE_MIN: usize = 136; pub const DEFAULT_SECRET_SIZE: usize = 192; pub const DEFAULT_SECRET: [u8; DEFAULT_SECRET_SIZE] = [ 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, ]; #[inline(always)] pub const fn xorshift64(value: u64, shift: u64) -> u64 { value ^ (value >> shift) } #[inline] pub const fn avalanche(mut value: u64) -> u64 { value = xorshift64(value, 37); value = value.wrapping_mul(0x165667919E3779F9); xorshift64(value, 32) } #[inline] pub const fn strong_avalanche(mut value: u64, len: u64) -> u64 { value ^= value.rotate_left(49) ^ value.rotate_left(24); value = value.wrapping_mul(0x9FB21C651E98DF25); value ^= (value >> 35).wrapping_add(len); value = value.wrapping_mul(0x9FB21C651E98DF25); xorshift64(value, 28) } #[inline] pub const fn mul64_to128(left: u64, right: u64) -> (u64, u64) { let product = left as u128 * right as u128; (product as u64, (product >> 64) as u64) } #[inline] pub const fn mul128_fold64(left: u64, right: u64) -> u64 { let (low, high) = mul64_to128(left, right); low ^ high } xxhash-rust-0.8.2/src/xxh64.rs000064400000000000000000000166250000000000000142360ustar 00000000000000//!64 bit version of xxhash algorithm //! //!Written using C implementation as reference. use core::{ptr, mem, slice}; use crate::xxh64_common::*; #[inline(always)] fn read_32le_unaligned(data: *const u8) -> u32 { let mut result = mem::MaybeUninit::::uninit(); unsafe { ptr::copy_nonoverlapping(data, result.as_mut_ptr() as _, mem::size_of::()); result.assume_init().to_le() } } #[inline(always)] fn read_32le_is_align(data: *const u8, is_aligned: bool) -> u32 { match is_aligned { true => unsafe { (*(data as *const u32)).to_le() }, false => read_32le_unaligned(data), } } #[inline(always)] fn read_64le_unaligned(data: *const u8) -> u64 { let mut result = mem::MaybeUninit::::uninit(); unsafe { ptr::copy_nonoverlapping(data, result.as_mut_ptr() as _, mem::size_of::()); result.assume_init().to_le() } } #[inline(always)] fn read_64le_is_align(data: *const u8, is_aligned: bool) -> u64 { match is_aligned { true => unsafe { (*(data as *const u64)).to_le() }, false => read_64le_unaligned(data), } } fn finalize(mut input: u64, mut data: &[u8], is_aligned: bool) -> u64 { while data.len() >= 8 { input ^= round(0, read_64le_is_align(data.as_ptr(), is_aligned)); data = &data[8..]; input = input.rotate_left(27).wrapping_mul(PRIME_1).wrapping_add(PRIME_4) } if data.len() >= 4 { input ^= (read_32le_is_align(data.as_ptr(), is_aligned) as u64).wrapping_mul(PRIME_1); data = &data[4..]; input = input.rotate_left(23).wrapping_mul(PRIME_2).wrapping_add(PRIME_3); } for byte in data.iter() { input ^= (*byte as u64).wrapping_mul(PRIME_5); input = input.rotate_left(11).wrapping_mul(PRIME_1); } avalanche(input) } ///Returns hash for the provided input. pub fn xxh64(mut input: &[u8], seed: u64) -> u64 { let input_len = input.len() as u64; let mut result; if input.len() >= CHUNK_SIZE { let mut v1 = seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2); let mut v2 = seed.wrapping_add(PRIME_2); let mut v3 = seed; let mut v4 = seed.wrapping_sub(PRIME_1); loop { v1 = round(v1, read_64le_is_align(input.as_ptr(), false)); input = &input[8..]; v2 = round(v2, read_64le_is_align(input.as_ptr(), false)); input = &input[8..]; v3 = round(v3, read_64le_is_align(input.as_ptr(), false)); input = &input[8..]; v4 = round(v4, read_64le_is_align(input.as_ptr(), false)); input = &input[8..]; if input.len() < CHUNK_SIZE { break; } } result = v1.rotate_left(1).wrapping_add(v2.rotate_left(7)) .wrapping_add(v3.rotate_left(12)) .wrapping_add(v4.rotate_left(18)); result = merge_round(result, v1); result = merge_round(result, v2); result = merge_round(result, v3); result = merge_round(result, v4); } else { result = seed.wrapping_add(PRIME_5) } result = result.wrapping_add(input_len); finalize(result, input, false) } ///XXH64 Streaming algorithm pub struct Xxh64 { total_len: u64, v1: u64, v2: u64, v3: u64, v4: u64, mem: [u64; 4], mem_size: usize, } impl Xxh64 { #[inline] ///Creates new state with provided seed. pub const fn new(seed: u64) -> Self { Self { total_len: 0, v1: seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2), v2: seed.wrapping_add(PRIME_2), v3: seed, v4: seed.wrapping_sub(PRIME_1), mem: [0, 0, 0, 0], mem_size: 0, } } ///Adds chunk of data to hash. pub fn update(&mut self, mut input: &[u8]) { self.total_len = self.total_len.wrapping_add(input.len() as u64); if (self.mem_size + input.len()) < CHUNK_SIZE { unsafe { ptr::copy_nonoverlapping(input.as_ptr(), (self.mem.as_mut_ptr() as *mut u8).add(self.mem_size), input.len()) } self.mem_size += input.len(); return } if self.mem_size > 0 { //previous if can fail only when we do not have enough space in buffer for input. //hence fill_len >= input.len() let fill_len = CHUNK_SIZE - self.mem_size; unsafe { ptr::copy_nonoverlapping(input.as_ptr(), (self.mem.as_mut_ptr() as *mut u8).add(self.mem_size), fill_len) } self.v1 = round(self.v1, self.mem[0].to_le()); self.v2 = round(self.v2, self.mem[1].to_le()); self.v3 = round(self.v3, self.mem[2].to_le()); self.v4 = round(self.v4, self.mem[3].to_le()); input = &input[fill_len..]; self.mem_size = 0; } if input.len() >= CHUNK_SIZE { //In general this loop is not that long running on small input //So it is questionable whether we want to allocate local vars here. //Streaming version is likely to be used with relatively small chunks anyway. loop { self.v1 = round(self.v1, read_64le_unaligned(input.as_ptr())); input = &input[8..]; self.v2 = round(self.v2, read_64le_unaligned(input.as_ptr())); input = &input[8..]; self.v3 = round(self.v3, read_64le_unaligned(input.as_ptr())); input = &input[8..]; self.v4 = round(self.v4, read_64le_unaligned(input.as_ptr())); input = &input[8..]; if input.len() < CHUNK_SIZE { break; } } } if input.len() > 0 { unsafe { ptr::copy_nonoverlapping(input.as_ptr(), self.mem.as_mut_ptr() as *mut u8, input.len()) } self.mem_size = input.len(); } } ///Finalize hashing. pub fn digest(&self) -> u64 { let mut result; if self.total_len >= CHUNK_SIZE as u64 { result = self.v1.rotate_left(1).wrapping_add(self.v2.rotate_left(7)) .wrapping_add(self.v3.rotate_left(12)) .wrapping_add(self.v4.rotate_left(18)); result = merge_round(result, self.v1); result = merge_round(result, self.v2); result = merge_round(result, self.v3); result = merge_round(result, self.v4); } else { result = self.v3.wrapping_add(PRIME_5) } result = result.wrapping_add(self.total_len); let input = unsafe { slice::from_raw_parts(self.mem.as_ptr() as *const u8, self.mem_size) }; finalize(result, input, true) } #[inline] ///Resets state with provided seed. pub fn reset(&mut self, seed: u64) { self.total_len = 0; self.v1 = seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2); self.v2 = seed.wrapping_add(PRIME_2); self.v3 = seed; self.v4 = seed.wrapping_sub(PRIME_1); self.mem_size = 0; } } impl core::hash::Hasher for Xxh64 { #[inline(always)] fn finish(&self) -> u64 { self.digest() } #[inline(always)] fn write(&mut self, input: &[u8]) { self.update(input) } } xxhash-rust-0.8.2/src/xxh64_common.rs000064400000000000000000000015400000000000000155740ustar 00000000000000#![allow(unused)] use core::mem; pub const CHUNK_SIZE: usize = mem::size_of::() * 4; pub const PRIME_1: u64 = 0x9E3779B185EBCA87; pub const PRIME_2: u64 = 0xC2B2AE3D27D4EB4F; pub const PRIME_3: u64 = 0x165667B19E3779F9; pub const PRIME_4: u64 = 0x85EBCA77C2B2AE63; pub const PRIME_5: u64 = 0x27D4EB2F165667C5; #[inline] pub const fn round(acc: u64, input: u64) -> u64 { acc.wrapping_add(input.wrapping_mul(PRIME_2)) .rotate_left(31) .wrapping_mul(PRIME_1) } #[inline] pub const fn merge_round(mut acc: u64, val: u64) -> u64 { acc ^= round(0, val); acc.wrapping_mul(PRIME_1).wrapping_add(PRIME_4) } #[inline] pub const fn avalanche(mut input: u64) -> u64 { input ^= input >> 33; input = input.wrapping_mul(PRIME_2); input ^= input >> 29; input = input.wrapping_mul(PRIME_3); input ^= input >> 32; input } xxhash-rust-0.8.2/tests/assert_correctness.rs000064400000000000000000000157240000000000000175420ustar 00000000000000#[cfg(feature = "xxh64")] #[test] fn assert_xxh64() { use getrandom::getrandom; use xxhash_c_sys as sys; use xxhash_rust::xxh64::xxh64; const SEED_1: u64 = 0; const SEED_2: u64 = 1; let mut hasher_1 = xxhash_rust::xxh64::Xxh64::new(SEED_1); let mut hasher_2 = xxhash_rust::xxh64::Xxh64::new(SEED_2); let mut input = Vec::with_capacity(2048); for num in 0..2048 { input.resize(num, 1); getrandom(&mut input).expect("getrandom"); println!("input(len={})", input.len()); let sys_result = unsafe { sys::XXH64(input.as_ptr() as _, input.len(), SEED_1) }; let result = xxh64(&input, SEED_1); assert_eq!(result, sys_result); hasher_1.update(&input); assert_eq!(hasher_1.digest(), result); let sys_result = unsafe { sys::XXH64(input.as_ptr() as _, input.len(), SEED_2) }; let result = xxh64(&input, SEED_2); assert_eq!(result, sys_result); hasher_2.update(&input); assert_eq!(hasher_2.digest(), result); hasher_1.reset(SEED_1); hasher_2.reset(SEED_2); } } #[cfg(feature = "xxh32")] #[test] fn assert_xxh32() { use getrandom::getrandom; use xxhash_c_sys as sys; use xxhash_rust::xxh32::xxh32; const SEED_1: u32 = 0; const SEED_2: u32 = 1; let mut hasher_1 = xxhash_rust::xxh32::Xxh32::new(SEED_1); let mut hasher_2 = xxhash_rust::xxh32::Xxh32::new(SEED_2); let mut input = Vec::with_capacity(2048); for num in 0..2048 { input.resize(num, 1); getrandom(&mut input).expect("getrandom"); println!("input(len={})", input.len()); let sys_result = unsafe { sys::XXH32(input.as_ptr() as _, input.len(), SEED_1) }; let result = xxh32(&input, SEED_1); assert_eq!(result, sys_result); hasher_1.update(&input); assert_eq!(hasher_1.digest(), result); let sys_result = unsafe { sys::XXH32(input.as_ptr() as _, input.len(), SEED_2) }; let result = xxh32(&input, SEED_2); assert_eq!(result, sys_result); hasher_2.update(&input); assert_eq!(hasher_2.digest(), result); hasher_1.reset(SEED_1); hasher_2.reset(SEED_2); } } #[cfg(feature = "const_xxh32")] #[test] fn assert_const_xxh32() { use getrandom::getrandom; use xxhash_c_sys as sys; use xxhash_rust::const_xxh32::xxh32; const SEED_1: u32 = 0; const SEED_2: u32 = 1; let mut input = Vec::with_capacity(2048); for num in 0..2048 { input.resize(num, 1); getrandom(&mut input).expect("getrandom"); println!("input(len={})", input.len()); let sys_result = unsafe { sys::XXH32(input.as_ptr() as _, input.len(), SEED_1) }; let result = xxh32(&input, SEED_1); assert_eq!(result, sys_result); let sys_result = unsafe { sys::XXH32(input.as_ptr() as _, input.len(), SEED_2) }; let result = xxh32(&input, SEED_2); assert_eq!(result, sys_result); } } #[cfg(feature = "const_xxh64")] #[test] fn assert_const_xxh64() { use getrandom::getrandom; use xxhash_c_sys as sys; use xxhash_rust::const_xxh64::xxh64; const SEED_1: u64 = 0; const SEED_2: u64 = 1; let mut input = Vec::with_capacity(2048); for num in 0..2048 { input.resize(num, 1); getrandom(&mut input).expect("getrandom"); println!("input(len={})", input.len()); let sys_result = unsafe { sys::XXH64(input.as_ptr() as _, input.len(), SEED_1) }; let result = xxh64(&input, SEED_1); assert_eq!(result, sys_result); let sys_result = unsafe { sys::XXH64(input.as_ptr() as _, input.len(), SEED_2) }; let result = xxh64(&input, SEED_2); assert_eq!(result, sys_result); } } #[cfg(feature = "const_xxh3")] #[test] fn assert_const_xxh3() { use getrandom::getrandom; use xxhash_c_sys as sys; use xxhash_rust::const_xxh3::{xxh3_64, xxh3_128, xxh3_64_with_seed, xxh3_128_with_seed}; let mut input = Vec::with_capacity(2048); for num in 0..2048 { input.resize(num, 1); getrandom(&mut input).expect("getrandom"); let input = input.as_slice(); println!("input(len={})", input.len()); let sys_result = unsafe { sys::XXH3_64bits(input.as_ptr() as _, input.len()) }; let result = xxh3_64(input); assert_eq!(result, sys_result); let sys_result = unsafe { sys::XXH3_64bits_withSeed(input.as_ptr() as _, input.len(), 1) }; let result = xxh3_64_with_seed(input, 1); assert_eq!(result, sys_result); let sys_result128 = unsafe { sys::XXH3_128bits(input.as_ptr() as _, input.len()) }; let result128 = xxh3_128(input); assert_eq!(result128 as u64, sys_result128.low64); assert_eq!((result128 >> 64) as u64, sys_result128.high64); let sys_result128 = unsafe { sys::XXH3_128bits_withSeed(input.as_ptr() as _, input.len(), 1) }; let result128 = xxh3_128_with_seed(input, 1); assert_eq!(result128 as u64, sys_result128.low64); assert_eq!((result128 >> 64) as u64, sys_result128.high64); } } #[cfg(feature = "xxh3")] #[test] fn assert_xxh3() { use getrandom::getrandom; use xxhash_c_sys as sys; use xxhash_rust::xxh3::{xxh3_64, xxh3_128, xxh3_64_with_seed, xxh3_128_with_seed, Xxh3}; let mut hasher_1 = Xxh3::new(); let mut hasher_2 = Xxh3::with_seed(1); let mut input = Vec::with_capacity(2048); for num in 0..2048 { input.resize(num, 1); println!("input(len={})", input.len()); getrandom(&mut input).expect("getrandom"); let input = input.as_slice(); let sys_result = unsafe { sys::XXH3_64bits(input.as_ptr() as _, input.len()) }; let result = xxh3_64(input); assert_eq!(result, sys_result); hasher_1.update(input); assert_eq!(hasher_1.digest(), result); let sys_result128 = unsafe { sys::XXH3_128bits(input.as_ptr() as _, input.len()) }; let result128 = xxh3_128(input); assert_eq!(result128 as u64, sys_result128.low64); assert_eq!((result128 >> 64) as u64, sys_result128.high64); let sys_result = unsafe { sys::XXH3_64bits_withSeed(input.as_ptr() as _, input.len(), 1) }; let result = xxh3_64_with_seed(input, 1); assert_eq!(result, sys_result); hasher_2.update(input); assert_eq!(hasher_2.digest(), result); hasher_1.reset(); hasher_2.reset(); let sys_result128 = unsafe { sys::XXH3_128bits_withSeed(input.as_ptr() as _, input.len(), 1) }; let result128 = xxh3_128_with_seed(input, 1); assert_eq!(result128 as u64, sys_result128.low64); assert_eq!((result128 >> 64) as u64, sys_result128.high64); } }