[前][次][番号順一覧][スレッド一覧]

ruby-changes:42894

From: nobu <ko1@a...>
Date: Tue, 10 May 2016 14:00:37 +0900 (JST)
Subject: [ruby-changes:42894] nobu:r54968 (trunk): random.c: use bytes

nobu	2016-05-10 14:57:11 +0900 (Tue, 10 May 2016)

  New Revision: 54968

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=54968

  Log:
    random.c: use bytes
    
    * random.c (obj_random_bytes): base on bytes method instead of
      rand method, not to call toplevel rand method.

  Modified files:
    trunk/ChangeLog
    trunk/lib/securerandom.rb
    trunk/random.c
    trunk/test/ruby/test_rand.rb
    trunk/test/test_securerandom.rb
Index: test/ruby/test_rand.rb
===================================================================
--- test/ruby/test_rand.rb	(revision 54967)
+++ test/ruby/test_rand.rb	(revision 54968)
@@ -526,6 +526,17 @@ END https://github.com/ruby/ruby/blob/trunk/test/ruby/test_rand.rb#L526
     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
Index: test/test_securerandom.rb
===================================================================
--- test/test_securerandom.rb	(revision 54967)
+++ test/test_securerandom.rb	(revision 54968)
@@ -157,6 +157,20 @@ end https://github.com/ruby/ruby/blob/trunk/test/test_securerandom.rb#L157
     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)
Index: lib/securerandom.rb
===================================================================
--- lib/securerandom.rb	(revision 54967)
+++ lib/securerandom.rb	(revision 54968)
@@ -75,6 +75,10 @@ module SecureRandom https://github.com/ruby/ruby/blob/trunk/lib/securerandom.rb#L75
       ret
     end
   end
+
+  class << self
+    alias bytes gen_random
+  end
 end
 
 module Random::Formatter
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 54967)
+++ ChangeLog	(revision 54968)
@@ -1,3 +1,8 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Tue May 10 14:57:09 2016  Nobuyoshi Nakada  <nobu@r...>
+
+	* 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@r...>
 
 	* configure.in (-fexcess-precision=standard): before r54895 -std=c99
Index: random.c
===================================================================
--- random.c	(revision 54967)
+++ random.c	(revision 54968)
@@ -898,17 +898,20 @@ rb_genrand_ulong_limited(unsigned long l https://github.com/ruby/ruby/blob/trunk/random.c#L898
     return limited_rand(default_mt(), limit);
 }
 
-static unsigned int
-obj_random_int32(VALUE obj)
+static VALUE
+obj_random_bytes(VALUE obj, void *p, long n)
 {
-#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));
+    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) https://github.com/ruby/ruby/blob/trunk/random.c#L925
 {
     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, https://github.com/ruby/ruby/blob/trunk/random.c#L938
     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) https://github.com/ruby/ruby/blob/trunk/random.c#L989
 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 https://github.com/ruby/ruby/blob/trunk/random.c#L1024
 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) https://github.com/ruby/ruby/blob/trunk/random.c#L1097
 {
     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) https://github.com/ruby/ruby/blob/trunk/random.c#L1629
 	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);
     }
 }
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]