module Bundler::Random::Formatter

def alphanumeric(n = nil, chars: ALPHANUMERIC)

prng.alphanumeric(10, chars: [*"!".."/"]) #=> ",.,++%/''."
prng = Random.new
# or
Random.alphanumeric(4, chars: [*"0".."9"]) #=> "2952"

prng.alphanumeric(10) #=> "i6K93NdqiH"
prng = Random.new
# or
Random.alphanumeric #=> "2BuBuLf3WfSKyQbR"

require 'bundler/vendor/securerandom/lib/random/formatter'

The result may contain A-Z, a-z and 0-9, unless _chars_ is specified.

It may be larger in the future.
If _n_ is not specified or is nil, 16 is assumed.

consist of.
The argument _chars_ specifies the character list which the result is
string to be generated.
The argument _n_ specifies the length, in characters, of the alphanumeric

Generate a random alphanumeric string.
def alphanumeric(n = nil, chars: ALPHANUMERIC)
  n = 16 if n.nil?
  choose(chars, n)
end

def base64(n=nil)

See RFC 3548 for the definition of base64.

prng.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
prng = Random.new
# or
Random.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="

require 'bundler/vendor/securerandom/lib/random/formatter'

The result may contain A-Z, a-z, 0-9, "+", "/" and "=".

It may be larger in the future.
If _n_ is not specified or is nil, 16 is assumed.

to be generated. The length of the result string is about 4/3 of _n_.
The argument _n_ specifies the length, in bytes, of the random number

Generate a random base64 string.
def base64(n=nil)
  [random_bytes(n)].pack("m0")
end

def choose(source, n)

prng.choose([*'0'..'9'], 5) #=> "27309"
prng.choose([*'l'..'r'], 16) #=> "lmrqpoonmmlqlron"

require 'bundler/vendor/securerandom/lib/random/formatter'

The result may contain whatever characters are in the source array.

generated.
The argument _n_ specifies the length, in characters, of the string to be
to generate the string.
The argument _source_ specifies the array of characters from which

source array of characters.
Generate a string that randomly draws from a
def choose(source, n)
 source.size
= size
limit * size <= 0x100000000
t *= size
 1
 = ''.dup
m <= n
 random_number(limit)
 rs.digits(size)
s.length).times { is << 0 }
lt << source.values_at(*is).join('')
 m
 n
 random_number(limit)
 rs.digits(size)
s.length < n
-is.length).times { is << 0 }

.pop while n < is.length
lt.concat source.values_at(*is).join('')

def gen_random(n)

Internal interface to Random; Generate random data _n_ bytes.
def gen_random(n)
ytes(n)

def hex(n=nil)

prng.hex #=> "91dc3bfb4de5b11d029d376634589b61"
prng = Random.new
# or
Random.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"

require 'bundler/vendor/securerandom/lib/random/formatter'

The result may contain 0-9 and a-f.

It may be larger in the future.
If _n_ is not specified or is nil, 16 is assumed.

The length of the resulting hexadecimal string is twice of _n_.
The argument _n_ specifies the length, in bytes, of the random number to be generated.

Generate a random hexadecimal string.
def hex(n=nil)
  random_bytes(n).unpack1("H*")
end

def random_bytes(n=nil)

prng.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
prng = Random.new
# or
Random.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"

require 'bundler/vendor/securerandom/lib/random/formatter'

The result may contain any byte: "\x00" - "\xff".

It may be larger in future.
If _n_ is not specified or is nil, 16 is assumed.

The argument _n_ specifies the length of the result string.

Generate a random binary string.
def random_bytes(n=nil)
  n = n ? n.to_int : 16
  gen_random(n)
end

def urlsafe_base64(n=nil, padding=false)

See RFC 3548 for the definition of URL-safe base64.

prng.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="
prng.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="

prng.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
prng = Random.new
# or
Random.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"

require 'bundler/vendor/securerandom/lib/random/formatter'

"=" is also used if _padding_ is true.
The result may contain A-Z, a-z, 0-9, "-" and "_".

By default, padding is not generated because "=" may be used as a URL delimiter.
Otherwise padding is generated.
If it is false or nil, padding is not generated.
The boolean argument _padding_ specifies the padding.

It may be larger in the future.
If _n_ is not specified or is nil, 16 is assumed.

to be generated. The length of the result string is about 4/3 of _n_.
The argument _n_ specifies the length, in bytes, of the random number

Generate a random URL-safe base64 string.
def urlsafe_base64(n=nil, padding=false)
  s = [random_bytes(n)].pack("m0")
  s.tr!("+/", "-_")
  s.delete!("=") unless padding
  s
end

def uuid


See RFC4122[https://datatracker.ietf.org/doc/html/rfc4122] for details of UUID.

The result contains 122 random bits (15.25 random bytes).

It doesn't contain meaningful information such as MAC addresses, timestamps, etc.
The version 4 UUID is purely random (except the version).

prng.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
prng = Random.new
# or
Random.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
Random.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"

require 'bundler/vendor/securerandom/lib/random/formatter'

Generate a random v4 UUID (Universally Unique IDentifier).
def uuid
  ary = random_bytes(16).unpack("NnnnnN")
  ary[2] = (ary[2] & 0x0fff) | 0x4000
  ary[3] = (ary[3] & 0x3fff) | 0x8000
  "%08x-%04x-%04x-%04x-%04x%08x" % ary
end

def uuid_v7(extra_timestamp_bits: 0)


of the specification.
{Section 6.2}[https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-07.html#monotonicity_counters]
not implemented. Applications with stricter requirements should follow
Counters and other mechanisms for stronger guarantees of monotonicity are

across distributed nodes.
a "leap smear" approach. NTP or PTP will also be needed to synchronize
this, the system clock can synchronize with an NTP server configured to use
on UTC, which excludes leap seconds and can rollback the clock. To avoid
Any rollbacks of the system clock will break monotonicity. UUIDv7 is based

# |<--- sorted -->||<---- random --->|
"0188d4c7-3333-7af9-87c3-8f612edac82e"]
"0188d4c7-3333-7ae2-995a-9f135dc44ead", # <- out of order
"0188d4c7-3333-7ae8-842e-bc3a8b7d0cf9", # <- out of order
["0188d4c7-3333-7a95-850a-de6edb858f7e",
# =>
Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 8) }

# |<--- sorted --->| |<-- random --->|
"0188d4c7-13da-7557-83e1-7cad9cda0d8d"]
"0188d4c7-13da-754a-88ea-ac0baeedd8db",
"0188d4c7-13da-753b-83a5-7fb9b2afaeea",
["0188d4c7-13da-74f9-8b53-22a786ffdd5a",
# =>
Array.new(4) { prng.uuid_v7(extra_timestamp_bits: 12) }
prng = Random.new

of precision, but only 62 random bits (7.75 random bytes).
of random bits. Setting extra_timestamp_bits: 12 provides ~244ns
+extra_timestamp_bits+. The extra timestamp precision comes at the expense
precision, up to 12 bits of additional timestamp may added with
order. To create UUIDs that are time-ordered with sub-millisecond
within the same millisecond are not issued in monotonically increasing
UUIDv7 has millisecond precision by default, so multiple UUIDs created

==== Monotonicity

for details of UUIDv7.
See draft-ietf-uuidrev-rfc4122bis[https://datatracker.ietf.org/doc/draft-ietf-uuidrev-rfc4122bis/]

includes not only random bits but also timestamp.
Note that this method cannot be made reproducable because its output

The result contains 74 random bits (9.25 random bytes).

data inserts.
records, which may have a significant performance benefit compared to random
UUIDs can be used for better database index locality of newly inserted
This allows version 7 UUIDs to be sorted by creation time. Time ordered

with random data, excluding the version and variant bits.
Unix timestamp (milliseconds since the epoch) and fills the remaining bits
The version 7 UUID starts with the least significant 48 bits of a 64 bit

prng.uuid_v7 # => "0188ca51-5e72-7950-a11d-def7ff977c98"
prng = Random.new
# or

# |<--sorted-->| |<----- random ---->|
Random.uuid_v7 # => "0188d4c3-1e74-7085-b14f-ef6415dc6f31"
Random.uuid_v7 # => "0188d4c3-1af8-764f-b049-c204ce0afa23"
Random.uuid_v7 # => "0188d4c3-16fe-744f-86af-38fa04c62bb5"
Random.uuid_v7 # => "0188d4c3-1311-7f96-85c7-242a7aa58f1e"

require 'bundler/vendor/securerandom/lib/random/formatter'

Generate a random v7 UUID (Universally Unique IDentifier).
def uuid_v7(extra_timestamp_bits: 0)
  case (extra_timestamp_bits = Integer(extra_timestamp_bits))
  when 0 # min timestamp precision
    ms = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
    rand = random_bytes(10)
    rand.setbyte(0, rand.getbyte(0) & 0x0f | 0x70) # version
    rand.setbyte(2, rand.getbyte(2) & 0x3f | 0x80) # variant
    "%08x-%04x-%s" % [
      (ms & 0x0000_ffff_ffff_0000) >> 16,
      (ms & 0x0000_0000_0000_ffff),
      rand.unpack("H4H4H12").join("-")
    ]
  when 12 # max timestamp precision
    ms, ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
      .divmod(1_000_000)
    extra_bits = ns * 4096 / 1_000_000
    rand = random_bytes(8)
    rand.setbyte(0, rand.getbyte(0) & 0x3f | 0x80) # variant
    "%08x-%04x-7%03x-%s" % [
      (ms & 0x0000_ffff_ffff_0000) >> 16,
      (ms & 0x0000_0000_0000_ffff),
      extra_bits,
      rand.unpack("H4H12").join("-")
    ]
  when (0..12) # the generic version is slower than the special cases above
    rand_a, rand_b1, rand_b2, rand_b3 = random_bytes(10).unpack("nnnN")
    rand_mask_bits = 12 - extra_timestamp_bits
    ms, ns = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
      .divmod(1_000_000)
    "%08x-%04x-%04x-%04x-%04x%08x" % [
      (ms & 0x0000_ffff_ffff_0000) >> 16,
      (ms & 0x0000_0000_0000_ffff),
      0x7000 |
        ((ns * (1 << extra_timestamp_bits) / 1_000_000) << rand_mask_bits) |
        rand_a & ((1 << rand_mask_bits) - 1),
      0x8000 | (rand_b1 & 0x3fff),
      rand_b2,
      rand_b3
    ]
  else
    raise ArgumentError, "extra_timestamp_bits must be in 0..12"
  end
end