Skip to content

Commit c9f8ae0

Browse files
Dretchbrson
authored andcommitted
add a seeded random number generator so that sequences of random numbers can be easily reproduced (for rust-lang#2379)
1 parent 64130f1 commit c9f8ae0

File tree

5 files changed

+116
-50
lines changed

5 files changed

+116
-50
lines changed

src/libcore/rand.rs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#[doc = "Random number generation"];
22

3-
export rng, weighted, extensions;
3+
export rng, seed, seeded_rng, weighted, extensions;
44

55
enum rctx {}
66

77
#[abi = "cdecl"]
88
native mod rustrt {
9+
fn rand_seed() -> [u8];
910
fn rand_new() -> *rctx;
11+
fn rand_new_seeded(seed: [u8]) -> *rctx;
1012
fn rand_next(c: *rctx) -> u32;
1113
fn rand_free(c: *rctx);
1214
}
@@ -227,20 +229,50 @@ impl extensions for rng {
227229

228230
}
229231

230-
#[doc = "Create a random number generator"]
231-
fn rng() -> rng {
232-
resource rand_res(c: *rctx) { rustrt::rand_free(c); }
232+
resource rand_res(c: *rctx) { rustrt::rand_free(c); }
233233

234-
impl of rng for @rand_res {
235-
fn next() -> u32 { ret rustrt::rand_next(**self); }
236-
}
234+
impl of rng for @rand_res {
235+
fn next() -> u32 { ret rustrt::rand_next(**self); }
236+
}
237237

238+
#[doc = "Create a new random seed for seeded_rng"]
239+
fn seed() -> [u8] {
240+
rustrt::rand_seed()
241+
}
242+
243+
#[doc = "Create a random number generator with a system specified seed"]
244+
fn rng() -> rng {
238245
@rand_res(rustrt::rand_new()) as rng
239246
}
240247

248+
#[doc = "Create a random number generator using the specified seed. A \
249+
generator constructed with a given seed will generate the same \
250+
sequence of values as all other generators constructed with the \
251+
same seed. The seed may be any length."]
252+
fn seeded_rng(seed: [u8]) -> rng {
253+
@rand_res(rustrt::rand_new_seeded(seed)) as rng
254+
}
255+
241256
#[cfg(test)]
242257
mod tests {
243258

259+
#[test]
260+
fn rng_seeded() {
261+
let seed = rand::seed();
262+
let ra = rand::seeded_rng(seed);
263+
let rb = rand::seeded_rng(seed);
264+
assert ra.gen_str(100u) == rb.gen_str(100u);
265+
}
266+
267+
#[test]
268+
fn rng_seeded_custom_seed() {
269+
// much shorter than generated seeds which are 1024 bytes
270+
let seed = [2u8, 32u8, 4u8, 32u8, 51u8];
271+
let ra = rand::seeded_rng(seed);
272+
let rb = rand::seeded_rng(seed);
273+
assert ra.gen_str(100u) == rb.gen_str(100u);
274+
}
275+
244276
#[test]
245277
fn gen_int_from() {
246278
let r = rand::rng();

src/rt/rust_builtin.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,41 @@ rust_str_push(rust_vec** sp, uint8_t byte) {
178178
(*sp)->fill = fill + 1;
179179
}
180180

181+
extern "C" CDECL rust_vec*
182+
rand_seed() {
183+
size_t size = sizeof(ub4) * RANDSIZ;
184+
rust_task *task = rust_get_current_task();
185+
rust_vec *v = (rust_vec *) task->kernel->malloc(vec_size<uint8_t>(size),
186+
"rand_seed");
187+
v->fill = v->alloc = size;
188+
isaac_seed((uint8_t*) &v->data);
189+
return v;
190+
}
191+
181192
extern "C" CDECL void *
182193
rand_new() {
183194
rust_task *task = rust_get_current_task();
184195
rust_sched_loop *thread = task->sched_loop;
185-
randctx *rctx = (randctx *) task->malloc(sizeof(randctx), "randctx");
196+
randctx *rctx = (randctx *) task->malloc(sizeof(randctx), "rand_new");
197+
if (!rctx) {
198+
task->fail();
199+
return NULL;
200+
}
201+
isaac_init(thread->kernel, rctx, NULL);
202+
return rctx;
203+
}
204+
205+
extern "C" CDECL void *
206+
rand_new_seeded(rust_vec* seed) {
207+
rust_task *task = rust_get_current_task();
208+
rust_sched_loop *thread = task->sched_loop;
209+
randctx *rctx = (randctx *) task->malloc(sizeof(randctx),
210+
"rand_new_seeded");
186211
if (!rctx) {
187212
task->fail();
188213
return NULL;
189214
}
190-
isaac_init(thread->kernel, rctx);
215+
isaac_init(thread->kernel, rctx, seed);
191216
return rctx;
192217
}
193218

src/rt/rust_sched_loop.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ rust_sched_loop::rust_sched_loop(rust_scheduler *sched,int id) :
2929
name("main")
3030
{
3131
LOGPTR(this, "new dom", (uintptr_t)this);
32-
isaac_init(kernel, &rctx);
32+
isaac_init(kernel, &rctx, NULL);
3333

3434
if (!tls_initialized)
3535
init_tls();

src/rt/rust_util.h

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -32,46 +32,6 @@ align_to(T size, size_t alignment) {
3232
return x;
3333
}
3434

35-
// Initialization helper for ISAAC RNG
36-
37-
inline void
38-
isaac_init(rust_kernel *kernel, randctx *rctx)
39-
{
40-
memset(rctx, 0, sizeof(randctx));
41-
42-
char *rust_seed = kernel->env->rust_seed;
43-
if (rust_seed != NULL) {
44-
ub4 seed = (ub4) atoi(rust_seed);
45-
for (size_t i = 0; i < RANDSIZ; i ++) {
46-
memcpy(&rctx->randrsl[i], &seed, sizeof(ub4));
47-
seed = (seed + 0x7ed55d16) + (seed << 12);
48-
}
49-
} else {
50-
#ifdef __WIN32__
51-
HCRYPTPROV hProv;
52-
kernel->win32_require
53-
(_T("CryptAcquireContext"),
54-
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
55-
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
56-
kernel->win32_require
57-
(_T("CryptGenRandom"),
58-
CryptGenRandom(hProv, sizeof(rctx->randrsl),
59-
(BYTE*)(&rctx->randrsl)));
60-
kernel->win32_require
61-
(_T("CryptReleaseContext"),
62-
CryptReleaseContext(hProv, 0));
63-
#else
64-
int fd = open("/dev/urandom", O_RDONLY);
65-
assert(fd > 0);
66-
assert(read(fd, (void*) &rctx->randrsl, sizeof(rctx->randrsl))
67-
== sizeof(rctx->randrsl));
68-
assert(close(fd) == 0);
69-
#endif
70-
}
71-
72-
randinit(rctx, 1);
73-
}
74-
7535
// Interior vectors (rust-user-code level).
7636

7737
struct
@@ -136,6 +96,53 @@ make_str_vec(rust_kernel* kernel, size_t nstrs, char **strs) {
13696
return v;
13797
}
13898

99+
// Initialization helpers for ISAAC RNG
100+
101+
inline void isaac_seed(uint8_t* dest)
102+
{
103+
size_t size = sizeof(ub4) * RANDSIZ;
104+
#ifdef __WIN32__
105+
HCRYPTPROV hProv;
106+
kernel->win32_require
107+
(_T("CryptAcquireContext"),
108+
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
109+
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
110+
kernel->win32_require
111+
(_T("CryptGenRandom"), CryptGenRandom(hProv, size, (BYTE*) dest));
112+
kernel->win32_require
113+
(_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0));
114+
#else
115+
int fd = open("/dev/urandom", O_RDONLY);
116+
assert(fd > 0);
117+
assert(read(fd, dest, size) == (int) size);
118+
assert(close(fd) == 0);
119+
#endif
120+
}
121+
122+
inline void
123+
isaac_init(rust_kernel *kernel, randctx *rctx, rust_vec* user_seed)
124+
{
125+
memset(rctx, 0, sizeof(randctx));
126+
127+
char *env_seed = kernel->env->rust_seed;
128+
if (user_seed != NULL) {
129+
// ignore bytes after the required length
130+
size_t seed_len = user_seed->fill < sizeof(rctx->randrsl)
131+
? user_seed->fill : sizeof(rctx->randrsl);
132+
memcpy(&rctx->randrsl, user_seed->data, seed_len);
133+
} else if (env_seed != NULL) {
134+
ub4 seed = (ub4) atoi(env_seed);
135+
for (size_t i = 0; i < RANDSIZ; i ++) {
136+
memcpy(&rctx->randrsl[i], &seed, sizeof(ub4));
137+
seed = (seed + 0x7ed55d16) + (seed << 12);
138+
}
139+
} else {
140+
isaac_seed((uint8_t*) &rctx->randrsl);
141+
}
142+
143+
randinit(rctx, 1);
144+
}
145+
139146
//
140147
// Local Variables:
141148
// mode: C++

src/rt/rustrt.def.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ rust_port_id_send
2626
rust_port_select
2727
rand_free
2828
rand_new
29+
rand_new_seeded
2930
rand_next
31+
rand_seed
3032
refcount
3133
rust_get_sched_id
3234
rust_new_sched

0 commit comments

Comments
 (0)