add a seeded random number generator so that sequences of random numbers can be easily reproduced (for https://github.com/mozilla/rust/issues/2379)

This commit is contained in:
Gareth Daniel Smith 2012-05-20 14:06:54 +01:00 committed by Brian Anderson
parent 64130f1589
commit c9f8ae02bc
5 changed files with 117 additions and 51 deletions

View File

@ -1,12 +1,14 @@
#[doc = "Random number generation"]; #[doc = "Random number generation"];
export rng, weighted, extensions; export rng, seed, seeded_rng, weighted, extensions;
enum rctx {} enum rctx {}
#[abi = "cdecl"] #[abi = "cdecl"]
native mod rustrt { native mod rustrt {
fn rand_seed() -> [u8];
fn rand_new() -> *rctx; fn rand_new() -> *rctx;
fn rand_new_seeded(seed: [u8]) -> *rctx;
fn rand_next(c: *rctx) -> u32; fn rand_next(c: *rctx) -> u32;
fn rand_free(c: *rctx); fn rand_free(c: *rctx);
} }
@ -227,20 +229,50 @@ impl extensions for rng {
} }
#[doc = "Create a random number generator"] resource rand_res(c: *rctx) { rustrt::rand_free(c); }
impl of rng for @rand_res {
fn next() -> u32 { ret rustrt::rand_next(**self); }
}
#[doc = "Create a new random seed for seeded_rng"]
fn seed() -> [u8] {
rustrt::rand_seed()
}
#[doc = "Create a random number generator with a system specified seed"]
fn rng() -> rng { fn rng() -> rng {
resource rand_res(c: *rctx) { rustrt::rand_free(c); }
impl of rng for @rand_res {
fn next() -> u32 { ret rustrt::rand_next(**self); }
}
@rand_res(rustrt::rand_new()) as rng @rand_res(rustrt::rand_new()) as rng
} }
#[doc = "Create a random number generator using the specified seed. A \
generator constructed with a given seed will generate the same \
sequence of values as all other generators constructed with the \
same seed. The seed may be any length."]
fn seeded_rng(seed: [u8]) -> rng {
@rand_res(rustrt::rand_new_seeded(seed)) as rng
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[test]
fn rng_seeded() {
let seed = rand::seed();
let ra = rand::seeded_rng(seed);
let rb = rand::seeded_rng(seed);
assert ra.gen_str(100u) == rb.gen_str(100u);
}
#[test]
fn rng_seeded_custom_seed() {
// much shorter than generated seeds which are 1024 bytes
let seed = [2u8, 32u8, 4u8, 32u8, 51u8];
let ra = rand::seeded_rng(seed);
let rb = rand::seeded_rng(seed);
assert ra.gen_str(100u) == rb.gen_str(100u);
}
#[test] #[test]
fn gen_int_from() { fn gen_int_from() {
let r = rand::rng(); let r = rand::rng();

View File

@ -178,16 +178,41 @@ rust_str_push(rust_vec** sp, uint8_t byte) {
(*sp)->fill = fill + 1; (*sp)->fill = fill + 1;
} }
extern "C" CDECL rust_vec*
rand_seed() {
size_t size = sizeof(ub4) * RANDSIZ;
rust_task *task = rust_get_current_task();
rust_vec *v = (rust_vec *) task->kernel->malloc(vec_size<uint8_t>(size),
"rand_seed");
v->fill = v->alloc = size;
isaac_seed((uint8_t*) &v->data);
return v;
}
extern "C" CDECL void * extern "C" CDECL void *
rand_new() { rand_new() {
rust_task *task = rust_get_current_task(); rust_task *task = rust_get_current_task();
rust_sched_loop *thread = task->sched_loop; rust_sched_loop *thread = task->sched_loop;
randctx *rctx = (randctx *) task->malloc(sizeof(randctx), "randctx"); randctx *rctx = (randctx *) task->malloc(sizeof(randctx), "rand_new");
if (!rctx) { if (!rctx) {
task->fail(); task->fail();
return NULL; return NULL;
} }
isaac_init(thread->kernel, rctx); isaac_init(thread->kernel, rctx, NULL);
return rctx;
}
extern "C" CDECL void *
rand_new_seeded(rust_vec* seed) {
rust_task *task = rust_get_current_task();
rust_sched_loop *thread = task->sched_loop;
randctx *rctx = (randctx *) task->malloc(sizeof(randctx),
"rand_new_seeded");
if (!rctx) {
task->fail();
return NULL;
}
isaac_init(thread->kernel, rctx, seed);
return rctx; return rctx;
} }

View File

@ -29,7 +29,7 @@ rust_sched_loop::rust_sched_loop(rust_scheduler *sched,int id) :
name("main") name("main")
{ {
LOGPTR(this, "new dom", (uintptr_t)this); LOGPTR(this, "new dom", (uintptr_t)this);
isaac_init(kernel, &rctx); isaac_init(kernel, &rctx, NULL);
if (!tls_initialized) if (!tls_initialized)
init_tls(); init_tls();

View File

@ -32,46 +32,6 @@ align_to(T size, size_t alignment) {
return x; return x;
} }
// Initialization helper for ISAAC RNG
inline void
isaac_init(rust_kernel *kernel, randctx *rctx)
{
memset(rctx, 0, sizeof(randctx));
char *rust_seed = kernel->env->rust_seed;
if (rust_seed != NULL) {
ub4 seed = (ub4) atoi(rust_seed);
for (size_t i = 0; i < RANDSIZ; i ++) {
memcpy(&rctx->randrsl[i], &seed, sizeof(ub4));
seed = (seed + 0x7ed55d16) + (seed << 12);
}
} else {
#ifdef __WIN32__
HCRYPTPROV hProv;
kernel->win32_require
(_T("CryptAcquireContext"),
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
kernel->win32_require
(_T("CryptGenRandom"),
CryptGenRandom(hProv, sizeof(rctx->randrsl),
(BYTE*)(&rctx->randrsl)));
kernel->win32_require
(_T("CryptReleaseContext"),
CryptReleaseContext(hProv, 0));
#else
int fd = open("/dev/urandom", O_RDONLY);
assert(fd > 0);
assert(read(fd, (void*) &rctx->randrsl, sizeof(rctx->randrsl))
== sizeof(rctx->randrsl));
assert(close(fd) == 0);
#endif
}
randinit(rctx, 1);
}
// Interior vectors (rust-user-code level). // Interior vectors (rust-user-code level).
struct struct
@ -136,6 +96,53 @@ make_str_vec(rust_kernel* kernel, size_t nstrs, char **strs) {
return v; return v;
} }
// Initialization helpers for ISAAC RNG
inline void isaac_seed(uint8_t* dest)
{
size_t size = sizeof(ub4) * RANDSIZ;
#ifdef __WIN32__
HCRYPTPROV hProv;
kernel->win32_require
(_T("CryptAcquireContext"),
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
kernel->win32_require
(_T("CryptGenRandom"), CryptGenRandom(hProv, size, (BYTE*) dest));
kernel->win32_require
(_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0));
#else
int fd = open("/dev/urandom", O_RDONLY);
assert(fd > 0);
assert(read(fd, dest, size) == (int) size);
assert(close(fd) == 0);
#endif
}
inline void
isaac_init(rust_kernel *kernel, randctx *rctx, rust_vec* user_seed)
{
memset(rctx, 0, sizeof(randctx));
char *env_seed = kernel->env->rust_seed;
if (user_seed != NULL) {
// ignore bytes after the required length
size_t seed_len = user_seed->fill < sizeof(rctx->randrsl)
? user_seed->fill : sizeof(rctx->randrsl);
memcpy(&rctx->randrsl, user_seed->data, seed_len);
} else if (env_seed != NULL) {
ub4 seed = (ub4) atoi(env_seed);
for (size_t i = 0; i < RANDSIZ; i ++) {
memcpy(&rctx->randrsl[i], &seed, sizeof(ub4));
seed = (seed + 0x7ed55d16) + (seed << 12);
}
} else {
isaac_seed((uint8_t*) &rctx->randrsl);
}
randinit(rctx, 1);
}
// //
// Local Variables: // Local Variables:
// mode: C++ // mode: C++

View File

@ -26,7 +26,9 @@ rust_port_id_send
rust_port_select rust_port_select
rand_free rand_free
rand_new rand_new
rand_new_seeded
rand_next rand_next
rand_seed
refcount refcount
rust_get_sched_id rust_get_sched_id
rust_new_sched rust_new_sched