tokogen 0.1.2 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/MOZGIII/tokogen.svg?branch=master)](https://travis-ci.org/MOZGIII/tokogen)
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/tokogen.svg)](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
|