tokogen 0.1.2 → 0.2.1
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 +4 -4
- data/.rubocop.yml +6 -2
- data/.travis.yml +2 -0
- data/Guardfile +15 -0
- data/README.md +2 -0
- data/bin/bench_ruby_binary +26 -0
- data/bin/bench_ruby_comparisons +28 -0
- data/exe/tokogen +6 -0
- data/lib/tokogen.rb +5 -2
- data/lib/tokogen/alphabet.rb +17 -0
- data/lib/tokogen/bit_combiner.rb +28 -0
- data/lib/tokogen/bit_splitter.rb +27 -0
- data/lib/tokogen/generator.rb +29 -39
- data/lib/tokogen/version.rb +1 -1
- data/tokogen.gemspec +2 -0
- metadata +39 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4935595f4088dbbf55cf945d762542593fd849e2
|
4
|
+
data.tar.gz: 6cf358d6f950178c266f9f1102822586c2e9e14d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 693e975d0e678c26e8eb5ecb8721fbc795573d834c8f4528b5f1f1af31c462646f148a2c06c9873d3af130e07b6fb27d029001fe51fcaa6a7405b97907ee03df
|
7
|
+
data.tar.gz: e1961e28a51c691c6c0201bc25009d5c9c664ed4a54860269988a4b92c407af63736eef1a0c3de3ed61e3e3c027051ceb75cd48f57cd8c8eee8a413bac96c2c7
|
data/.rubocop.yml
CHANGED
@@ -5,6 +5,7 @@ AllCops:
|
|
5
5
|
- '**/*.gemspec'
|
6
6
|
- '**/Gemfile'
|
7
7
|
- '**/Rakefile'
|
8
|
+
- '**/Guardfile'
|
8
9
|
- '**/*.rake'
|
9
10
|
Rails:
|
10
11
|
Enabled: false
|
@@ -25,14 +26,17 @@ Style/IndentHash:
|
|
25
26
|
Style/FirstParameterIndentation:
|
26
27
|
EnforcedStyle: consistent
|
27
28
|
Style/CaseIndentation:
|
28
|
-
|
29
|
+
EnforcedStyle: end
|
29
30
|
Style/Alias:
|
30
31
|
EnforcedStyle: prefer_alias_method
|
31
32
|
Lint/EndAlignment:
|
32
|
-
|
33
|
+
EnforcedStyleAlignWith: variable
|
33
34
|
Style/MultilineMethodCallIndentation:
|
34
35
|
EnforcedStyle: indented
|
35
36
|
Style/SpaceInLambdaLiteral:
|
36
37
|
Enabled: false
|
37
38
|
Bundler/OrderedGems:
|
38
39
|
Enabled: false
|
40
|
+
Metrics/BlockLength:
|
41
|
+
Exclude:
|
42
|
+
- 'spec/**/*.rb'
|
data/.travis.yml
CHANGED
data/Guardfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
3
|
+
require 'guard/rspec/dsl'
|
4
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
5
|
+
|
6
|
+
# RSpec files
|
7
|
+
rspec = dsl.rspec
|
8
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
9
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
10
|
+
watch(rspec.spec_files)
|
11
|
+
|
12
|
+
# Ruby files
|
13
|
+
ruby = dsl.ruby
|
14
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
15
|
+
end
|
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# Tokogen
|
2
|
+
[](https://travis-ci.org/MOZGIII/tokogen)
|
3
|
+
[](https://badge.fury.io/rb/tokogen)
|
2
4
|
|
3
5
|
A ruby gem that allows you to generate customizable random tokens.
|
4
6
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
TIMES = 500_000
|
6
|
+
|
7
|
+
puts '# Sequential'
|
8
|
+
puts
|
9
|
+
|
10
|
+
Benchmark.bmbm(12) do |b|
|
11
|
+
b.report('(1 << x) - 1') { TIMES.times { |x| (1 << x) - 1 } }
|
12
|
+
b.report('~(-1 << x)') { TIMES.times { |x| ~(-1 << x) } }
|
13
|
+
end
|
14
|
+
|
15
|
+
puts
|
16
|
+
|
17
|
+
puts '# Random'
|
18
|
+
puts
|
19
|
+
|
20
|
+
top = 1 << 32
|
21
|
+
numbers = Array.new(TIMES) { Random::DEFAULT.rand(top) }
|
22
|
+
|
23
|
+
Benchmark.bmbm(12) do |b|
|
24
|
+
b.report('(1 << x) - 1') { numbers.each { |x| (1 << x) - 1 } }
|
25
|
+
b.report('~(-1 << x)') { numbers.each { |x| ~(-1 << x) } }
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
TIMES = 100_000_000
|
6
|
+
|
7
|
+
puts '# Sequential'
|
8
|
+
puts
|
9
|
+
|
10
|
+
Benchmark.bmbm(12) do |b|
|
11
|
+
b.report('x <= 255') { TIMES.times { |x| x <= 255 } }
|
12
|
+
b.report('x < 256') { TIMES.times { |x| x < 256 } }
|
13
|
+
b.report('x.bit_length <= 8') { TIMES.times { |x| x.bit_length <= 8 } }
|
14
|
+
end
|
15
|
+
|
16
|
+
puts
|
17
|
+
|
18
|
+
puts '# Random'
|
19
|
+
puts
|
20
|
+
|
21
|
+
top = 1 << 32
|
22
|
+
numbers = Array.new(TIMES) { Random::DEFAULT.rand(top) }
|
23
|
+
|
24
|
+
Benchmark.bmbm(12) do |b|
|
25
|
+
b.report('x <= 255') { numbers.each { |x| x <= 255 } }
|
26
|
+
b.report('x < 256') { numbers.each { |x| x < 256 } }
|
27
|
+
b.report('x.bit_length <= 8') { numbers.each { |x| x.bit_length <= 8 } }
|
28
|
+
end
|
data/exe/tokogen
ADDED
data/lib/tokogen.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'securerandom'
|
3
3
|
require 'tokogen/version'
|
4
|
+
require 'tokogen/bit_splitter'
|
5
|
+
require 'tokogen/bit_combiner'
|
4
6
|
require 'tokogen/generator'
|
7
|
+
require 'tokogen/alphabet'
|
5
8
|
|
6
9
|
module Tokogen
|
7
10
|
def self.token(length = 32)
|
@@ -12,7 +15,7 @@ module Tokogen
|
|
12
15
|
generator # calling explicitly without arguments
|
13
16
|
end
|
14
17
|
|
15
|
-
def self.generator(randomness_source: SecureRandom,
|
16
|
-
Generator.new(randomness_source: randomness_source,
|
18
|
+
def self.generator(randomness_source: SecureRandom, alphabet: Alphabet::BASE62)
|
19
|
+
Generator.new(randomness_source: randomness_source, alphabet: alphabet)
|
17
20
|
end
|
18
21
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Tokogen
|
3
|
+
module Alphabet
|
4
|
+
BASE62 = (('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a).join.freeze
|
5
|
+
BASE58 = (('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a - %w(0 O I l)).join.freeze
|
6
|
+
BASE64 = (('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/']).join.freeze
|
7
|
+
|
8
|
+
# Note: This is NOT ASCII85 coding alphabet, it's from RFC1924.
|
9
|
+
# Do not use in unless you're sure you need it!
|
10
|
+
BASE85 = (('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['!', '#', '$', '%', '&', '(', ')', '*', '+', '-', ';', '<', '=', '>', '?', '@', '^', '_', '`', '{', '|', '}', '~']).join.freeze
|
11
|
+
|
12
|
+
# Some simple alphabets.
|
13
|
+
# Mainly used for testing but you can use them too.
|
14
|
+
BASE2 = ('0'..'1').to_a.join.freeze
|
15
|
+
BASE3 = ('0'..'2').to_a.join.freeze
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Tokogen
|
3
|
+
class BitCombiner
|
4
|
+
attr_reader :size
|
5
|
+
|
6
|
+
def initialize(bits_enum, width_in_bits)
|
7
|
+
@bits_enum = bits_enum
|
8
|
+
@width_in_bits = width_in_bits
|
9
|
+
|
10
|
+
bits_enum_size = @bits_enum.size
|
11
|
+
@size = bits_enum_size / @width_in_bits if bits_enum_size
|
12
|
+
end
|
13
|
+
|
14
|
+
def each(&block) # rubocop:disable Metrics/MethodLength
|
15
|
+
enum = Enumerator.new(@size) do |y|
|
16
|
+
@bits_enum.each_slice(@width_in_bits) do |slice|
|
17
|
+
num = 0
|
18
|
+
slice.size.times do |i|
|
19
|
+
num |= (slice[i] << i)
|
20
|
+
end
|
21
|
+
y.yield(num)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
return enum if block.nil?
|
25
|
+
enum.each(&block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Tokogen
|
3
|
+
class BitSplitter
|
4
|
+
BYTE_SIZE = 8
|
5
|
+
|
6
|
+
attr_reader :size
|
7
|
+
|
8
|
+
def initialize(bytes_enum)
|
9
|
+
@bytes_enum = bytes_enum
|
10
|
+
|
11
|
+
bytes_enum_size = @bytes_enum.size
|
12
|
+
@size = bytes_enum_size * BYTE_SIZE if bytes_enum_size
|
13
|
+
end
|
14
|
+
|
15
|
+
def each(&block)
|
16
|
+
enum = Enumerator.new(@bytes_enum_bit_size) do |y|
|
17
|
+
@bytes_enum.each do |b|
|
18
|
+
BYTE_SIZE.times do |i|
|
19
|
+
y.yield(b[i])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
return enum if block.nil?
|
24
|
+
enum.each(&block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/tokogen/generator.rb
CHANGED
@@ -1,67 +1,57 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Tokogen
|
3
3
|
class Generator
|
4
|
-
|
4
|
+
class AssertionFail < StandardError; end
|
5
5
|
|
6
|
-
attr_reader :randomness_source
|
6
|
+
attr_reader :randomness_source, :alphabet
|
7
7
|
|
8
|
-
def initialize(randomness_source:, alphabet:
|
8
|
+
def initialize(randomness_source:, alphabet:)
|
9
9
|
@randomness_source = randomness_source
|
10
10
|
@alphabet = alphabet
|
11
|
+
|
12
|
+
@alphabet_size = @alphabet.size
|
13
|
+
@max_char_index = @alphabet_size - 1
|
14
|
+
@bits_per_char = @max_char_index.bit_length
|
11
15
|
end
|
12
16
|
|
13
|
-
def generate(length)
|
14
|
-
token_bits_amount = length * bits_per_char
|
17
|
+
def generate(length) # rubocop:disable Metrics/AbcSize
|
18
|
+
token_bits_amount = length * @bits_per_char
|
15
19
|
bytes_to_read = full_bytes_in_bits(token_bits_amount)
|
16
20
|
bytes = random_bytes(bytes_to_read)
|
17
|
-
|
21
|
+
splitter = BitSplitter.new(bytes.each_byte)
|
22
|
+
combiner = BitCombiner.new(splitter.each, @bits_per_char)
|
18
23
|
# It's possible we've read a couple exta bits of randomness,
|
19
24
|
# since randomness is rounded to bytes.
|
20
25
|
# Here we only take first `length` of bit that we need.
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
.
|
26
|
+
indexes = combiner.each.take(length)
|
27
|
+
raise AssertionFail, 'Invalid length' if indexes.size != length
|
28
|
+
indexes.map do |index|
|
29
|
+
# We split out random data into chunks of bits with fixed length.
|
30
|
+
# Therefore it's possible to have an index value that is larger than
|
31
|
+
# an alphabet size.
|
32
|
+
# In this case we'd resolve to nil, so we're just using modulo of the
|
33
|
+
# alphabet size. This will probably ruin the distribution that
|
34
|
+
# the randromness source provides, but it will at least work.
|
35
|
+
# If you don't want this behavior, just ensure you're using an alphabet
|
36
|
+
# with an even size - then there will always be a bijection between
|
37
|
+
# the generated indicies and the alphabet and the described issue
|
38
|
+
# will never occur.
|
39
|
+
alphabet_char(index % @alphabet_size)
|
40
|
+
end.join
|
25
41
|
end
|
26
42
|
|
27
43
|
def random_bytes(size)
|
28
44
|
@randomness_source.random_bytes(size)
|
29
45
|
end
|
30
46
|
|
31
|
-
def
|
32
|
-
@alphabet
|
47
|
+
def alphabet_char(index)
|
48
|
+
@alphabet[index]
|
33
49
|
end
|
34
50
|
|
35
|
-
|
36
|
-
max_char_index.bit_length
|
37
|
-
end
|
51
|
+
private
|
38
52
|
|
39
53
|
def full_bytes_in_bits(bits)
|
40
54
|
(bits + 7) >> 3
|
41
55
|
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def bit_string_split(bits, bits_per_char, &block) # rubocop:disable Metrics/MethodLength
|
46
|
-
top = max_char_index
|
47
|
-
curry = 0
|
48
|
-
last_curry = 0
|
49
|
-
bits.each_char.each_slice(bits_per_char).map do |binary_ord|
|
50
|
-
val = binary_ord.join.to_i(2) + curry
|
51
|
-
last_curry = curry
|
52
|
-
if val <= top
|
53
|
-
current = val
|
54
|
-
curry = 0
|
55
|
-
else
|
56
|
-
current = top
|
57
|
-
curry = val % top
|
58
|
-
end
|
59
|
-
current
|
60
|
-
end.each(&block)
|
61
|
-
end
|
62
|
-
|
63
|
-
def alphabet_char(index)
|
64
|
-
@alphabet[index]
|
65
|
-
end
|
66
56
|
end
|
67
57
|
end
|
data/lib/tokogen/version.rb
CHANGED
data/tokogen.gemspec
CHANGED
@@ -24,4 +24,6 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency 'bundler', '~> 1.13'
|
25
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
26
26
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
27
|
+
spec.add_development_dependency 'guard', '~> 2.14.0'
|
28
|
+
spec.add_development_dependency 'guard-rspec', '~> 4.7.3'
|
27
29
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tokogen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- MOZGIII
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,10 +52,39 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: guard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.14.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.14.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 4.7.3
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 4.7.3
|
55
83
|
description: A ruby gem that allows you to generate customizable random tokens.
|
56
84
|
email:
|
57
85
|
- mike-n@narod.ru
|
58
|
-
executables:
|
86
|
+
executables:
|
87
|
+
- tokogen
|
59
88
|
extensions: []
|
60
89
|
extra_rdoc_files: []
|
61
90
|
files:
|
@@ -66,11 +95,18 @@ files:
|
|
66
95
|
- ".rubocop_todo.yml"
|
67
96
|
- ".travis.yml"
|
68
97
|
- Gemfile
|
98
|
+
- Guardfile
|
69
99
|
- README.md
|
70
100
|
- Rakefile
|
101
|
+
- bin/bench_ruby_binary
|
102
|
+
- bin/bench_ruby_comparisons
|
71
103
|
- bin/console
|
72
104
|
- bin/setup
|
105
|
+
- exe/tokogen
|
73
106
|
- lib/tokogen.rb
|
107
|
+
- lib/tokogen/alphabet.rb
|
108
|
+
- lib/tokogen/bit_combiner.rb
|
109
|
+
- lib/tokogen/bit_splitter.rb
|
74
110
|
- lib/tokogen/generator.rb
|
75
111
|
- lib/tokogen/version.rb
|
76
112
|
- tokogen.gemspec
|