Merge branch 'random' into 'master'
Implement random() and friends See merge request redox-os/relibc!284
This commit is contained in:
+74
-12
@@ -25,6 +25,7 @@ use crate::{
|
||||
};
|
||||
|
||||
mod lcg48;
|
||||
mod random;
|
||||
mod sort;
|
||||
|
||||
pub const EXIT_FAILURE: c_int = 1;
|
||||
@@ -369,9 +370,31 @@ pub extern "C" fn grantpt(fildes: c_int) -> c_int {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
// #[no_mangle]
|
||||
pub extern "C" fn initstate(seec: c_uint, state: *mut c_char, size: size_t) -> *mut c_char {
|
||||
unimplemented!();
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn initstate(seed: c_uint, state: *mut c_char, size: size_t) -> *mut c_char {
|
||||
// Ported from musl
|
||||
|
||||
if size < 8 {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
// TODO: lock?
|
||||
let old_state = random::save_state();
|
||||
random::N = match size {
|
||||
0..=7 => unreachable!(), // ensured above
|
||||
8..=31 => 0,
|
||||
32..=63 => 7,
|
||||
64..=127 => 15,
|
||||
128..=255 => 31,
|
||||
_ => 63,
|
||||
};
|
||||
|
||||
random::X_PTR = (state as *mut u32).offset(1);
|
||||
random::seed(seed);
|
||||
random::save_state();
|
||||
// TODO: unlock?
|
||||
|
||||
old_state as _
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -731,9 +754,37 @@ pub unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int {
|
||||
}
|
||||
}
|
||||
|
||||
// #[no_mangle]
|
||||
pub extern "C" fn random() -> c_long {
|
||||
unimplemented!();
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn random() -> c_long {
|
||||
// Ported from musl
|
||||
|
||||
let k: u32;
|
||||
// TODO: lock?
|
||||
random::ensure_x_ptr_init();
|
||||
|
||||
if random::N == 0 {
|
||||
*random::X_PTR = random::lcg31_step(*random::X_PTR);
|
||||
k = *random::X_PTR;
|
||||
} else {
|
||||
*random::X_PTR.add(usize::from(random::I)) += *random::X_PTR.add(usize::from(random::J));
|
||||
|
||||
k = *random::X_PTR.add(usize::from(random::I)) >> 1;
|
||||
|
||||
random::I += 1;
|
||||
if random::I == random::N {
|
||||
random::I = 0;
|
||||
}
|
||||
|
||||
random::J += 1;
|
||||
if random::J == random::N {
|
||||
random::J = 0;
|
||||
}
|
||||
}
|
||||
// TODO: unlock?
|
||||
|
||||
/* Both branches of this function result in a "u31", which will
|
||||
* always fit in a c_long. */
|
||||
c_long::try_from(k).unwrap()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -858,9 +909,16 @@ pub extern "C" fn setkey(key: *const c_char) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
// #[no_mangle]
|
||||
pub extern "C" fn setstate(state: *const c_char) -> *mut c_char {
|
||||
unimplemented!();
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn setstate(state: *mut c_char) -> *mut c_char {
|
||||
/* Ported from musl. The state parameter is no longer const in newer
|
||||
* versions of POSIX. */
|
||||
|
||||
// TODO: lock?
|
||||
let old_state = random::save_state();
|
||||
random::load_state(state as *mut u32);
|
||||
// TODO: unlock?
|
||||
old_state as _
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -879,9 +937,13 @@ pub unsafe extern "C" fn srand48(seedval: c_long) {
|
||||
lcg48::DEFAULT_XSUBI = lcg48::ushort_arr3_from_u48(xsubi_value);
|
||||
}
|
||||
|
||||
// #[no_mangle]
|
||||
pub extern "C" fn srandom(seed: c_uint) {
|
||||
unimplemented!();
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn srandom(seed: c_uint) {
|
||||
// Ported from musl
|
||||
|
||||
// TODO: lock?
|
||||
random::seed(seed);
|
||||
// TODO: unlock?
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
//! Helper functions for random() and friends, see https://pubs.opengroup.org/onlinepubs/7908799/xsh/initstate.html
|
||||
/* Ported from musl's implementation (src/prng/random.c). Does not
|
||||
* currently implement locking, though. */
|
||||
|
||||
use crate::platform::types::*;
|
||||
use core::{convert::TryFrom, ptr};
|
||||
|
||||
#[rustfmt::skip]
|
||||
static mut X_INIT: [u32; 32] = [
|
||||
0x00000000, 0x5851f42d, 0xc0b18ccf, 0xcbb5f646,
|
||||
0xc7033129, 0x30705b04, 0x20fd5db4, 0x9a8b7f78,
|
||||
0x502959d8, 0xab894868, 0x6c0356a7, 0x88cdb7ff,
|
||||
0xb477d43f, 0x70a3a52b, 0xa8e4baf1, 0xfd8341fc,
|
||||
0x8ae16fd9, 0x742d2f7a, 0x0d1f0796, 0x76035e09,
|
||||
0x40f7702c, 0x6fa72ca5, 0xaaa84157, 0x58a0df74,
|
||||
0xc74a0364, 0xae533cc4, 0x04185faf, 0x6de3b115,
|
||||
0x0cab8628, 0xf043bfa4, 0x398150e9, 0x37521657,
|
||||
];
|
||||
|
||||
/* N needs to accommodate values up to 63, corresponding to the maximum
|
||||
* state array size of 256 bytes. I and J must be able to accommodate
|
||||
* values less than or equal to N. */
|
||||
pub static mut N: u8 = 31;
|
||||
pub static mut I: u8 = 3;
|
||||
pub static mut J: u8 = 0;
|
||||
|
||||
/* Unlike in C, we can't take the address of the initializing array
|
||||
* outside of a function. */
|
||||
pub static mut X_PTR: *mut u32 = ptr::null_mut();
|
||||
|
||||
// To be called in any function that may read from X_PTR
|
||||
pub unsafe fn ensure_x_ptr_init() {
|
||||
if X_PTR.is_null() {
|
||||
X_PTR = &mut X_INIT[1];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lcg31_step(x: u32) -> u32 {
|
||||
1103515245_u32.wrapping_mul(x).wrapping_add(12345_u32) & 0x7fffffff
|
||||
}
|
||||
|
||||
pub fn lcg64_step(x: u64) -> u64 {
|
||||
6364136223846793005_u64.wrapping_mul(x).wrapping_add(1_u64)
|
||||
}
|
||||
|
||||
pub unsafe fn save_state() -> *mut u32 {
|
||||
ensure_x_ptr_init();
|
||||
*X_PTR.offset(-1) = (u32::from(N) << 16) | (u32::from(I) << 8) | u32::from(J);
|
||||
X_PTR.offset(-1)
|
||||
}
|
||||
|
||||
pub unsafe fn load_state(state_ptr: *mut u32) {
|
||||
let prev_x = *state_ptr;
|
||||
X_PTR = state_ptr.offset(1);
|
||||
|
||||
/* This calculation of N does not have a bit mask in the musl
|
||||
* original, in principle resulting in a u16, but obtaining a value
|
||||
* larger than 63 can probably be dismissed as pathological. */
|
||||
N = u8::try_from((prev_x >> 16) & 0xff).unwrap();
|
||||
|
||||
// I and J calculations are straight from musl
|
||||
I = u8::try_from((prev_x >> 8) & 0xff).unwrap();
|
||||
J = u8::try_from(prev_x & 0xff).unwrap();
|
||||
}
|
||||
|
||||
pub unsafe fn seed(seed: c_uint) {
|
||||
ensure_x_ptr_init();
|
||||
|
||||
let mut s = seed as u64;
|
||||
|
||||
if N == 0 {
|
||||
*X_PTR = s as u32;
|
||||
} else {
|
||||
I = if N == 31 || N == 7 { 3 } else { 1 };
|
||||
|
||||
J = 0;
|
||||
|
||||
for k in 0..usize::from(N) {
|
||||
s = lcg64_step(s);
|
||||
|
||||
// Conversion will always succeed (value is a 32-bit right-
|
||||
// shift of a 64-bit integer).
|
||||
*X_PTR.add(k) = u32::try_from(s >> 32).unwrap();
|
||||
}
|
||||
|
||||
// ensure X contains at least one odd number
|
||||
*X_PTR |= 1;
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ EXPECT_NAMES=\
|
||||
stdlib/lcg48 \
|
||||
stdlib/mkostemps \
|
||||
stdlib/rand \
|
||||
stdlib/random \
|
||||
stdlib/strtod \
|
||||
stdlib/strtol \
|
||||
stdlib/strtoul \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,100 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "test_helpers.h"
|
||||
|
||||
/* The output of these tests are checked against that from musl. Other
|
||||
* algorithms may yield different results and still comply with the
|
||||
* POSIX requirements. */
|
||||
|
||||
int main(void) {
|
||||
/* Should be enough to exercise the rollover branching in random()
|
||||
* for all possible state array sizes (up to 256 bytes, i.e. 64
|
||||
* 32-bit values). */
|
||||
size_t test_seq_len = 70;
|
||||
|
||||
long random_result;
|
||||
|
||||
// Should give same result as with seed 1
|
||||
puts("Uninitialized:");
|
||||
for (size_t i = 0; i < test_seq_len; i++) {
|
||||
random_result = random();
|
||||
printf("%ld\n", random_result);
|
||||
}
|
||||
|
||||
puts("\nSeed 1:");
|
||||
srandom(1);
|
||||
for (size_t i = 0; i < test_seq_len; i++) {
|
||||
random_result = random();
|
||||
printf("%ld\n", random_result);
|
||||
}
|
||||
|
||||
puts("\nSeed 1337:");
|
||||
srandom(1337);
|
||||
for (size_t i = 0; i < test_seq_len; i++) {
|
||||
random_result = random();
|
||||
printf("%ld\n", random_result);
|
||||
}
|
||||
|
||||
/* 256 bytes (as below) is the largest possible amount of state
|
||||
* data. Created as a uint32_t to avoid possible alignment issues
|
||||
* with char. */
|
||||
uint32_t new_state[64] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
unsigned int seed = 42;
|
||||
|
||||
// Should exercise the different branches in initstate()
|
||||
size_t sizes[] = {8, 31, 32, 63, 64, 127, 128, 255, 256};
|
||||
|
||||
for (size_t j = 0; j < sizeof(sizes)/sizeof(size_t); j++) {
|
||||
size_t size = sizes[j];
|
||||
printf("\nSeed %d, size %ld:\n", seed, size);
|
||||
initstate(seed, (char *)new_state, size);
|
||||
|
||||
for (size_t i = 0; i < test_seq_len; i++) {
|
||||
random_result = random();
|
||||
printf("%ld\n", random_result);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test that setstate() allows the use of a different state array,
|
||||
* and that it correctly returns the old value. */
|
||||
uint32_t other_new_state[64] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
initstate(seed, (char *)other_new_state, 32);
|
||||
printf("\nSeed %d, other state array:\n", seed);
|
||||
for (size_t i = 0; i < test_seq_len; i++) {
|
||||
random_result = random();
|
||||
printf("%ld\n", random_result);
|
||||
}
|
||||
|
||||
char *should_be_other_new_state_ptr = setstate((char *)new_state);
|
||||
if (should_be_other_new_state_ptr == (char *)other_new_state) {
|
||||
puts("\nState data pointer restored correctly by setstate().");
|
||||
}
|
||||
else {
|
||||
puts("\nState data pointer NOT restored correctly by setstate().");
|
||||
}
|
||||
printf("\nSeed %d, back to first state array:\n", seed);
|
||||
for (size_t i = 0; i < test_seq_len; i++) {
|
||||
random_result = random();
|
||||
printf("%ld\n", random_result);
|
||||
}
|
||||
|
||||
// Should yield NULL
|
||||
char *state_with_size_less_than_8 = initstate(seed, (char *)new_state, 7);
|
||||
printf("\nPointer returned by initstate with size < 8: %p\n",
|
||||
state_with_size_less_than_8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user