Skip to content

Commit

Permalink
Simplify interface (#121)
Browse files Browse the repository at this point in the history
* Use a runtime-rng feature flag instead of using OS detection (#82)
* Use cfg-if to simplify conditions
* Require rng for RandomState::default and new.
* Don't use runtime rng when fuzzing
* Add support for hash_one

Signed-off-by: Tom Kaitchuck <Tom.Kaitchuck@gmail.com>

Co-authored-by: Amanieu d'Antras <amanieu@gmail.com>
Co-authored-by: bl-ue <54780737+bl-ue@users.noreply.github.com>
  • Loading branch information
3 people committed Aug 10, 2022
1 parent 76dd8d2 commit 24491f6
Show file tree
Hide file tree
Showing 14 changed files with 417 additions and 330 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Expand Up @@ -148,4 +148,4 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: check
args: --target wasm32-unknown-unknown
args: --target wasm32-unknown-unknown --no-default-features
33 changes: 17 additions & 16 deletions Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ahash"
version = "0.7.6"
version = "0.8.0"
authors = ["Tom Kaitchuck <Tom.Kaitchuck@gmail.com>"]
license = "MIT OR Apache-2.0"
description = "A non-cryptographic hash function using AES-NI for high performance"
Expand All @@ -22,15 +22,18 @@ bench = true
doc = true

[features]
default = ["std"]
default = ["std", "runtime-rng"]

# Enabling this will enable `AHashMap` and `AHashSet`.
std = []

# This is an alternitive to runtime key generation which does compile time key generation if getrandom is not available.
# (If getrandom is available this does nothing.)
# If this is on (and getrandom is off) it implies the produced binary will not be identical.
# If this is disabled and gerrandom is unavailable constant keys are used.
# Runtime random key generation using getrandom.
runtime-rng = ["getrandom"]

# This is an alternative to runtime key generation which does compile time key generation if runtime-rng is not available.
# (If runtime-rng is enabled this does nothing.)
# If this is on (and runtime-rng is off) it implies the produced binary will not be identical.
# If this is disabled and runtime-rng is unavailable constant keys are used.
compile-time-rng = ["const-random"]

[[bench]]
Expand Down Expand Up @@ -62,29 +65,27 @@ debug-assertions = false
codegen-units = 1

[build-dependencies]
version_check = "0.9"

[target.'cfg(any(target_os = "linux", target_os = "android", target_os = "windows", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "solaris", target_os = "illumos", target_os = "fuchsia", target_os = "redox", target_os = "cloudabi", target_os = "haiku", target_os = "vxworks", target_os = "emscripten", target_os = "wasi"))'.dependencies]
getrandom = { version = "0.2.3" }
const-random = { version = "0.1.12", optional = true }
serde = { version = "1.0.117", optional = true }
version_check = "0.9.4"

[target.'cfg(not(any(target_os = "linux", target_os = "android", target_os = "windows", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "solaris", target_os = "illumos", target_os = "fuchsia", target_os = "redox", target_os = "cloudabi", target_os = "haiku", target_os = "vxworks", target_os = "emscripten", target_os = "wasi")))'.dependencies]
[dependencies]
getrandom = { version = "0.2.3", optional = true }
const-random = { version = "0.1.12", optional = true }
serde = { version = "1.0.117", optional = true }
cfg-if = "1.0"

[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
once_cell = { version = "1.8", default-features = false, features = ["alloc"] }
once_cell = { version = "1.8", default-features = false, features = ["unstable", "alloc"] }

[dev-dependencies]
no-panic = "0.1.10"
criterion = {version = "0.3.2"}
criterion = {version = "0.3.2", features = ["html_reports"] }
seahash = "4.0"
fnv = "1.0.5"
fxhash = "0.2.1"
hex = "0.4.2"
rand = "0.7.3"
rand = "0.8.5"
serde_json = "1.0.59"
hashbrown = "0.12.3"

[package.metadata.docs.rs]
rustc-args = ["-C", "target-feature=+aes"]
Expand Down
21 changes: 0 additions & 21 deletions build.rs
Expand Up @@ -10,27 +10,6 @@ fn main() {
if let Some(true) = version_check::supports_feature("stdsimd") {
println!("cargo:rustc-cfg=feature=\"stdsimd\"");
}
let os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS was not set");
if os.eq_ignore_ascii_case("linux")
|| os.eq_ignore_ascii_case("android")
|| os.eq_ignore_ascii_case("windows")
|| os.eq_ignore_ascii_case("macos")
|| os.eq_ignore_ascii_case("ios")
|| os.eq_ignore_ascii_case("freebsd")
|| os.eq_ignore_ascii_case("openbsd")
|| os.eq_ignore_ascii_case("dragonfly")
|| os.eq_ignore_ascii_case("solaris")
|| os.eq_ignore_ascii_case("illumos")
|| os.eq_ignore_ascii_case("fuchsia")
|| os.eq_ignore_ascii_case("redox")
|| os.eq_ignore_ascii_case("cloudabi")
|| os.eq_ignore_ascii_case("haiku")
|| os.eq_ignore_ascii_case("vxworks")
|| os.eq_ignore_ascii_case("emscripten")
|| os.eq_ignore_ascii_case("wasi")
{
println!("cargo:rustc-cfg=feature=\"runtime-rng\"");
}
let arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH was not set");
if arch.eq_ignore_ascii_case("x86_64")
|| arch.eq_ignore_ascii_case("aarch64")
Expand Down
8 changes: 5 additions & 3 deletions smhasher/ahash-cbindings/src/lib.rs
@@ -1,10 +1,12 @@
#![feature(build_hasher_simple_hash_one)]

use ahash::*;
use core::slice;
use std::hash::{BuildHasher, Hasher};
use std::hash::{BuildHasher};

#[no_mangle]
pub extern "C" fn ahash64(buf: *const (), len: usize, seed: u64) -> u64 {
let buf: &[u8] = unsafe { slice::from_raw_parts(buf as *const u8, len) };
let build_hasher = RandomState::with_seeds(seed, 0, 0, 0);
<[u8]>::get_hash(&buf, &build_hasher)
let build_hasher = RandomState::with_seed(seed as usize);
build_hasher.hash_one(&buf)
}
4 changes: 1 addition & 3 deletions src/aes_hash.rs
@@ -1,6 +1,4 @@
use crate::convert::*;
#[cfg(feature = "specialize")]
use crate::fallback_hash::MULTIPLE;
use crate::operations::*;
use crate::RandomState;
use core::hash::Hasher;
Expand Down Expand Up @@ -50,7 +48,7 @@ impl AHasher {
/// println!("Hash is {:x}!", hasher.finish());
/// ```
#[inline]
pub fn new_with_keys(key1: u128, key2: u128) -> Self {
pub(crate) fn new_with_keys(key1: u128, key2: u128) -> Self {
let pi: [u128; 2] = PI.convert();
let key1 = key1 ^ pi[0];
let key2 = key2 ^ pi[1];
Expand Down
7 changes: 4 additions & 3 deletions src/fallback_hash.rs
@@ -1,12 +1,13 @@
use crate::convert::*;
use crate::operations::folded_multiply;
use crate::operations::read_small;
use crate::operations::MULTIPLE;
use crate::random_state::PI;
use crate::RandomState;
use core::hash::Hasher;

///This constant come from Kunth's prng (Empirically it works better than those from splitmix32).
pub(crate) const MULTIPLE: u64 = 6364136223846793005;


const ROT: u32 = 23; //17

/// A `Hasher` for hashing an arbitrary stream of bytes.
Expand All @@ -31,7 +32,7 @@ impl AHasher {
/// Creates a new hasher keyed to the provided key.
#[inline]
#[allow(dead_code)] // Is not called if non-fallback hash is used.
pub fn new_with_keys(key1: u128, key2: u128) -> AHasher {
pub(crate) fn new_with_keys(key1: u128, key2: u128) -> AHasher {
let pi: [u128; 2] = PI.convert();
let key1: [u64; 2] = (key1 ^ pi[0]).convert();
let key2: [u64; 2] = (key2 ^ pi[1]).convert();
Expand Down
2 changes: 0 additions & 2 deletions src/hash_map.rs
Expand Up @@ -163,8 +163,6 @@ where
/// types that can be `==` without being identical. See the [module-level
/// documentation] for more.
///
/// [module-level documentation]: crate::collections#insert-and-complex-keys
///
/// # Examples
///
/// ```
Expand Down
146 changes: 68 additions & 78 deletions src/lib.rs
Expand Up @@ -8,103 +8,92 @@
//!
//! aHash uses the hardware AES instruction on x86 processors to provide a keyed hash function.
//! aHash is not a cryptographically secure hash.
//!
//! # Example
//! ```
//! use ahash::{AHasher, RandomState};
//! use std::collections::HashMap;
//!
//! let mut map: HashMap<i32, i32, RandomState> = HashMap::default();
//! map.insert(12, 34);
//! ```
//! For convenience, both new-type wrappers and type aliases are provided. The new type wrappers are called called `AHashMap` and `AHashSet`. These do the same thing with slightly less typing.
//! The type aliases are called `ahash::HashMap`, `ahash::HashSet` are also provided and alias the
//! std::[HashMap] and std::[HashSet]. Why are there two options? The wrappers are convenient but
//! can't be used where a generic `std::collection::HashMap<K, V, S>` is required.
//!
//! ```ignore
//! use ahash::AHashMap;
//!
//! let mut map: AHashMap<i32, i32> = AHashMap::with_capacity(4);
//! map.insert(12, 34);
//! map.insert(56, 78);
//! // There are also type aliases provieded together with some extension traits to make
//! // it more of a drop in replacement for the std::HashMap/HashSet
//! use ahash::{HashMapExt, HashSetExt}; // Used to get with_capacity()
//! let mut map = ahash::HashMap::with_capacity(10);
//! map.insert(12, 34);
//! let mut set = ahash::HashSet::with_capacity(10);
//! set.insert(10);
//! ```
#![cfg_attr(any(feature = "compile-time-rng", feature = "runtime-rng"), doc = r##"
# Example
```
use ahash::{AHasher, RandomState};
use std::collections::HashMap;
let mut map: HashMap<i32, i32, RandomState> = HashMap::default();
map.insert(12, 34);
```
"##)]
#![cfg_attr(feature = "std", doc = r##"
For convenience, both new-type wrappers and type aliases are provided. The new type wrappers are called called `AHashMap` and `AHashSet`. These do the same thing with slightly less typing.
The type aliases are called `ahash::HashMap`, `ahash::HashSet` are also provided and alias the
std::[HashMap] and std::[HashSet]. Why are there two options? The wrappers are convenient but
can't be used where a generic `std::collection::HashMap<K, V, S>` is required.
```
use ahash::AHashMap;
let mut map: AHashMap<i32, i32> = AHashMap::with_capacity(4);
map.insert(12, 34);
map.insert(56, 78);
// There are also type aliases provieded together with some extension traits to make
// it more of a drop in replacement for the std::HashMap/HashSet
use ahash::{HashMapExt, HashSetExt}; // Used to get with_capacity()
let mut map = ahash::HashMap::with_capacity(10);
map.insert(12, 34);
let mut set = ahash::HashSet::with_capacity(10);
set.insert(10);
```
"##)]
#![deny(clippy::correctness, clippy::complexity, clippy::perf)]
#![allow(clippy::pedantic, clippy::cast_lossless, clippy::unreadable_literal)]
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
#![cfg_attr(feature = "specialize", feature(min_specialization))]
#![cfg_attr(feature = "specialize", feature(build_hasher_simple_hash_one))]
#![cfg_attr(feature = "stdsimd", feature(stdsimd))]

#[macro_use]
mod convert;

#[cfg(any(
all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
all(
any(target_arch = "arm", target_arch = "aarch64"),
any(target_feature = "aes", target_feature = "crypto"),
not(miri),
feature = "stdsimd"
)
))]
mod aes_hash;
mod fallback_hash;

cfg_if::cfg_if! {
if #[cfg(any(
all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
all(any(target_arch = "arm", target_arch = "aarch64"),
any(target_feature = "aes", target_feature = "crypto"),
not(miri),
feature = "stdsimd")
))] {
mod aes_hash;
pub use crate::aes_hash::AHasher;
} else {
pub use crate::fallback_hash::AHasher;
}
}

cfg_if::cfg_if! {
if #[cfg(feature = "std")] {
mod hash_map;
mod hash_set;

pub use crate::hash_map::AHashMap;
pub use crate::hash_set::AHashSet;

/// [Hasher]: std::hash::Hasher
/// [HashMap]: std::collections::HashMap
/// Type alias for [HashMap]<K, V, ahash::RandomState>
pub type HashMap<K, V> = std::collections::HashMap<K, V, crate::RandomState>;

/// Type alias for [HashSet]<K, ahash::RandomState>
pub type HashSet<K> = std::collections::HashSet<K, crate::RandomState>;
}
}

#[cfg(test)]
mod hash_quality_test;

#[cfg(feature = "std")]
/// [Hasher]: std::hash::Hasher
/// [HashMap]: std::collections::HashMap
/// Type alias for [HashMap]<K, V, ahash::RandomState>
pub type HashMap<K, V> = std::collections::HashMap<K, V, crate::RandomState>;
#[cfg(feature = "std")]
/// Type alias for [HashSet]<K, ahash::RandomState>
pub type HashSet<K> = std::collections::HashSet<K, crate::RandomState>;

#[cfg(feature = "std")]
mod hash_map;
#[cfg(feature = "std")]
mod hash_set;
mod operations;
mod random_state;
pub mod random_state;
mod specialize;

#[cfg(any(
all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
all(
any(target_arch = "arm", target_arch = "aarch64"),
any(target_feature = "aes", target_feature = "crypto"),
not(miri),
feature = "stdsimd"
)
))]
pub use crate::aes_hash::AHasher;

