pool_of_entropy 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 512c263491f2d05bebe3e96ac23d1a3c0e3d49e9
4
- data.tar.gz: 9833ccfc16899dc106a5839e739a435bbac3b2b4
2
+ SHA256:
3
+ metadata.gz: 4cf2426e234e3234f458defe0d59aadda7b95e08b3161a3edc5c79fed8f829dc
4
+ data.tar.gz: 5573c75b6a5a565296c4271c1e32acc0e14989996bc3b85f660f94072f0e7d38
5
5
  SHA512:
6
- metadata.gz: 971e11a66fa0b70be64e5d4a27d72aa6786552fa7dcd4f790bfdc8d8253486102db7cfb668583566951d04303dedf9f6664c0d4ca5b91881c84351ad7d263f61
7
- data.tar.gz: a481cb8eef794fe07692743c007ac190c68e37d5eebaaec9645f7f52bdfc7c02815068b3672d63ec5468aee1f1e080f8417000e11f244c12c64a7ab371be4ed3
6
+ metadata.gz: 6bb69625454353f670b3fe6e12a8cf8d70b2bf11aeeb145e6d3fea93a3a967f8bccb0a985bc3060b6c856a7d794bcc83a9c206f87a2b10f99ccc2af989b95c37
7
+ data.tar.gz: e03b6919f0ca301e74c3c7255a4e8302381f763e04f199dcc144d5e0a3601b560b9bdd70769e62dd74625e2d3e481b7eb9185c913814c7c29d4508284ea65cd0
@@ -0,0 +1,23 @@
1
+ name: RSpec
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ jobs:
7
+ test:
8
+ runs-on: ubuntu-latest
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ ruby-version:
13
+ - '3.3'
14
+ - '3.4'
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby-version }}
21
+ bundler-cache: true
22
+ - run: bundle exec rubocop
23
+ - run: bundle exec rspec
data/.rubocop.yml ADDED
@@ -0,0 +1,18 @@
1
+ plugins:
2
+ - rubocop-rake
3
+ - rubocop-rspec
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 3.3
7
+ NewCops: enable
8
+
9
+ Layout/LineLength:
10
+ Max: 120
11
+
12
+ Metrics/BlockLength:
13
+ Exclude:
14
+ - spec/**/*
15
+ AllowedMethods:
16
+ - context
17
+ - describe
18
+ - shared_examples
data/Gemfile CHANGED
@@ -1,4 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
- # Dependencies are in pool_of_entropy.gemspec
4
5
  gemspec
6
+
7
+ gem 'bundler', '>= 2.5', '< 5'
8
+ gem 'rake', '~> 13.2'
9
+ gem 'rspec', '~> 3.13'
10
+ gem 'rubocop', '~> 1.88'
11
+ gem 'rubocop-rake', '~> 0.7'
12
+ gem 'rubocop-rspec', '~> 3.10'
13
+ gem 'simplecov', '~> 1.0'
14
+ gem 'yard', '~> 0.9.37'
data/README.md CHANGED
@@ -1,10 +1,5 @@
1
1
  # PoolOfEntropy
