diff --git a/Gemfile.lock b/Gemfile.lock index 618f71e..8987e2f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,17 +1,17 @@ PATH remote: . specs: - uuid (2.3.7) + uuid (2.3.9) macaddr (~> 1.0) GEM remote: https://rubygems.org/ specs: - macaddr (1.6.7) + macaddr (1.7.1) systemu (~> 2.6.2) mocha (0.9.12) rake (0.8.7) - systemu (2.6.4) + systemu (2.6.5) yard (0.6.7) PLATFORMS diff --git a/lib/uuid.rb b/lib/uuid.rb index 6cd9149..7d0447b 100644 --- a/lib/uuid.rb +++ b/lib/uuid.rb @@ -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. @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/test/test-uuid.rb b/test/test-uuid.rb index 335bb0d..9198379 100644 --- a/test/test-uuid.rb +++ b/test/test-uuid.rb @@ -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"