levinalex-base32 0.0.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.
Files changed (3) hide show
  1. data/README.markdown +20 -0
  2. data/lib/base32/crockford.rb +199 -0
  3. metadata +54 -0
@@ -0,0 +1,20 @@
1
+ An implementation of Douglas Crockfords Base32-Encoding in Ruby
2
+
3
+ see <http://www.crockford.com/wrmg/base32.html>
4
+
5
+ Installation
6
+ ============
7
+
8
+ $ gem sources -a http://gems.github.com
9
+ $ sudo gem install levinalex-base32
10
+
11
+ Usage
12
+ =====
13
+
14
+ #!/usr/bin/env ruby
15
+
16
+ require 'base32/crockford'
17
+
18
+ Base32::Crockford.encode(1234) # => "16J"
19
+ Base32::Crockford.encode(100**10, :split=>5, :length=>15) # => "02PQH-TY5NH-H0000"
20
+ Base32::Crockford.decode("2pqh-ty5nh-hoooo") # => 10**100
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # (c) 2008, Levin Alexander <http://levinalex.net>
4
+ #
5
+ # This file is released under the same license as ruby.
6
+ #
7
+
8
+ require 'enumerator'
9
+
10
+ module Base32
11
+ end
12
+
13
+ # encode a value with the encoding defined by _Douglas_ _Crockford_ in
14
+ # <http://www.crockford.com/wrmg/base32.html>
15
+ #
16
+ # this is *not* the same as the Base32 encoding defined in RFC 4648
17
+ #
18
+ #
19
+ # The Base32 symbol set is a superset of the Base16 symbol set.
20
+ #
21
+ # We chose a symbol set of 10 digits and 22 letters. We exclude 4 of the 26
22
+ # letters: I L O U.
23
+ #
24
+ # Excluded Letters
25
+ #
26
+ # I:: Can be confused with 1
27
+ # L:: Can be confused with 1
28
+ # O:: Can be confused with 0
29
+ # U:: Accidental obscenity
30
+ #
31
+ # When decoding, upper and lower case letters are accepted, and i and l will
32
+ # be treated as 1 and o will be treated as 0. When encoding, only upper case
33
+ # letters are used.
34
+ #
35
+ # If the bit-length of the number to be encoded is not a multiple of 5 bits,
36
+ # then zero-extend the number to make its bit-length a multiple of 5.
37
+ #
38
+ # Hyphens (-) can be inserted into symbol strings. This can partition a
39
+ # string into manageable pieces, improving readability by helping to prevent
40
+ # confusion. Hyphens are ignored during decoding. An application may look for
41
+ # hyphens to assure symbol string correctness.
42
+ #
43
+ #
44
+ class Base32::Crockford
45
+ ENCODE_CHARS =
46
+ %w(0 1 2 3 4 5 6 7 8 9 A B C D E F G H J K M N P Q R S T V W X Y Z ?)
47
+
48
+ DECODE_MAP = ENCODE_CHARS.to_enum(:each_with_index).inject({}) do |h,(c,i)|
49
+ h[c] = i; h
50
+ end.merge({'I' => 1, 'L' => 1, 'O' => 0})
51
+
52
+ # encodes an integer into a string
53
+ #
54
+ # when +split+ is given a hyphen is inserted every <n> characters to improve
55
+ # readability
56
+ #
57
+ # when +length+ is given, the resulting string is zero-padded to be exactly
58
+ # this number of characters long (hyphens are ignored)
59
+ #
60
+ # Base32::Crockford.encode(1234) # => "16J"
61
+ # Base32::Crockford.encode(123456789012345, :split=>5) # => "3G923-0VQVS"
62
+ #
63
+ def self.encode(number, opts = {})
64
+ # verify options
65
+ raise ArgumentError unless (opts.keys - [:length, :split] == [])
66
+
67
+ str = number.to_s(2).reverse.scan(/.{1,5}/).map do |bits|
68
+ ENCODE_CHARS[bits.reverse.to_i(2)]
69
+ end.reverse.join
70
+
71
+ str = str.rjust(opts[:length], '0') if opts[:length]
72
+
73
+ if opts[:split]
74
+ str = str.reverse
75
+ str = str.scan(/.{1,#{opts[:split]}}/).map { |x| x.reverse }
76
+ str = str.reverse.join("-")
77
+ end
78
+
79
+ str
80
+ end
81
+
82
+ # decode a string to an integer using Douglas Crockfords Base32 Encoding
83
+ #
84
+ # the string is converted to uppercase and hyphens are stripped before
85
+ # decoding
86
+ #
87
+ # I,i,l,L decodes to 1
88
+ # O,o decodes to 0
89
+ #
90
+ # Base32::Crockford.decode("16J") # => 1234
91
+ # Base32::Crockford.decode("OI") # => 1
92
+ # Base32::Crockford.decode("3G923-0VQVS") # => 123456789012345
93
+ #
94
+ # returns +nil+ if the string contains invalid characters and can't be
95
+ # decoded
96
+ #
97
+ def self.decode(string)
98
+ clean(string).split(//).map { |char|
99
+ DECODE_MAP[char] or return nil
100
+ }.inject(0) { |result,val| (result << 5) + val }
101
+ end
102
+
103
+ # same as decode, but raises ArgumentError when the string can't be decoded
104
+ #
105
+ def self.decode!(string)
106
+ decode(string) or raise ArgumentError
107
+ end
108
+
109
+ # return the canonical encoding of a string. converts it to uppercase
110
+ # and removes hyphens
111
+ #
112
+ # replaces invalid characters with a question mark ('?')
113
+ #
114
+ def self.normalize(string)
115
+ clean(string).split(//).map { |char|
116
+ ENCODE_CHARS[DECODE_MAP[char] || 32]
117
+ }.join
118
+ end
119
+
120
+ # returns false iff the string contains invalid characters and can't be
121
+ # decoded
122
+ #
123
+ def self.valid?(string)
124
+ !(normalize(string) =~ /\?/)
125
+ end
126
+
127
+ class << self
128
+ def clean(string)
129
+ string.gsub(/-/,'').upcase
130
+ end
131
+ private :clean
132
+ end
133
+ end
134
+
135
+
136
+
137
+ if __FILE__ == $0
138
+
139
+ require 'test/unit'
140
+
141
+ class TestBase32Crockford < Test::Unit::TestCase
142
+
143
+ def test_encoding_and_decoding_single_chars
144
+ from = (0..31).to_a
145
+ to = %w(0 1 2 3 4 5 6 7 8 9 A B C D E F G H J K M N P Q R S T V W X Y Z)
146
+
147
+ from.zip(to) do |symbol_value, encode_symbol|
148
+ assert_equal encode_symbol, Base32::Crockford.encode(symbol_value)
149
+ assert_equal symbol_value, Base32::Crockford.decode(encode_symbol)
150
+ end
151
+ end
152
+
153
+ def test_encoding_larger_numbers
154
+ assert_equal("10", Base32::Crockford.encode(32))
155
+ assert_equal("16J", Base32::Crockford.encode(1234))
156
+ end
157
+
158
+ def test_decoding_strings
159
+ assert_equal(1234, Base32::Crockford.decode("16J"))
160
+ end
161
+
162
+ def test_decoding_normalizes_symbols
163
+ assert_equal Base32::Crockford.decode('11100110'),
164
+ Base32::Crockford.decode('IL1O0ilo')
165
+ end
166
+
167
+ def test_decoding_lowercase
168
+ assert_equal Base32::Crockford.decode("abcdefghijklmnopqrstvwxyz"),
169
+ Base32::Crockford.decode("ABCDEFGHIJKLMNOPQRSTVWXYZ")
170
+ end
171
+
172
+ def test_decoding_invalid_strings
173
+ assert_equal nil, Base32::Crockford.decode("Ü'+?")
174
+ assert_raises(ArgumentError) { Base32::Crockford.decode!("'+?") }
175
+ end
176
+
177
+ def test_decode_should_ignore_hyphens
178
+ assert_equal 1234, Base32::Crockford.decode("1-6-j")
179
+ end
180
+
181
+ def test_normalize
182
+ assert_equal "HE110W0R1D", Base32::Crockford.normalize("hello-world")
183
+ assert_equal "B?123", Base32::Crockford.normalize("BU-123")
184
+ end
185
+
186
+ def test_valid
187
+ assert_equal true, Base32::Crockford.valid?("hello-world")
188
+ assert_equal false, Base32::Crockford.valid?("BU-123")
189
+ end
190
+
191
+ def test_length_and_hyphenization
192
+ assert_equal "0016J", Base32::Crockford.encode(1234, :length => 5)
193
+ assert_equal "0-01-6J",
194
+ Base32::Crockford.encode(1234, :length => 5, :split => 2)
195
+ assert_equal "00-010",
196
+ Base32::Crockford.encode(32, :length => 5, :split => 3)
197
+ end
198
+ end
199
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: levinalex-base32
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Levin Alexander
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-27 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: mail@levinalex.net
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/base32/crockford.rb
26
+ - README.markdown
27
+ has_rdoc: true
28
+ homepage: http://levinalex.net/src/base32
29
+ post_install_message:
30
+ rdoc_options: []
31
+
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: "0"
39
+ version:
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ requirements: []
47
+
48
+ rubyforge_project:
49
+ rubygems_version: 1.2.0
50
+ signing_key:
51
+ specification_version: 2
52
+ summary: 32-symbol notation for expressing numbers in a form that can be conveniently and accurately transmitted between humans
53
+ test_files: []
54
+