diff --git a/ChangeLog b/ChangeLog index feff938fd5..151c3f51de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Sat Feb 14 12:20:01 2015 Nobuyoshi Nakada + + * random.c (rand_random_number): add a method to return a random + number like SecureRandom to Random::Formatter. + + * lib/securerandom.rb (random_bytes): move to Random::Formatter, + the base method of the module. + Sat Feb 14 12:01:32 2015 Nobuyoshi Nakada * random.c (random_raw_seed): extract platform dependent random diff --git a/lib/securerandom.rb b/lib/securerandom.rb index 14853bd9c2..2f34e78374 100644 --- a/lib/securerandom.rb +++ b/lib/securerandom.rb @@ -47,26 +47,6 @@ end # module SecureRandom - - # SecureRandom.random_bytes generates a random binary string. - # - # The argument _n_ specifies the length of the result string. - # - # If _n_ is not specified or is nil, 16 is assumed. - # It may be larger in future. - # - # The result may contain any byte: "\x00" - "\xff". - # - # p SecureRandom.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6" - # p SecureRandom.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97" - # - # If a secure random number generator is not available, - # +NotImplementedError+ is raised. - def self.random_bytes(n=nil) - n = n ? n.to_int : 16 - gen_random(n) - end - if defined? OpenSSL::Random def self.gen_random(n) @pid = 0 unless defined?(@pid) @@ -94,6 +74,26 @@ module SecureRandom end module Random::Formatter + + # SecureRandom.random_bytes generates a random binary string. + # + # The argument _n_ specifies the length of the result string. + # + # If _n_ is not specified or is nil, 16 is assumed. + # It may be larger in future. + # + # The result may contain any byte: "\x00" - "\xff". + # + # p SecureRandom.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6" + # p SecureRandom.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97" + # + # If a secure random number generator is not available, + # +NotImplementedError+ is raised. + def random_bytes(n=nil) + n = n ? n.to_int : 16 + gen_random(n) + end + # SecureRandom.hex generates a random hexadecimal string. # # The argument _n_ specifies the length, in bytes, of the random number to be generated. @@ -168,6 +168,7 @@ module Random::Formatter s end +=begin # SecureRandom.random_number generates a random number. # # If a positive integer is given as _n_, @@ -212,6 +213,7 @@ module Random::Formatter Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG) end end +=end # SecureRandom.uuid generates a random v4 UUID (Universally Unique IDentifier). # @@ -230,6 +232,11 @@ module Random::Formatter ary[3] = (ary[3] & 0x3fff) | 0x8000 "%08x-%04x-%04x-%04x-%04x%08x" % ary end + + private + def gen_random(n) + self.bytes(n) + end end SecureRandom.extend(Random::Formatter) diff --git a/random.c b/random.c index a4a3265a88..47246dbcd6 100644 --- a/random.c +++ b/random.c @@ -196,16 +196,26 @@ genrand_int32(struct MT *mt) } /* generates a random number on [0,1) with 53-bit resolution*/ +static double int_pair_to_real_exclusive(uint32_t a, uint32_t b); static double genrand_real(struct MT *mt) { /* mt must be initialized */ - unsigned int a = genrand_int32(mt)>>5, b = genrand_int32(mt)>>6; + unsigned int a = genrand_int32(mt), b = genrand_int32(mt); + return int_pair_to_real_exclusive(a, b); +} + +static double +int_pair_to_real_exclusive(uint32_t a, uint32_t b) +{ + a >>= 5; + b >>= 6; return(a*67108864.0+b)*(1.0/9007199254740992.0); } /* generates a random number on [0,1] with 53-bit resolution*/ static double int_pair_to_real_inclusive(uint32_t a, uint32_t b); +#if 0 static double genrand_real2(struct MT *mt) { @@ -213,6 +223,7 @@ genrand_real2(struct MT *mt) uint32_t a = genrand_int32(mt), b = genrand_int32(mt); return int_pair_to_real_inclusive(a, b); } +#endif /* These real versions are due to Isaku Wada, 2002/01/09 added */ @@ -839,10 +850,9 @@ rb_genrand_ulong_limited(unsigned long limit) return limited_rand(default_mt(), limit); } -unsigned int -rb_random_int32(VALUE obj) +static unsigned int +random_int32(VALUE obj, rb_random_t *rnd) { - rb_random_t *rnd = try_get_rnd(obj); if (!rnd) { #if SIZEOF_LONG * CHAR_BIT > 32 VALUE lim = ULONG2NUM(0x100000000UL); @@ -856,6 +866,25 @@ rb_random_int32(VALUE obj) return genrand_int32(&rnd->mt); } +unsigned int +rb_random_int32(VALUE obj) +{ + return random_int32(obj, try_get_rnd(obj)); +} + +static double +random_real(VALUE obj, rb_random_t *rnd, int excl) +{ + uint32_t a = random_int32(obj, rnd); + uint32_t b = random_int32(obj, rnd); + if (excl) { + return int_pair_to_real_exclusive(a, b); + } + else { + return int_pair_to_real_inclusive(a, b); + } +} + double rb_random_real(VALUE obj) { @@ -887,10 +916,9 @@ ulong_to_num_plus_1(unsigned long n) #endif } -unsigned long -rb_random_ulong_limited(VALUE obj, unsigned long limit) +static unsigned long +random_ulong_limited(VALUE obj, rb_random_t *rnd, unsigned long limit) { - rb_random_t *rnd = try_get_rnd(obj); if (!rnd) { extern int rb_num_negative_p(VALUE); VALUE lim = ulong_to_num_plus_1(limit); @@ -907,6 +935,32 @@ rb_random_ulong_limited(VALUE obj, unsigned long limit) return limited_rand(&rnd->mt, limit); } +static VALUE +random_ulong_limited_big(VALUE obj, rb_random_t *rnd, VALUE vmax) +{ + if (!rnd) { + extern int rb_num_negative_p(VALUE); + VALUE lim = rb_big_plus(vmax, INT2FIX(1)); + VALUE v = rb_to_int(rb_funcall2(obj, id_rand, 1, &lim)); + if (rb_num_negative_p(v)) { + rb_raise(rb_eRangeError, "random number too small %"PRIsVALUE, v); + } + if (FIX2LONG(rb_big_cmp(vmax, v)) < 0) { + rb_raise(rb_eRangeError, "random number too big %"PRIsVALUE, v); + } + return v; + } + return limited_big_rand(&rnd->mt, vmax); +} + +unsigned long +rb_random_ulong_limited(VALUE obj, unsigned long limit) +{ + return random_ulong_limited(obj, try_get_rnd(obj), limit); +} + +static VALUE genrand_bytes(rb_random_t *rnd, long n); + /* * call-seq: prng.bytes(size) -> a_string * @@ -918,21 +972,16 @@ rb_random_ulong_limited(VALUE obj, unsigned long limit) static VALUE random_bytes(VALUE obj, VALUE len) { - return rb_random_bytes(obj, NUM2LONG(rb_to_int(len))); + return genrand_bytes(get_rnd(obj), NUM2LONG(rb_to_int(len))); } -VALUE -rb_random_bytes(VALUE obj, long n) +static VALUE +genrand_bytes(rb_random_t *rnd, long n) { - rb_random_t *rnd = try_get_rnd(obj); VALUE bytes; char *ptr; unsigned int r, i; - if (!rnd) { - VALUE len = LONG2NUM(n); - return rb_funcall2(obj, id_bytes, 1, &len); - } bytes = rb_str_new(0, n); ptr = RSTRING_PTR(bytes); for (; n >= SIZEOF_INT32; n -= SIZEOF_INT32) { @@ -953,6 +1002,17 @@ rb_random_bytes(VALUE obj, long n) return bytes; } +VALUE +rb_random_bytes(VALUE obj, long n) +{ + rb_random_t *rnd = try_get_rnd(obj); + if (!rnd) { + VALUE len = LONG2NUM(n); + return rb_funcall2(obj, id_bytes, 1, &len); + } + return genrand_bytes(rnd, n); +} + static VALUE range_values(VALUE vmax, VALUE *begp, VALUE *endp, int *exclp) { @@ -967,20 +1027,19 @@ range_values(VALUE vmax, VALUE *begp, VALUE *endp, int *exclp) } static VALUE -rand_int(struct MT *mt, VALUE vmax, int restrictive) +rand_int(VALUE obj, rb_random_t *rnd, VALUE vmax, int restrictive) { /* mt must be initialized */ - long max; unsigned long r; if (FIXNUM_P(vmax)) { - max = FIX2LONG(vmax); + long max = FIX2LONG(vmax); if (!max) return Qnil; if (max < 0) { if (restrictive) return Qnil; max = -max; } - r = limited_rand(mt, (unsigned long)max - 1); + r = random_ulong_limited(obj, rnd, (unsigned long)max - 1); return ULONG2NUM(r); } else { @@ -992,30 +1051,37 @@ rand_int(struct MT *mt, VALUE vmax, int restrictive) } vmax = rb_big_minus(vmax, INT2FIX(1)); if (FIXNUM_P(vmax)) { - max = FIX2LONG(vmax); + long max = FIX2LONG(vmax); if (max == -1) return Qnil; - r = limited_rand(mt, max); + r = random_ulong_limited(obj, rnd, max); return LONG2NUM(r); } - ret = limited_big_rand(mt, vmax); + ret = random_ulong_limited_big(obj, rnd, vmax); RB_GC_GUARD(vmax); return ret; } } +NORETURN(static void domain_error(void)); +static void +domain_error(void) +{ + VALUE error = INT2FIX(EDOM); + rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError)); +} + static inline double float_value(VALUE v) { double x = RFLOAT_VALUE(v); if (isinf(x) || isnan(x)) { - VALUE error = INT2FIX(EDOM); - rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError)); + domain_error(); } return x; } static inline VALUE -rand_range(struct MT* mt, VALUE range) +rand_range(VALUE obj, rb_random_t* rnd, VALUE range) { VALUE beg = Qundef, end = Qundef, vmax, v; int excl = 0; @@ -1029,7 +1095,7 @@ rand_range(struct MT* mt, VALUE range) if (FIXNUM_P(vmax)) { fixnum: if ((max = FIX2LONG(vmax) - excl) >= 0) { - unsigned long r = limited_rand(mt, (unsigned long)max); + unsigned long r = random_ulong_limited(obj, rnd, (unsigned long)max); v = ULONG2NUM(r); } } @@ -1039,7 +1105,7 @@ rand_range(struct MT* mt, VALUE range) excl = 0; goto fixnum; } - v = limited_big_rand(mt, vmax); + v = random_ulong_limited_big(obj, rnd, vmax); } } else if (v = rb_check_to_float(vmax), !NIL_P(v)) { @@ -1052,17 +1118,12 @@ rand_range(struct MT* mt, VALUE range) mid = max + min; max -= min; } - else { - float_value(v); + else if (isnan(max)) { + domain_error(); } v = Qnil; if (max > 0.0) { - if (excl) { - r = genrand_real(mt); - } - else { - r = genrand_real2(mt); - } + r = random_real(obj, rnd, excl); if (scale > 1) { return rb_float_new(+(+(+(r - 0.5) * max) * scale) + mid); } @@ -1095,7 +1156,7 @@ rand_range(struct MT* mt, VALUE range) return v; } -static VALUE rand_random(int argc, VALUE *argv, rb_random_t *rnd); +static VALUE rand_random(int argc, VALUE *argv, VALUE obj, rb_random_t *rnd); /* * call-seq: @@ -1129,16 +1190,17 @@ static VALUE rand_random(int argc, VALUE *argv, rb_random_t *rnd); static VALUE random_rand(int argc, VALUE *argv, VALUE obj) { - return rand_random(argc, argv, get_rnd(obj)); + return rand_random(argc, argv, obj, get_rnd(obj)); } static VALUE -rand_random(int argc, VALUE *argv, rb_random_t *rnd) +rand_random(int argc, VALUE *argv, VALUE obj, rb_random_t *rnd) { VALUE vmax, v; + double max = 0.0; if (argc == 0) { - return rb_float_new(genrand_real(&rnd->mt)); + goto float_rand; } else { rb_check_arity(argc, 0, 1); @@ -1148,16 +1210,26 @@ rand_random(int argc, VALUE *argv, rb_random_t *rnd) v = Qnil; } else if (!RB_TYPE_P(vmax, T_FLOAT) && (v = rb_check_to_integer(vmax, "to_int"), !NIL_P(v))) { - v = rand_int(&rnd->mt, v, 1); + v = rand_int(obj, rnd, v, 1); } else if (v = rb_check_to_float(vmax), !NIL_P(v)) { - double max = float_value(v); - if (max > 0.0) - v = rb_float_new(max * genrand_real(&rnd->mt)); - else + max = float_value(v); + if (max < 0.0) { v = Qnil; + } + else { + uint32_t a, b; + double r; + + float_rand: + a = random_int32(obj, rnd); + b = random_int32(obj, rnd); + r = int_pair_to_real_exclusive(a, b); + if (max > 0.0) r *= max;; + v = rb_float_new(r); + } } - else if ((v = rand_range(&rnd->mt, vmax)) != Qfalse) { + else if ((v = rand_range(obj, rnd, vmax)) != Qfalse) { /* nothing to do */ } else { @@ -1171,6 +1243,13 @@ rand_random(int argc, VALUE *argv, rb_random_t *rnd) return v; } +static VALUE +rand_random_number(int argc, VALUE *argv, VALUE obj) +{ + if (argc == 1 && argv[0] == INT2FIX(0)) --argc; + return rand_random(argc, argv, obj, try_get_rnd(obj)); +} + /* * call-seq: * prng1 == prng2 -> true or false @@ -1244,18 +1323,18 @@ static VALUE rb_f_rand(int argc, VALUE *argv, VALUE obj) { VALUE v, vmax, r; - struct MT *mt = default_mt(); + rb_random_t *rnd = rand_start(&default_rand); if (argc == 0) goto zero_arg; rb_scan_args(argc, argv, "01", &vmax); if (NIL_P(vmax)) goto zero_arg; - if ((v = rand_range(mt, vmax)) != Qfalse) { + if ((v = rand_range(Qnil, rnd, vmax)) != Qfalse) { return v; } vmax = rb_to_int(vmax); - if (vmax == INT2FIX(0) || NIL_P(r = rand_int(mt, vmax, 0))) { + if (vmax == INT2FIX(0) || NIL_P(r = rand_int(Qnil, rnd, vmax, 0))) { zero_arg: - return DBL2NUM(genrand_real(mt)); + return DBL2NUM(genrand_real(&rnd->mt)); } return r; } @@ -1271,7 +1350,7 @@ rb_f_rand(int argc, VALUE *argv, VALUE obj) static VALUE random_s_rand(int argc, VALUE *argv, VALUE obj) { - return rand_random(argc, argv, rand_start(&default_rand)); + return rand_random(argc, argv, Qnil, rand_start(&default_rand)); } #define SIP_HASH_STREAMING 0 @@ -1430,6 +1509,12 @@ InitVM_Random(void) rb_define_singleton_method(rb_cRandom, "raw_seed", random_raw_seed, 1); rb_define_private_method(CLASS_OF(rb_cRandom), "state", random_s_state, 0); rb_define_private_method(CLASS_OF(rb_cRandom), "left", random_s_left, 0); + + { + VALUE m = rb_define_module_under(rb_cRandom, "Formatter"); + rb_include_module(rb_cRandom, m); + rb_define_method(m, "random_number", rand_random_number, -1); + } } #undef rb_intern