This UUID class is here to make Assaf Arkin’s uuid gem not write to $stdout. Used under MIT license (see source code).
To generate a UUID:
UUID.setup uuid = UUID.new uuid.generate
Clock gap is the number of ticks (resolution: 10ns) between two Ruby Time ticks.
Clock multiplier. Converts Time (resolution: seconds) to UUID clock (resolution: 10ns)
Formats supported by the UUID generator.
:default
Produces 36 characters, including hyphens separating the UUID value parts
:compact
Produces a 32 digits (hexadecimal) value with no hyphens
:urn
Adds the prefix urn:uuid:
to the :default
format
File holding the NIC MAC address
Version number stamped into the UUID to identify it as time-based.
Creates a new UUID generator using the NIC stored in NIC_FILE.
# File lib/UPnP/UUID.rb, line 119 def initialize(nic_file = NIC_FILE) if File.exist? nic_file then address = File.read nic_file raise Error, "invalid MAC address #{address}" unless address =~ %r([\da-f]{2}[:\-]){5}[\da-f]{2}/ @address = address.scan(%r[0-9a-fA-F]{2}/).join.hex & 0x7FFFFFFFFFFF else @address = rand(0x800000000000) | 0xF00000000000 end @drift = 0 @last_clock = (Time.new.to_f * CLOCK_MULTIPLIER).to_i @mutex = Mutex.new @sequence = rand 0x10000 end
Discovers the NIC MAC address and saves it to nic_file
. Works
for UNIX (ifconfig) and Windows (ipconfig).
# File lib/UPnP/UUID.rb, line 93 def self.setup(nic_file = NIC_FILE) nic_file = File.expand_path nic_file return if File.exist? nic_file FileUtils.mkdir_p File.dirname(nic_file) # Run ifconfig for UNIX, or ipconfig for Windows. config = '' Dir.chdir Dir.tmpdir do config << %xifconfig 2>/dev/null` config << %xipconfig /all 2>NUL` end addresses = config.scan(%r[^:\-](?:[\da-z][\da-z][:\-]){5}[\da-z][\da-z][^:\-]/) addresses = addresses.map { |addr| addr[1..-2] } raise Error, 'MAC address not found via ifconfig or ipconfig' if addresses.empty? open nic_file, 'w' do |io| io.write addresses.first end end
Generates a new UUID string using
format
. See FORMATS for a
list of supported formats.
# File lib/UPnP/UUID.rb, line 140 def generate(format = :default) template = FORMATS[format] raise ArgumentError, "unknown UUID format #{format.inspect}" if template.nil? # The clock must be monotonically increasing. The clock resolution is at # best 100 ns (UUID spec), but practically may be lower (on my setup, # around 1ms). If this method is called too fast, we don't have a # monotonically increasing clock, so the solution is to just wait. # # It is possible for the clock to be adjusted backwards, in which case we # would end up blocking for a long time. When backward clock is detected, # we prevent duplicates by asking for a new sequence number and continue # with the new clock. clock = @mutex.synchronize do clock = (Time.new.to_f * CLOCK_MULTIPLIER).to_i & 0xFFFFFFFFFFFFFFF0 if clock > @last_clock then @drift = 0 @last_clock = clock elsif clock == @last_clock then drift = @drift += 1 if drift < 10000 @last_clock += 1 else Thread.pass nil end else @sequence = rand 0x10000 @last_clock = clock end end while not clock template % [ clock & 0xFFFFFFFF, (clock >> 32) & 0xFFFF, ((clock >> 48) & 0xFFFF | VERSION_CLOCK), @sequence & 0xFFFF, @address & 0xFFFFFFFFFFFF ] end