#[cfg(not(any(
all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
all(
any(target_arch = "arm", target_arch = "aarch64"),
any(target_feature = "aes", target_feature = "crypto"),
not(miri),
feature = "stdsimd"
)
)))]
pub use crate::fallback_hash::AHasher;
pub use crate::random_state::RandomState;

pub use crate::specialize::CallHasher;

#[cfg(feature = "std")]
pub use crate::hash_map::AHashMap;
#[cfg(feature = "std")]
pub use crate::hash_set::AHashSet;
use core::hash::BuildHasher;
use core::hash::Hash;
use core::hash::Hasher;
Expand Down Expand Up @@ -279,6 +268,7 @@ mod test {
use crate::*;
use std::collections::HashMap;
use std::hash::Hash;
use crate::specialize::CallHasher;

#[test]
fn test_ahash_alias_map_construction() {
Expand Down
6 changes: 5 additions & 1 deletion src/operations.rs
@@ -1,5 +1,8 @@
use crate::convert::*;

///This constant come from Kunth's prng (Empirically it works better than those from splitmix32).
pub(crate) const MULTIPLE: u64 = 6364136223846793005;

/// This is a constant with a lot of special properties found by automated search.
/// See the unit tests below. (Below are alternative values)
#[cfg(all(target_feature = "ssse3", not(miri)))]
Expand Down Expand Up @@ -60,7 +63,7 @@ pub(crate) fn add_and_shuffle(a: u128, b: u128) -> u128 {
shuffle(sum.convert())
}

#[allow(unused)] //not used by fallbac
#[allow(unused)] //not used by fallback
#[inline(always)]
pub(crate) fn shuffle_and_add(base: u128, to_add: u128) -> u128 {
let shuffled: [u64; 2] = shuffle(base).convert();
Expand Down Expand Up @@ -146,6 +149,7 @@ pub(crate) fn aesdec(value: u128, xor: u128) -> u128 {
}
}

#[allow(unused)]
#[inline(always)]
pub(crate) fn add_in_length(enc: &mut u128, len: u64) {
#[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
Expand Down

0 comments on commit 24491f6

Please sign in to comment.