Skip to content

Instantly share code, notes, and snippets.

@esquinas
Last active August 25, 2020 15:37
Show Gist options
  • Save esquinas/5c4e139689fd3bf1f99ae57d606b5b60 to your computer and use it in GitHub Desktop.
Save esquinas/5c4e139689fd3bf1f99ae57d606b5b60 to your computer and use it in GitHub Desktop.
Convert integers (or hex ids) into url-safe base64 strings saving space compared to hexadecimal (like Mongo) IE: "b380d04df5a7ea1d22cc4a54" could be passed as "s4DQTfWn6h0izEpU" in an url param saving 8 characters.
# frozen_string_literal: true
require 'base64'
def number_to_base64(number, base: nil)
valid_base = ->(b) {
b.is_a?(Integer) && b > 1
}
hex2int = ->(hex_chars) {
hex_chars.to_i(16)
}
case number
when NilClass
warn 'nil passed as number, was coerced to 0'
return ''
when String
base = 10 if base.nil?
raise ArgumentError, 'Specify a base when passing numbers as string' unless valid_base.call(base)
hex_n = base == 16 ? number : number.to_i(base).to_s(16)
when Integer
hex_n = number.to_s(16)
else
raise ArgumentError, "Invalid number #{number.inspect}"
end
Base64.urlsafe_encode64(
hex_n.reverse.scan(/[0-9a-z]{1,2}/i).map(&:reverse).reverse.map(&hex2int).pack('C*'),
padding: false
)
end
def base64_to_number(string)
return nil unless string&.is_a?(String)
number = 0
Base64.urlsafe_decode64(string).chars.reverse.each_with_index do |char, position|
char_value = char.unpack1('C') * (256**position)
number += char_value
end
number
end
# Following are the "tests", copy/paste and save into a new file `test_number_to_base64.rb`
# then run `ruby test_number_to_base64.rb`:
=begin
#!/usr/local/bin/ruby -w
# frozen_string_literal: true
require_relative './number2base64'
def assert(something)
return true if something == true
puts "#{something.inspect} FAILED!"
false
end
def assert_nil(something)
return true if assert(something.nil?)
puts "#{something.inspect} is not nil."
false
end
def assert_equal(something, other_thing)
return true if assert(something == other_thing)
puts "#{something.inspect} doesn't equal #{other_thing.inspect}"
false
end
puts 'START'
puts '-----------'
success = true
success &&= assert_equal('', number_to_base64(nil))
success &&= assert_nil(base64_to_number(nil))
success &&= assert_equal(0, base64_to_number(''))
success &&=
success &&= assert_equal(number_to_base64(1), number_to_base64("1"))
success &&= assert_equal(number_to_base64(10), number_to_base64("10"))
success &&= assert_equal(number_to_base64(10), number_to_base64("a", base: 16))
success &&= assert_equal(base64_to_number(number_to_base64(10)), 10)
success &&= assert_equal(base64_to_number(number_to_base64(0)), 0)
success &&= assert_equal(base64_to_number(number_to_base64(1234567890)), 1234567890)
success &&= assert_equal('abcdef'.to_i(16), base64_to_number(number_to_base64('abcdef'.to_i(16))))
success &&= assert_equal('abcdef'.to_i(16), base64_to_number(number_to_base64('abcdef', base: 16)))
success &&= assert_equal('5f4510717c9cfa441df2df51'.to_i(16), base64_to_number(number_to_base64('5f4510717c9cfa441df2df51', base: 16)))
32.times do |digits|
random_number = rand(10**(digits+1))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(2), base: 2)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(3), base: 3)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(8), base: 8)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(10), base: 10)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(16), base: 16)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(32), base: 32)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(36), base: 36)))
end
puts '-----------'
print 'FINISHED... '
puts 'OK' if success
puts 'WITH ERRORS' unless success
=end
# Following are the "tests", copy/paste and save into a new file `test_number_to_base64.rb`
# then run `ruby test_number_to_base64.rb`:
=begin
#!/usr/local/bin/ruby -w
# frozen_string_literal: true
require_relative './number2base64'
def assert(something)
return true if something == true
puts "#{something.inspect} FAILED!"
false
end
def assert_nil(something)
return true if assert(something.nil?)
puts "#{something.inspect} is not nil."
false
end
def assert_equal(something, other_thing)
return true if assert(something == other_thing)
puts "#{something.inspect} doesn't equal #{other_thing.inspect}"
false
end
puts 'START'
puts '-----------'
success = true
success &&= assert_equal('', number_to_base64(nil))
success &&= assert_nil(base64_to_number(nil))
success &&= assert_equal(0, base64_to_number(''))
success &&=
success &&= assert_equal(number_to_base64(1), number_to_base64("1"))
success &&= assert_equal(number_to_base64(10), number_to_base64("10"))
success &&= assert_equal(number_to_base64(10), number_to_base64("a", base: 16))
success &&= assert_equal(base64_to_number(number_to_base64(10)), 10)
success &&= assert_equal(base64_to_number(number_to_base64(0)), 0)
success &&= assert_equal(base64_to_number(number_to_base64(1234567890)), 1234567890)
success &&= assert_equal('abcdef'.to_i(16), base64_to_number(number_to_base64('abcdef'.to_i(16))))
success &&= assert_equal('abcdef'.to_i(16), base64_to_number(number_to_base64('abcdef', base: 16)))
success &&= assert_equal('b380d04df5a7ea1d22cc4a54'.to_i(16), base64_to_number(number_to_base64('b380d04df5a7ea1d22cc4a54', base: 16)))
success &&= assert_equal('b380d04df5a7ea1d22cc4a549a45aee0'.to_i(16), base64_to_number(number_to_base64('b380d04df5a7ea1d22cc4a549a45aee0', base: 16)))
32.times do |digits|
random_number = rand(10**(digits+1))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(2), base: 2)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(3), base: 3)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(8), base: 8)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(10), base: 10)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(16), base: 16)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(32), base: 32)))
success &&= assert_equal(random_number, base64_to_number(number_to_base64(random_number.to_s(36), base: 36)))
end
puts '-----------'
print 'FINISHED... '
puts 'OK' if success
puts 'WITH ERRORS' unless success
=end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment