# frozen_string_literal: true

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'benchmark-ips', require: false
end

require 'benchmark/ips'
require 'stringio'

# Pure ruby equivalent to unsigned leb128
module Varint
  module_function

  def length(value)
    return 1 if value < 128

    (value.bit_length + 6) / 7
  end

  def read(readable)
    value = bits = 0
    loop do
      byte = readable.readbyte
      value |= (byte & 0x7f) << bits
      bits += 7
      break if byte < 0x80
    end
    value
  end

  def write(writeable, value)
    until value < 0x80
      writeable << ((value & 0x7f) | 0x80)
      value >>= 7
    end
    writeable << value
  end
end

# N = (0..10)
N = (2_147_483_637..2_147_483_647)

# a stream of varint/leb128
io = StringIO.new.binmode
N.each do |i|
  buffer = []
  Varint.write(buffer, i)
  io.write(buffer.pack('C*'))
end

Benchmark.ips do |x|
  x.config(warmup: 1, time: 5)

  x.report('write varint (pure ruby)') do
    io = StringIO.new
    N.each do |i|
      buffer = []
      Varint.write(buffer, i)
      io.write(buffer.pack('C*'))
    end
  end

  x.report('write leb128 (native)   ') do
    io = StringIO.new
    N.each do |i|
      io.write([i].pack('R'))
    end
  end

  x.compare!
end

Benchmark.ips do |x|
  x.config(warmup: 1, time: 5)

  x.report('read varint (pure ruby)') do
    io.rewind
    N.each do
      Varint.read(io)
    end
  end

  x.report('read leb128 (native)   ') do
    io.rewind
    N.each do
      buffer = []
      loop do
        byte = io.readbyte
        buffer << byte
        break if byte < 0x80
      end
      buffer.pack('C*').unpack1('R')
    end
  end

  x.compare!
end
