use super::use_file;
use crate::Error;
use core::{
ffi::c_void,
mem::{MaybeUninit, transmute},
ptr::{self, NonNull},
};
use use_file::utils;
pub use crate::util::{inner_u32, inner_u64};
type GetRandomFn = unsafe extern "C" fn(*mut c_void, libc::size_t, libc::c_uint) -> libc::ssize_t;
const NOT_AVAILABLE: NonNull<c_void> = unsafe { NonNull::new_unchecked(usize::MAX as *mut c_void) };
#[cold]
#[inline(never)]
fn init() -> NonNull<c_void> {
#[cfg(not(target_env = "musl"))]
let raw_ptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"getrandom".as_ptr()) };
#[cfg(target_env = "musl")]
let raw_ptr = {
let fptr: GetRandomFn = libc::getrandom;
unsafe { transmute::<GetRandomFn, *mut c_void>(fptr) }
};
let res_ptr = match NonNull::new(raw_ptr) {
Some(fptr) => {
let getrandom_fn = unsafe { transmute::<*mut c_void, GetRandomFn>(fptr.as_ptr()) };
let res = unsafe { getrandom_fn(ptr::dangling_mut(), 0, 0) };
if cfg!(getrandom_test_linux_fallback) {
NOT_AVAILABLE
} else if res.is_negative() {
match utils::get_errno() {
libc::ENOSYS => NOT_AVAILABLE, #[cfg(target_os = "linux")]
libc::EPERM => NOT_AVAILABLE, _ => fptr,
}
} else {
fptr
}
}
None => NOT_AVAILABLE,
};
#[cfg(getrandom_test_linux_without_fallback)]
if res_ptr == NOT_AVAILABLE {
panic!("Fallback is triggered with enabled `getrandom_test_linux_without_fallback`")
}
res_ptr
}
#[inline(never)]
fn use_file_fallback(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
use_file::fill_inner(dest)
}
#[inline]
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
#[path = "../utils/lazy_ptr.rs"]
mod lazy;
static GETRANDOM_FN: lazy::LazyPtr<c_void> = lazy::LazyPtr::new();
let fptr = GETRANDOM_FN.unsync_init(init);
if fptr == NOT_AVAILABLE {
use_file_fallback(dest)
} else {
let getrandom_fn = unsafe { transmute::<*mut c_void, GetRandomFn>(fptr.as_ptr()) };
utils::sys_fill_exact(dest, |buf| unsafe {
getrandom_fn(buf.as_mut_ptr().cast(), buf.len(), 0)
})
}
}