use crate::config::hashing_env;
use core::panic::{RefUnwindSafe, UnwindSafe};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
cell::UnsafeCell,
marker::PhantomData,
mem,
mem::MaybeUninit,
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
};
#[must_use]
struct HokmaLock {
lock: AtomicUsize,
}
impl HokmaLock {
#[inline(always)]
pub const fn new() -> Self {
Self {
lock: AtomicUsize::new(0),
}
}
pub fn write(&'static self) -> WhenTheHokmaSuppression {
loop {
let previous = self.lock.swap(1, Ordering::SeqCst);
if previous != 1 {
return WhenTheHokmaSuppression {
hokma: self,
state: previous,
};
}
}
}
}
struct WhenTheHokmaSuppression {
hokma: &'static HokmaLock,
state: usize,
}
impl WhenTheHokmaSuppression {
#[inline]
pub fn the_price_of_silence(self) {
self.hokma.lock.store(self.state, Ordering::SeqCst);
mem::forget(self);
}
}
impl Drop for WhenTheHokmaSuppression {
#[inline]
fn drop(&mut self) {
self.hokma
.lock
.store(self.state.wrapping_add(2), Ordering::SeqCst);
}
}
#[inline(always)]
fn hokmalock(address: usize) -> &'static HokmaLock {
const LEN: usize = 787;
#[allow(clippy::declare_interior_mutable_const)]
const LCK: HokmaLock = HokmaLock::new();
static RECORDS: [HokmaLock; LEN] = [LCK; LEN];
&RECORDS[address % LEN]
}
#[must_use]
pub struct SusLock<T: 'static> {
initialized: AtomicBool,
data: UnsafeCell<MaybeUninit<T>>,
_marker: PhantomData<T>,
}
impl<T: 'static> Default for SusLock<T> {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl<T: 'static> SusLock<T> {
#[inline]
pub const fn new() -> Self {
Self {
initialized: AtomicBool::new(false),
data: UnsafeCell::new(MaybeUninit::uninit()),
_marker: PhantomData,
}
}
#[inline(always)]
#[must_use]
pub fn is_initialized(&self) -> bool {
self.initialized.load(Ordering::SeqCst)
}
#[inline]
#[must_use]
pub fn get(&self) -> Option<&'static T> {
if self.initialized.load(Ordering::SeqCst) {
let hokma = hokmalock(self.data.get() as usize);
let guard = hokma.write();
let cast: *const T = self.data.get().cast();
let val = unsafe { &*cast.cast::<T>() };
guard.the_price_of_silence();
Some(val)
} else {
None
}
}
#[inline]
#[must_use]
pub fn get_or_init(&self, f: impl FnOnce() -> T) -> &'static T {
if !self.initialized.load(Ordering::SeqCst) {
self.initialized.store(true, Ordering::SeqCst);
let hokma = hokmalock(self.data.get() as usize);
hokma.write();
unsafe {
self.data.get().write(MaybeUninit::new(f()));
}
}
self.get().unwrap()
}
#[inline]
pub fn init(&self, value: T) -> Result<(), T> {
if self.initialized.load(Ordering::SeqCst) {
Err(value)
} else {
let _ = self.get_or_init(|| value);
Ok(())
}
}
}
unsafe impl<T: Sync + Send> Sync for SusLock<T> {}
unsafe impl<T: Send> Send for SusLock<T> {}
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SusLock<T> {}
impl<T: 'static> Drop for SusLock<T> {
#[inline]
fn drop(&mut self) {
if self.initialized.load(Ordering::SeqCst) {
unsafe { (*self.data.get()).assume_init_drop() };
}
}
}
static AHASH_SEED: SusLock<Option<[u64; 4]>> = SusLock::new();
#[inline(always)]
pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> {
AHASH_SEED.init(new_seed)
}
#[inline]
#[must_use]
pub fn get_ahash_seed() -> &'static Option<[u64; 4]> {
if !AHASH_SEED.is_initialized() {
return &hashing_env::AHASH_SEED;
}
AHASH_SEED.get().unwrap_or(&hashing_env::AHASH_SEED)
}