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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d37166686245cf99243365f86bcef7c4a8ec2b1a
4
- data.tar.gz: 978498c998a874852766f7c0e0f1ad6809cf633f
3
+ metadata.gz: 4935595f4088dbbf55cf945d762542593fd849e2
4
+ data.tar.gz: 6cf358d6f950178c266f9f1102822586c2e9e14d
5
5
  SHA512:
6
- metadata.gz: 7ac95fe534f71d357d48b4c08fb1f461553314d6f5fa1c3912e4f20c0c08529a2e4b7bc00130b95954d9d8195656d84a708f36d8ef4c78a9dacf79a4636c2a3f
7
- data.tar.gz: d73f36a631cf3385dde27a3607456987848366044f06f3e56c59c01bad28f8cfe1b49c3b466d3c3123e3516e493a13ad83e379c748055eec8256987c32b5abeb
6
+ metadata.gz: 693e975d0e678c26e8eb5ecb8721fbc795573d834c8f4528b5f1f1af31c462646f148a2c06c9873d3af130e07b6fb27d029001fe51fcaa6a7405b97907ee03df
7
+ data.tar.gz: e1961e28a51c691c6c0201bc25009d5c9c664ed4a54860269988a4b92c407af63736eef1a0c3de3ed61e3e3c027051ceb75cd48f57cd8c8eee8a413bac96c2c7
@@ -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
- IndentWhenRelativeTo: end
29
+ EnforcedStyle: end
29
30
  Style/Alias:
30
31
  EnforcedStyle: prefer_alias_method
31
32
  Lint/EndAlignment:
32
- AlignWith: variable
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'
@@ -2,6 +2,8 @@ sudo: false
2
2
  language: ruby
3
3
  rvm:
4
4
  - 2.3.1
5
+ - 2.3.3
6
+ - 2.4.0
5
7
  before_install: gem install bundler -v 1.13.6
6
8
  install:
7
9
  - bundle install --retry=3
@@ -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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ require 'tokogen'
4
+ args = []
5
+ args << ARGV[0].to_i if ARGV[0]
6
+ puts Tokogen.token(*args)
@@ -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, **options)
16
- Generator.new(randomness_source: randomness_source, **options)
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
@@ -1,67 +1,57 @@
1
1
  # frozen_string_literal: true
2
2
  module Tokogen
3
3
  class Generator
4
- DEFAULT_ALPHABET = (('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a).join.freeze
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: DEFAULT_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
- bits = bytes.unpack('b*')[0]
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
- bit_string_split(bits, bits_per_char)
22
- .take(length)
23
- .map { |index| alphabet_char(index) }
24
- .join
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 max_char_index
32
- @alphabet.size - 1
47
+ def alphabet_char(index)
48
+ @alphabet[index]
33
49
  end
34
50
 
35
- def bits_per_char
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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Tokogen
3
- VERSION = '0.1.2'
3
+ VERSION = '0.2.1'
4
4
  end
@@ -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.2
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-01-11 00:00:00.000000000 Z
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