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.
- data/README.markdown +20 -0
- data/lib/base32/crockford.rb +199 -0
- metadata +54 -0
data/README.markdown
ADDED
@@ -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
|
+
|