base45 0.1.0 → 0.3.0

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
2
  SHA256:
3
- metadata.gz: 391fbba1f795babf955ab02b30fbe0f9016cd4440d33727bf76883feeb6a341d
4
- data.tar.gz: c533882b3b041d2d5d5c5e6e68830900bc5ff63a42ea1c7c17b8d5867c5a7888
3
+ metadata.gz: 90f2a21cb5b510525fc9936d7513f1279398b029ed20ba64835ad6e6cb5c1540
4
+ data.tar.gz: 86d3fbe7bbb9fbf152732cf2dff58904d8ae94e4b2c8aab3ef5d75913bc13a44
5
5
  SHA512:
6
- metadata.gz: 86c794ec6a105a7db8a7eab5d934d9bcccb2d6d58d38d75ab75fc2ff57047363f43fed0b025947fb92418b4c138b41e87e0b1ce1e42123d7fca3d4df1e285056
7
- data.tar.gz: 937cad72b4c7b54d87f23eb8484a76a6589e029e3e8eb8afc0be86de63974f0c7bd44ecf15905d5c1253d32d3796c005cd6c20a9e0c19caba465afd0115f14df
6
+ metadata.gz: 65c5af1e33830c554e4d047a2f2f40e40037043e5d97242717f4f62bf302bf95890533bc07f1957799cea32a9a5575e2e791bab16fdb254920fbf60e5ab7dd9e
7
+ data.tar.gz: 021a2e76f56bd6038af906bd76c5bf32bf1ceb840f63741f05617f34adbd97647e27cb78ab22956c94a249b89cf8bf8cc1784ec47ac0db6aa74ae46a989859cf
data/.rubocop.yml CHANGED
@@ -1,13 +1,12 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 3.0.2
3
3
 
4
- Style/StringLiterals:
5
- Enabled: true
6
- EnforcedStyle: double_quotes
7
-
8
4
  Style/StringLiteralsInInterpolation:
9
5
  Enabled: true
10
6
  EnforcedStyle: double_quotes
11
7
 
12
8
  Layout/LineLength:
13
9
  Max: 120
10
+
11
+ Metrics/BlockLength:
12
+ ExcludedMethods: ['describe']
data/Gemfile CHANGED
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source "https://rubygems.org"
3
+ source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
6
 
7
- gem "rake", "~> 13.0"
7
+ gem 'rake', '~> 13.0'
8
8
 
9
- gem "rspec", "~> 3.0"
9
+ gem 'rspec', '~> 3.2'
10
10
 
