Class: Base32::URL

Inherits:
Object
  • Object
show all
Defined in:
lib/base32/url.rb

Overview

encode a value with the encoding defined by Douglas Crockford in <www.crockford.com/wrmg/base32.html>

this is not the same as the Base32 encoding defined in RFC 4648

The Base32 symbol set is a superset of the Base16 symbol set.

We chose a symbol set of 10 digits and 22 letters. We exclude 4 of the 26 letters: I L O U.

Excluded Letters

I

Can be confused with 1

L

Can be confused with 1

O

Can be confused with 0

U

Accidental obscenity

When decoding, upper and lower case letters are accepted, and i and l will be treated as 1 and o will be treated as 0. When encoding, lower upper case letters are used.

If the bit-length of the number to be encoded is not a multiple of 5 bits, then zero-extend the number to make its bit-length a multiple of 5.

Hyphens (-) can be inserted into symbol strings. This can partition a string into manageable pieces, improving readability by helping to prevent confusion. Hyphens are ignored during decoding. An application may look for hyphens to assure symbol string correctness.

Constant Summary collapse

ENCODE_CHARS =
%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 ?)
DECODE_MAP =
ENCODE_CHARS.to_enum(:each_with_index).inject({}) do |h,(c,i)|
  h[c] = i; h
end.merge({'i' => 1, 'l' => 1, 'o' => 0})
CHECKSUM_CHARS =
%w(i l o u)
CHECKSUM_MAP =
{ "i" => 32, "l" => 33, "o" => 34, "u" => 35 }

Class Method Summary collapse

Class Method Details

.clean(string) ⇒ Object



152
153
154
# File 'lib/base32/url.rb', line 152

def clean(string)
  string.gsub(/-/,'').downcase
end

.decode(string, opts = {}) ⇒ Object

decode a string to an integer using Douglas Crockfords Base32 Encoding

the string is converted to uppercase and hyphens are stripped before decoding

I,i,l,L decodes to 1
O,o decodes to 0

Base32::Crockford.decode("16J") # => 1234
Base32::Crockford.decode("OI") # => 1
Base32::Crockford.decode("3G923-0VQVS") # => 123456789012345

returns nil if the string contains invalid characters and can’t be decoded, or if checksum option is used and checksum is incorrect



106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/base32/url.rb', line 106

def self.decode(string, opts = {})
  if opts[:checksum]
    checksum_char = string.slice!(-1)
    checksum_number = DECODE_MAP.merge(CHECKSUM_MAP)[checksum_char]
  end

  number = clean(string).split(//).map { |char|
    DECODE_MAP[char] or return nil
  }.inject(0) { |result,val| (result << 5) + val }

  return nil if opts[:checksum] && (number % 36 != checksum_number)

  number
end

.decode!(string, opts = {}) ⇒ Object

same as decode, but raises ArgumentError when the string can’t be decoded



123
124
125
# File 'lib/base32/url.rb', line 123

def self.decode!(string, opts = {})
  decode(string) or raise ArgumentError
end

.encode(number, opts = {}) ⇒ Object

encodes an integer into a string

when checksum is given, a checksum is added at the end of the the string, calculated as modulo 36 of number. Four additional checksum symbols are used for symbol values 32-35

when split is given a hyphen is inserted every <n> characters to improve readability

when length is given, the resulting string is zero-padded to be exactly this number of characters long (hyphens are ignored)

Base32::URL.encode(1234) # => "16j"
Base32::URL.encode(123456789012345, :split=>5) # => "3g923-0vqvs"

Raises:

  • (ArgumentError)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/base32/url.rb', line 70

def self.encode(number, opts = {})
  # verify options
  raise ArgumentError unless (opts.keys - [:length, :split, :checksum] == [])

  str = number.to_s(2).reverse.scan(/.{1,5}/).map do |bits|
    ENCODE_CHARS[bits.reverse.to_i(2)]
  end.reverse.join

  str += (ENCODE_CHARS + CHECKSUM_CHARS)[number % 36] if opts[:checksum]

  str = str.rjust(opts[:length], '0') if opts[:length]

  if opts[:split]
    str = str.reverse
    str = str.scan(/.{1,#{opts[:split]}}/).map { |x| x.reverse }
    str = str.reverse.join("-")
  end

  str
end

.normalize(string, opts = {}) ⇒ Object

return the canonical encoding of a string. converts it to uppercase and removes hyphens

replaces invalid characters with a question mark (‘?’)



132
133
134
135
136
137
138
139
140
141
142
# File 'lib/base32/url.rb', line 132

def self.normalize(string, opts = {})
  checksum_char = string.slice!(-1) if opts[:checksum]

  string = clean(string).split(//).map do |char|
    ENCODE_CHARS[DECODE_MAP[char] || 32]
  end.join

  string += checksum_char if opts[:checksum]

  string
end

.valid?(string, opts = {}) ⇒ Boolean

returns false if the string contains invalid characters and can’t be decoded

Returns:

  • (Boolean)


147
148
149
# File 'lib/base32/url.rb', line 147

def self.valid?(string, opts = {})
  !(normalize(string, opts) =~ /\?/)
end