Browse Source

stdlib: implement rand_r() using XorShiftRng

Alex Lyon 6 years ago
parent
commit
13a10ce7af
3 changed files with 55 additions and 3 deletions
  1. 15 3
      src/header/stdlib/mod.rs
  2. 4 0
      tests/expected/stdlib/rand.stdout
  3. 36 0
      tests/stdlib/rand.c

+ 15 - 3
src/header/stdlib/mod.rs

@@ -628,9 +628,21 @@ pub unsafe extern "C" fn rand() -> c_int {
     }
 }
 
-// #[no_mangle]
-pub extern "C" fn rand_r(seed: *mut c_uint) -> c_int {
-    unimplemented!();
+#[no_mangle]
+pub unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int {
+    if seed.is_null() {
+        errno::EINVAL
+    } else {
+        // set the type explicitly so this will fail if the array size for XorShiftRng changes
+        let seed_arr: [u8; 16] = mem::transmute([*seed; 16 / mem::size_of::<c_uint>()]);
+
+        let mut rng = XorShiftRng::from_seed(seed_arr);
+        let ret = rng.gen_range(0, RAND_MAX);
+
+        *seed = ret as _;
+
+        ret
+    }
 }
 
 // #[no_mangle]

+ 4 - 0
tests/expected/stdlib/rand.stdout

@@ -2,3 +2,7 @@
 201425341
 201425341
 67141780
+264204
+271585843
+264204
+271585843

+ 36 - 0
tests/stdlib/rand.c

@@ -31,4 +31,40 @@ int main(void) {
         puts("srand(1) doesn't work");
         exit(EXIT_FAILURE);
     }
+
+    // Ensure rand_r() fails with NULL input
+    if (rand_r(NULL) != EINVAL) {
+        puts("rand_r(NULL) doesn't return EINVAL");
+        exit(EXIT_FAILURE);
+    }
+
+    // Ensure rand_r() produces unique values
+    int seed = 259;
+    int rand_r_seed259_1 = rand_r((unsigned *)&seed);
+    printf("%d\n", rand_r_seed259_1);
+
+    int rand_r_seed259_2 = rand_r((unsigned *)&seed);
+    printf("%d\n", rand_r_seed259_2);
+
+    if (rand_r_seed259_1 == rand_r_seed259_2) {
+        puts("rand_r() fails to produce unique values");
+        exit(EXIT_FAILURE);
+    }
+
+    // Ensure rand_r() returns reproducible values
+    seed = 259;
+    int rand_r_seed259_1_2 = rand_r((unsigned *)&seed);
+    printf("%d\n", rand_r_seed259_1_2);
+
+    int rand_r_seed259_2_2 = rand_r((unsigned *)&seed);
+    printf("%d\n", rand_r_seed259_2_2);
+
+    if (rand_r_seed259_1 != rand_r_seed259_1_2
+        || rand_r_seed259_2 != rand_r_seed259_2_2)
+    {
+        puts("rand_r() is not reproducible");
+	exit(EXIT_FAILURE);
+    }
+
+    return 0;
 }