11
- gem "rubocop", "~> 1.7"
11
+ gem 'rubocop', '~> 1.7'
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ base45 (0.3.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ diff-lcs (1.4.4)
11
+ parallel (1.21.0)
12
+ parser (3.0.2.0)
13
+ ast (~> 2.4.1)
14
+ rainbow (3.0.0)
15
+ rake (13.0.6)
16
+ regexp_parser (2.1.1)
17
+ rexml (3.2.5)
18
+ rspec (3.10.0)
19
+ rspec-core (~> 3.10.0)
20
+ rspec-expectations (~> 3.10.0)
21
+ rspec-mocks (~> 3.10.0)
22
+ rspec-core (3.10.1)
23
+ rspec-support (~> 3.10.0)
24
+ rspec-expectations (3.10.1)
25
+ diff-lcs (>= 1.2.0, < 2.0)
26
+ rspec-support (~> 3.10.0)
27
+ rspec-mocks (3.10.2)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.10.0)
30
+ rspec-support (3.10.2)
31
+ rubocop (1.22.3)
32
+ parallel (~> 1.10)
33
+ parser (>= 3.0.0.0)
34
+ rainbow (>= 2.2.2, < 4.0)
35
+ regexp_parser (>= 1.8, < 3.0)
36
+ rexml
37
+ rubocop-ast (>= 1.12.0, < 2.0)
38
+ ruby-progressbar (~> 1.7)
39
+ unicode-display_width (>= 1.4.0, < 3.0)
40
+ rubocop-ast (1.12.0)
41
+ parser (>= 3.0.1.1)
42
+ ruby-progressbar (1.11.0)
43
+ unicode-display_width (2.1.0)
44
+
45
+ PLATFORMS
46
+ x86_64-linux
47
+
48
+ DEPENDENCIES
49
+ base45!
50
+ rake (~> 13.0)
51
+ rspec (~> 3.2)
52
+ rubocop (~> 1.7)
53
+
54
+ BUNDLED WITH
55
+ 2.2.27
data/README.md CHANGED
@@ -3,12 +3,6 @@
3
3
  The Base45 module provides for the encoding (#encode) and
4
4
  decoding (#decode) of binary data using a Base45 representation.
5
5
 
6
- Some resources used:
7
-
8
- * [Base 45 Draft](https://datatracker.ietf.org/doc/draft-faltstrom-base45/)
9
- * [A JS Base 45 implementation](http://base45-decode-encode.net/)
10
- * [SO question](https://stackoverflow.com/questions/68114693/decode-base45-string)
11
-
12
6
  ## Installation
13
7
 
14
8
  Add this line to your application's Gemfile:
@@ -59,3 +53,9 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/wattsw
59
53
  ## License
60
54
 
61
55
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
56
+
57
+ ## Resources used
58
+
59
+ * [Base 45 Draft](https://datatracker.ietf.org/doc/draft-faltstrom-base45/)
60
+ * [A JS Base 45 implementation](http://base45-decode-encode.net/)
61
+ * [SO question](https://stackoverflow.com/questions/68114693/decode-base45-string)
data/Rakefile CHANGED
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
- require "rubocop/rake_task"
8
+ require 'rubocop/rake_task'
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
data/base45.gemspec CHANGED
@@ -1,22 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "lib/base45/version"
3
+ require_relative 'lib/base45/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "base45"
6
+ spec.name = 'base45'
7
7
  spec.version = Base45::VERSION
8
- spec.authors = ["Wattswing"]
9
- spec.email = ["wattswing@gmail.com"]
8
+ spec.authors = ['Wattswing']
9
+ spec.email = ['wattswing@gmail.com']
10
10
 
11
- spec.summary = "Decode / encode in base 45"
11
+ spec.summary = 'Decode / encode in base 45'
12
12
  spec.description = spec.summary
13
13
 
14
- spec.homepage = "https://github.com/wattswing/base45"
15
- spec.license = "MIT"
16
- spec.required_ruby_version = ">= 3.0.2"
14
+ spec.homepage = 'https://github.com/wattswing/base45'
15
+ spec.license = 'MIT'
16
+ spec.required_ruby_version = '>= 3.0.2'
17
17
 
18
- spec.metadata["homepage_uri"] = spec.homepage
19
- spec.metadata["source_code_uri"] = spec.homepage
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = spec.homepage
20
20
 
21
21
  # Specify which files should be added to the gem when it is released.
22
22
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -25,7 +25,8 @@ Gem::Specification.new do |spec|
25
25
  (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
26
  end
27
27
  end
28
- spec.bindir = "exe"
28
+ spec.bindir = 'exe'
29
29
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
- spec.require_paths = ["lib"]
30
+ spec.require_paths = ['lib']
31
+ spec.add_development_dependency 'rspec', '~> 3.2'
31
32
  end
data/bin/console CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "bundler/setup"
5
- require "base45"
4
+ require 'bundler/setup'
5
+ require 'base45'
6
6
 
7
7
  # You can add fixtures and/or initialization code here to make experimenting
8
8
  # with your gem easier. You can also use a different console, if you like.
@@ -11,5 +11,5 @@ require "base45"
11
11
  # require "pry"
12
12
  # Pry.start
13
13
 
14
- require "irb"
14
+ require 'irb'
15
15
  IRB.start(__FILE__)
@@ -1,53 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Base45
4
- BASE_45_TABLE = {
5
- "00" => "0",
6
- "01" => "1",
7
- "02" => "2",
8
- "03" => "3",
9
- "04" => "4",
10
- "05" => "5",
11
- "06" => "6",
12
- "07" => "7",
13
- "08" => "8",
14
- "09" => "9",
15
- "10" => "A",
16
- "11" => "B",
17
- "12" => "C",
18
- "13" => "D",
19
- "14" => "E",
20
- "15" => "F",
21
- "16" => "G",
22
- "17" => "H",
23
- "18" => "I",
24
- "19" => "J",
25
- "20" => "K",
26
- "21" => "L",
27
- "22" => "M",
28
- "23" => "N",
29
- "24" => "O",
30
- "25" => "P",
31
- "26" => "Q",
32
- "27" => "R",
33
- "28" => "S",
34
- "29" => "T",
35
- "30" => "U",
36
- "31" => "V",
37
- "32" => "W",
38
- "33" => "X",
39
- "34" => "Y",
40
- "35" => "Z",
41
- "36" => " ",
42
- "37" => "$",
43
- "38" => "%",
44
- "39" => "*",
45
- "40" => "+",
46
- "41" => "-",
47
- "42" => ".",
48
- "43" => "/",
49
- "44" => ":"
50
- }.freeze
4
+ BASE_45_TABLE = [
5
+ *('0'..'9').to_a,
6
+ *('A'..'Z').to_a,
7
+ ' ', '$', '%', '*', '+', '-', '.', '/', ':'
8
+ ].each_with_object({}).with_index { |(index, memo), i| memo[i] = index }
51
9
 
52
10
  INVERTED_BASE_45_TABLE = BASE_45_TABLE.invert.freeze
53
11
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Base45
4
+ class IllegalCharacterError < StandardError; end
5
+
6
+ class ForbiddenLengthError < StandardError; end
7
+
8
+ class OverflowError < StandardError; end
9
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Base45
4
- VERSION = "0.1.0"
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/base45.rb CHANGED
@@ -7,83 +7,78 @@
7
7
  # The Base45 module provides for the encoding (#encode) and
8
8
  # decoding (#decode) of binary data using a Base45 representation.
9
9
 
10
- require_relative "base45/version"
11
- require_relative "base45/encoding_table"
10
+ require_relative 'base45/version'
11
+ require_relative 'base45/encoding_table'
12
+ require_relative 'base45/errors'
12
13
 
13
14
  # Exposes two methods to decode and encode in base 45
14
15
  module Base45
15
- class Error < StandardError; end
16
+ SQUARE_45 = 45**2
16
17
 
17
18
  class << self
18
19
  # Returns the Base45-encoded version of +payload+
19
20
  #
20
21
  # require 'base45'
21
- # Base45.encode("Encoding in base 45 !")
22
+ # Base45.encode('Encoding in base 45 !')
22
23
  #
23
24
  # <i>Generates:</i>
24
25
  #
25
26
  # :Y8UPCAVC3/DH44M-DUJCLQE934AW6X0
26
27
  def encode(payload)
27
- return if payload.length.zero?
28
-
29
- return encode_for_single_char(payload) if payload.bytesize < 2
28
+ sliced_bytes = payload.each_byte.each_slice(2)
29
+ base45_bytes = sliced_bytes.flat_map do |byte_a, byte_b|
30
+ byte_b ? encode_two_bytes(byte_a, byte_b) : encode_one_byte(byte_a)
31
+ end
30
32
 
31
- encode_for_multipe_chars(payload)
33
+ base45_bytes.map do |byte45|
34
+ BASE_45_TABLE[byte45]
35
+ end.join
32
36
  end
33
37
 
34
38
  # Returns the Base45-decoded version of +payload+
35
39
  #
36
40
  # require 'base45'
37
- # Base45.encode(":Y8UPCAVC3/DH44M-DUJCLQE934AW6X0")
41
+ # Base45.encode(':Y8UPCAVC3/DH44M-DUJCLQE934AW6X0')
38
42
  #
39
43
  # <i>Generates:</i>
40
44
  #
41
45
  # Encoding in base 45 !
42
46
  def decode(payload)
43
- return if payload.length < 2
47
+ map45_chars(payload).each_slice(3).flat_map do |c, d, e|
48
+ raise ForbiddenLengthError if d.nil?
44
49
 
45
- lookup = sliced_payload(payload)
46
- base45_vals = lookup.map { |c, d, e| c + d * 45 + (e ? e * 45**2 : 0) }
47
-
48
- base45_vals.pack("S>*").gsub(/\x00/, "")
50
+ v = c + d * 45
51
+ bytes_from_base45(e, v)
52
+ end.pack('C*')
49
53
  end
50
54
 
51
55
  private
52
56
 
53
- def sliced_payload(payload)
54
- payload.chars.each_slice(3).map do |slice|
55
- slice.map { |char| INVERTED_BASE_45_TABLE[char]&.to_i }
56
- end
57
- end
57
+ def bytes_from_base45(last_triplet_byte, factor45)
58
+ return [factor45] unless last_triplet_byte
58
59
 
59
- def encode_for_single_char(payload)
60
- keys = payload.bytes[0].divmod(45).reverse
60
+ factor45 += last_triplet_byte * SQUARE_45
61
+ x, y = factor45.divmod(256)
62
+ raise OverflowError unless x < 256
61
63
 
62
- keys.map { |i| BASE_45_TABLE[i.to_s.rjust(2, "0")] }.join
64
+ [x, y]
63
65
  end
64
66
 
65
- def encode_for_multipe_chars(payload)
66
- # 16-bit unsigned (unsigned char) - except big endian
67
- bytes16 = payload.unpack("S>*")
68
-
69
- is_odd = payload.bytesize.odd? && payload.bytes[-1] < 256
70
- bytes16 << payload.bytes[-1] if is_odd
67
+ def encode_one_byte(byte)
68
+ byte.divmod(45).reverse
69
+ end
71
70
 
72
- modulo45_bytes = modulo45_array(bytes16)
71
+ def encode_two_bytes(first_byte, second_byte)
72
+ x = (first_byte << 8) + second_byte
73
+ e, x = x.divmod(SQUARE_45)
74
+ d, c = x.divmod(45)
73
75
 
74
- modulo45_bytes.flatten.map do |i|
75
- BASE_45_TABLE[i.to_s.rjust(2, "0")]
76
- end.join
76
+ [c, d, e]
77
77
  end
78
78
 
79
- def modulo45_array(bytes16)
80
- bytes16.map do |byte|
81
- arr = []
82
- multiplier, rest = byte.divmod(45**2)
83
- arr << multiplier if multiplier.positive?
84
- arr.push(*rest.divmod(45))
85
-
86
- arr.reverse
79
+ def map45_chars(string)
80
+ string.chars.map do |char|
81
+ INVERTED_BASE_45_TABLE[char] || raise(IllegalCharacterError, char)
87
82
  end
88
83
  end
89
84
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: base45
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wattswing
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-02 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-03-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
13
27
  description: Decode / encode in base 45
14
28
  email:
15
29
  - wattswing@gmail.com
@@ -20,6 +34,7 @@ files:
20
34
  - ".rspec"
21
35
  - ".rubocop.yml"
22
36
  - Gemfile
37
+ - Gemfile.lock
23
38
  - LICENSE.txt
24
39
  - README.md
25
40
  - Rakefile
@@ -28,6 +43,7 @@ files:
28
43
  - bin/setup
29
44
  - lib/base45.rb
30
45
  - lib/base45/encoding_table.rb
46
+ - lib/base45/errors.rb
31
47
  - lib/base45/version.rb
32
48
  homepage: https://github.com/wattswing/base45
33
49
  licenses:
@@ -50,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
66
  - !ruby/object:Gem::Version
51
67
  version: '0'
52
68
  requirements: []
53
- rubygems_version: 3.2.29
69
+ rubygems_version: 3.4.22
54
70
  signing_key:
55
71
  specification_version: 4
56
72
  summary: Decode / encode in base 45