Pure ruby UUID generator, which is compatible with RFC4122
create the “version 1” UUID with current system clock, current UTC timestamp, and the IEEE 802 address (so-called MAC address).
Speed notice: it's slow. It writes some data into hard drive on every invokation. If you want to speed this up, try remounting tmpdir with a memory based filesystem (such as tmpfs). STILL slow? then no way but rewrite it with c :)
# File lib/uuid.rb, line 131 def create clock=nil, time=Time.now, mac_addr=nil c = t = m = nil Dir.chdir Dir.tmpdir do unless FileTest.exist? STATE_FILE then # Generate a pseudo MAC address because we have no pure-ruby way # to know the MAC address of the NIC this system uses. Note # that cheating with pseudo arresses here is completely legal: # see Section 4.5 of RFC4122 for details. sha1 = Digest::SHA1.new 256.times do r = [prand].pack "N" sha1.update r end ary = sha1.digest.bytes.to_a node = ary.last 6 node[0] |= 0x01 # multicast bit node = node.pack "C*" k = rand 0x40000 open STATE_FILE, 'w' do |fp| fp.flock IO::LOCK_EX write_state fp, k, node fp.chmod 0o777 # must be world writable end end open STATE_FILE, 'r+' do |fp| fp.flock IO::LOCK_EX c, m = read_state fp c += 1 # important; increment here write_state fp, c, m end end c = clock & 0b11_1111_1111_1111 if clock m = mac_addr if mac_addr time = Time.at time if time.is_a? Float case time when Time t = time.to_i * 10_000_000 + time.tv_usec * 10 + UNIXEpoch when Integer t = time + UNIXEpoch else raise TypeError, "cannot convert ``#{time}'' into Time." end tl = t & 0xFFFF_FFFF tm = t >> 32 tm = tm & 0xFFFF th = t >> 48 th = th & 0b0000_1111_1111_1111 th = th | 0b0001_0000_0000_0000 cl = c & 0b0000_0000_1111_1111 ch = c & 0b0011_1111_0000_0000 ch = ch >> 8 ch = ch | 0b1000_0000 pack tl, tm, th, ch, cl, m end
UUID generation using MD5 (for backward compat.)
# File lib/uuid.rb, line 92 def create_md5 str, namespace md5 = Digest::MD5.new md5.update namespace.raw_bytes md5.update str sum = md5.digest raw = mask 3, sum[0..16] new raw end
UUID generation using random-number generator. From it's random nature, there's no warranty that the created ID is really universaly unique.
# File lib/uuid.rb, line 104 def create_random rnd = [prand, prand, prand, prand].pack "N4" raw = mask 4, rnd new raw end
UUID generation using SHA1. Recommended over create_md5. Namespace object is another UUID, some of them are pre-defined below.
# File lib/uuid.rb, line 82 def create_sha1 str, namespace sha1 = Digest::SHA1.new sha1.update namespace.raw_bytes sha1.update str sum = sha1.digest raw = mask 5, sum[0..15] new raw end
# File lib/uuid.rb, line 37 def initialize str tmp = str.unpack "C*" @num = tmp.inject do |r, i| r * 256 | i end @num.freeze self.freeze end
The 'primitive constructor' of this class Note ::pack == uuid
# File lib/uuid.rb, line 198 def pack tl, tm, th, ch, cl, n raw = [tl, tm, th, ch, cl, n].pack "NnnCCa6" new raw end
A simple GUID parser: just ignores unknown characters and convert hexadecimal dump into 16-octet object.
# File lib/uuid.rb, line 189 def parse obj str = obj.to_s.sub /\Aurn:uuid:/, '' str.gsub! /[^0-9A-Fa-f]/, '' raw = [str[0..31]].pack 'H*' new raw end
UUIDs are comparable (don't know what benefits are there, though).
# File lib/uuid.rb, line 288 def <=> other to_s <=> other.to_s end
Two UUIDs are said to be equal if and only if their (byte-order canonicalized) integer representations are equivallent. Refer RFC4122 for details.
# File lib/uuid.rb, line 276 def == other to_i == other.to_i end
The clock sequence of this UUID
# File lib/uuid.rb, line 234 def clock a = unpack ch = a[3] & 0b0001_1111 cl = a[4] c = cl c += ch << 8 c end
Two identical UUIDs should have same hash
# File lib/uuid.rb, line 282 def hash to_i end
The IEEE 802 address in a hexadecimal format
# File lib/uuid.rb, line 244 def node m = unpack[5].unpack 'C*' '%02x%02x%02x%02x%02x%02x' % m end
# File lib/uuid.rb, line 48 def raw_bytes ret = String.new tmp = @num 16.times do |i| x, y = tmp.divmod 256 ret << y tmp = x end ret.reverse! ret end
The timestamp of this UUID. Throws RageError if that time exceeds UNIX time range
# File lib/uuid.rb, line 212 def time a = unpack tl = a[0] tm = a[1] th = a[2] & 0x0FFF t = tl t += tm << 32 t += th << 48 t -= UNIXEpoch tv_sec = t / 10_000_000 t -= tv_sec * 10_000_000 tv_usec = t / 10 Time.at tv_sec, tv_usec end
Convert into 128-bit unsigned integer Typically a Bignum instance, but can be a Fixnum.
# File lib/uuid.rb, line 268 def to_int @num end
Generate the string representation (a.k.a GUID) of this UUID
# File lib/uuid.rb, line 252 def to_s a = unpack a[-1] = mac_address "%08x-%04x-%04x-%02x%02x-%s" % a end
Convert into a RFC4122-comforming URN representation
# File lib/uuid.rb, line 260 def to_uri "urn:uuid:" + self.to_s end
The 'primitive deconstructor', or the dual to pack. Note ::pack == uuid
# File lib/uuid.rb, line 206 def unpack raw_bytes.unpack "NnnCCa6" end
The version of this UUID
# File lib/uuid.rb, line 228 def version v = unpack[2] & 0b1111_0000_0000_0000 v >> 12 end