Skip to content

Commit 15665a5

Browse files
Merge pull request #32 from rust-embedded-community/updated-rand
Alternate rand, rand_r and srand implementation
2 parents 4f6da13 + e069a28 commit 15665a5

File tree

5 files changed

+139
-0
lines changed

5 files changed

+139
-0
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ all = [
2929
"itoa",
3030
"memchr",
3131
"qsort",
32+
"rand_r",
33+
"rand",
3234
"snprintf",
3335
"strcat",
3436
"strchr",
@@ -59,6 +61,9 @@ isupper = []
5961
itoa = []
6062
memchr = []
6163
qsort = []
64+
rand_r = []
65+
rand = ["rand_r", "dep:portable-atomic"]
66+
rand_max_i16 = []
6267
signal = ["dep:portable-atomic"]
6368
signal-cs = ["portable-atomic/critical-section"]
6469
snprintf = []

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ This crate basically came about so that the [nrfxlib](https://github.com/NordicP
3535
* snprintf
3636
* vsnprintf
3737
* qsort
38+
* rand
3839
* alloc (optional)
3940
* malloc
4041
* calloc
@@ -49,6 +50,7 @@ This crate basically came about so that the [nrfxlib](https://github.com/NordicP
4950

5051
* itoa
5152
* utoa
53+
* rand_r
5254

5355
## To Do
5456

src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ mod abs;
2626
#[cfg(feature = "abs")]
2727
pub use self::abs::abs;
2828

29+
mod rand_r;
30+
#[cfg(feature = "rand_r")]
31+
pub use self::rand_r::{rand_r, RAND_MAX};
32+
#[cfg(feature = "rand")]
33+
mod rand;
34+
#[cfg(feature = "rand")]
35+
pub use self::rand::{rand, srand};
36+
2937
mod strcmp;
3038
#[cfg(feature = "strcmp")]
3139
pub use self::strcmp::strcmp;

src/rand.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//! Rust implementation of C library functions `rand` and `srand`
2+
//!
3+
//! Licensed under the Blue Oak Model Licence 1.0.0
4+
use core::{
5+
ffi::{c_int, c_uint},
6+
sync::atomic::Ordering,
7+
};
8+
9+
use portable_atomic::AtomicU32;
10+
11+
static RAND_STATE: AtomicU32 = AtomicU32::new(1);
12+
13+
/// Rust implementation of C library function `srand`
14+
#[cfg_attr(feature = "rand", no_mangle)]
15+
pub extern "C" fn srand(seed: c_uint) {
16+
// we do this cast to support platforms where c_uint is u16.
17+
// but it complains on platforms where c_uint is u32.
18+
#[allow(clippy::unnecessary_cast)]
19+
RAND_STATE.store(seed as u32, Ordering::Relaxed);
20+
}
21+
22+
/// Rust implementation of C library function `rand`.
23+
#[cfg_attr(feature = "rand", no_mangle)]
24+
pub extern "C" fn rand() -> c_int {
25+
let mut state = RAND_STATE.load(Ordering::Relaxed) as c_uint;
26+
let result = unsafe { crate::rand_r(&mut state) };
27+
RAND_STATE.store(state as u32, Ordering::Relaxed);
28+
result
29+
}
30+
31+
#[cfg(test)]
32+
mod test {
33+
use super::*;
34+
#[test]
35+
fn test_rand() {
36+
if c_int::MAX == 32767 || cfg!(feature = "rand_max_i16") {
37+
srand(1);
38+
assert_eq!(rand(), 16838);
39+
assert_eq!(rand(), 5758);
40+
assert_eq!(rand(), 10113);
41+
srand(5);
42+
assert_eq!(rand(), 18655);
43+
assert_eq!(rand(), 8457);
44+
assert_eq!(rand(), 10616);
45+
} else {
46+
srand(1);
47+
assert_eq!(rand(), 476707713);
48+
assert_eq!(rand(), 1186278907);
49+
assert_eq!(rand(), 505671508);
50+
srand(5);
51+
assert_eq!(rand(), 234104184);
52+
assert_eq!(rand(), 1214203244);
53+
assert_eq!(rand(), 1803669308);
54+
}
55+
}
56+
}

src/rand_r.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//! Rust implementation of C library function `rand_r`
2+
//!
3+
//! Licensed under the Blue Oak Model Licence 1.0.0
4+
use core::ffi::{c_int, c_uint};
5+
6+
#[cfg_attr(not(feature = "rand_r"), export_name = "tinyrlibc_RAND_MAX")]
7+
#[cfg_attr(feature = "rand_r", no_mangle)]
8+
pub static RAND_MAX: c_int = c_int::MAX;
9+
10+
/// Rust implementation of C library function `rand_r`
11+
///
12+
/// Passing NULL (core::ptr::null()) gives undefined behaviour.
13+
#[cfg_attr(not(feature = "rand_r"), export_name = "tinyrlibc_rand_r")]
14+
#[cfg_attr(feature = "rand_r", no_mangle)]
15+
pub unsafe extern "C" fn rand_r(seedp: *mut c_uint) -> c_int {
16+
let mut result: c_int;
17+
18+
fn pump(input: u32) -> u32 {
19+
// This algorithm is mentioned in the ISO C standard
20+
input.wrapping_mul(1103515245).wrapping_add(12345)
21+
}
22+
23+
fn select_top(state: u32, bits: usize) -> c_int {
24+
// ignore the lower 16 bits, as they are low quality
25+
((state >> 16) & ((1 << bits) - 1)) as c_int
26+
}
27+
28+
let mut next = *seedp as u32;
29+
if c_int::MAX == 32767 || cfg!(feature = "rand_max_i16") {
30+
// pull 15 bits in one go
31+
next = pump(next);
32+
result = select_top(next, 15);
33+
} else {
34+
// pull 31 bits in three goes
35+
next = pump(next);
36+
result = select_top(next, 11) << 20;
37+
next = pump(next);
38+
result |= select_top(next, 10) << 10;
39+
next = pump(next);
40+
result |= select_top(next, 10);
41+
}
42+
*seedp = next as c_uint;
43+
44+
result as c_int
45+
}
46+
47+
#[cfg(test)]
48+
mod test {
49+
use super::*;
50+
#[test]
51+
fn test_rand_r() {
52+
if c_int::MAX == 32767 || cfg!(feature = "rand_max_i16") {
53+
unsafe {
54+
let mut seed = 5;
55+
assert_eq!(rand_r(&mut seed), 18655);
56+
assert_eq!(rand_r(&mut seed), 8457);
57+
assert_eq!(rand_r(&mut seed), 10616);
58+
}
59+
} else {
60+
unsafe {
61+
let mut seed = 5;
62+
assert_eq!(rand_r(&mut seed), 234104184);
63+
assert_eq!(rand_r(&mut seed), 1214203244);
64+
assert_eq!(rand_r(&mut seed), 1803669308);
65+
}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)