2022-10-31 16:59:07 +01:00
|
|
|
//! Fixed hashing seeds for stable hashing.
|
|
|
|
//!
|
|
|
|
//! Set to [`None`] to disable stable hashing.
|
|
|
|
//!
|
2022-11-11 09:46:28 +01:00
|
|
|
//! See [`rhai::config::hashing::set_ahash_seed`][set_ahash_seed].
|
2022-10-31 16:59:07 +01:00
|
|
|
//!
|
2022-11-04 14:22:31 +01:00
|
|
|
//! # Example
|
2022-10-31 16:59:07 +01:00
|
|
|
//!
|
2022-11-04 14:22:31 +01:00
|
|
|
//! ```rust
|
|
|
|
//! // Set the hashing seed to [1, 2, 3, 4]
|
|
|
|
//! rhai::config::hashing::set_ahash_seed(Some([1, 2, 3, 4])).unwrap();
|
|
|
|
//! ```
|
|
|
|
//! Alternatively, set this at compile time via the `RHAI_AHASH_SEED` environment variable.
|
|
|
|
//!
|
|
|
|
//! # Example
|
|
|
|
//!
|
|
|
|
//! ```sh
|
|
|
|
//! env RHAI_AHASH_SEED ="[236,800,954,213]"
|
|
|
|
//! ```
|
2022-10-31 16:59:07 +01:00
|
|
|
// [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100
|
|
|
|
|
2022-11-04 07:36:18 +01:00
|
|
|
use crate::config::hashing_env;
|
2022-11-05 03:49:34 +01:00
|
|
|
use core::panic::{RefUnwindSafe, UnwindSafe};
|
2022-11-04 14:22:31 +01:00
|
|
|
#[cfg(feature = "no_std")]
|
|
|
|
use std::prelude::v1::*;
|
|
|
|
use std::{
|
2022-11-01 13:25:45 +01:00
|
|
|
cell::UnsafeCell,
|
|
|
|
marker::PhantomData,
|
2022-11-04 14:22:31 +01:00
|
|
|
mem,
|
2022-11-01 13:25:45 +01:00
|
|
|
mem::MaybeUninit,
|
2022-11-04 07:36:18 +01:00
|
|
|
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
|
2022-11-01 13:25:45 +01:00
|
|
|
};
|
|
|
|
|
2022-11-04 07:36:18 +01:00
|
|
|
// omg its hokma from record team here to record our locks
|
|
|
|
// what does this do?
|
|
|
|
// so what this does is keep track of a global address in memory that acts as a global lock
|
|
|
|
// i stole this from crossbeam so read their docs for more
|
2022-11-04 14:22:31 +01:00
|
|
|
#[must_use]
|
2022-11-04 07:36:18 +01:00
|
|
|
struct HokmaLock {
|
|
|
|
lock: AtomicUsize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HokmaLock {
|
2022-11-04 14:22:31 +01:00
|
|
|
#[inline(always)]
|
2022-11-04 07:36:18 +01:00
|
|
|
pub const fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
lock: AtomicUsize::new(0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-04 14:22:31 +01:00
|
|
|
pub fn write(&'static self) -> WhenTheHokmaSuppression {
|
2022-11-04 07:36:18 +01:00
|
|
|
loop {
|
2023-06-14 16:23:12 +02:00
|
|
|
// We are only interested in error results
|
2023-06-15 04:34:54 +02:00
|
|
|
if let Err(previous) =
|
|
|
|
self.lock
|
|
|
|
.compare_exchange(1, 1, Ordering::SeqCst, Ordering::SeqCst)
|
2023-06-14 16:23:12 +02:00
|
|
|
{
|
2023-06-14 16:29:51 +02:00
|
|
|
// If we failed, previous cannot be 1
|
|
|
|
return WhenTheHokmaSuppression {
|
|
|
|
hokma: self,
|
|
|
|
state: previous,
|
|
|
|
};
|
2022-11-04 07:36:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-04 14:22:31 +01:00
|
|
|
struct WhenTheHokmaSuppression {
|
2022-11-04 07:36:18 +01:00
|
|
|
hokma: &'static HokmaLock,
|
|
|
|
state: usize,
|
|
|
|
}
|
|
|
|
|
2022-11-04 14:22:31 +01:00
|
|
|
impl WhenTheHokmaSuppression {
|
|
|
|
#[inline]
|
2022-11-04 07:36:18 +01:00
|
|
|
pub fn the_price_of_silence(self) {
|
|
|
|
self.hokma.lock.store(self.state, Ordering::SeqCst);
|
2022-11-23 09:14:11 +01:00
|
|
|
mem::forget(self);
|
2022-11-04 07:36:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-04 14:22:31 +01:00
|
|
|
impl Drop for WhenTheHokmaSuppression {
|
|
|
|
#[inline]
|
2022-11-04 07:36:18 +01:00
|
|
|
fn drop(&mut self) {
|
|
|
|
self.hokma
|
|
|
|
.lock
|
2022-11-23 09:14:11 +01:00
|
|
|
.store(self.state.wrapping_add(2), Ordering::SeqCst);
|
2022-11-04 07:36:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-04 14:22:31 +01:00
|
|
|
#[inline(always)]
|
2022-11-04 07:36:18 +01:00
|
|
|
fn hokmalock(address: usize) -> &'static HokmaLock {
|
|
|
|
const LEN: usize = 787;
|
2022-11-23 09:14:11 +01:00
|
|
|
#[allow(clippy::declare_interior_mutable_const)]
|
2022-11-04 07:36:18 +01:00
|
|
|
const LCK: HokmaLock = HokmaLock::new();
|
|
|
|
static RECORDS: [HokmaLock; LEN] = [LCK; LEN];
|
|
|
|
|
|
|
|
&RECORDS[address % LEN]
|
|
|
|
}
|
|
|
|
|
2022-11-25 04:49:00 +01:00
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// LOL, there is a reason its called `SusLock`
|
2022-11-04 14:22:31 +01:00
|
|
|
#[must_use]
|
2022-11-25 04:49:00 +01:00
|
|
|
pub struct SusLock<T: 'static> {
|
2022-11-04 14:22:31 +01:00
|
|
|
initialized: AtomicBool,
|
2022-11-01 13:25:45 +01:00
|
|
|
data: UnsafeCell<MaybeUninit<T>>,
|
|
|
|
_marker: PhantomData<T>,
|
|
|
|
}
|
2022-10-31 16:59:07 +01:00
|
|
|
|
2022-11-25 04:49:00 +01:00
|
|
|
impl<T: 'static> Default for SusLock<T> {
|
|
|
|
#[inline(always)]
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-23 09:14:11 +01:00
|
|
|
impl<T: 'static> SusLock<T> {
|
2022-11-25 04:49:00 +01:00
|
|
|
/// Create a new [`SusLock`].
|
2022-11-04 14:22:31 +01:00
|
|
|
#[inline]
|
2022-11-23 09:14:11 +01:00
|
|
|
pub const fn new() -> Self {
|
|
|
|
Self {
|
2022-11-04 14:22:31 +01:00
|
|
|
initialized: AtomicBool::new(false),
|
2022-11-01 13:25:45 +01:00
|
|
|
data: UnsafeCell::new(MaybeUninit::uninit()),
|
|
|
|
_marker: PhantomData,
|
|
|
|
}
|
2022-10-31 16:59:07 +01:00
|
|
|
}
|
|
|
|
|
2022-11-25 04:49:00 +01:00
|
|
|
/// Is the [`SusLock`] initialized?
|
2022-11-09 14:16:28 +01:00
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn is_initialized(&self) -> bool {
|
|
|
|
self.initialized.load(Ordering::SeqCst)
|
|
|
|
}
|
|
|
|
|
2022-11-25 04:49:00 +01:00
|
|
|
/// Return the value of the [`SusLock`] (if initialized).
|
|
|
|
#[inline]
|
2022-11-04 14:22:31 +01:00
|
|
|
#[must_use]
|
2022-11-05 14:46:08 +01:00
|
|
|
pub fn get(&self) -> Option<&'static T> {
|
2022-11-04 14:22:31 +01:00
|
|
|
if self.initialized.load(Ordering::SeqCst) {
|
2022-11-25 02:46:13 +01:00
|
|
|
let hokma = hokmalock(self.data.get() as usize);
|
2022-11-04 07:36:18 +01:00
|
|
|
// we forgo the optimistic read, because we don't really care
|
|
|
|
let guard = hokma.write();
|
2022-11-09 14:16:28 +01:00
|
|
|
let cast: *const T = self.data.get().cast();
|
2022-11-23 09:14:11 +01:00
|
|
|
let val = unsafe { &*cast.cast::<T>() };
|
2022-11-04 07:36:18 +01:00
|
|
|
guard.the_price_of_silence();
|
|
|
|
Some(val)
|
2022-11-01 13:25:45 +01:00
|
|
|
} else {
|
2022-11-09 14:16:28 +01:00
|
|
|
None
|
2022-10-31 16:59:07 +01:00
|
|
|
}
|
2022-11-01 13:25:45 +01:00
|
|
|
}
|
2022-10-31 16:59:07 +01:00
|
|
|
|
2022-11-25 04:49:00 +01:00
|
|
|
/// Return the value of the [`SusLock`], initializing it if not yet done.
|
|
|
|
#[inline]
|
2022-11-04 14:22:31 +01:00
|
|
|
#[must_use]
|
2022-11-09 14:16:28 +01:00
|
|
|
pub fn get_or_init(&self, f: impl FnOnce() -> T) -> &'static T {
|
2022-11-04 14:22:31 +01:00
|
|
|
if !self.initialized.load(Ordering::SeqCst) {
|
|
|
|
self.initialized.store(true, Ordering::SeqCst);
|
2022-11-23 09:14:11 +01:00
|
|
|
let hokma = hokmalock(self.data.get() as usize);
|
2022-11-04 07:36:18 +01:00
|
|
|
hokma.write();
|
2022-11-01 13:25:45 +01:00
|
|
|
unsafe {
|
2022-11-09 14:16:28 +01:00
|
|
|
self.data.get().write(MaybeUninit::new(f()));
|
2022-10-31 16:59:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-09 14:16:28 +01:00
|
|
|
self.get().unwrap()
|
2022-10-31 16:59:07 +01:00
|
|
|
}
|
|
|
|
|
2022-11-25 04:49:00 +01:00
|
|
|
/// Initialize the value of the [`SusLock`].
|
|
|
|
///
|
|
|
|
/// # Error
|
|
|
|
///
|
|
|
|
/// If the [`SusLock`] has already been initialized, the current value is returned as error.
|
|
|
|
#[inline]
|
|
|
|
pub fn init(&self, value: T) -> Result<(), T> {
|
2022-11-04 14:22:31 +01:00
|
|
|
if self.initialized.load(Ordering::SeqCst) {
|
2022-11-01 13:25:45 +01:00
|
|
|
Err(value)
|
|
|
|
} else {
|
|
|
|
let _ = self.get_or_init(|| value);
|
|
|
|
Ok(())
|
2022-10-31 16:59:07 +01:00
|
|
|
}
|
|
|
|
}
|
2022-11-01 13:25:45 +01:00
|
|
|
}
|
2022-10-31 16:59:07 +01:00
|
|
|
|
2022-11-23 09:14:11 +01:00
|
|
|
unsafe impl<T: Sync + Send> Sync for SusLock<T> {}
|
|
|
|
unsafe impl<T: Send> Send for SusLock<T> {}
|
2022-11-23 06:24:14 +01:00
|
|
|
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SusLock<T> {}
|
2022-10-31 16:59:07 +01:00
|
|
|
|
2022-11-23 09:14:11 +01:00
|
|
|
impl<T: 'static> Drop for SusLock<T> {
|
2022-11-04 14:22:31 +01:00
|
|
|
#[inline]
|
2022-11-01 13:25:45 +01:00
|
|
|
fn drop(&mut self) {
|
2022-11-04 14:22:31 +01:00
|
|
|
if self.initialized.load(Ordering::SeqCst) {
|
2022-11-23 06:24:14 +01:00
|
|
|
unsafe { (*self.data.get()).assume_init_drop() };
|
2022-11-01 13:25:45 +01:00
|
|
|
}
|
2022-10-31 16:59:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-01 13:25:45 +01:00
|
|
|
static AHASH_SEED: SusLock<Option<[u64; 4]>> = SusLock::new();
|
|
|
|
|
2022-11-04 14:22:31 +01:00
|
|
|
/// Set the hashing seed. This is used to hash functions etc.
|
2022-11-01 13:25:45 +01:00
|
|
|
///
|
2022-11-04 14:22:31 +01:00
|
|
|
/// This is a static global value and affects every Rhai instance.
|
2022-11-01 13:25:45 +01:00
|
|
|
/// This should not be used _unless_ you know you need it.
|
|
|
|
///
|
2022-11-04 14:22:31 +01:00
|
|
|
/// # Warning
|
|
|
|
///
|
|
|
|
/// * You can only call this function **ONCE** for the entire duration of program execution.
|
2022-11-11 09:46:28 +01:00
|
|
|
/// * You **MUST** call this before performing **ANY** Rhai operation (e.g. creating an [`Engine`][crate::Engine]).
|
2022-11-04 14:22:31 +01:00
|
|
|
///
|
|
|
|
/// # Error
|
2022-11-01 13:25:45 +01:00
|
|
|
///
|
2022-11-04 14:22:31 +01:00
|
|
|
/// Returns an error containing the existing hashing seed if already set.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # use rhai::Engine;
|
|
|
|
/// // Set the hashing seed to [1, 2, 3, 4]
|
|
|
|
/// rhai::config::hashing::set_ahash_seed(Some([1, 2, 3, 4])).unwrap();
|
|
|
|
///
|
|
|
|
/// // Use Rhai AFTER setting the hashing seed
|
|
|
|
/// let engine = Engine::new();
|
|
|
|
/// ```
|
|
|
|
#[inline(always)]
|
2022-11-01 13:25:45 +01:00
|
|
|
pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> {
|
2022-11-25 04:49:00 +01:00
|
|
|
AHASH_SEED.init(new_seed)
|
2022-11-01 13:25:45 +01:00
|
|
|
}
|
2022-10-31 16:59:07 +01:00
|
|
|
|
2022-11-04 14:22:31 +01:00
|
|
|
/// Get the current hashing Seed.
|
|
|
|
///
|
|
|
|
/// If the seed is not yet defined, the `RHAI_AHASH_SEED` environment variable (if any) is used.
|
|
|
|
///
|
|
|
|
/// Otherwise, the hashing seed is randomized to protect against DOS attacks.
|
2022-11-01 13:25:45 +01:00
|
|
|
///
|
2022-11-11 09:46:28 +01:00
|
|
|
/// See [`rhai::config::hashing::set_ahash_seed`][set_ahash_seed] for more.
|
2022-11-04 14:22:31 +01:00
|
|
|
#[inline]
|
|
|
|
#[must_use]
|
2022-11-05 14:53:58 +01:00
|
|
|
pub fn get_ahash_seed() -> &'static Option<[u64; 4]> {
|
2022-11-09 14:16:28 +01:00
|
|
|
if !AHASH_SEED.is_initialized() {
|
|
|
|
return &hashing_env::AHASH_SEED;
|
2022-11-05 14:46:08 +01:00
|
|
|
}
|
2022-11-09 14:16:28 +01:00
|
|
|
|
|
|
|
AHASH_SEED.get().unwrap_or(&hashing_env::AHASH_SEED)
|
2022-11-01 13:25:45 +01:00
|
|
|
}
|