hel-random 0.5.2

Simple RNG with weak entropy source (alloc) and xoshiro256++ hashing
Documentation
//! A simple pseudo non-cryptographic random number generator.
//! Using xoshiro256++ under the hood.
#![warn(missing_docs)]

const STATE_SIZE: usize = 4;

type Target = u64;
type StateType = [Target; STATE_SIZE];

static mut STATE: StateType = [0, 0, 0, 0];

/// Gets current state of generator
pub fn get_state() -> StateType {
	unsafe { STATE }
}

#[used]
#[cfg_attr(target_os = "linux", link_section = ".init_array")]
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")]
static INIT: extern "C" fn() = {
	extern "C" fn init() {
		unsafe {
			use std::alloc::*;

			let mut res = STATE;

			const ALLOC: usize = STATE_SIZE * STATE_SIZE;

			let layout = Layout::array::<Target>(ALLOC).unwrap();
			let ptr = alloc(layout);

			if ptr.is_null() {
				handle_alloc_error(layout);
			}

			let garbage_arr = &mut *(ptr as *mut [Target; ALLOC]);

			// Will be used if there's no garbage on the heap
			let addr = std::hint::black_box(ptr as Target);
			let now = std::hint::black_box(
				std::time::SystemTime::now()
					.duration_since(std::time::UNIX_EPOCH)
					.expect("to get system time")
					.subsec_micros() as u64,
			);
			let now_bits = now ^ (now << 32) ^ (now.rotate_left(25));
			let mut bits = addr ^ (addr >> 11) ^ (addr.rotate_right(30)) ^ now_bits;

			// Looking for garbage on the heap, while writing some garbage back
			for (i, garbage) in garbage_arr.iter_mut().enumerate() {
				let current = &mut res[i % STATE_SIZE];

				let mut val = std::hint::black_box(*garbage);
				val ^= bits;

				let msb = ((bits & 1) ^ ((bits >> 1) & 1)) << (Target::BITS - 1);
				bits >>= 1;
				bits |= msb;

				*current ^= val;
				std::ptr::write_volatile(garbage, val);
			}

			STATE = res;

			dealloc(ptr, layout);
		}
	}

	init
};

#[inline]
fn xoshiro256pp() {
	unsafe {
		let s = STATE[1] << 17;

		STATE[2] ^= STATE[0];
		STATE[3] ^= STATE[1];
		STATE[1] ^= STATE[2];
		STATE[0] ^= STATE[3];

		STATE[2] ^= s;

		STATE[3] = STATE[3].rotate_left(45);
	}
}

/// A helper trait to generate random values
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
pub trait Random: Sized {
	/// Will generate a random [`Self`]
	fn random() -> Self;
}

/// Generic function that returns a random [`T`]
///
/// # Example
/// ```
/// use hel_random::generate;
///
/// let a: u64 = generate();
/// let b: u32 = generate();
/// let c = generate::<i128>();
///
/// println!("a = {a}");
/// println!("b = {b}");
/// println!("c = {c}");
/// ```
#[inline(always)]
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
pub fn generate<T: Random>() -> T {
	T::random()
}

macro_rules! make {
	($type: ident, $code: block) => {
		#[doc = concat!("Will generate a random ", stringify!($type))]
		///
		/// # Example
		/// ```
		#[doc = concat!("let a: ", stringify!($type), " = hel_random::", stringify!($type), "();")]
		#[doc = concat!("let b: ", stringify!($type), " = hel_random::", stringify!($type), "();")]
		/// // Examples generated by macro, so we check if type is `bool` or `i8` or `u8` to avoid collisions
		#[doc = concat!("if std::mem::size_of::<", stringify!($type), ">() > 1 {")]
		///     assert!(a != b);
		/// }
		/// ```
		#[inline]
		#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
		pub fn $type() -> $type {
			$code
		}

		impl Random for $type {
			#[doc = concat!("Will generate a random ", stringify!($type))]
			///
			/// # Example
			/// ```
			/// use hel_random::Random;
			///
			#[doc = concat!("let r = ", stringify!($type), "::random();")]
			/// println!("r = {r}");
			/// ```
			#[inline(always)]
			fn random() -> Self {
				$type()
			}
		}
	};

	($type: ident) => {
		make!($type, { u64() as $type });
	};
}

make!(u128, {
	xoshiro256pp();

	unsafe {
		STATE[0].wrapping_add(STATE[2]) as u128 | (((STATE[1]).wrapping_add(STATE[3]) as u128) << 64)
	}
});
make!(i128, { u128() as i128 });

make!(u64, {
	xoshiro256pp();
	unsafe {
		STATE[0]
			.wrapping_add(STATE[3])
			.rotate_left(23)
			.wrapping_add(STATE[0])
	}
});
make!(i64);
make!(u32);
make!(i32);
make!(u16);
make!(i16);
make!(u8);
make!(i8);

make!(bool, {
	unsafe {
		// runtime check is necessary to avoid infinite loop
		if STATE[0] == 0 {
			return false;
		}

		loop {
			xoshiro256pp();

			let a = (STATE[0] & 1) == 1;
			let b = (STATE[1] & 1) == 1;

			if a != b {
				return a;
			}
		}
	}
});

// TODO floats