xxhash-rust-0.8.6/.cargo_vcs_info.json0000644000000001360000000000100133450ustar { "git": { "sha1": "dd52be9e5774f28933b91f34ab3b496045d6780f" }, "path_in_vcs": "" }xxhash-rust-0.8.6/Cargo.toml0000644000000022720000000000100113460ustar # 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 are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "xxhash-rust" version = "0.8.6" authors = ["Douman "] include = [ "**/*.rs", "Cargo.toml", "README.md", "LICENSE", ] description = "Implementation of xxhash" readme = "README.md" keywords = [ "hash", "xxhash", "xxh3", "hasher", ] 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", ] [dev-dependencies.getrandom] version = "0.2" [dev-dependencies.xxhash-c-sys] version = "0.8.3" [features] const_xxh3 = [] const_xxh32 = [] const_xxh64 = [] xxh3 = [] xxh32 = [] xxh64 = [] xxhash-rust-0.8.6/Cargo.toml.orig000064400000000000000000000016070072674642500150600ustar 00000000000000[package] name = "xxhash-rust" version = "0.8.6" 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", "xxh3", "hasher"] categories = ["algorithms"] include = [ "**/*.rs", "Cargo.toml", "README.md", "LICENSE", ] [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] xxhash-c-sys = "0.8.3" getrandom = "0.2" [package.metadata.docs.rs] features = ["xxh32", "const_xxh32", "xxh64", "const_xxh64", "xxh3", "const_xxh3"] xxhash-rust-0.8.6/LICENSE000064400000000000000000000024720072674642500131770ustar 00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. xxhash-rust-0.8.6/README.md000064400000000000000000000054450072674642500134540ustar 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 Each algorithm is implemented via feature, allowing precise control over code size. ## Example - Cargo.toml ```toml [dependencies.xxhash-rust] version = "0.8.5" features = ["xxh3", "const_xxh3"] ``` - main.rs ```rust use xxhash_rust::const_xxh3::xxh3_64 as const_xxh3; use xxhash_rust::xxh3::xxh3_64; const TEST: u64 = const_xxh3(b"TEST"); fn test_input(text: &str) -> bool { match xxh3_64(text.as_bytes()) { TEST => true, _ => false } } assert!(!test_input("tEST")); assert!(test_input("TEST")); ``` ## Features: By default all features are off. - `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; ## Streaming vs One-shot For performance reasons one-shot version of algorithm does not re-use streaming version. Unless needed, user is advised to use one-shot version which tends to be more optimal. ## `cosnt fn` version While `const fn` provides compile time implementation, it does so at performance cost. Hence you should only use it at _compile_ time. To guarantee that something is computed at compile time make sure to initialize hash output as `const` or `static` variable, otherwise it is possible function is executed at runtime, which would be worse than regular algorithm. `const fn` is implemented in best possible way while conforming to limitations of Rust `const fn`, but these limitations are quite strict making any high performance code impossible. ## Version note - `0.8.*` corresponds to C's `0.8.*` In order to keep up with original implementation version I'm not planning to bump major/minor until C implementation does so. ## Comparison with twox-hash Refer to my [comment](https://github.com/DoumanAsh/xxhash-rust/issues/10#issuecomment-980488647) xxhash-rust-0.8.6/src/const_xxh3.rs000064400000000000000000000451330072674642500154300ustar 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) } #[allow(clippy::too_many_arguments)] #[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.6/src/const_xxh32.rs000064400000000000000000000043270072674642500155120ustar 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.6/src/const_xxh64.rs000064400000000000000000000056730072674642500155240ustar 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.6/src/lib.rs000064400000000000000000000062300072674642500140710ustar 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. //! //!## Example //! //!- Cargo.toml //! //!```toml //![dependencies.xxhash-rust] //!version = "0.8.5" //!features = ["xxh3", "const_xxh3"] //!``` //! //!- main.rs //! //!```rust //!use xxhash_rust::const_xxh3::xxh3_64 as const_xxh3; //!use xxhash_rust::xxh3::xxh3_64; //! //!const TEST: u64 = const_xxh3(b"TEST"); //! //!fn test_input(text: &str) -> bool { //! match xxh3_64(text.as_bytes()) { //! TEST => true, //! _ => false //! } //!} //! //!assert!(!test_input("tEST")); //!assert!(test_input("TEST")); //!``` //! //!## Features: //! //!By default all features are off. //! //!- `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; //! //!## Streaming vs One-shot //! //!For performance reasons one-shot version of algorithm does not re-use streaming version. //!Unless needed, user is advised to use one-shot version which tends to be more optimal. //! //!## `cosnt fn` version //! //!While `const fn` provides compile time implementation, it does so at performance cost. //!Hence you should only use it at _compile_ time. //! //!To guarantee that something is computed at compile time make sure to initialize hash output //!as `const` or `static` variable, otherwise it is possible function is executed at runtime, which //!would be worse than regular algorithm. //! //!`const fn` is implemented in best possible way while conforming to limitations of Rust `const //!fn`, but these limitations are quite strict making any high performance code impossible. #![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.6/src/xxh3.rs000064400000000000000000001126610072674642500142230ustar 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(always)] 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)) } #[inline(never)] 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)), } } #[inline(never)] fn xxh3_64_long_default(input: &[u8], _seed: u64, _secret: &[u8]) -> u64 { xxh3_64_long_impl(input, &DEFAULT_SECRET) } #[inline(never)] 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; const STRIPES_PER_BLOCK: usize = (DEFAULT_SECRET_SIZE - STRIPE_LEN) / SECRET_CONSUME_RATE; #[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 { 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) } } #[inline] ///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); //big input incoming if input.len() > (STRIPES_PER_BLOCK * STRIPE_LEN) { let mut nb_stripes = (input.len() - 1) / STRIPE_LEN; debug_assert!(self.nb_stripes_acc <= STRIPES_PER_BLOCK); { let stripes_to_end = STRIPES_PER_BLOCK - self.nb_stripes_acc; accumulate_loop(&mut self.acc, input.as_ptr(), slice_offset_ptr(&self.custom_secret.0, self.nb_stripes_acc * SECRET_CONSUME_RATE), stripes_to_end); scramble_acc(&mut self.acc, slice_offset_ptr(&self.custom_secret.0, DEFAULT_SECRET_SIZE - STRIPE_LEN)); self.nb_stripes_acc = 0; input = &input[stripes_to_end * STRIPE_LEN..]; nb_stripes -= stripes_to_end } while nb_stripes >= STRIPES_PER_BLOCK { accumulate_loop(&mut self.acc, input.as_ptr(), self.custom_secret.0.as_ptr(), STRIPES_PER_BLOCK); scramble_acc(&mut self.acc, slice_offset_ptr(&self.custom_secret.0, DEFAULT_SECRET_SIZE - STRIPE_LEN)); input = &input[STRIPES_PER_BLOCK * STRIPE_LEN..]; nb_stripes -= STRIPES_PER_BLOCK; } accumulate_loop(&mut self.acc, input.as_ptr(), self.custom_secret.0.as_ptr(), nb_stripes); input = &input[nb_stripes * STRIPE_LEN..]; debug_assert_ne!(input.len(), 0); self.nb_stripes_acc = nb_stripes; 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) } } else 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) } } ///Computes hash as 128bit integer. pub fn digest128(&self) -> u128 { if self.total_len > MID_SIZE_MAX as u64 { let mut acc = self.acc.clone(); self.digest_internal(&mut acc); let low = merge_accs(&mut acc, slice_offset_ptr(&self.custom_secret.0, SECRET_MERGEACCS_START), self.total_len.wrapping_mul(xxh64::PRIME_1)); let high = merge_accs(&mut acc, slice_offset_ptr(&self.custom_secret.0, self.custom_secret.0.len() - mem::size_of_val(&self.acc) - SECRET_MERGEACCS_START), !self.total_len.wrapping_mul(xxh64::PRIME_2)); ((high as u128) << 64) | (low as u128) } 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_128_internal(&self.buffer.0[..self.buffered_size as usize], self.seed, &DEFAULT_SECRET, xxh3_128_long_with_seed) } else { xxh3_128_internal(&self.buffer.0[..self.buffered_size as usize], self.seed, &self.custom_secret.0, xxh3_128_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) } } #[derive(Clone, Copy)] ///Hash builder for `Xxh3` pub struct Xxh3Builder { seed: u64, secret: [u8; DEFAULT_SECRET_SIZE], } impl Xxh3Builder { #[inline(always)] ///Creates new instance with default params. pub const fn new() -> Self { Self { seed: 0, secret: DEFAULT_SECRET, } } #[inline(always)] ///Sets `seed` for `xxh3` algorithm pub const fn with_seed(mut self, seed: u64) -> Self { self.seed = seed; self } #[inline(always)] ///Sets custom `secret` for `xxh3` algorithm pub const fn with_secret(mut self, secret: [u8; DEFAULT_SECRET_SIZE]) -> Self { self.secret = secret; self } #[inline(always)] ///Creates `Xxh3` instance pub const fn build(self) -> Xxh3 { Xxh3::with_custom_ops(self.seed, self.secret) } } impl core::hash::BuildHasher for Xxh3Builder { type Hasher = Xxh3; #[inline(always)] fn build_hasher(&self) -> Self::Hasher { self.build() } } impl Default for Xxh3Builder { #[inline(always)] fn default() -> Self { Self::new() } } // //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.6/src/xxh32.rs000064400000000000000000000150160072674642500143010ustar 00000000000000//!32 bit version of xxhash algorithm //! //!Written using C implementation as reference. use core::{ptr, slice}; use crate::xxh32_common::*; #[inline(always)] fn read_le_unaligned(data: *const u8) -> u32 { debug_assert!(!data.is_null()); unsafe { ptr::read_unaligned(data as *const u32).to_le() } } #[inline(always)] fn read_le_aligned(data: *const u8) -> u32 { debug_assert!(!data.is_null()); unsafe { ptr::read(data as *const u32).to_le() } } #[inline(always)] fn read_le_is_align(data: *const u8, is_aligned: bool) -> u32 { match is_aligned { true => read_le_aligned(data), 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_unaligned(input.as_ptr())); input = &input[4..]; v2 = round(v2, read_le_unaligned(input.as_ptr())); input = &input[4..]; v3 = round(v3, read_le_unaligned(input.as_ptr())); input = &input[4..]; v4 = round(v4, read_le_unaligned(input.as_ptr())); 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.6/src/xxh32_common.rs000064400000000000000000000012430072674642500156460ustar 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.6/src/xxh3_common.rs000064400000000000000000000050400072674642500155630ustar 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.6/src/xxh64.rs000064400000000000000000000202730072674642500143070ustar 00000000000000//!64 bit version of xxhash algorithm //! //!Written using C implementation as reference. use core::{ptr, slice}; use crate::xxh64_common::*; #[inline(always)] fn read_32le_unaligned(data: *const u8) -> u32 { debug_assert!(!data.is_null()); unsafe { ptr::read_unaligned(data as *const u32).to_le() } } #[inline(always)] fn read_32le_aligned(data: *const u8) -> u32 { debug_assert!(!data.is_null()); unsafe { ptr::read(data as *const u32).to_le() } } #[inline(always)] fn read_32le_is_align(data: *const u8, is_aligned: bool) -> u32 { match is_aligned { true => read_32le_aligned(data), false => read_32le_unaligned(data), } } #[inline(always)] fn read_64le_unaligned(data: *const u8) -> u64 { debug_assert!(!data.is_null()); unsafe { ptr::read_unaligned(data as *const u64).to_le() } } #[inline(always)] fn read_64le_aligned(data: *const u8) -> u64 { debug_assert!(!data.is_null()); unsafe { ptr::read(data as *const u64).to_le() } } #[inline(always)] fn read_64le_is_align(data: *const u8, is_aligned: bool) -> u64 { match is_aligned { true => read_64le_aligned(data), 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_unaligned(input.as_ptr())); input = &input[8..]; v2 = round(v2, read_64le_unaligned(input.as_ptr())); input = &input[8..]; v3 = round(v3, read_64le_unaligned(input.as_ptr())); input = &input[8..]; v4 = round(v4, read_64le_unaligned(input.as_ptr())); 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: u64, } 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 as usize + input.len()) < CHUNK_SIZE { unsafe { ptr::copy_nonoverlapping(input.as_ptr(), (self.mem.as_mut_ptr() as *mut u8).add(self.mem_size as usize), input.len()) } self.mem_size += input.len() as u64; 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).add(self.mem_size as usize), 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() as u64; } } ///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 as usize) }; 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) } } impl Default for Xxh64 { #[inline(always)] fn default() -> Self { Xxh64Builder::new(0).build() } } #[derive(Clone, Copy, Default)] ///Hash builder for `Xxh64` pub struct Xxh64Builder { seed: u64 } impl Xxh64Builder { #[inline(always)] ///Creates builder with provided `seed` pub const fn new(seed: u64) -> Self { Self { seed } } #[inline(always)] ///Creates hasher. pub const fn build(self) -> Xxh64 { Xxh64::new(self.seed) } } impl core::hash::BuildHasher for Xxh64Builder { type Hasher = Xxh64; #[inline(always)] fn build_hasher(&self) -> Self::Hasher { self.build() } } xxhash-rust-0.8.6/src/xxh64_common.rs000064400000000000000000000015400072674642500156530ustar 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.6/tests/assert_correctness.rs000064400000000000000000000164430072674642500176200ustar 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 hasher_1_128 = Xxh3::new(); let mut hasher_2_128 = 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); hasher_1_128.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); assert_eq!(hasher_1_128.digest128(), result128); 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); hasher_2_128.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); assert_eq!(hasher_2_128.digest128(), result128); hasher_1_128.reset(); hasher_2_128.reset(); } } xxhash-rust-0.8.6/tests/size.rs000064400000000000000000000005660072674642500146560ustar 00000000000000#[cfg(feature = "xxh32")] #[test] fn size_xxh32() { assert_eq!(core::mem::size_of::(), 44); } #[cfg(feature = "xxh64")] #[test] fn size_xxh64() { assert_eq!(core::mem::size_of::(), 80); } #[cfg(feature = "xxh3")] #[test] fn size_xxh3() { assert_eq!(core::mem::size_of::(), 576); } xxhash-rust-0.8.6/tests/test-vectors.rs000064400000000000000000000052210072674642500163370ustar 00000000000000#![allow(unused)] use core::fmt::{self, Write}; use std::io::{self, BufReader, BufRead}; struct Manifest { text: String, buffer: String, input: io::Lines>, line: usize, } impl Manifest { fn new(input_file: &str) -> Self { let text = std::fs::read_to_string("tests/manifesto.txt").expect("To read"); assert_eq!(text.len(), 5158); let input = std::fs::File::open(input_file).expect("To open input"); let input = BufReader::new(input).lines(); Self { text, buffer: String::with_capacity(20), input, line: 1, } } #[inline(always)] fn get(&self, len: usize) -> &str { &self.text[..len] } #[inline(always)] fn format(&mut self, data: T) -> &str { self.buffer.clear(); let _ = write!(&mut self.buffer, "{:08x}", data); self.buffer.as_str() } //returns (manifesto len, expected output) fn next_input(&mut self) -> Option<(usize, String)> { match self.input.next() { Some(input) => { let input = input.expect("Cannot read test input"); let mut input = input.split(','); let len = input.next().unwrap(); let expected = match input.next() { Some(expected) => expected.trim().to_owned(), None => panic!("test file is missing at line={}", self.line), }; if let Some(unexpected) = input.next() { panic!("test file contains unexpected input='{}' at line={}", unexpected, self.line); } let len = match len.parse() { Ok(len) => len, Err(error) => panic!("test file contains invalid len='{}' at line = {}", len, self.line), }; self.line += 1; Some((len, expected)) }, None => None, } } } #[cfg(feature = "xxh3")] #[test] fn test_vectors_xxh3() { use xxhash_rust::xxh3::{Xxh3, xxh3_64}; let mut hasher = Xxh3::new(); let mut fixture = Manifest::new("tests/xxh3_64_test_inputs.txt"); while let Some((len, expected)) = fixture.next_input() { let manifest = fixture.get(len); hasher.update(manifest.as_bytes()); let digest = hasher.digest(); assert_eq!(xxh3_64(manifest.as_bytes()), digest, "Streaming variant contradict oneshot function"); let digest = fixture.format(digest); assert_eq!(digest, expected); hasher.reset(); } assert_eq!(fixture.line, 5158 + 1); }