Module: Crockford32
- Defined in:
- lib/crockford32.rb,
lib/crockford32/errors.rb,
lib/crockford32/version.rb
Overview
A fast little-endian implementation of Douglas Crockford’s Base32 specification.
Defined Under Namespace
Classes: ChecksumError, DecodeError, EncodeError, Error, InvalidCharacterError, LengthTooSmallError, UnsupportedDecodingTypeError, UnsupportedEncodingTypeError
Constant Summary collapse
- ENCODED_BITS =
The number of bits encoded per symbol.
0x05
- CHECK_SYMBOL_MIN_VALUE =
The minimum value of a check symbol.
0x20
- CHECKSUM_PRIME =
The prime number used to implement error detection.
0x25
- DASH =
The ordinal value of an ASCII dash character.
"-".ord.freeze
- DECODE_ORDINALS =
Symbol values in order by encoded ASCII ordinal values.
[ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 34, nil, nil, nil, nil, nil, 32, nil, nil, nil, nil, nil, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, nil, nil, nil, 35, nil, nil, nil, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, 0, 22, 23, 24, 25, 26, 36, 27, 28, 29, 30, 31, nil, nil, nil, nil, nil, nil, 10, 11, 12, 13, 14, 15, 16, 17, 1, 18, 19, 1, 20, 21, 0, 22, 23, 24, 25, 26, 36, 27, 28, 29, 30, 31, nil, nil, nil, 33 ].freeze
- ENCODE_SYMBOLS =
Encoding symbols ordered by bit value.
[ "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", "*", "~", "$", "=", "U" ].freeze
- VERSION =
The library version.
"1.1.0"
Public Methods collapse
-
.decode(value, into: :integer, check: false, length: nil) ⇒ Integer, String
Decode a Base32 value.
-
.encode(value, length: nil, check: false) ⇒ String
Encode a value as Base32.
Private Methods collapse
-
.convert(result, type, length) ⇒ Integer, String
private
Convert a decoded result into the destination type.
-
.into_string(result, length) ⇒ String
private
Convert an Integer into a String.
-
.le_decode_number(encoded_value) ⇒ Integer
private
Decode a value with the expectation that it is in little-endian order.
-
.le_encode_number(number, length, check) ⇒ String
private
Encode an Integer as a Base32 value.
-
.raw_value_to_number(value) ⇒ Integer
private
Convert a raw value into an Integer for encoding.
-
.string_to_integer(s) ⇒ Integer
private
Convert a String to an Integer one byte per iteration.
-
.string_to_integer_unrolled(s, iterations) ⇒ Integer
private
Convert a String to an Integer 8 bytes per iteration.
Class Method Details
.convert(result, type, length) ⇒ Integer, String (private)
Convert a decoded result into the destination type.
124 125 126 127 128 129 130 131 132 133 |
# File 'lib/crockford32.rb', line 124 def self.convert(result, type, length) case type when :integer result when :string into_string result, length else raise UnsupportedDecodingTypeError.new(type) end end |
.decode(value, into: :integer, check: false, length: nil) ⇒ Integer, String
Decode a Base32 value.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/crockford32.rb', line 65 def self.decode(value, into: :integer, check: false, length: nil) checksum = check ? value[-1] : nil value = check ? value[0...-1] : value result = le_decode_number value if check actual = result % CHECKSUM_PRIME required = DECODE_ORDINALS[checksum.ord] raise ChecksumError.new(value, actual, required) if actual != required end convert result, into, length end |
.encode(value, length: nil, check: false) ⇒ String
Encode a value as Base32.
92 93 94 |
# File 'lib/crockford32.rb', line 92 def self.encode(value, length: nil, check: false) le_encode_number(raw_value_to_number(value), length, check) end |
.into_string(result, length) ⇒ String (private)
Convert an Integer into a String.
Each 8-bit sequence is packed into a String in little-endian order.
142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/crockford32.rb', line 142 def self.into_string(result, length) q, r = result.bit_length.divmod(0x08) q += 1 if r > 0 bytes = Array.new(q) q.times do |i| bytes[i] = result & 0xff result >>= 0x08 end bstr = bytes.pack("C*") return bstr if length.nil? bstr.ljust(length, "\x00") end |
.le_decode_number(encoded_value) ⇒ Integer (private)
Decode a value with the expectation that it is in little-endian order.
105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/crockford32.rb', line 105 def self.le_decode_number(encoded_value) symbol = -1 bits = -ENCODED_BITS encoded_value.bytes.reduce(0) do |result, ch| symbol += 1 next result if ch == DASH val = DECODE_ORDINALS[ch.ord] raise InvalidCharacterError.new(encoded_value, symbol) if val.nil? || val >= CHECK_SYMBOL_MIN_VALUE bits += ENCODED_BITS result | (val << bits) end end |
.le_encode_number(number, length, check) ⇒ String (private)
Encode an Integer as a Base32 value.
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/crockford32.rb', line 216 def self.le_encode_number(number, length, check) result = +"" n = number loop do chunk = n & 0x1F result << ENCODE_SYMBOLS[chunk] n >>= ENCODED_BITS break if n == 0 end rlen = result.length + (check ? 1 : 0) if length raise LengthTooSmallError.new(number, rlen, length) if rlen > length result << "0" * (length - rlen) end result << ENCODE_SYMBOLS[number % CHECKSUM_PRIME] if check result.freeze end |
.raw_value_to_number(value) ⇒ Integer (private)
Convert a raw value into an Integer for encoding.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/crockford32.rb', line 161 def self.raw_value_to_number(value) case value when String q, r = value.bytesize.divmod(8) if r == 0 string_to_integer_unrolled value, q else string_to_integer value end when Integer value else raise UnsupportedEncodingTypeError.new value.class end end |
.string_to_integer(s) ⇒ Integer (private)
Convert a String to an Integer one byte per iteration.
181 182 183 184 185 186 187 |
# File 'lib/crockford32.rb', line 181 def self.string_to_integer(s) shift = -0x08 s.each_byte.reduce(0) do |n, b| shift += 0x08 n + (b << shift) end end |
.string_to_integer_unrolled(s, iterations) ⇒ Integer (private)
Convert a String to an Integer 8 bytes per iteration.
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/crockford32.rb', line 193 def self.string_to_integer_unrolled(s, iterations) n = 0 bytes = s.bytes while iterations > 0 o = (iterations - 1) * 0x40 i = iterations * 0x08 n += bytes[i - 1] << (o + 0x38) n += bytes[i - 2] << (o + 0x30) n += bytes[i - 3] << (o + 0x28) n += bytes[i - 4] << (o + 0x20) n += bytes[i - 5] << (o + 0x18) n += bytes[i - 6] << (o + 0x10) n += bytes[i - 7] << (o + 0x08) n += bytes[i - 8] << (o + 0x00) iterations -= 1 end n end |