sanbiv
10/4/2017 - 9:29 AM

Firebase push algorithm in Ruby (ref: https://www.firebase.com/blog/2015-02-11-firebase-unique-identifiers.html)

require 'date'

class FirebasePush
  PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'

  def initialize
    @prev_ts = 0
    @rand_chars = Array.new(12)
  end

  def assert(cond, msg = '')
    raise "Firebase INTERNAL ASSERT FAILED:#{msg}" unless cond
  end

  def next_id(ts = seed)
    is_dup = (ts == @prev_ts)
    @prev_ts = ts
    ts_chars = Array.new(8)

    7.step(0, -1) { |i|
      ts_chars[i] = PUSH_CHARS[ts % 64]
      ts = (ts / 64).floor
    }

    assert(ts == 0)

    id = ts_chars.join ''

    unless is_dup
      12.times { |i| @rand_chars[i] = (rand * 64).floor }
    else
      11.step(0, -1) { |i|
        unless @rand_chars[i] == 63
          @rand_chars[i] += 1

          break
        end

        @rand_chars[i] = 0
      }
    end

    12.times { |i| id << PUSH_CHARS[@rand_chars[i]] }

    assert(id.length == 20, 'next_id: Length should be 20.')

    id
  end

  def seed
    DateTime.now.strftime('%Q').to_i
  end
end

if __FILE__ == $0
  fb = FirebasePush.new
  ts = fb.seed

  (ARGV[0] || 1).to_i.times { |_|
    puts fb.next_id(ts)
  }
end