Module: Tou
- Defined in:
- lib/tou.rb,
lib/tou/version.rb,
lib/tou/generator.rb
Overview
A generator for time-ordered UUIDs
Defined Under Namespace
Modules: Generator
Constant Summary collapse
- VERSION =
"0.1.0"
Class Method Summary collapse
-
.uuid(**params_for_uuid_bytes) ⇒ String
Generates the bag of 16 bytes with UUID in binary form.
-
.uuid_bytes(random: SecureRandom, time: Time.now) ⇒ String
Generates the bag of 16 bytes with UUID in binary form.
Class Method Details
.uuid(**params_for_uuid_bytes) ⇒ String
Generates the bag of 16 bytes with UUID in binary form. This the canonical representation but can be converted into one.
60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/tou.rb', line 60 def self.uuid(**params_for_uuid_bytes) # N : 32 bit unsigned int # n : 16 bit unsigned int # This will separate the whole random bytestring into a couple groups # (as various sized integers) that match the uuid group size # so [32bit int, 16bit int, 16bit int, 16bit int, 16bit int, 32bit int] # Which then can be shown in hex into the string literal to make up the final uuid. ary = uuid_bytes(**params_for_uuid_bytes).unpack("NnnnnN") # format in hex notation with dashes "%08x-%04x-%04x-%04x-%04x%08x" % ary end |
.uuid_bytes(random: SecureRandom, time: Time.now) ⇒ String
Generates the bag of 16 bytes with UUID in binary form. This is not the canonical representation but can be converted into one.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/tou.rb', line 16 def self.uuid_bytes(random: SecureRandom, time: Time.now) # Use microseconds for the timestamp epoch_micros = (time.to_f * 1_000_000).round # Encode an 8-byte unsigned big-endian uint, and skip the first byte since it's 0, so we're left with 7 bytes. # This gives us sufficient timestamp resolution to last us into year # This will limit our timestamp to the year 4307. # Q> : 64 bit unsigned big-endian int # We want more significant bytes first for better sorting in Postgres epoch_micros_unsigned_uint_bytes = [epoch_micros].pack("Q>")[1..] ts_bytes = epoch_micros_unsigned_uint_bytes # Use the remaining bytes for randomness byte_str = ts_bytes + random.bytes(16 - ts_bytes.bytesize) # This last part encodes the version, the timecode and the variant. # To prevent we have a variant, 4 bits of random and then the timecode I move up the timecode 4 bits. # We end up with: 4bit variant - 8bit timecode - 4bit random from original byte string, hence 2 bytes in total. # # V4 random UUIDs use 4 bits to indicate a version and another 2-3 bits to indicate a variant. # Most V4s (including these ones) are variant 1, which is 2 bits. version = "4" # version 4, 4 bits. timepart = ts_bytes[-1].unpack1("H*") # last byte of timecode (ts_bytes) as hex, 8 bits. rest = byte_str[7].unpack1("h") # last 4 bits of 7th byte as hex. # Our uuid with encoded timestamp will look something like this 16 bytes in total: # ts = timestamp byte # rnd = random byte # |ts| ts, ts, ts, ts, ts, version(4bits) + first half ts(4bits), last half of ts(4bits) + rnd, variant(2bits) + rnd, rnd, rnd, rnd, rnd, rnd, rnd, rnd] byte_str_part_with_time_and_variant = [version + timepart + rest].pack("H*") byte_str.setbyte(6, byte_str_part_with_time_and_variant.getbyte(0)) byte_str.setbyte(7, byte_str_part_with_time_and_variant.getbyte(1)) byte_str.setbyte(8, (byte_str.getbyte(8) & 0x3f) | 0x80) # variant 1 (10 binary) byte_str end |