Skip to content

Commit

Permalink
Implement rand and srand
Browse files Browse the repository at this point in the history
  • Loading branch information
Sympatron committed Oct 23, 2024
1 parent 9173e9d commit 3a84efe
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ readme = "README.md"
repository = "https://github.com/rust-embedded-community/tinyrlibc"

[dependencies]
critical-section = { version = "1.2.0", optional = true }
portable-atomic = { version = "1.6.0", optional = true }

[dev-dependencies]
static-alloc = "0.2.4"
critical-section = { version = "1.2.0", features = ["std"] }

[build-dependencies]
cc = "1.0"
Expand Down Expand Up @@ -61,6 +63,7 @@ itoa = []
memchr = []
qsort = []
rand_r = []
rand = ["dep:critical-section"]
signal = ["dep:portable-atomic"]
signal-cs = ["portable-atomic/critical-section"]
snprintf = []
Expand Down
1 change: 1 addition & 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 Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ pub use self::abs::abs;
mod rand_r;
#[cfg(feature = "rand_r")]
pub use self::rand_r::rand_r;
#[cfg(feature = "rand")]
mod rand;
#[cfg(feature = "rand")]
pub use self::rand::{rand, srand};

mod strcmp;
#[cfg(feature = "strcmp")]
Expand Down
95 changes: 95 additions & 0 deletions src/rand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//! 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};

struct GnuRand {
r: [u32; 344],
n: usize,
}

impl GnuRand {
pub fn new(mut seed: u32) -> GnuRand {
let mut r = [0u32; 344];

if seed == 0 {
// Make sure seed is not 0
seed = 1;
}

r[0] = seed;
for i in 1..31 {
// This does:
// state[i] = (16807 * state[i - 1]) % 2147483647;
// but avoids overflowing 31 bits.
let hi = (r[i - 1] / 127773) as i32;
let lo = (r[i - 1] % 127773) as i32;
let mut word = 16807 * lo - 2836 * hi;
if word < 0 {
word += i32::MAX;
}
r[i] = word as u32;
}
for i in 31..34 {
r[i] = r[i - 31];
}
for i in 34..344 {
r[i] = r[i - 31].wrapping_add(r[i - 3]);
}

GnuRand { r, n: 0 }
}

pub fn next(&mut self) -> i32 {
let x = self.r[(self.n + 313) % 344].wrapping_add(self.r[(self.n + 341) % 344]);
self.r[self.n % 344] = x;
self.n = (self.n + 1) % 344;
(x >> 1) as i32
}
}

static mut RAND: Option<GnuRand> = None;

/// Rust implementation of C library function `srand`
///
/// Relies on [`critical-section`](https://docs.rs/critical-section/1.2.0/critical_section/) for thread-safety
#[cfg_attr(feature = "rand", no_mangle)]
pub extern "C" fn srand(seed: c_uint) {
let rnd = GnuRand::new(seed);
critical_section::with(|_| unsafe { RAND = Some(rnd) });
}

/// Rust implementation of C library function `rand`
///
/// Relies on [`critical-section`](https://docs.rs/critical-section/1.2.0/critical_section/) for thread-safety
#[cfg_attr(feature = "rand", no_mangle)]
pub extern "C" fn rand() -> c_int {
critical_section::with(|_| {
let rnd = unsafe { RAND.get_or_insert_with(|| GnuRand::new(1)) };
rnd.next()
})
}

#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_rand() {
unsafe {
// Values taken from glibc implementation
assert_eq!(rand(), 1804289383);
assert_eq!(rand(), 846930886);
assert_eq!(rand(), 1681692777);
}
}
#[test]
fn test_srand() {
unsafe {
srand(5);
// Values taken from glibc implementation
assert_eq!(rand(), 590011675);
assert_eq!(rand(), 99788765);
assert_eq!(rand(), 2131925610);
}
}
}

0 comments on commit 3a84efe

Please sign in to comment.