Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alternate rand, rand_r and srand implementation #32

Merged
merged 11 commits into from
Nov 22, 2024
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ all = [
"itoa",
"memchr",
"qsort",
"rand_r",
"rand",
"snprintf",
"strcat",
"strchr",
Expand Down Expand Up @@ -59,6 +61,9 @@ isupper = []
itoa = []
memchr = []
qsort = []
rand_r = []
rand = ["rand_r", "dep:portable-atomic"]
rand_max_i16 = []
signal = ["dep:portable-atomic"]
signal-cs = ["portable-atomic/critical-section"]
snprintf = []
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ This crate basically came about so that the [nrfxlib](https://github.com/NordicP
* snprintf
* vsnprintf
* qsort
* rand
* alloc (optional)
* malloc
* calloc
Expand All @@ -49,6 +50,7 @@ This crate basically came about so that the [nrfxlib](https://github.com/NordicP

* itoa
* utoa
* rand_r

## To Do

Expand Down
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ mod abs;
#[cfg(feature = "abs")]
pub use self::abs::abs;

mod rand_r;
#[cfg(feature = "rand_r")]
pub use self::rand_r::{rand_r, RAND_MAX};
#[cfg(feature = "rand")]
mod rand;
#[cfg(feature = "rand")]
pub use self::rand::{rand, srand};

mod strcmp;
#[cfg(feature = "strcmp")]
pub use self::strcmp::strcmp;
Expand Down
56 changes: 56 additions & 0 deletions src/rand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Rust implementation of C library functions `rand` and `srand`
//!
//! Licensed under the Blue Oak Model Licence 1.0.0
use core::{
ffi::{c_int, c_uint},
sync::atomic::Ordering,
};

use portable_atomic::AtomicU32;

static RAND_STATE: AtomicU32 = AtomicU32::new(1);

/// Rust implementation of C library function `srand`
#[cfg_attr(feature = "rand", no_mangle)]
pub extern "C" fn srand(seed: c_uint) {
// we do this cast to support platforms where c_uint is u16.
// but it complains on platforms where c_uint is u32.
#[allow(clippy::unnecessary_cast)]
RAND_STATE.store(seed as u32, Ordering::Relaxed);
}

/// Rust implementation of C library function `rand`.
#[cfg_attr(feature = "rand", no_mangle)]
pub extern "C" fn rand() -> c_int {
let mut state = RAND_STATE.load(Ordering::Relaxed) as c_uint;
let result = unsafe { crate::rand_r(&mut state) };
RAND_STATE.store(state as u32, Ordering::Relaxed);
result
}

#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_rand() {
if c_int::MAX == 32767 || cfg!(feature = "rand_max_i16") {
srand(1);
assert_eq!(rand(), 16838);
assert_eq!(rand(), 5758);
assert_eq!(rand(), 10113);
srand(5);
assert_eq!(rand(), 18655);
assert_eq!(rand(), 8457);
assert_eq!(rand(), 10616);
} else {
srand(1);
assert_eq!(rand(), 476707713);
assert_eq!(rand(), 1186278907);
assert_eq!(rand(), 505671508);
srand(5);
assert_eq!(rand(), 234104184);
assert_eq!(rand(), 1214203244);
assert_eq!(rand(), 1803669308);
}
}
}
68 changes: 68 additions & 0 deletions src/rand_r.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//! Rust implementation of C library function `rand_r`
//!
//! Licensed under the Blue Oak Model Licence 1.0.0
use core::ffi::{c_int, c_uint};

#[cfg_attr(not(feature = "rand_r"), export_name = "tinyrlibc_RAND_MAX")]
#[cfg_attr(feature = "rand_r", no_mangle)]
pub static RAND_MAX: c_int = c_int::MAX;

/// Rust implementation of C library function `rand_r`
///
/// Passing NULL (core::ptr::null()) gives undefined behaviour.
#[cfg_attr(not(feature = "rand_r"), export_name = "tinyrlibc_rand_r")]
#[cfg_attr(feature = "rand_r", no_mangle)]
pub unsafe extern "C" fn rand_r(seedp: *mut c_uint) -> c_int {
let mut result: c_int;

fn pump(input: u32) -> u32 {
// This algorithm is mentioned in the ISO C standard
input.wrapping_mul(1103515245).wrapping_add(12345)
}

fn select_top(state: u32, bits: usize) -> c_int {
// ignore the lower 16 bits, as they are low quality
((state >> 16) & ((1 << bits) - 1)) as c_int
}

let mut next = *seedp as u32;

Check warning on line 28 in src/rand_r.rs

View workflow job for this annotation

GitHub Actions / clippy

casting to the same type is unnecessary (`u32` -> `u32`)

warning: casting to the same type is unnecessary (`u32` -> `u32`) --> src/rand_r.rs:28:17 | 28 | let mut next = *seedp as u32; | ^^^^^^^^^^^^^ help: try: `*seedp` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast = note: `#[warn(clippy::unnecessary_cast)]` on by default
if c_int::MAX == 32767 || cfg!(feature = "rand_max_i16") {
// pull 15 bits in one go
next = pump(next);
result = select_top(next, 15);
} else {
// pull 31 bits in three goes
next = pump(next);
result = select_top(next, 11) << 20;
next = pump(next);
result |= select_top(next, 10) << 10;
next = pump(next);
result |= select_top(next, 10);
}
*seedp = next as c_uint;

result as c_int
}

#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_rand_r() {
if c_int::MAX == 32767 || cfg!(feature = "rand_max_i16") {
unsafe {
let mut seed = 5;
assert_eq!(rand_r(&mut seed), 18655);
assert_eq!(rand_r(&mut seed), 8457);
assert_eq!(rand_r(&mut seed), 10616);
}
} else {
unsafe {
let mut seed = 5;
assert_eq!(rand_r(&mut seed), 234104184);
assert_eq!(rand_r(&mut seed), 1214203244);
assert_eq!(rand_r(&mut seed), 1803669308);
}
}
}
}
Loading