use std::io;
use std::fs::File;
use std::io::Read;
use std::fs::metadata;
use std::os::unix::fs::FileTypeExt;
use std::os::fd::IntoRawFd;
use std::sync::atomic::Ordering;
const RAND_BUFF_SIZE: usize = 8;
thread_local! {
static RAND_BUFF: std::cell::RefCell<[u32; RAND_BUFF_SIZE]> = const {
std::cell::RefCell::new([0; RAND_BUFF_SIZE])
};
static BUF_CNT: std::cell::Cell<usize> = const {
std::cell::Cell::new(0)
};
}
pub fn get_srandom() -> u32 { let cnt = BUF_CNT.with(|c| c.get());
if cnt == 0 { let mut bytes = [0u8; RAND_BUFF_SIZE * 4]; if getrandom_buffer(&mut bytes).is_ok() { RAND_BUFF.with(|r| {
let mut buf = r.borrow_mut();
for (i, chunk) in bytes.chunks_exact(4).enumerate() { buf[i] = u32::from_ne_bytes(
[chunk[0], chunk[1], chunk[2], chunk[3]],
);
}
});
}
BUF_CNT.with(|c| c.set(RAND_BUFF_SIZE)); }
let new_cnt = BUF_CNT.with(|c| c.get()) - 1; BUF_CNT.with(|c| c.set(new_cnt));
RAND_BUFF.with(|r| r.borrow()[new_cnt]) }
pub fn random_u32() -> u32 {
let mut buf = [0u8; 4];
let _ = getrandom_buffer(&mut buf);
u32::from_ne_bytes(buf)
}
pub fn random_u64() -> u64 {
let mut buf = [0u8; 8];
let _ = getrandom_buffer(&mut buf);
u64::from_ne_bytes(buf)
}
pub fn bounded(max: u32) -> u32 {
if max == 0 {
return 0;
}
if max == u32::MAX {
return random_u32();
}
let mut x = random_u32();
let mut m = (x as u64) * (max as u64);
let mut l = m as u32;
if l < max {
let threshold = (-(max as i64) as u64 % max as u64) as u32;
while l < threshold {
x = random_u32();
m = (x as u64) * (max as u64);
l = m as u32;
}
}
(m >> 32) as u32
}
#[cfg(target_os = "macos")]
pub fn getrandom_buffer(buf: &mut [u8]) -> io::Result<()> { unsafe {
libc::arc4random_buf(buf.as_mut_ptr() as *mut libc::c_void, buf.len());
}
Ok(())
}
#[cfg(target_os = "linux")]
pub fn getrandom_buffer(buf: &mut [u8]) -> io::Result<()> { let mut filled = 0;
while filled < buf.len() {
let ret = unsafe {
libc::getrandom(
buf[filled..].as_mut_ptr() as *mut libc::c_void,
buf.len() - filled,
0,
)
};
if ret < 0 {
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::Interrupted {
continue;
}
return Err(err);
}
filled += ret as usize;
}
Ok(())
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
pub fn getrandom_buffer(m: &mut [u8]) -> io::Result<()> {
let mut file = File::open("/dev/urandom")?;
file.read_exact(m)?;
Ok(())
}
pub fn get_bound_random_buffer(buffer: &mut [u32], max: u32) { for item in buffer.iter_mut() {
*item = bounded(max);
}
}
pub fn math_zrand_int(upper: Option<i64>, lower: Option<i64>, inclusive: bool) -> Result<i64, String> { let lower = lower.unwrap_or(0);
let upper = upper.unwrap_or(u32::MAX as i64);
if lower < 0 || lower > u32::MAX as i64 {
return Err(format!(
"Lower bound ({}) out of range: 0-4294967295",
lower
));
}
if upper < lower {
return Err(format!(
"Upper bound ({}) must be greater than Lower Bound ({})",
upper, lower
));
}
if upper < 0 || upper > u32::MAX as i64 {
return Err(format!(
"Upper bound ({}) out of range: 0-4294967295",
upper
));
}
let incl = if inclusive { 1 } else { 0 };
let diff = (upper - lower + incl) as u32;
if diff == 0 {
return Ok(upper);
}
let r = bounded(diff);
Ok(r as i64 + lower)
}
pub fn math_zrand_float() -> f64 { random_real()
}
pub use crate::ported::modules::random_real::random_real;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_random_state() {
let r1 = get_srandom();
let r2 = get_srandom();
let r3 = get_srandom();
assert!(r1 != r2 || r2 != r3);
}
#[test]
fn test_get_random_u32() {
let r1 = random_u32();
let r2 = random_u32();
let r3 = random_u32();
assert!(r1 != r2 || r2 != r3);
}
#[test]
fn test_get_random_u64() {
let r1 = random_u64();
let r2 = random_u64();
assert_ne!(r1, r2);
}
#[test]
fn test_bounded_random() {
for _ in 0..100 {
let r = bounded(10);
assert!(r < 10);
}
}
#[test]
fn test_bounded_random_one() {
for _ in 0..10 {
let r = bounded(1);
assert_eq!(r, 0);
}
}
#[test]
fn test_zrand_int() {
let r = math_zrand_int(Some(100), Some(50), false).unwrap();
assert!((50..100).contains(&r));
let r = math_zrand_int(Some(100), Some(50), true).unwrap();
assert!((50..=100).contains(&r));
}
#[test]
fn test_zrand_int_no_args() {
let r = math_zrand_int(None, None, false).unwrap();
assert!(r >= 0);
}
#[test]
fn test_zrand_int_errors() {
assert!(math_zrand_int(Some(50), Some(100), false).is_err());
assert!(math_zrand_int(Some(-1), None, false).is_err());
}
#[test]
fn test_zrand_float() {
for _ in 0..100 {
let r = math_zrand_float();
assert!((0.0..1.0).contains(&r));
}
}
#[test]
fn test_random_real() {
for _ in 0..100 {
let r = random_real();
assert!((0.0..1.0).contains(&r));
}
}
#[test]
fn test_shuffle() {
let mut arr = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let original = arr.clone();
let n = arr.len();
for i in (1..n).rev() {
let j = bounded((i + 1) as u32) as usize;
arr.swap(i, j);
}
arr.sort();
assert_eq!(arr, original.to_vec());
}
#[test]
fn test_fill_random_bytes() {
let mut buf = [0u8; 32];
getrandom_buffer(&mut buf).unwrap();
assert!(!buf.iter().all(|&b| b == 0));
}
}
use crate::ported::zsh_h::module;
pub static RANDFD: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1);
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 { match metadata("/dev/urandom") { Ok(md) => {
if !md.file_type().is_char_device() { crate::ported::utils::zwarn(
"Error getting kernel random pool: not a char device");
return 1;
}
}
Err(e) => {
crate::ported::utils::zwarn(
&format!("Error getting kernel random pool: {}", e));
return 1;
}
}
0 }
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 { *features = featuresarray(m, module_features());
0
}
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 { handlefeatures(m, module_features(), enables)
}
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 { match std::fs::OpenOptions::new().read(true).open("/dev/urandom") { Ok(f) => {
let fd = f.into_raw_fd(); RANDFD.store(fd, Ordering::SeqCst);
0
}
Err(e) => {
crate::ported::utils::zwarn(
&format!("Could not access kernel random pool: {}", e));
1 }
}
}
pub fn cleanup_(m: *const module) -> i32 { setfeatureenables(m, module_features(), None)
}
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 { let fd = RANDFD.swap(-1, Ordering::SeqCst);
if fd >= 0 {
unsafe { libc::close(fd) }; }
0
}
use crate::ported::zsh_h::features as features_t;
use std::sync::{Mutex, OnceLock};
static MODULE_FEATURES: OnceLock<Mutex<features_t>> = OnceLock::new();
fn module_features() -> &'static Mutex<features_t> {
MODULE_FEATURES.get_or_init(|| Mutex::new(features_t {
bn_list: None,
bn_size: 0,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 2,
pd_list: None,
pd_size: 1,
n_abstract: 0,
}))
}
fn featuresarray(_m: *const module, _f: &Mutex<features_t>) -> Vec<String> {
vec!["f:zrand_float".to_string(), "f:zrand_int".to_string(), "p:SRANDOM".to_string()]
}
fn handlefeatures(
_m: *const module,
_f: &Mutex<features_t>,
enables: &mut Option<Vec<i32>>,
) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 3]);
}
0
}
fn setfeatureenables(
_m: *const module,
_f: &Mutex<features_t>,
_e: Option<&[i32]>,
) -> i32 {
0
}