2
2
  [![Gem Version](https://badge.fury.io/rb/pool_of_entropy.png)](http://badge.fury.io/rb/pool_of_entropy)
3
- [![Build Status](https://travis-ci.org/neilslater/pool_of_entropy.png?branch=master)](http://travis-ci.org/neilslater/pool_of_entropy)
4
- [![Coverage Status](https://coveralls.io/repos/neilslater/pool_of_entropy/badge.png?branch=master)](https://coveralls.io/r/neilslater/pool_of_entropy?branch=master)
5
- [![Inline docs](http://inch-ci.org/github/neilslater/pool_of_entropy.png?branch=master)](http://inch-ci.org/github/neilslater/pool_of_entropy)
6
- [![Code Climate](https://codeclimate.com/github/neilslater/pool_of_entropy.png)](https://codeclimate.com/github/neilslater/pool_of_entropy)
7
- [![Dependency Status](https://gemnasium.com/neilslater/pool_of_entropy.png)](https://gemnasium.com/neilslater/pool_of_entropy)
8
3
 
9
4
  PoolOfEntropy is a pseudo random number generator (PRNG) based on secure hashes,
10
5
  intended to bring back the feeling of 'personal agency' that some gamers may feel when rolling
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
3
2
 
4
- desc "PoolOfEntropy unit tests"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc 'PoolOfEntropy unit tests'
5
7
  RSpec::Core::RakeTask.new(:test) do |t|
6
- t.pattern = "spec/*_spec.rb"
8
+ t.pattern = 'spec/**/*_spec.rb'
7
9
  t.verbose = false
8
10
  end
9
11
 
10
- task :default => [:test]
12
+ task default: [:test]
@@ -1,165 +1,171 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest/sha2'
2
4
  require 'securerandom'
3
5
 
4
- # This class implements a random number generator based on SHA-512
5
- #
6
- # An object of the class has internal state that is modified on each call to
7
- # #update or any #read_... method (internally the #read_... methods call #update).
8
- # The #read_... methods generate a pseudo-random number from the current
9
- # state pool using SHA-512 and use it in the return value. It is not feasible to
10
- # determine the internal state from the pseudo-random data received, and not
11
- # possible to manipulate results from the PRNG in a predictable manner without
12
- # knowing the internal state.
13
- #
14
- # @example Using default internal state, initialised using SecureRandom.random_bytes
15
- # prng = PoolOfEntropy::CorePRNG.new
16
- # prng.read_bytes
17
- # # E.g. => "]\x12\x9E\xF5\x17\xF3\xC2\x1A\x15\xDFu]\x95\nd\x12"
18
- # prng.read_hex
19
- # # E.g. => "a0e00d2848242ec49e0a15ef411ba647"
20
- # prng.read_bignum
21
- # # E.g. => 33857278877368906880463811096418580004
22
- # prng.read_float
23
- # # E.g. => 0.6619838265836278
24
- # prng.generate_integer( 20 )
25
- # # E.g. => 7
26
- #
27
-
28
- class PoolOfEntropy::CorePRNG
29
-
30
- # Creates a new random number source. All parameters are optional.
31
- # @param [Integer] size Number of 64-byte blocks, between 1 and 256 (16KB)
32
- # @param [String] initial_state Sets contents of state pool (default uses SecureRandom)
33
- # @param [Integer] mix_block_id
34
- # @return [PoolOfEntropy::CorePRNG]
35
- def initialize size = 1, initial_state = SecureRandom.random_bytes( 64 * Integer(size) ), mix_block_id = 0
36
- @size = validate_size( size )
37
- @state = validate_state( initial_state, size )
38
- @mix_block_id = Integer( mix_block_id ) % @size
39
- end
40
-
41
- # The number of 64-byte blocks used in the internal state pool.
42
- # @return [Integer]
43
- attr_reader :size
44
-
45
- # Identifies the next 64-byte block in the pool that will be altered
46
- # by a read or update process.
47
- # @return [Integer]
48
- attr_reader :mix_block_id
49
-
50
- # A clone of the internal state pool. In combination with #mix_block_id, describes
51
- # the whole PRNG. If this value is supplied to an end user, then they can easily
52
- # predict future values of the PRNG.
53
- # @return [PoolOfEntropy::CorePRNG]
54
- def state
55
- @state.clone
56
- end
57
-
58
- # The clone of a PoolOfEntropy::CorePRNG object includes separate copy of internal state
59
- # @return [PoolOfEntropy::CorePRNG]
60
- def clone
61
- PoolOfEntropy::CorePRNG.new( self.size, self.state, self.mix_block_id )
62
- end
6
+ class PoolOfEntropy
7
+ # This class implements a random number generator based on SHA-512
8
+ #
9
+ # An object of the class has internal state that is modified on each call to
10
+ # #update or any #read_... method (internally the #read_... methods call #update).
11
+ # The #read_... methods generate a pseudo-random number from the current
12
+ # state pool using SHA-512 and use it in the return value. It is not feasible to
13
+ # determine the internal state from the pseudo-random data received, and not
14
+ # possible to manipulate results from the PRNG in a predictable manner without
15
+ # knowing the internal state.
16
+ #
17
+ # @example Using default internal state, initialised using SecureRandom.random_bytes
18
+ # prng = PoolOfEntropy::CorePRNG.new
19
+ # prng.read_bytes
20
+ # # E.g. => "]\x12\x9E\xF5\x17\xF3\xC2\x1A\x15\xDFu]\x95\nd\x12"
21
+ # prng.read_hex
22
+ # # E.g. => "a0e00d2848242ec49e0a15ef411ba647"
23
+ # prng.read_bignum
24
+ # # E.g. => 33857278877368906880463811096418580004
25
+ # prng.read_float
26
+ # # E.g. => 0.6619838265836278
27
+ # prng.generate_integer( 20 )
28
+ # # E.g. => 7
29
+ #
30
+ class CorePRNG
31
+ # Creates a new random number source. All parameters are optional.
32
+ # @param [Integer] size Number of 64-byte blocks, between 1 and 256 (16KB)
33
+ # @param [String] initial_state Sets contents of state pool (default uses SecureRandom)
34
+ # @param [Integer] mix_block_id
35
+ # @return [PoolOfEntropy::CorePRNG]
36
+ def initialize(size = 1, initial_state = SecureRandom.random_bytes(64 * Integer(size)), mix_block_id = 0)
37
+ @size = validate_size(size)
38
+ @state = validate_state(initial_state, size)
39
+ @mix_block_id = Integer(mix_block_id) % @size
40
+ end
63
41
 
64
- # Mixes supplied data into the curent state. This is called
65
- # internally by #read_... methods as well.
66
- # @param [String] data Data to be mixed. Note empty string '' and nil are equivalent and *do* change the state.
67
- # @return [nil]
68
- def update data
69
- new_block = Digest::SHA512.digest( @state + data.to_s )
70
- @state[64*@mix_block_id,64] = new_block
71
- @mix_block_id = (@mix_block_id + 1) % @size
72
- nil
73
- end
42
+ # The number of 64-byte blocks used in the internal state pool.
43
+ # @return [Integer]
44
+ attr_reader :size
45
+
46
+ # Identifies the next 64-byte block in the pool that will be altered
47
+ # by a read or update process.
48
+ # @return [Integer]
49
+ attr_reader :mix_block_id
50
+
51
+ # A clone of the internal state pool. In combination with #mix_block_id, describes
52
+ # the whole PRNG. If this value is supplied to an end user, then they can easily
53
+ # predict future values of the PRNG.
54
+ # @return [PoolOfEntropy::CorePRNG]
55
+ def state
56
+ @state.clone
57
+ end
74
58
 
75
- # Statistically flat distribution of 128 bits (16 bytes)
76
- # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect return value, but not internal state
77
- # @return [String] 16 characters in ASCII-8BIT encoding
78
- def read_bytes *adjustments
79
- raw_digest = Digest::SHA512.digest( @state )
80
- self.update( raw_digest )
81
- adjustments.compact.each do |adjust|
82
- raw_digest = Digest::SHA512.digest( raw_digest + adjust )
59
+ # The clone of a PoolOfEntropy::CorePRNG object includes separate copy of internal state
60
+ # @return [PoolOfEntropy::CorePRNG]
61
+ def clone
62
+ PoolOfEntropy::CorePRNG.new(size, state, mix_block_id)
83
63
  end
84
- fold_bits( fold_bits( raw_digest ) )
85
- end
86
64
 
87
- # Statistically flat distribution of 32 hex digits
88
- # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect return value, but not internal state
89
- # @return [String] 32 hex digits
90
- def read_hex *adjustments
91
- read_bytes( *adjustments ).unpack('H*').first
92
- end
65
+ # Mixes supplied data into the curent state. This is called
66
+ # internally by #read_... methods as well.
67
+ # @param [String] data Data to be mixed. Note empty string '' and nil are equivalent and *do* change the state.
68
+ # @return [nil]
69
+ def update(data)
70
+ new_block = Digest::SHA512.digest(@state + data.to_s)
71
+ @state[64 * @mix_block_id, 64] = new_block
72
+ @mix_block_id = (@mix_block_id + 1) % @size
73
+ nil
74
+ end
93
75
 
94
- # Statistically flat distribution from range 0...2**128
95
- # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect return value, but not internal state
96
- # @return [Bignum,Fixnum] between 0 and 0xffffffffffffffffffffffffffffffff
97
- def read_bignum *adjustments
98
- nums = read_bytes( *adjustments ).unpack('Q>*')
99
- nums.inject(0) { |sum,v| (sum << 64) + v }
100
- end
76
+ # Statistically flat distribution of 128 bits (16 bytes)
77
+ # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect
78
+ # the return value, but not internal state
79
+ # @return [String] 16 characters in ASCII-8BIT encoding
80
+ def read_bytes(*adjustments)
81
+ raw_digest = Digest::SHA512.digest(@state)
82
+ update(raw_digest)
83
+ adjustments.compact.each do |adjust|
84
+ raw_digest = Digest::SHA512.digest(raw_digest + adjust)
85
+ end
86
+ fold_bits(fold_bits(raw_digest))
87
+ end
101
88
 
102
- # Statistically flat distribution from interval 0.0...1.0, with 53-bit precision
103
- # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect return value, but not internal state
104
- # @return [Float] between 0.0 and 0.9999999999999999
105
- def read_float *adjustments
106
- num = read_bytes( *adjustments ).unpack('Q>*').first >> 11
107
- num.to_f / 2 ** 53
108
- end
89
+ # Statistically flat distribution of 32 hex digits
90
+ # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect
91
+ # the return value, but not internal state
92
+ # @return [String] 32 hex digits
93
+ def read_hex(*adjustments)
94
+ read_bytes(*adjustments).unpack1('H*')
95
+ end
109
96
 
110
- # Statistically flat distribution from range (0...top).
111
- # If necessary, it will read more data to ensure absolute fairness. This method
112
- # can generate an unbiased distribution of Bignums up to roughly half the maximum bit size
113
- # allowed by Ruby (i.e. much larger than 2**128 generated in a single read)
114
- # @param [Fixnum,Bignum] top upper bound of distribution, not inclusive
115
- # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect return value, but not internal state
116
- # @return [Fixnum,Bignum] between 0 and top-1 inclusive
117
- def generate_integer top, *adjustments
118
- power = 1
119
- sum = 0
120
- words = []
121
-
122
- loop do
123
- words = read_bytes( *adjustments ).unpack('L>*') if words.empty?
124
- sum = 2**32 * sum + words.shift
125
- power *= 2**32
126
- lower_bound = sum * top / power
127
- break lower_bound if lower_bound == ( (sum + 1) * top ) / power
97
+ # Statistically flat distribution from range 0...2**128
98
+ # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect
99
+ # the return value, but not internal state
100
+ # @return [Bignum,Fixnum] between 0 and 0xffffffffffffffffffffffffffffffff
101
+ def read_bignum(*adjustments)
102
+ nums = read_bytes(*adjustments).unpack('Q>*')
103
+ nums.inject(0) { |sum, v| (sum << 64) + v }
128
104
  end
129
- end
130
105
 
131
- private
106
+ # Statistically flat distribution from interval 0.0...1.0, with 53-bit precision
107
+ # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect
108
+ # the return value, but not internal state
109
+ # @return [Float] between 0.0 and 0.9999999999999999
110
+ def read_float(*adjustments)
111
+ num = read_bytes(*adjustments).unpack1('Q>*') >> 11
112
+ num.to_f / (2**53)
113
+ end
132
114
 
133
- # Xors first half of a message with second half. Only works for messages
134
- # which are multiples of 8 bytes long.
135
- # @param [String] msg bytes to fold
136
- # @return [String] folded message, half the length of original
137
- def fold_bits msg
138
- l = msg.length/2
139
- folded_32bits = msg[0,l].unpack('L>*').zip( msg[l,l].unpack('L>*') ).map do |x,y|
140
- x ^ y
115
+ # Statistically flat distribution from range (0...top).
116
+ # If necessary, it will read more data to ensure absolute fairness. This method
117
+ # can generate an unbiased distribution of Bignums up to roughly half the maximum bit size
118
+ # allowed by Ruby (i.e. much larger than 2**128 generated in a single read)
119
+ # @param [Fixnum,Bignum] top upper bound of distribution, not inclusive
120
+ # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect
121
+ # the return value, but not internal state
122
+ # @return [Fixnum,Bignum] between 0 and top-1 inclusive
123
+ def generate_integer(top, *adjustments)
124
+ power = 1
125
+ sum = 0
126
+ words = []
127
+
128
+ loop do
129
+ words = read_bytes(*adjustments).unpack('L>*') if words.empty?
130
+ sum = ((2**32) * sum) + words.shift
131
+ power *= 2**32
132
+ lower_bound = sum * top / power
133
+ break lower_bound if lower_bound == ((sum + 1) * top) / power
134
+ end
141
135
  end
142
- folded_32bits.pack('L>*')
143
- end
144
136
 
145
- def validate_size i
146
- size = Integer( i )
147
- if size < 1 || size > 256
148
- raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}"
137
+ private
138
+
139
+ # Xors first half of a message with second half. Only works for messages
140
+ # which are multiples of 8 bytes long.
141
+ # @param [String] msg bytes to fold
142
+ # @return [String] folded message, half the length of original
143
+ def fold_bits(msg)
144
+ l = msg.length / 2
145
+ folded_32bits = msg[0, l].unpack('L>*').zip(msg[l, l].unpack('L>*')).map do |x, y|
146
+ x ^ y
147
+ end
148
+ folded_32bits.pack('L>*')
149
149
  end
150
- size
151
- end
152
150
 
153
- def validate_state( initial_state, size )
154
- unless initial_state.is_a? String
155
- raise TypeError, "Initial state must be a String, got #{initial_state.inspect}"
151
+ def validate_size(value)
152
+ size = Integer(value)
153
+ raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}" if size < 1 || size > 256
154
+
155
+ size
156
156
  end
157
- state = initial_state.clone
158
- state.force_encoding( 'BINARY' )
159
157
 
160
- if state.size != size * 64
161
- raise ArgumentError, "Initial state bad size - expected #{size * 64} bytes, got #{state.size} bytes"
158
+ def validate_state(initial_state, size)
159
+ raise TypeError, "Initial state must be a String, got #{initial_state.inspect}" unless initial_state.is_a? String
160
+
161
+ state = initial_state.clone
162
+ state.force_encoding('BINARY')
163
+
164
+ if state.size != size * 64
165
+ raise ArgumentError, "Initial state bad size - expected #{size * 64} bytes, got #{state.size} bytes"
166
+ end
167
+
168
+ state
162
169
  end
163
- state
164
170
  end
165
171
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class PoolOfEntropy
2
- VERSION = "0.0.4"
4
+ VERSION = '0.0.5'
3
5
  end
@@ -1,5 +1,7 @@
1
- require "pool_of_entropy/version"
2
- require "pool_of_entropy/core_prng"
1
+ # frozen_string_literal: true
2
+
3
+ require 'pool_of_entropy/version'
4
+ require 'pool_of_entropy/core_prng'
3
5
 
4
6
  # This class models a random number generator that can mix user input into
5
7
  # the generation mechanism in a few different ways.
@@ -20,28 +22,23 @@ require "pool_of_entropy/core_prng"
20
22
  # # E.g. => 12
21
23
  # sleep 5
22
24
  # end
23
- #
24
-
25
25
  class PoolOfEntropy
26
-
27
26
  # Creates a new random number source. All parameters are optional.
28
27
  # @param [Hash] options
29
28
  # @option options [Integer] :size, number of 512-bit (64 byte) blocks to use as internal state, defaults to 1
30
29
  # @option options [Boolean] :blank, if true then initial state is all zeroes, otherwise use SecureRandom
31
30
  # @option options [Array<String>] :seeds, if provided these are sent to #add_to_pool during initialize
32
31
  # @return [PoolOfEntropy]
33
- def initialize options = {}
34
- unless options.is_a? Hash
35
- raise TypeError, "Expecting an options hash, got #{options.inspect}"
36
- end
32
+ def initialize(options = {})
33
+ raise TypeError, "Expecting an options hash, got #{options.inspect}" unless options.is_a? Hash
37
34
 
38
- size = size_from_options( options )
35
+ size = size_from_options(options)
39
36
 
40
- initial_state = state_from_options( options, size )
37
+ initial_state = state_from_options(options, size)
41
38
 
42
- @core_prng = CorePRNG.new( size, initial_state )
39
+ @core_prng = CorePRNG.new(size, initial_state)
43
40
 
44
- seed_from_options( options )
41
+ seed_from_options(options)
45
42
 
46
43
  @next_modifier_queue = []
47
44
  @fixed_modifier = nil
@@ -51,9 +48,9 @@ class PoolOfEntropy
51
48
  # @return [PoolOfEntropy]
52
49
  def clone
53
50
  copy = super
54
- copy.instance_variable_set( :@core_prng, @core_prng.clone )
55
- copy.instance_variable_set( :@fixed_modifier, @fixed_modifier.clone ) if @fixed_modifier
56
- copy.instance_variable_set( :@next_modifier_queue, @next_modifier_queue.map { |m| m.clone } )
51
+ copy.instance_variable_set(:@core_prng, @core_prng.clone)
52
+ copy.instance_variable_set(:@fixed_modifier, @fixed_modifier.clone) if @fixed_modifier
53
+ copy.instance_variable_set(:@next_modifier_queue, @next_modifier_queue.map(&:clone))
57
54
  copy
58
55
  end
59
56
 
@@ -62,19 +59,14 @@ class PoolOfEntropy
62
59
  # two modifiers that are in effect.
63
60
  # @param [Integer,Range] max if 0 then will return a Float
64
61
  # @return [Float,Fixnum,Bignum] type depends on value of max
65
- def rand max = 0
66
- if max.is_a? Range
67
- bottom = max.first
68
- top = max.last
69
- return( nil ) if top < bottom
70
- return bottom + generate_integer( ( top - bottom + 1 ) )
62
+ def rand(max = 0)
63
+ return rand_from_range(max) if max.is_a? Range
64
+
65
+ effective_max = max.to_i.abs
66
+ if effective_max.zero?
67
+ generate_float
71
68
  else
72
- effective_max = max.to_i.abs
73
- if effective_max == 0
74
- return generate_float
75
- else
76
- return generate_integer( effective_max )
77
- end
69
+ generate_integer(effective_max)
78
70
  end
79
71
  end
80
72
 
@@ -85,13 +77,13 @@ class PoolOfEntropy
85
77
  # affect the internal state of the data pool used by the generator.
86
78
  # @param [Array<String>] modifiers
87
79
  # @return [PoolOfEntropy] self
88
- def modify_next *modifiers
80
+ def modify_next(*modifiers)
89
81
  modifiers.each do |modifier|
90
- if modifier.nil?
91
- @next_modifier_queue << nil
92
- else
93
- @next_modifier_queue << Digest::SHA512.digest( modifier.to_s )
94
- end
82
+ @next_modifier_queue << if modifier.nil?
83
+ nil
84
+ else
85
+ Digest::SHA512.digest(modifier.to_s)
86
+ end
95
87
  end
96
88
  self
97
89
  end
@@ -104,11 +96,9 @@ class PoolOfEntropy
104
96
  # affect the internal state of the data pool used by the generator.
105
97
  # @param [String,nil] modifier
106
98
  # @return [PoolOfEntropy] self
107
- def modify_all modifier
99
+ def modify_all(modifier)
108
100
  @fixed_modifier = modifier
109
- unless @fixed_modifier.nil?
110
- @fixed_modifier = Digest::SHA512.digest( @fixed_modifier.to_s )
111
- end
101
+ @fixed_modifier = Digest::SHA512.digest(@fixed_modifier.to_s) unless @fixed_modifier.nil?
112
102
  self
113
103
  end
114
104
 
@@ -117,8 +107,8 @@ class PoolOfEntropy
117
107
  # from #rand() and cannot be undone.
118
108
  # @param [String] data
119
109
  # @return [PoolOfEntropy] self
120
- def add_to_pool data
121
- @core_prng.update( data )
110
+ def add_to_pool(data)
111
+ @core_prng.update(data)
122
112
  self
123
113
  end
124
114
 
@@ -132,45 +122,51 @@ class PoolOfEntropy
132
122
 
133
123
  private
134
124
 
125
+ def rand_from_range(range)
126
+ bottom = range.first
127
+ top = range.last
128
+ return nil if top < bottom
129
+
130
+ bottom + generate_integer(top - bottom + 1)
131
+ end
132
+
135
133
  def use_adjustments
136
- [ @fixed_modifier, @next_modifier_queue.shift ].compact
134
+ [@fixed_modifier, @next_modifier_queue.shift].compact
137
135
  end
138
136
 
139
137
  def generate_float
140
- @core_prng.read_float( *use_adjustments )
138
+ @core_prng.read_float(*use_adjustments)
141
139
  end
142
140
 
143
- def generate_integer max
144
- @core_prng.generate_integer( max, *use_adjustments )
141
+ def generate_integer(max)
142
+ @core_prng.generate_integer(max, *use_adjustments)
145
143
  end
146
144
 
147
- def state_from_options( options, size )
145
+ def state_from_options(options, size)
148
146
  if options[:blank]
149
147
  "\x0" * size * 64
150
148
  else
151
- SecureRandom.random_bytes( size * 64 )
149
+ SecureRandom.random_bytes(size * 64)
152
150
  end
153
151
  end
154
152
 
155
- def size_from_options( options )
153
+ def size_from_options(options)
156
154
  size = 1
157
155
  if options[:size]
158
- size = Integer( options[:size] )
159
- if size < 1 || size > 256
160
- raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}"
161
- end
156
+ size = Integer(options[:size])
157
+ raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}" if size < 1 || size > 256
162
158
  end
163
159
  size
164
160
  end
165
161
 
166
- def seed_from_options( options )
167
- if options[:seeds]
168
- unless options[:seeds].is_a? Array
169
- raise TypeError, "Expected value for :seeds to be an Array, got #{options[:seeds].inspect}"
170
- end
171
- options[:seeds].each do |seed|
172
- add_to_pool( seed )
173
- end
162
+ def seed_from_options(options)
163
+ return unless options[:seeds]
164
+ unless options[:seeds].is_a? Array
165
+ raise TypeError, "Expected value for :seeds to be an Array, got #{options[:seeds].inspect}"
166
+ end
167
+
168
+ options[:seeds].each do |seed|
169
+ add_to_pool(seed)
174
170
  end
175
171
  end
176
172
  end