1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

random.c: rand_random_number

* 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.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@49596 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2015-02-14 03:20:04 +00:00
parent 1f13a179d3
commit b1adbd14e5
3 changed files with 170 additions and 70 deletions

View file

@ -1,3 +1,11 @@
Sat Feb 14 12:20:01 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
* 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 <nobu@ruby-lang.org> Sat Feb 14 12:01:32 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
* random.c (random_raw_seed): extract platform dependent random * random.c (random_raw_seed): extract platform dependent random

View file

@ -47,26 +47,6 @@ end
# #
module SecureRandom 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 if defined? OpenSSL::Random
def self.gen_random(n) def self.gen_random(n)
@pid = 0 unless defined?(@pid) @pid = 0 unless defined?(@pid)
@ -94,6 +74,26 @@ module SecureRandom
end end
module Random::Formatter 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. # SecureRandom.hex generates a random hexadecimal string.
# #
# The argument _n_ specifies the length, in bytes, of the random number to be generated. # The argument _n_ specifies the length, in bytes, of the random number to be generated.
@ -168,6 +168,7 @@ module Random::Formatter
s s
end end
=begin
# SecureRandom.random_number generates a random number. # SecureRandom.random_number generates a random number.
# #
# If a positive integer is given as _n_, # 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) Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
end end
end end
=end
# SecureRandom.uuid generates a random v4 UUID (Universally Unique IDentifier). # SecureRandom.uuid generates a random v4 UUID (Universally Unique IDentifier).
# #
@ -230,6 +232,11 @@ module Random::Formatter
ary[3] = (ary[3] & 0x3fff) | 0x8000 ary[3] = (ary[3] & 0x3fff) | 0x8000
"%08x-%04x-%04x-%04x-%04x%08x" % ary "%08x-%04x-%04x-%04x-%04x%08x" % ary
end end
private
def gen_random(n)
self.bytes(n)
end
end end
SecureRandom.extend(Random::Formatter) SecureRandom.extend(Random::Formatter)

185
random.c
View file

