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/