#![allow(unused)]
use crate::crypto::base::{mix64, shuffle_with};
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
#[inline(always)]
pub fn probe() -> u64 {
const SHIFT: u8 = 48;
const TARGET_FREQ: u128 = 1_000_000_000; static MULTIPLIER_Q48: AtomicU64 = AtomicU64::new(0);
#[inline(always)]
fn raw_probe() -> u64 {
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
{
#[cfg(target_arch = "x86_64")]
unsafe {
core::arch::x86_64::_rdtsc()
}
#[cfg(target_arch = "x86")]
unsafe {
core::arch::x86::_rdtsc()
}
}
#[cfg(target_arch = "aarch64")]
{
let tsc: u64;
unsafe {
core::arch::asm!("mrs {}, cntvct_el0", out(reg) tsc, options(nomem, nostack))
};
tsc
}
#[cfg(target_arch = "arm")]
{
let mut lo: u32;
let mut hi: u32;
unsafe {
core::arch::asm!("mrrc p15, 1, {}, {}, c14", out(reg) lo, out(reg) hi, options(nomem, nostack))
};
((hi as u64) << 32) | (lo as u64)
}
#[cfg(target_arch = "riscv64")]
{
let tsc: u64;
unsafe { core::arch::asm!("rdtime {}", out(reg) tsc, options(nomem, nostack)) };
tsc
}
#[cfg(target_arch = "riscv32")]
{
let mut hi: u32;
let mut lo: u32;
let mut hi2: u32;
unsafe {
core::arch::asm!(
"2:", "rdtimeh {hi}", "rdtime {lo}", "rdtimeh {hi2}", "bne {hi}, {hi2}, 2b",
hi = out(reg) hi, lo = out(reg) lo, hi2 = out(reg) hi2, options(nomem, nostack, preserves_flags)
);
}
((hi as u64) << 32) | (lo as u64)
}
#[cfg(target_arch = "loongarch64")]
{
let tsc: u64;
unsafe { core::arch::asm!("rdtime.d {}, $r0", out(reg) tsc, options(nomem, nostack)) };
tsc
}
#[cfg(target_arch = "powerpc")]
{
let tsc: u64;
unsafe { core::arch::asm!("mftb {}", out(reg) tsc, options(nomem, nostack)) };
tsc
}
#[cfg(target_arch = "wasm32")]
{
#[cfg(target_os = "wasi")]
{
let mut timespec = core::mem::MaybeUninit::uninit();
unsafe {
let _ = wasi::clock_time_get(wasi::CLOCKID_MONOTONIC, 1, timespec.as_mut_ptr());
timespec.assume_init()
}
}
#[cfg(not(target_os = "wasi"))]
{
#[wasm_bindgen::prelude::wasm_bindgen]
extern "C" {
#[wasm_bindgen::prelude::wasm_bindgen(js_namespace = performance)]
fn now() -> f64;
}
(now() * 1_000_000.0) as u64
}
}
}
fn autodetect_hardware_freq() -> u64 {
#[cfg(target_arch = "aarch64")]
{
let freq: u64;
unsafe {
core::arch::asm!("mrs {}, cntfrq_el0", out(reg) freq, options(nomem, nostack))
};
return freq;
}
#[cfg(target_arch = "loongarch64")]
{
let freq: u32;
unsafe {
core::arch::asm!("cpucfg {}, {}", out(reg) freq, in(reg) 4, options(nomem, nostack))
};
return freq as u64;
}
#[cfg(target_arch = "wasm32")]
{
return 1_000_000_000; }
#[cfg(not(any(
target_arch = "aarch64",
target_arch = "loongarch64",
target_arch = "wasm32"
)))]
{
2_500_000_000
}
}
let raw = raw_probe();
let mut mult = MULTIPLIER_Q48.load(Ordering::Relaxed);
if mult == 0 {
let hz = autodetect_hardware_freq();
let final_mult = if hz == 0 {
1u64 << SHIFT
} else {
((TARGET_FREQ << SHIFT) / hz as u128) as u64
};
mult = match MULTIPLIER_Q48.compare_exchange(
0,
final_mult,
Ordering::SeqCst,
Ordering::Relaxed,
) {
Ok(_) => final_mult,
Err(existing) => existing,
};
}
((raw as u128 * mult as u128) >> SHIFT) as u64
}
#[inline(never)]
fn reg_sig() -> u64 {
let state: u64;
let gpr1: u64;
let gpr2: u64;
let gpr3: u64;
let code: u64;
let stack: u64;
#[cfg(target_arch = "x86_64")]
unsafe {
core::arch::asm!(
"pushfq",
"pop {0}",
"lea {1}, [rip]",
"mov {2}, rsp",
out(reg) state, out(reg) code, out(reg) stack,
out("rcx") gpr1, out("r10") gpr2, out("r11") gpr3,
);
}
#[cfg(target_arch = "aarch64")]
unsafe {
core::arch::asm!(
"mrs {0}, nzcv",
"adr {1}, .",
"mov {2}, sp",
out(reg) state, out(reg) code, out(reg) stack,
out("x1") gpr1, out("x2") gpr2, out("x3") gpr3,
options(nomem, nostack)
);
}
#[cfg(target_arch = "riscv64")]
unsafe {
core::arch::asm!(
"rdinstret {0}",
"auipc {1}, 0",
"mv {2}, sp",
out(reg) state, out(reg) code, out(reg) stack,
out("t0") gpr1, out("t1") gpr2, out("t2") gpr3,
options(nomem, nostack)
);
}
#[cfg(target_arch = "x86")]
unsafe {
let (s32, c32, sp32, g1, g2, g3): (u32, u32, u32, u32, u32, u32);
core::arch::asm!(
"pushfd",
"pop {0}",
"call 2f",
"2:",
"pop {1}",
"mov {2}, esp",
out(reg) s32, out(reg) c32, out(reg) sp32,
out("ecx") g1, out("edx") g2, out("edi") g3,
);
state = s32 as u64;
code = c32 as u64;
stack = sp32 as u64;
gpr1 = g1 as u64;
gpr2 = g2 as u64;
gpr3 = g3 as u64;
}
#[cfg(target_arch = "arm")]
unsafe {
let (s32, c32, sp32, g1, g2, g3): (u32, u32, u32, u32, u32, u32);
core::arch::asm!(
"mrs {0}, CPSR", "mov {1}, pc", "mov {2}, sp",
out(reg) s32, out(reg) c32, out(reg) sp32,
out("r1") g1, out("r2") g2, out("r3") g3,
options(nomem, nostack)
);
state = s32 as u64;
code = c32 as u64;
stack = sp32 as u64;
gpr1 = g1 as u64;
gpr2 = g2 as u64;
gpr3 = g3 as u64;
}
#[cfg(target_arch = "riscv32")]
unsafe {
let (s32, c32, sp32, g1, g2, g3): (u32, u32, u32, u32, u32, u32);
core::arch::asm!(
"rdinstret {0}", "auipc {1}, 0",
"mv {2}, sp",
out(reg) s32, out(reg) c32, out(reg) sp32,
out("t0") g1, out("t1") g2, out("t2") g3,
options(nomem, nostack)
);
state = s32 as u64;
code = c32 as u64;
stack = sp32 as u64;
gpr1 = g1 as u64;
gpr2 = g2 as u64;
gpr3 = g3 as u64;
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64",
target_arch = "x86",
target_arch = "arm",
target_arch = "riscv32"
)))]
{
state = 0;
gpr1 = 0;
gpr2 = 0;
gpr3 = 0;
code = 0;
stack = 0;
}
let mut s = state.rotate_left(13);
s ^= gpr1.rotate_right(17);
s ^= gpr2.rotate_left(11);
s ^= gpr3.rotate_right(3);
s ^= code.rotate_left(17);
s ^= stack.rotate_right(5);
mix64(s)
}
pub fn seed() -> u64 {
use core::hint::black_box;
static COUNTER: AtomicUsize = AtomicUsize::new(0);
const STACK_MASK: usize = 767;
static CONFIG: (u8, u8, u8) = (32, 16, 8);
let c = COUNTER.fetch_add(1, Ordering::Relaxed) as u8;
let x = probe();
let y = reg_sig();
#[cfg(feature = "rand-safe-stack")]
let stack: core::mem::MaybeUninit<[u8; STACK_MASK]> = core::mem::MaybeUninit::uninit();
let sp = unsafe {
#[cfg(feature = "rand-safe-stack")]
{
black_box(stack.as_ptr() as *const u8)
}
#[cfg(not(feature = "rand-safe-stack"))]
{
black_box(&c as *const u8) }
};
let rs = |idx| unsafe { black_box(core::ptr::read_volatile(sp.add(idx))).saturating_add(2) };
let t2s = |t: u64, mut s: u64, i: u8| {
if t.rotate_left(i as u32) as u8 & 1 == 0 {
let idx = ((t as u16) & (STACK_MASK as u16)) as usize;
s = s.rotate_left(rs(idx) as u32);
let idx2 = (((s >> 16) as u16) & (STACK_MASK as u16)) as usize;
s = s.wrapping_mul(rs(idx2) as u64);
} else {
let idx = (((t >> 16) as u16) & (STACK_MASK as u16)) as usize;
s = s.rotate_right(rs(idx) as u32);
let idx2 = ((s as u16) & (STACK_MASK as u16)) as usize;
s = s.wrapping_mul(rs(idx2) as u64);
}
s = match s.rotate_left(i as u32) as u8 {
0 => s ^ 0x9e3779b97f4a7c15,
255 => s.wrapping_mul(probe().saturating_add(2)),
127 => s.rotate_right(7),
3 => s.wrapping_mul(STACK_MASK as u64),
7 => s.wrapping_add(0x9e3779b97f4a7c15),
32 => s.wrapping_mul(0x94d049bb133111eb),
_ => s,
};
s
};
let mut s = (x ^ y).rotate_right(c as u32);
for i in 0..black_box(CONFIG.0) {
if (s >> 8) as u8 > 127 {
continue;
}
let t1 = probe().wrapping_mul((i as u64 ^ s).wrapping_mul(0x9e3779b97f4a7c15));
s ^= t1;
s = t2s(t1, s, i);
for j in 0..black_box(CONFIG.1) {
if (s >> 16) as u8 <= 127 {
continue;
}
let t2 = probe().wrapping_mul(((i ^ j) as u64 ^ s).wrapping_mul(0x9e3779b97f4a7c15));
s ^= t2;
s = t2s(t2, s, i ^ j);
for k in 0..black_box(CONFIG.2) {
if (s >> 32) as u8 > 127 {
continue;
}
let t3 =
probe().wrapping_mul(((i ^ j ^ k) as u64 ^ s).wrapping_mul(0x9e3779b97f4a7c15));
s ^= t3;
s = t2s(t3, s, i ^ j ^ k);
}
}
}
mix64(s)
}
pub fn shuffle<T, U>(mut table: U) -> U
where
U: AsMut<[T]>,
{
shuffle_with(table, next())
}
pub fn next() -> u64 {
static SEED: AtomicU64 = AtomicU64::new(0);
static COUNTER: AtomicUsize = AtomicUsize::new(0);
let c = COUNTER.fetch_add(1, Ordering::Relaxed);
let mut s = SEED.fetch_add(0x9e3779b97f4a7c15, Ordering::Relaxed);
if s == 0 || s == 0x9e3779b97f4a7c15 || c.is_multiple_of(1024) {
s ^= seed();
SEED.store(s, Ordering::Relaxed);
s
} else {
mix64(s)
}
}
#[cfg(feature = "rand-expand")]
pub trait Random {
fn random() -> Self;
}
#[cfg(feature = "rand-expand")]
#[inline(always)]
pub fn random<T: Random>() -> T {
T::random()
}
macro_rules! impl_random_int {
($($t:ty),*) => {
$(
#[cfg(feature = "rand-expand")]
impl Random for $t {
#[inline(always)]
fn random() -> Self {
next() as Self
}
}
)*
};
}
impl_random_int!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
#[cfg(feature = "rand-expand")]
impl Random for bool {
#[inline(always)]
fn random() -> Self {
(next() & 1) == 1
}
}
#[cfg(feature = "rand-expand")]
impl Random for f64 {
#[inline(always)]
fn random() -> Self {
let fraction = next() >> 11;
fraction as f64 / (1u64 << 53) as f64
}
}
#[cfg(feature = "rand-expand")]
impl Random for f32 {
#[inline(always)]
fn random() -> Self {
let fraction = (next() as u32) >> 8;
fraction as f32 / (1u32 << 24) as f32
}
}
#[cfg(feature = "rand-expand")]
pub fn fill_bytes(dest: &mut [u8]) {
let (chunks, remainder) = dest.as_chunks_mut::<8>();
for chunk in chunks {
*chunk = next().to_le_bytes();
}
if !remainder.is_empty() {
let bytes = next().to_le_bytes();
remainder.copy_from_slice(&bytes[..remainder.len()]);
}
}
#[cfg(feature = "rand-expand")]
pub trait SampleRange {
type Output;
fn sample(self) -> Self::Output;
}
#[cfg(feature = "rand-expand")]
#[inline(always)]
pub fn random_range<R: SampleRange>(range: R) -> R::Output {
range.sample()
}
macro_rules! impl_sample_range_int {
($($t:ty, $u:ty),*) => {
$(
#[cfg(feature = "rand-expand")]
impl SampleRange for core::ops::Range<$t> {
type Output = $t;
#[inline]
fn sample(self) -> $t {
assert!(self.start < self.end, "Invalid range: start must be less than end");
let start = self.start as $u;
let end = self.end as $u;
let span = end.wrapping_sub(start);
let threshold = span.wrapping_neg() % span;
loop {
let r = next() as $u;
if r >= threshold {
let offset = r % span;
return start.wrapping_add(offset) as $t;
}
}
}
}
#[cfg(feature = "rand-expand")]
impl SampleRange for core::ops::RangeInclusive<$t> {
type Output = $t;
#[inline]
fn sample(self) -> $t {
let (start_val, end_val) = (*self.start(), *self.end());
assert!(start_val <= end_val, "Invalid range: start must be less than or equal to end");
if start_val == <$t>::MIN && end_val == <$t>::MAX {
return next() as $t;
}
let start = start_val as $u;
let end = end_val as $u;
let span = end.wrapping_sub(start).wrapping_add(1);
let threshold = span.wrapping_neg() % span;
loop {
let r = next() as $u;
if r >= threshold {
let offset = r % span;
return start.wrapping_add(offset) as $t;
}
}
}
}
)*
};
}
impl_sample_range_int!(
u8, u8, u16, u16, u32, u32, u64, u64, usize, usize, i8, u8, i16, u16, i32, u32, i64, u64,
isize, usize
);
macro_rules! impl_sample_range_float {
($($t:ty),*) => {
$(
#[cfg(feature = "rand-expand")]
impl SampleRange for core::ops::Range<$t> {
type Output = $t;
#[inline]
fn sample(self) -> $t {
assert!(self.start < self.end, "Invalid range: start must be less than end");
self.start + random::<$t>() * (self.end - self.start)
}
}
#[cfg(feature = "rand-expand")]
impl SampleRange for core::ops::RangeInclusive<$t> {
type Output = $t;
#[inline]
fn sample(self) -> $t {
let (start, end) = (*self.start(), *self.end());
assert!(start <= end, "Invalid range: start must be less than or equal to end");
if start == end {
return start;
}
start + random::<$t>() * (end - start)
}
}
)*
};
}
impl_sample_range_float!(f32, f64);
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[cfg(feature = "std")]
#[test]
fn test() {
for _ in 0..1024 {
let a = std::time::Instant::now();
let s = next();
println!("{:<30}{:?}", s, a.elapsed());
}
}
}