aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-05-10 05:57:11 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-05-10 05:57:11 +0000
commitb402cc71616169bab03fb856e73a7d6519330ca3 (patch)
treedadead0ea1a5a4b8e5febef318f9b1b3e60f77c8
parent518713fd651c8d53a6493a354eadb41361c18029 (diff)
downloadruby-b402cc71616169bab03fb856e73a7d6519330ca3.tar.gz
random.c: use bytes
* random.c (obj_random_bytes): base on bytes method instead of rand method, not to call toplevel rand method. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@54968 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog5
-rw-r--r--lib/securerandom.rb4
-rw-r--r--random.c87
-rw-r--r--test/ruby/test_rand.rb11
-rw-r--r--test/test_securerandom.rb14
5 files changed, 92 insertions, 29 deletions
diff --git a/ChangeLog b/ChangeLog
index 2a00110ac6..c0e2de06cb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Tue May 10 14:57:09 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * random.c (obj_random_bytes): base on bytes method instead of
+ rand method, not to call toplevel rand method.
+
Tue May 10 13:07:28 2016 NARUSE, Yui <naruse@ruby-lang.org>
* configure.in (-fexcess-precision=standard): before r54895 -std=c99
diff --git a/lib/securerandom.rb b/lib/securerandom.rb
index d8afc7d01b..9201c9337a 100644
--- a/lib/securerandom.rb
+++ b/lib/securerandom.rb
@@ -75,6 +75,10 @@ module SecureRandom
ret
end
end
+
+ class << self
+ alias bytes gen_random
+ end
end
module Random::Formatter
diff --git a/random.c b/random.c
index 6e6dae700a..21d1ea0840 100644
--- a/random.c
+++ b/random.c
@@ -898,17 +898,20 @@ rb_genrand_ulong_limited(unsigned long limit)
return limited_rand(default_mt(), limit);
}
-static unsigned int
-obj_random_int32(VALUE obj)
-{
-#if SIZEOF_LONG * CHAR_BIT > 32
- VALUE lim = ULONG2NUM(0x100000000UL);
-#elif defined HAVE_LONG_LONG
- VALUE lim = ULL2NUM((LONG_LONG)0xffffffff+1);
-#else
- VALUE lim = rb_big_plus(ULONG2NUM(0xffffffff), INT2FIX(1));
-#endif
- return (unsigned int)NUM2ULONG(rb_funcall2(obj, id_rand, 1, &lim));
+static VALUE
+obj_random_bytes(VALUE obj, void *p, long n)
+{
+ VALUE len = LONG2NUM(n);
+ VALUE v = rb_funcallv_public(obj, id_bytes, 1, &len);
+ long l;
+ Check_Type(v, T_STRING);
+ l = RSTRING_LEN(v);
+ if (l < n)
+ rb_raise(rb_eRangeError, "random data too short %ld", l);
+ else if (l > n)
+ rb_raise(rb_eRangeError, "random data too long %ld", l);
+ if (p) memcpy(p, RSTRING_PTR(v), n);
+ return v;
}
static unsigned int
@@ -922,7 +925,9 @@ rb_random_int32(VALUE obj)
{
rb_random_t *rnd = try_get_rnd(obj);
if (!rnd) {
- return obj_random_int32(obj);
+ uint32_t x;
+ obj_random_bytes(obj, &x, sizeof(x));
+ return (unsigned int)x;
}
return random_int32(rnd);
}
@@ -933,8 +938,10 @@ random_real(VALUE obj, rb_random_t *rnd, int excl)
uint32_t a, b;
if (!rnd) {
- a = obj_random_int32(obj);
- b = obj_random_int32(obj);
+ uint32_t x[2] = {0, 0};
+ obj_random_bytes(obj, x, sizeof(x));
+ a = x[0];
+ b = x[1];
}
else {
a = random_int32(rnd);
@@ -982,6 +989,22 @@ ulong_to_num_plus_1(unsigned long n)
static unsigned long
random_ulong_limited(VALUE obj, rb_random_t *rnd, unsigned long limit)
{
+ if (!limit) return 0;
+ if (!rnd) {
+ unsigned long val, mask = make_mask(limit);
+ do {
+ obj_random_bytes(obj, &val, sizeof(unsigned long));
+ val &= mask;
+ } while (limit < val);
+ return val;
+ }
+ return limited_rand(&rnd->mt, limit);
+}
+
+unsigned long
+rb_random_ulong_limited(VALUE obj, unsigned long limit)
+{
+ rb_random_t *rnd = try_get_rnd(obj);
if (!rnd) {
VALUE lim = ulong_to_num_plus_1(limit);
VALUE v = rb_to_int(rb_funcall2(obj, id_rand, 1, &lim));
@@ -1001,25 +1024,31 @@ static VALUE
random_ulong_limited_big(VALUE obj, rb_random_t *rnd, VALUE vmax)
{
if (!rnd) {
- 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);
+ VALUE v, vtmp;
+ size_t i, nlz, len = rb_absint_numwords(vmax, 32, &nlz);
+ uint32_t *tmp = ALLOCV_N(uint32_t, vtmp, len * 2);
+ uint32_t mask = (uint32_t)~0 >> nlz;
+ uint32_t *lim_array = tmp;
+ uint32_t *rnd_array = tmp + len;
+ int flag = INTEGER_PACK_MSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER;
+ rb_integer_pack(vmax, lim_array, len, sizeof(uint32_t), 0, flag);
+
+ retry:
+ obj_random_bytes(obj, rnd_array, len * sizeof(uint32_t));
+ rnd_array[0] &= mask;
+ for (i = 0; i < len; ++i) {
+ if (lim_array[i] < rnd_array[i])
+ goto retry;
+ if (rnd_array[i] < lim_array[i])
+ break;
}
+ v = rb_integer_unpack(rnd_array, len, sizeof(uint32_t), 0, flag);
+ ALLOCV_END(vtmp);
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);
/*
@@ -1068,8 +1097,7 @@ 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 obj_random_bytes(obj, NULL, n);
}
return genrand_bytes(rnd, n);
}
@@ -1601,6 +1629,7 @@ InitVM_Random(void)
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);
+ rb_define_method(m, "rand", rand_random_number, -1);
}
}
diff --git a/test/ruby/test_rand.rb b/test/ruby/test_rand.rb
index d4258a7f1c..c24d6cd1ff 100644
--- a/test/ruby/test_rand.rb
+++ b/test/ruby/test_rand.rb
@@ -526,6 +526,17 @@ END
assert_equal(2, gen.limit, bug7935)
end
+ def test_random_ulong_limited_no_rand
+ c = Class.new do
+ undef rand
+ def bytes(n)
+ "\0"*n
+ end
+ end
+ gen = c.new.extend(Random::Formatter)
+ assert_equal(1, [1, 2].sample(random: gen))
+ end
+
def test_default_seed
assert_separately([], <<-End)
seed = Random::DEFAULT::seed
diff --git a/test/test_securerandom.rb b/test/test_securerandom.rb
index 515d05e6b2..cd5cb1dee2 100644
--- a/test/test_securerandom.rb
+++ b/test/test_securerandom.rb
@@ -157,6 +157,20 @@ end
end
end
+ def test_s_random_number_not_default
+ msg = "SecureRandom#random_number should not be affected by srand"
+ seed = srand(0)
+ x = @it.random_number(1000)
+ 10.times do|i|
+ srand(0)
+ return unless @it.random_number(1000) == x
+ end
+ srand(0)
+ assert_not_equal(x, @it.random_number(1000), msg)
+ ensure
+ srand(seed) if seed
+ end
+
def test_uuid
uuid = @it.uuid
assert_equal(36, uuid.size)