@ -196,16 +196,26 @@ genrand_int32(struct MT *mt)
} }
/* generates a random number on [0,1) with 53-bit resolution*/ /* 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 static double
genrand_real(struct MT *mt) genrand_real(struct MT *mt)
{ {
/* mt must be initialized */ /* 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); return(a*67108864.0+b)*(1.0/9007199254740992.0);
} }
/* generates a random number on [0,1] with 53-bit resolution*/ /* generates a random number on [0,1] with 53-bit resolution*/
static double int_pair_to_real_inclusive(uint32_t a, uint32_t b); static double int_pair_to_real_inclusive(uint32_t a, uint32_t b);
#if 0
static double static double
genrand_real2(struct MT *mt) genrand_real2(struct MT *mt)
{ {
@ -213,6 +223,7 @@ genrand_real2(struct MT *mt)
uint32_t a = genrand_int32(mt), b = genrand_int32(mt); uint32_t a = genrand_int32(mt), b = genrand_int32(mt);
return int_pair_to_real_inclusive(a, b); return int_pair_to_real_inclusive(a, b);
} }
#endif
/* These real versions are due to Isaku Wada, 2002/01/09 added */ /* 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); return limited_rand(default_mt(), limit);
} }
unsigned int static unsigned int
rb_random_int32(VALUE obj) random_int32(VALUE obj, rb_random_t *rnd)
{ {
rb_random_t *rnd = try_get_rnd(obj);
if (!rnd) { if (!rnd) {
#if SIZEOF_LONG * CHAR_BIT > 32 #if SIZEOF_LONG * CHAR_BIT > 32
VALUE lim = ULONG2NUM(0x100000000UL); VALUE lim = ULONG2NUM(0x100000000UL);
@ -856,6 +866,25 @@ rb_random_int32(VALUE obj)
return genrand_int32(&rnd->mt); 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 double
rb_random_real(VALUE obj) rb_random_real(VALUE obj)
{ {
@ -887,10 +916,9 @@ ulong_to_num_plus_1(unsigned long n)
#endif #endif
} }
unsigned long static unsigned long
rb_random_ulong_limited(VALUE obj, unsigned long limit) random_ulong_limited(VALUE obj, rb_random_t *rnd, unsigned long limit)
{ {
rb_random_t *rnd = try_get_rnd(obj);
if (!rnd) { if (!rnd) {
extern int rb_num_negative_p(VALUE); extern int rb_num_negative_p(VALUE);
VALUE lim = ulong_to_num_plus_1(limit); 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); 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 * call-seq: prng.bytes(size) -> a_string
* *
@ -918,21 +972,16 @@ rb_random_ulong_limited(VALUE obj, unsigned long limit)
static VALUE static VALUE
random_bytes(VALUE obj, VALUE len) 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 static VALUE
rb_random_bytes(VALUE obj, long n) genrand_bytes(rb_random_t *rnd, long n)
{ {
rb_random_t *rnd = try_get_rnd(obj);
VALUE bytes; VALUE bytes;
char *ptr; char *ptr;
unsigned int r, i; unsigned int r, i;
if (!rnd) {
VALUE len = LONG2NUM(n);
return rb_funcall2(obj, id_bytes, 1, &len);
}
bytes = rb_str_new(0, n); bytes = rb_str_new(0, n);
ptr = RSTRING_PTR(bytes); ptr = RSTRING_PTR(bytes);
for (; n >= SIZEOF_INT32; n -= SIZEOF_INT32) { for (; n >= SIZEOF_INT32; n -= SIZEOF_INT32) {
@ -953,6 +1002,17 @@ rb_random_bytes(VALUE obj, long n)
return bytes; 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 static VALUE
range_values(VALUE vmax, VALUE *begp, VALUE *endp, int *exclp) 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 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 */ /* mt must be initialized */
long max;
unsigned long r; unsigned long r;
if (FIXNUM_P(vmax)) { if (FIXNUM_P(vmax)) {
max = FIX2LONG(vmax); long max = FIX2LONG(vmax);
if (!max) return Qnil; if (!max) return Qnil;
if (max < 0) { if (max < 0) {
if (restrictive) return Qnil; if (restrictive) return Qnil;
max = -max; max = -max;
} }
r = limited_rand(mt, (unsigned long)max - 1); r = random_ulong_limited(obj, rnd, (unsigned long)max - 1);
return ULONG2NUM(r); return ULONG2NUM(r);
} }
else { else {
@ -992,30 +1051,37 @@ rand_int(struct MT *mt, VALUE vmax, int restrictive)
} }
vmax = rb_big_minus(vmax, INT2FIX(1)); vmax = rb_big_minus(vmax, INT2FIX(1));
if (FIXNUM_P(vmax)) { if (FIXNUM_P(vmax)) {
max = FIX2LONG(vmax); long max = FIX2LONG(vmax);
if (max == -1) return Qnil; if (max == -1) return Qnil;
r = limited_rand(mt, max); r = random_ulong_limited(obj, rnd, max);
return LONG2NUM(r); return LONG2NUM(r);
} }
ret = limited_big_rand(mt, vmax); ret = random_ulong_limited_big(obj, rnd, vmax);
RB_GC_GUARD(vmax); RB_GC_GUARD(vmax);
return ret; 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 static inline double
float_value(VALUE v) float_value(VALUE v)
{ {
double x = RFLOAT_VALUE(v); double x = RFLOAT_VALUE(v);
if (isinf(x) || isnan(x)) { if (isinf(x) || isnan(x)) {
VALUE error = INT2FIX(EDOM); domain_error();
rb_exc_raise(rb_class_new_instance(1, &error, rb_eSystemCallError));
} }
return x; return x;
} }
static inline VALUE 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; VALUE beg = Qundef, end = Qundef, vmax, v;
int excl = 0; int excl = 0;
@ -1029,7 +1095,7 @@ rand_range(struct MT* mt, VALUE range)
if (FIXNUM_P(vmax)) { if (FIXNUM_P(vmax)) {
fixnum: fixnum:
if ((max = FIX2LONG(vmax) - excl) >= 0) { 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); v = ULONG2NUM(r);
} }
} }
@ -1039,7 +1105,7 @@ rand_range(struct MT* mt, VALUE range)
excl = 0; excl = 0;
goto fixnum; 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)) { 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; mid = max + min;
max -= min; max -= min;
} }
else { else if (isnan(max)) {
float_value(v); domain_error();
} }
v = Qnil; v = Qnil;
if (max > 0.0) { if (max > 0.0) {
if (excl) { r = random_real(obj, rnd, excl);
r = genrand_real(mt);
}
else {
r = genrand_real2(mt);
}
if (scale > 1) { if (scale > 1) {
return rb_float_new(+(+(+(r - 0.5) * max) * scale) + mid); return rb_float_new(+(+(+(r - 0.5) * max) * scale) + mid);
} }
@ -1095,7 +1156,7 @@ rand_range(struct MT* mt, VALUE range)
return v; 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: * call-seq:
@ -1129,16 +1190,17 @@ static VALUE rand_random(int argc, VALUE *argv, rb_random_t *rnd);
static VALUE static VALUE
random_rand(int argc, VALUE *argv, VALUE obj) 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 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; VALUE vmax, v;
double max = 0.0;
if (argc == 0) { if (argc == 0) {
return rb_float_new(genrand_real(&rnd->mt)); goto float_rand;
} }
else { else {
rb_check_arity(argc, 0, 1); rb_check_arity(argc, 0, 1);
@ -1148,16 +1210,26 @@ rand_random(int argc, VALUE *argv, rb_random_t *rnd)
v = Qnil; v = Qnil;
} }
else if (!RB_TYPE_P(vmax, T_FLOAT) && (v = rb_check_to_integer(vmax, "to_int"), !NIL_P(v))) { 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)) { else if (v = rb_check_to_float(vmax), !NIL_P(v)) {
double max = float_value(v); max = float_value(v);
if (max > 0.0) if (max < 0.0) {
v = rb_float_new(max * genrand_real(&rnd->mt));
else
v = Qnil; 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 */ /* nothing to do */
} }
else { else {
@ -1171,6 +1243,13 @@ rand_random(int argc, VALUE *argv, rb_random_t *rnd)
return v; 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: * call-seq:
* prng1 == prng2 -> true or false * prng1 == prng2 -> true or false
@ -1244,18 +1323,18 @@ static VALUE
rb_f_rand(int argc, VALUE *argv, VALUE obj) rb_f_rand(int argc, VALUE *argv, VALUE obj)
{ {
VALUE v, vmax, r; VALUE v, vmax, r;
struct MT *mt = default_mt(); rb_random_t *rnd = rand_start(&default_rand);
if (argc == 0) goto zero_arg; if (argc == 0) goto zero_arg;
rb_scan_args(argc, argv, "01", &vmax); rb_scan_args(argc, argv, "01", &vmax);
if (NIL_P(vmax)) goto zero_arg; if (NIL_P(vmax)) goto zero_arg;
if ((v = rand_range(mt, vmax)) != Qfalse) { if ((v = rand_range(Qnil, rnd, vmax)) != Qfalse) {
return v; return v;
} }
vmax = rb_to_int(vmax); 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: zero_arg:
return DBL2NUM(genrand_real(mt)); return DBL2NUM(genrand_real(&rnd->mt));
} }
return r; return r;
} }
@ -1271,7 +1350,7 @@ rb_f_rand(int argc, VALUE *argv, VALUE obj)
static VALUE static VALUE
random_s_rand(int argc, VALUE *argv, VALUE obj) 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 #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_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), "state", random_s_state, 0);
rb_define_private_method(CLASS_OF(rb_cRandom), "left", random_s_left, 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 #undef rb_intern