Skip to content

Instantly share code, notes, and snippets.

@midnight-wonderer
Last active June 10, 2022 14:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save midnight-wonderer/8fec1c670bd07f26b9466010842d2421 to your computer and use it in GitHub Desktop.
Save midnight-wonderer/8fec1c670bd07f26b9466010842d2421 to your computer and use it in GitHub Desktop.
Random code with Ruby
# frozen_string_literal: true
require 'securerandom'
class RandomCode
class NotSupported < ::StandardError
end
class P_Single < ::StandardError
end
private_constant :P_Single
def initialize(
random: ::SecureRandom,
choose_from: [*('A'..'Z'), *('0'..'9')]
)
digit_limit = choose_from.size
system_limit = 0x100000000
raise NotSupported unless digit_limit < system_limit
raise NotSupported unless 0 < digit_limit
raise P_Single unless 1 < digit_limit
batch_limit = digit_limit
digit_per_batch = 1
loop do
next_try = batch_limit * digit_limit
break if system_limit <= next_try
batch_limit = next_try
digit_per_batch += 1
end
bias_limit = system_limit / batch_limit * batch_limit
@generator = ::Enumerator.new do |yielder|
loop do
yielder << random.bytes(16).unpack('L*')
end
end.lazy.flat_map(&:lazy).select do |source_int|
# discard biased values
source_int < bias_limit
end.flat_map do |source_int|
::Enumerator.new do |yielder|
# we already discarded biased values
# it is safe to generate up to digit_per_batch digits here and discard the remaining
remaining = source_int
digit_per_batch.times do
current_digit = remaining % digit_limit
yielder << choose_from[current_digit]
remaining /= digit_limit
end
end.lazy
end
rescue P_Single
@generator = ::Enumerator.new do |yielder|
digit = choose_from.last
loop do
yielder << digit
end
end.lazy
ensure
freeze
end
def call(length)
@generator.take(length).to_a.join
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment