diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 11ca384f..fc782151 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: flags: - "" - "--features debugging" - - "--features metadata,serde,internals" + - "--features metadata,serde,internals,static_hash" - "--features unchecked,serde,metadata,internals,debugging" - "--features sync,serde,metadata,internals,debugging" - "--features no_position,serde,metadata,internals,debugging" @@ -89,35 +89,35 @@ jobs: command: test args: ${{matrix.flags}} - nightly_features_build: - name: NightlyFeatureBuild - runs-on: ${{matrix.os}} - continue-on-error: ${{matrix.experimental}} - strategy: - matrix: - os: [ubuntu-latest] - flags: - - "--features stable_hash" - toolchain: [ nightly ] - experimental: [ false ] - include: - # smoketests for different toolchains - - { toolchain: nightly, os: windows-latest, experimental: false, flags: "" } - - { toolchain: nightly, os: macos-latest, experimental: false, flags: "" } - fail-fast: false - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup Toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{matrix.toolchain}} - override: true - - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: ${{matrix.flags}} +# nightly_features_build: +# name: NightlyFeatureBuild +# runs-on: ${{matrix.os}} +# continue-on-error: ${{matrix.experimental}} +# strategy: +# matrix: +# os: [ubuntu-latest] +# flags: +# - "--features stable_hash" +# toolchain: [ nightly ] +# experimental: [ false ] +# include: +# # smoketests for different toolchains +# - { toolchain: nightly, os: windows-latest, experimental: false, flags: "" } +# - { toolchain: nightly, os: macos-latest, experimental: false, flags: "" } +# fail-fast: false +# steps: +# - name: Checkout +# uses: actions/checkout@v2 +# - name: Setup Toolchain +# uses: actions-rs/toolchain@v1 +# with: +# toolchain: ${{matrix.toolchain}} +# override: true +# - name: Test +# uses: actions-rs/cargo@v1 +# with: +# command: test +# args: ${{matrix.flags}} # no-std builds are a bit more extensive to test @@ -127,6 +127,9 @@ jobs: continue-on-error: ${{matrix.experimental}} strategy: matrix: + flags: + - "" + - "--features static_hash" include: - {os: ubuntu-latest, flags: "--profile unix", experimental: false} - {os: windows-latest, flags: "--profile windows", experimental: true} diff --git a/Cargo.toml b/Cargo.toml index 310ceb40..e5edb293 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for ident metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata internals = [] # expose internal data structures debugging = ["internals"] # enable debugging -stable_hash = ["std"] +static_hash = [] serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types # compiling for no-std diff --git a/build.rs b/build.rs index 76abd9ee..dda0850e 100644 --- a/build.rs +++ b/build.rs @@ -20,9 +20,9 @@ fn main() { contents = contents.replace("{{AHASH_SEED}}", &seed); - File::create("src/config.rs") + File::create("src/config/hashing.rs") .expect("cannot create `config.rs`") .write_all(contents.as_bytes()) - .expect("cannot write to `config.rs`"); + .expect("cannot write to `config/hashing.rs`"); } } diff --git a/build.template b/build.template index ed5e161d..709a3f65 100644 --- a/build.template +++ b/build.template @@ -1,60 +1,129 @@ -#[cfg(feature = "stable_hash")] -use core::cell::OnceCell; +//! Fixed hashing seeds for stable hashing. +//! +//! Set to [`None`] to disable stable hashing. +//! +//! See [`set_rhai_ahash_seed`] for more. +//! +//! Alternatively, You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` +//! environment variable instead. +//! +//! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` +// [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 -// [236,800,954,213], haha funny yume nikki reference +pub use internal::get_ahash_seed; +#[cfg(feature = "static_hash")] +pub use internal::set_ahash_seed; -#[cfg(not(feature = "stable_hash"))] -/// Fixed hashing seeds for stable hashing. -/// -/// Set to [`None`] to disable stable hashing. -/// -/// See [`set_rhai_ahash_seed`] for more. -/// -/// You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` -/// environment variable instead. -/// -/// E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` -const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}}; -#[cfg(feature = "stable_hash")] -/// Fixed hashing seeds for stable hashing. -/// -/// Set to [`None`] to disable stable hashing. -/// -/// See [`set_rhai_ahash_seed`] for more. -/// -/// You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` -/// environment variable instead. -/// -/// E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` -static AHASH_SEED: OnceCell> = OnceCell::new(); +#[cfg(feature = "static_hash")] +mod internal { + use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + sync::atomic::{AtomicBool, Ordering}, + panic::{RefUnwindSafe, UnwindSafe} + }; -#[cfg(feature = "stable_hash")] -#[doc(cfg(feature = "stable_hash"))] -/// Sets the Rhai Ahash seed. This is used to hash functions and the like. -/// -/// This is a global variable, and thus will affect every Rhai instance. -/// This should not be used _unless_ you know you need it. -/// -/// **WARNING**: -/// - You can only call this function **ONCE** for the whole of your program execution. -/// - You should gracefully handle the `Err(())`. -/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). -pub fn set_rhai_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { - AHASH_SEED.set(new_seed) + struct SusLock where T: 'static { + initalized: AtomicBool, + data: UnsafeCell>, + _marker: PhantomData, + } + + impl SusLock { + pub const fn new() -> SusLock { + SusLock { + initalized: AtomicBool::new(false), + data: UnsafeCell::new(MaybeUninit::uninit()), + _marker: PhantomData + } + } + + pub fn get(&self) -> Option<&T> { + if self.initalized.load(Ordering::SeqCst) { + Some( + unsafe { + (&*self.data.get()).assume_init_ref() + } + ) + } else { + return None + } + } + + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { + let value = f(); + if !self.initalized.load(Ordering::SeqCst) { + unsafe { + self.data.get().write(MaybeUninit::new(value)); + } + self.initalized.store(true, Ordering::SeqCst); + } + + self.get() + } + + pub fn set(&self, value: T) -> Result<(), T> { + if self.initalized.load(Ordering::SeqCst) { + Err(value) + } else { + let _ = self.get_or_init(|| value); + Ok(()) + } + } + } + + unsafe impl Sync for SusLock {} + unsafe impl Send for SusLock {} + impl RefUnwindSafe for SusLock {} + + impl Drop for SusLock { + fn drop(&mut self) { + if self.initalized.load(Ordering::SeqCst) { + unsafe { (&mut *self.data.get()).assume_init_drop() }; + } + } + } + + static AHASH_SEED: SusLock> = SusLock::new(); + + // #[doc(cfg(feature = "stable_hash"))] + /// Sets the Rhai Ahash seed. This is used to hash functions and the like. + /// + /// This is a global variable, and thus will affect every Rhai instance. + /// This should not be used _unless_ you know you need it. + /// + /// # Warnings + /// - You can only call this function **ONCE** for the whole of your program execution. + /// - You should gracefully handle the `Err(())`. + /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). + /// + /// # Errors + /// This will error if the AHashSeed is already set. + pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) + } + + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_rhai_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + const FUNNY_YUMENIKKI_REFERENCE: [u64; 4] = [236,800,954,213]; + + AHASH_SEED.get_or_init(|| Some(FUNNY_YUMENIKKI_REFERENCE)).map(|x| *x).flatten() + } } -#[cfg(feature = "stable_hash")] -/// Gets the current Rhai Ahash Seed. -/// -/// See [`set_rhai_ahash_seed`] for more. -pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED.get().map(|x| *x).flatten() -} +#[cfg(not(feature = "static_hash"))] +mod internal { + const AHASH_SEED: Option<[u64; 4]> = {{ AHASH_SEED }}; -#[cfg(not(feature = "stable_hash"))] -/// Gets the current Rhai Ahash Seed. -/// -/// See [`AHASH_SEED`] and [`set_rhai_ahash_seed`] for more. -pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED + } } \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index e7611418..709a3f65 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,44 +10,120 @@ //! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` // [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 -#[cfg(feature = "stable_hash")] -use std::sync::OnceLock; +pub use internal::get_ahash_seed; +#[cfg(feature = "static_hash")] +pub use internal::set_ahash_seed; -#[cfg(not(feature = "stable_hash"))] -const AHASH_SEED: Option<[u64; 4]> = None; -#[cfg(feature = "stable_hash")] -static AHASH_SEED: OnceLock> = OnceLock::new(); +#[cfg(feature = "static_hash")] +mod internal { + use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + sync::atomic::{AtomicBool, Ordering}, + panic::{RefUnwindSafe, UnwindSafe} + }; -#[cfg(feature = "stable_hash")] -// #[doc(cfg(feature = "stable_hash"))] -/// Sets the Rhai Ahash seed. This is used to hash functions and the like. -/// -/// This is a global variable, and thus will affect every Rhai instance. -/// This should not be used _unless_ you know you need it. -/// -/// # Warning: -/// - You can only call this function **ONCE** for the whole of your program execution. -/// - You should gracefully handle the `Err(())`. -/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). -/// -/// # Errors -/// This will error if the AHashSeed is already set. -pub fn set_rhai_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { - AHASH_SEED.set(new_seed) + struct SusLock where T: 'static { + initalized: AtomicBool, + data: UnsafeCell>, + _marker: PhantomData, + } + + impl SusLock { + pub const fn new() -> SusLock { + SusLock { + initalized: AtomicBool::new(false), + data: UnsafeCell::new(MaybeUninit::uninit()), + _marker: PhantomData + } + } + + pub fn get(&self) -> Option<&T> { + if self.initalized.load(Ordering::SeqCst) { + Some( + unsafe { + (&*self.data.get()).assume_init_ref() + } + ) + } else { + return None + } + } + + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { + let value = f(); + if !self.initalized.load(Ordering::SeqCst) { + unsafe { + self.data.get().write(MaybeUninit::new(value)); + } + self.initalized.store(true, Ordering::SeqCst); + } + + self.get() + } + + pub fn set(&self, value: T) -> Result<(), T> { + if self.initalized.load(Ordering::SeqCst) { + Err(value) + } else { + let _ = self.get_or_init(|| value); + Ok(()) + } + } + } + + unsafe impl Sync for SusLock {} + unsafe impl Send for SusLock {} + impl RefUnwindSafe for SusLock {} + + impl Drop for SusLock { + fn drop(&mut self) { + if self.initalized.load(Ordering::SeqCst) { + unsafe { (&mut *self.data.get()).assume_init_drop() }; + } + } + } + + static AHASH_SEED: SusLock> = SusLock::new(); + + // #[doc(cfg(feature = "stable_hash"))] + /// Sets the Rhai Ahash seed. This is used to hash functions and the like. + /// + /// This is a global variable, and thus will affect every Rhai instance. + /// This should not be used _unless_ you know you need it. + /// + /// # Warnings + /// - You can only call this function **ONCE** for the whole of your program execution. + /// - You should gracefully handle the `Err(())`. + /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). + /// + /// # Errors + /// This will error if the AHashSeed is already set. + pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) + } + + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_rhai_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + const FUNNY_YUMENIKKI_REFERENCE: [u64; 4] = [236,800,954,213]; + + AHASH_SEED.get_or_init(|| Some(FUNNY_YUMENIKKI_REFERENCE)).map(|x| *x).flatten() + } } -#[cfg(feature = "stable_hash")] -/// Gets the current Rhai Ahash Seed. -/// -/// See [`set_rhai_ahash_seed`] for more. -pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED.get().map(|x| *x).flatten() -} +#[cfg(not(feature = "static_hash"))] +mod internal { + const AHASH_SEED: Option<[u64; 4]> = {{ AHASH_SEED }}; -#[cfg(not(feature = "stable_hash"))] -/// Gets the current Rhai Ahash Seed. -/// -/// See [`AHASH_SEED`] and [`set_rhai_ahash_seed`] for more. -pub fn get_rhai_ahash_seed() -> Option<[u64; 4]> { - AHASH_SEED -} + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED + } +} \ No newline at end of file diff --git a/src/config/hashing.rs b/src/config/hashing.rs new file mode 100644 index 00000000..709a3f65 --- /dev/null +++ b/src/config/hashing.rs @@ -0,0 +1,129 @@ +//! Fixed hashing seeds for stable hashing. +//! +//! Set to [`None`] to disable stable hashing. +//! +//! See [`set_rhai_ahash_seed`] for more. +//! +//! Alternatively, You can also set this at compile time by disabling `stable_hash` and setting the `RHAI_AHASH_SEED` +//! environment variable instead. +//! +//! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` +// [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 + +pub use internal::get_ahash_seed; +#[cfg(feature = "static_hash")] +pub use internal::set_ahash_seed; + +#[cfg(feature = "static_hash")] +mod internal { + use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + sync::atomic::{AtomicBool, Ordering}, + panic::{RefUnwindSafe, UnwindSafe} + }; + + struct SusLock where T: 'static { + initalized: AtomicBool, + data: UnsafeCell>, + _marker: PhantomData, + } + + impl SusLock { + pub const fn new() -> SusLock { + SusLock { + initalized: AtomicBool::new(false), + data: UnsafeCell::new(MaybeUninit::uninit()), + _marker: PhantomData + } + } + + pub fn get(&self) -> Option<&T> { + if self.initalized.load(Ordering::SeqCst) { + Some( + unsafe { + (&*self.data.get()).assume_init_ref() + } + ) + } else { + return None + } + } + + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&T> { + let value = f(); + if !self.initalized.load(Ordering::SeqCst) { + unsafe { + self.data.get().write(MaybeUninit::new(value)); + } + self.initalized.store(true, Ordering::SeqCst); + } + + self.get() + } + + pub fn set(&self, value: T) -> Result<(), T> { + if self.initalized.load(Ordering::SeqCst) { + Err(value) + } else { + let _ = self.get_or_init(|| value); + Ok(()) + } + } + } + + unsafe impl Sync for SusLock {} + unsafe impl Send for SusLock {} + impl RefUnwindSafe for SusLock {} + + impl Drop for SusLock { + fn drop(&mut self) { + if self.initalized.load(Ordering::SeqCst) { + unsafe { (&mut *self.data.get()).assume_init_drop() }; + } + } + } + + static AHASH_SEED: SusLock> = SusLock::new(); + + // #[doc(cfg(feature = "stable_hash"))] + /// Sets the Rhai Ahash seed. This is used to hash functions and the like. + /// + /// This is a global variable, and thus will affect every Rhai instance. + /// This should not be used _unless_ you know you need it. + /// + /// # Warnings + /// - You can only call this function **ONCE** for the whole of your program execution. + /// - You should gracefully handle the `Err(())`. + /// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). + /// + /// # Errors + /// This will error if the AHashSeed is already set. + pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) + } + + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_rhai_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + const FUNNY_YUMENIKKI_REFERENCE: [u64; 4] = [236,800,954,213]; + + AHASH_SEED.get_or_init(|| Some(FUNNY_YUMENIKKI_REFERENCE)).map(|x| *x).flatten() + } +} + +#[cfg(not(feature = "static_hash"))] +mod internal { + const AHASH_SEED: Option<[u64; 4]> = {{ AHASH_SEED }}; + + /// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. + /// The default seed is not stable and may change between versions. + /// + /// See [`set_ahash_seed`] for more. + pub fn get_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED + } +} \ No newline at end of file diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 00000000..194ac6da --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,3 @@ +//! Contains Configuration for Rhai. + +pub mod hashing; \ No newline at end of file diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 406a21c6..855dcd3d 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -77,7 +77,7 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] #[must_use] pub fn get_hasher() -> ahash::AHasher { - match config::get_rhai_ahash_seed() { + match config::hashing::get_ahash_seed() { Some([seed1, seed2, seed3, seed4]) if seed1 | seed2 | seed3 | seed4 != 0 => { ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher() } diff --git a/src/lib.rs b/src/lib.rs index d2d6eae7..f2a0cad5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ #![allow(clippy::negative_feature_names)] #![allow(clippy::module_inception)] -#![cfg_attr(feature = "stable_hash", feature(once_cell))] +#![cfg_attr(feature = "static_hash", feature(once_cell))] #[cfg(feature = "no_std")] extern crate alloc;