Skip to content
This repository has been archived by the owner on Jan 1, 2024. It is now read-only.

Commit

Permalink
Implement RFC4122 epoch and fixed bits correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
andy-k committed Apr 10, 2019
1 parent b465305 commit 12796a9
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 6 deletions.
28 changes: 22 additions & 6 deletions lib/uuid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,23 @@ module Version

##
# Version number stamped into the UUID to identify it as time-based.
VERSION_CLOCK = 0x0100
VERSION_CLOCK = 0x1000

##
# Time.at(UUID_EPOCH_OFFSET / -1e7).utc == 1582-10-15 00:00:00 UTC.
UUID_EPOCH_OFFSET = 0x01B21DD213814000

##
# Get time as 100ns ticks since UUID epoch.
if Process.respond_to?(:clock_gettime)
def uuid_tick
Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond) / 100 + UUID_EPOCH_OFFSET
end
else
def uuid_tick
(Time.now.to_f * 10_000_000).to_i + UUID_EPOCH_OFFSET
end
end

##
# Formats supported by the UUID generator.
Expand Down Expand Up @@ -255,7 +271,7 @@ def mac_address
# Create a new UUID generator. You really only need to do this once.
def initialize
@drift = 0
@last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i
@last_clock = uuid_tick
@mutex = Mutex.new

state_file = self.class.state_file
Expand Down Expand Up @@ -296,7 +312,7 @@ def generate(format = :default)
# with the new clock.

clock = @mutex.synchronize do
clock = (Time.new.to_f * CLOCK_MULTIPLIER).to_i & 0xFFFFFFFFFFFFFFF0
clock = uuid_tick & 0xFFFFFFFFFFFFFFF0

if clock > @last_clock then
@drift = 0
Expand All @@ -319,8 +335,8 @@ def generate(format = :default)
template % [
clock & 0xFFFFFFFF,
(clock >> 32) & 0xFFFF,
((clock >> 48) & 0xFFFF | VERSION_CLOCK),
@sequence & 0xFFFF,
((clock >> 48) & 0x0FFF) | VERSION_CLOCK,
(@sequence & 0x3FFF) | 0x8000,
@mac & 0xFFFFFFFFFFFF
]
end
Expand All @@ -347,7 +363,7 @@ def next_sequence
write_state io
end
ensure
@last_clock = (Time.now.to_f * CLOCK_MULTIPLIER).to_i
@last_clock = uuid_tick
@drift = 0
end

Expand Down
22 changes: 22 additions & 0 deletions test/test-uuid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,28 @@ class << bar = UUID.new
assert_equal foo.sequence + 1, bar.sequence
end

def test_uuidv1_fixed_bits
uuid = UUID.generate(:default)
v = uuid.gsub("-", "").to_i(16)
assert_equal(
"%x" % [0x00000000_0000_1000_8000_000000000000],
"%x" % [0x00000000_0000_f000_c000_000000000000 & v],
"unexpected bits in #{uuid}")
end

def test_uuidv1_time_field
t1 = Time.now.to_f
uuid = UUID.generate(:default)
t2 = Time.now.to_f
v = uuid.gsub("-", "").to_i(16)
time_field = (
(((v >> 64) & 0x0fff) << 48) |
(((v >> 80) & 0xffff) << 32) |
((v >> 96) & 0xffff_ffff))
t = time_field * 1e-7 - 12219292800
assert_equal true, (t1 <= t && t <= t2), "invalid time in #{uuid}"
end

def test_pseudo_random_mac_address
uuid_gen = UUID.new
Mac.stubs(:addr).returns "00:00:00:00:00:00"
Expand Down

0 comments on commit 12796a9

Please sign in to comment.