diff --git a/extmod/modurandom.c b/extmod/modurandom.c index ab83b0f70..5a736c1eb 100644 --- a/extmod/modurandom.c +++ b/extmod/modurandom.c @@ -31,15 +31,29 @@ #if MICROPY_PY_URANDOM +// Work out if the seed will be set on import or not. +#if MICROPY_MODULE_BUILTIN_INIT && defined(MICROPY_PY_URANDOM_SEED_INIT_FUNC) +#define SEED_ON_IMPORT (1) +#else +#define SEED_ON_IMPORT (0) +#endif + // Yasmarang random number generator // by Ilya Levin // http://www.literatecode.com/yasmarang // Public Domain #if !MICROPY_ENABLE_DYNRUNTIME +#if SEED_ON_IMPORT +// If the state is seeded on import then keep these variables in the BSS. +STATIC uint32_t yasmarang_pad, yasmarang_n, yasmarang_d; +STATIC uint8_t yasmarang_dat; +#else +// Without seed-on-import these variables must be initialised via the data section. STATIC uint32_t yasmarang_pad = 0xeda4baba, yasmarang_n = 69, yasmarang_d = 233; STATIC uint8_t yasmarang_dat = 0; #endif +#endif STATIC uint32_t yasmarang(void) { yasmarang_pad += yasmarang_dat + yasmarang_d * yasmarang_n; @@ -83,15 +97,24 @@ STATIC mp_obj_t mod_urandom_getrandbits(mp_obj_t num_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_getrandbits_obj, mod_urandom_getrandbits); -STATIC mp_obj_t mod_urandom_seed(mp_obj_t seed_in) { - mp_uint_t seed = mp_obj_get_int_truncated(seed_in); +STATIC mp_obj_t mod_urandom_seed(size_t n_args, const mp_obj_t *args) { + mp_uint_t seed; + if (n_args == 0 || args[0] == mp_const_none) { + #ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC + seed = MICROPY_PY_URANDOM_SEED_INIT_FUNC; + #else + mp_raise_ValueError(MP_ERROR_TEXT("no default seed")); + #endif + } else { + seed = mp_obj_get_int_truncated(args[0]); + } yasmarang_pad = seed; yasmarang_n = 69; yasmarang_d = 233; yasmarang_dat = 0; return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_seed_obj, mod_urandom_seed); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_urandom_seed_obj, 0, 1, mod_urandom_seed); #if MICROPY_PY_URANDOM_EXTRA_FUNCS @@ -189,9 +212,15 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_urandom_uniform_obj, mod_urandom_uniform); #endif // MICROPY_PY_URANDOM_EXTRA_FUNCS -#ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC +#if SEED_ON_IMPORT STATIC mp_obj_t mod_urandom___init__() { - mod_urandom_seed(MP_OBJ_NEW_SMALL_INT(MICROPY_PY_URANDOM_SEED_INIT_FUNC)); + // This module may be imported by more than one name so need to ensure + // that it's only ever seeded once. + static bool seeded = false; + if (!seeded) { + seeded = true; + mod_urandom_seed(0, NULL); + } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom___init___obj, mod_urandom___init__); @@ -200,7 +229,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom___init___obj, mod_urandom___init__) #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t mp_module_urandom_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_urandom) }, - #ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC + #if SEED_ON_IMPORT { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&mod_urandom___init___obj) }, #endif { MP_ROM_QSTR(MP_QSTR_getrandbits), MP_ROM_PTR(&mod_urandom_getrandbits_obj) }, diff --git a/tests/extmod/urandom_seed_default.py b/tests/extmod/urandom_seed_default.py new file mode 100644 index 000000000..a032b9362 --- /dev/null +++ b/tests/extmod/urandom_seed_default.py @@ -0,0 +1,30 @@ +# test urandom.seed() without any arguments + +try: + import urandom as random +except ImportError: + try: + import random + except ImportError: + print("SKIP") + raise SystemExit + +try: + random.seed() +except ValueError: + # no default seed on this platform + print("SKIP") + raise SystemExit + + +def rng_seq(): + return [random.getrandbits(16) for _ in range(10)] + + +# seed with default and check that doesn't produce the same RNG sequence +random.seed() +seq = rng_seq() +random.seed() +print(seq == rng_seq()) +random.seed(None) +print(seq == rng_seq())