diff --git a/Cargo.toml b/Cargo.toml index 09b42f1..aba97f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,16 +14,23 @@ exclude = ["article/*"] [features] default = ["std"] -std = [] +std = ["rand_core/std"] # Only relevant for throughput benchmarks bench-csv = [] bench-md = [] bench-plot = [] [dependencies] -rand = "0.8" +rand_chacha = { version = "0.3", default_features = false } +rand_core = "0.6" + +# (no_std) RDRAND is still preferable to constant seed +[target.'cfg(target_feature = "rdrand")'.dependencies] +getrandom = { version = "0.2", features = ["rdrand"] } +rand_core = { version = "0.6", features = ["getrandom"] } [dev-dependencies] +rand = "0.8" lazy_static = { version = "1.4" } itertools = "0.12.0" # Benchmarks @@ -38,6 +45,7 @@ metrohash = "1.0.6" fnv = "1.0.3" [build-dependencies] +rand = "0.8" rustc_version = "0.4.0" [dev-dependencies.plotters] diff --git a/build.rs b/build.rs index dd11580..eac6e12 100644 --- a/build.rs +++ b/build.rs @@ -2,6 +2,25 @@ extern crate rustc_version; use rustc_version::{version_meta, Channel}; fn main() { + // generate seed for GxBuildHasher RNG if entropy isn't available + let entropy = cfg!(target_feature = "rdrand") || cfg!(feature = "std"); + if entropy { + println!("cargo:rustc-cfg=entropy") + } else { + let out_dir = std::env::var_os("OUT_DIR").unwrap(); + let seed_path = std::path::Path::new(&out_dir).join("seed.rs"); + + use rand::RngCore; + let mut seed = [0u8; 32]; + let mut rng = rand::thread_rng(); + rng.fill_bytes(&mut seed); + + let mut string = "const SEED: [u8; 32] = [".to_owned(); + string.push_str(&seed.into_iter().map(|i| i.to_string()).collect::>().join(",")); + string.push_str("];"); + std::fs::write(seed_path, &string).unwrap(); + } + if version_meta().unwrap().channel == Channel::Nightly && cfg!(target_arch = "x86_64") && cfg!(target_feature = "avx2") diff --git a/src/hasher.rs b/src/hasher.rs index 01341d1..1aaf7ed 100644 --- a/src/hasher.rs +++ b/src/hasher.rs @@ -1,8 +1,9 @@ +#[cfg(feature = "std")] use std::collections::{HashMap, HashSet}; -use std::hash::{BuildHasher, Hasher}; -use std::mem::MaybeUninit; +use core::hash::{BuildHasher, Hasher}; +use core::mem::MaybeUninit; -use rand::RngCore; +use rand_core::{RngCore, SeedableRng}; use crate::gxhash::platform::*; use crate::gxhash::*; @@ -84,7 +85,7 @@ impl GxHasher { /// unsigned integer. #[inline] pub fn finish_u128(&self) -> u128 { - debug_assert!(std::mem::size_of::() >= std::mem::size_of::()); + debug_assert!(core::mem::size_of::() >= core::mem::size_of::()); unsafe { let p = &finalize(self.state) as *const State as *const u128; @@ -135,14 +136,20 @@ impl Hasher for GxHasher { #[derive(Clone, Debug)] pub struct GxBuildHasher(State); +#[cfg(not(entropy))] +include!(concat!(env!("OUT_DIR"), "/seed.rs")); + impl Default for GxBuildHasher { #[inline] fn default() -> GxBuildHasher { let mut uninit: MaybeUninit = MaybeUninit::uninit(); - let mut rng = rand::thread_rng(); + #[cfg(entopy)] + let mut rng = rand_chacha::ChaCha12Rng::from_entropy(); + #[cfg(not(entropy))] + let mut rng = rand_chacha::ChaCha12Rng::from_seed(SEED); unsafe { let ptr = uninit.as_mut_ptr() as *mut u8; - let slice = std::slice::from_raw_parts_mut(ptr, VECTOR_SIZE); + let slice = core::slice::from_raw_parts_mut(ptr, VECTOR_SIZE); rng.fill_bytes(slice); GxBuildHasher(uninit.assume_init()) } @@ -158,9 +165,11 @@ impl BuildHasher for GxBuildHasher { } /// A `HashMap` using a (DOS-resistant) [`GxBuildHasher`]. +#[cfg(feature = "std")] pub type GxHashMap = HashMap; /// A `HashSet` using a (DOS-resistant) [`GxBuildHasher`]. +#[cfg(feature = "std")] pub type GxHashSet = HashSet; #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 5c51447..830f6d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,5 @@ mod gxhash; pub use crate::gxhash::*; -#[cfg(feature = "std")] mod hasher; -#[cfg(feature = "std")] pub use crate::hasher::*; \ No newline at end of file