-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2372 from herwinw/securerandom_random_bytes
Implement Securerandom.random_bytes
- Loading branch information
Showing
4 changed files
with
195 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
#include "natalie.hpp" | ||
#include <random> | ||
|
||
using namespace Natalie; | ||
|
||
static Value generate_random(double min, double max) { | ||
std::random_device rd; | ||
std::uniform_real_distribution<double> random_number(min, max); | ||
return new FloatObject { random_number(rd) }; | ||
} | ||
|
||
static Value generate_random(nat_int_t min, nat_int_t max) { | ||
std::random_device rd; | ||
std::uniform_int_distribution<nat_int_t> random_number(min, max); | ||
return Value::integer(random_number(rd)); | ||
} | ||
|
||
Value SecureRandom_random_number(Env *env, Value self, Args &&args, Block *) { | ||
args.ensure_argc_between(env, 0, 1); | ||
auto arg = args.at(0, NilObject::the()); | ||
if (arg->is_nil()) { | ||
return generate_random(0.0, 1.0); | ||
} else { | ||
if (arg->is_float()) { | ||
double max = arg->as_float()->to_double(); | ||
if (max <= 0) { | ||
env->raise("ArgumentError", "invalid argument - {}", arg->inspect_str(env)); | ||
} | ||
return generate_random(0.0, max); | ||
} else if (arg->is_range()) { | ||
Value min = arg->as_range()->begin(); | ||
Value max = arg->as_range()->end(); | ||
// TODO: There can be different types of objects that respond to + and - (according to the docs) | ||
// I'm not sure how we should handle those though (coerce via to_int or to_f?) | ||
if (min->is_numeric() && max->is_numeric()) { | ||
if (min.send(env, ">"_s, { max })->is_true()) { | ||
env->raise("ArgumentError", "invalid argument - {}", arg->inspect_str(env)); | ||
} | ||
|
||
if (min->is_float() || max->is_float()) { | ||
double min_rand, max_rand; | ||
if (min->is_float()) { | ||
min_rand = min->as_float()->to_double(); | ||
} else { | ||
min_rand = static_cast<double>(IntegerObject::convert_to_native_type<nat_int_t>(env, min)); | ||
} | ||
|
||
if (max->is_float()) { | ||
max_rand = max->as_float()->to_double(); | ||
} else { | ||
max_rand = static_cast<double>(IntegerObject::convert_to_native_type<nat_int_t>(env, max)); | ||
} | ||
|
||
return generate_random(min_rand, max_rand); | ||
} else { | ||
nat_int_t min_rand = IntegerObject::convert_to_native_type<nat_int_t>(env, min); | ||
nat_int_t max_rand = IntegerObject::convert_to_native_type<nat_int_t>(env, max); | ||
|
||
if (arg->as_range()->exclude_end()) { | ||
max_rand -= 1; | ||
} | ||
|
||
return generate_random(min_rand, max_rand); | ||
} | ||
} | ||
env->raise("ArgumentError", "bad value for range"); | ||
} | ||
|
||
nat_int_t max = IntegerObject::convert_to_nat_int_t(env, arg); | ||
if (max <= 0) { | ||
env->raise("ArgumentError", "invalid argument - {}", arg->inspect_str(env)); | ||
} | ||
return generate_random(0, max - 1); | ||
} | ||
} | ||
|
||
Value init_securerandom(Env *env, Value self) { | ||
return NilObject::the(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
require_relative '../../spec_helper' | ||
require_relative '../../core/random/shared/rand' | ||
|
||
require 'securerandom' | ||
|
||
describe "SecureRandom.random_number" do | ||
it_behaves_like :random_number, :rand, SecureRandom | ||
it_behaves_like :random_number, :random_number, SecureRandom | ||
|
||
it "generates a random positive number smaller then the positive integer argument" do | ||
(1..64).each do |idx| | ||
num = SecureRandom.random_number(idx) | ||
num.should be_kind_of(Integer) | ||
0.should <= num | ||
num.should < idx | ||
end | ||
end | ||
|
||
it "generates a random (potentially bignum) integer value for bignum argument" do | ||
max = 12345678901234567890 | ||
NATFIXME 'it generates a random (potentially bignum) integer value for bignum argument', exception: RangeError, message: "bignum too big to convert into `long'" do | ||
11.times do | ||
num = SecureRandom.random_number max | ||
num.should be_kind_of(Integer) | ||
0.should <= num | ||
num.should < max | ||
end | ||
end | ||
end | ||
|
||
it "generates a random float number between 0.0 and 1.0 if no argument provided" do | ||
64.times do | ||
num = SecureRandom.random_number | ||
num.should be_kind_of(Float) | ||
0.0.should <= num | ||
num.should < 1.0 | ||
end | ||
end | ||
|
||
it "generates a random value in given (integer) range limits" do | ||
64.times do | ||
num = SecureRandom.random_number 11...13 | ||
num.should be_kind_of(Integer) | ||
11.should <= num | ||
num.should < 13 | ||
end | ||
end | ||
|
||
it "generates a random value in given big (integer) range limits" do | ||
lower = 12345678901234567890 | ||
upper = 12345678901234567890 + 5 | ||
NATFIXME 'it generates a random value in given big (integer) range limits', exception: RangeError, message: "bignum too big to convert into `long long int'" do | ||
32.times do | ||
num = SecureRandom.random_number lower..upper | ||
num.should be_kind_of(Integer) | ||
lower.should <= num | ||
num.should <= upper | ||
end | ||
end | ||
end | ||
|
||
it "generates a random value in given (float) range limits" do | ||
64.times do | ||
num = SecureRandom.random_number 0.6..0.9 | ||
num.should be_kind_of(Float) | ||
0.6.should <= num | ||
num.should <= 0.9 | ||
end | ||
end | ||
|
||
it "generates a random float number between 0.0 and 1.0 if argument is negative" do | ||
NATFIXME 'it generates a random float number between 0.0 and 1.0 if argument is negative', exception: ArgumentError, message: 'invalid argument - -10' do | ||
num = SecureRandom.random_number(-10) | ||
num.should be_kind_of(Float) | ||
0.0.should <= num | ||
num.should < 1.0 | ||
end | ||
end | ||
|
||
it "generates a random float number between 0.0 and 1.0 if argument is negative float" do | ||
NATFIXME 'it generates a random float number between 0.0 and 1.0 if argument is negative float', exception: ArgumentError, message: 'invalid argument - -11.1' do | ||
num = SecureRandom.random_number(-11.1) | ||
num.should be_kind_of(Float) | ||
0.0.should <= num | ||
num.should < 1.0 | ||
end | ||
end | ||
|
||
it "generates different float numbers with subsequent invocations" do | ||
# quick and dirty check, but good enough | ||
values = [] | ||
256.times do | ||
val = SecureRandom.random_number | ||
# make sure the random values are not repeating | ||
values.should_not include(val) | ||
values << val | ||
end | ||
end | ||
|
||
it "raises ArgumentError if the argument is non-numeric" do | ||
NATFIXME 'it raises ArgumentError if the argument is non-numeric', exception: SpecFailedException do | ||
-> { | ||
SecureRandom.random_number(Object.new) | ||
}.should raise_error(ArgumentError) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters