Class: Mortar::UUID

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/vendor/mortar/uuid.rb

Constant Summary collapse

UNIXEpoch =

UUID epoch is 15th Oct. 1582

0x01B21DD213814000
STATE_FILE =
'ruby-uuid'
NameSpace_DNS =

Pre-defined UUID Namespaces described in RFC4122 Appendix C.

parse "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
NameSpace_URL =
parse "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
NameSpace_OID =
parse "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
NameSpace_X500 =
parse "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
Nil =

The Nil UUID in RFC4122 Section 4.1.7

parse "00000000-0000-0000-0000-000000000000"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.create(clock = nil, time = Time.now, mac_addr = nil) ⇒ Object

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 :)



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/vendor/mortar/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

.create_md5(str, namespace) ⇒ Object

UUID generation using MD5 (for backward compat.)



92
93
94
95
96
97
98
99
# File 'lib/vendor/mortar/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

.create_randomObject

UUID generation using random-number generator. From it’s random nature, there’s no warranty that the created ID is really universaly unique.



104
105
106
107
108
# File 'lib/vendor/mortar/uuid.rb', line 104

def create_random
  rnd = [prand, prand, prand, prand].pack "N4"
  raw = mask 4, rnd
  new raw
end

.create_sha1(str, namespace) ⇒ Object

UUID generation using SHA1. Recommended over create_md5. Namespace object is another UUID, some of them are pre-defined below.



82
83
84
85
86
87
88
89
# File 'lib/vendor/mortar/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

.mask(ver, str) ⇒ Object

:nodoc:



61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/vendor/mortar/uuid.rb', line 61

def mask ver, str # :nodoc:
  ver = ver & 15
  v = str[6].ord
  v &= 0b0000_1111
  v |= ver << 4
  str[6] = v.chr
  r = str[8].ord
  r &= 0b0011_1111
  r |= 0b1000_0000
  str[8] = r.chr
  str
end

.pack(tl, tm, th, ch, cl, n) ⇒ Object

The ‘primitive constructor’ of this class Note UUID.pack(uuid.unpack) == uuid



198
199
200
201
# File 'lib/vendor/mortar/uuid.rb', line 198

def pack tl, tm, th, ch, cl, n
  raw = [tl, tm, th, ch, cl, n].pack "NnnCCa6"
  new raw
end

.parse(obj) ⇒ Object

A simple GUID parser: just ignores unknown characters and convert hexadecimal dump into 16-octet object.



189
190
191
192
193
194
# File 'lib/vendor/mortar/uuid.rb', line 189

def parse obj
  str = obj.to_s.sub %r/\Aurn:uuid:/, ''
  str.gsub! %r/[^0-9A-Fa-f]/, ''
  raw = [str[0..31]].pack 'H*'
  new raw
end

.prandObject

:nodoc:



74
75
76
# File 'lib/vendor/mortar/uuid.rb', line 74

def prand # :nodoc:
  rand 0x100000000
end

.read_state(fp) ⇒ Object

:nodoc:



110
111
112
113
# File 'lib/vendor/mortar/uuid.rb', line 110

def read_state fp       # :nodoc:
  fp.rewind
  Marshal.load fp.read
end

.write_state(fp, c, m) ⇒ Object

:nodoc:



115
116
117
118
119
# File 'lib/vendor/mortar/uuid.rb', line 115

def write_state fp, c, m  # :nodoc:
  fp.rewind
  str = Marshal.dump [c, m]
  fp.write str
end

Instance Method Details

#<=>(other) ⇒ Object

UUIDs are comparable (don’t know what benefits are there, though).



288
289
290
# File 'lib/vendor/mortar/uuid.rb', line 288

def <=> other
  to_s <=> other.to_s
end

#==(other) ⇒ Object Also known as: eql?

Two UUIDs are said to be equal if and only if their (byte-order canonicalized) integer representations are equivallent. Refer RFC4122 for details.



276
277
278
# File 'lib/vendor/mortar/uuid.rb', line 276

def == other
  to_i == other.to_i
end

#clockObject

The clock sequence of this UUID



234
235
236
237
238
239
240
241
# File 'lib/vendor/mortar/uuid.rb', line 234

def clock
  a = unpack
  ch = a[3] & 0b0001_1111
  cl = a[4]
  c = cl
  c += ch << 8
  c
end

#hashObject

Two identical UUIDs should have same hash



282
283
284
# File 'lib/vendor/mortar/uuid.rb', line 282

def hash
  to_i
end

#nodeObject Also known as: mac_address, ieee802

The IEEE 802 address in a hexadecimal format



244
245
246
247
# File 'lib/vendor/mortar/uuid.rb', line 244

def node
  m = unpack[5].unpack 'C*'
  '%02x%02x%02x%02x%02x%02x' % m
end

#raw_bytesObject



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/vendor/mortar/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

#timeObject

The timestamp of this UUID. Throws RageError if that time exceeds UNIX time range



212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/vendor/mortar/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

#to_intObject Also known as: to_i

Convert into 128-bit unsigned integer Typically a Bignum instance, but can be a Fixnum.



268
269
270
# File 'lib/vendor/mortar/uuid.rb', line 268

def to_int
  @num
end

#to_sObject Also known as: guid

Generate the string representation (a.k.a GUID) of this UUID



252
253
254
255
256
# File 'lib/vendor/mortar/uuid.rb', line 252

def to_s
  a = unpack
  a[-1] = mac_address
  "%08x-%04x-%04x-%02x%02x-%s" % a
end

#to_uriObject Also known as: urn, inspect

Convert into a RFC4122-comforming URN representation



260
261
262
# File 'lib/vendor/mortar/uuid.rb', line 260

def to_uri
  "urn:uuid:" + self.to_s
end

#unpackObject

The ‘primitive deconstructor’, or the dual to pack. Note UUID.pack(uuid.unpack) == uuid



206
207
208
# File 'lib/vendor/mortar/uuid.rb', line 206

def unpack
  raw_bytes.unpack "NnnCCa6"
end

#versionObject

The version of this UUID



228
229
230
231
# File 'lib/vendor/mortar/uuid.rb', line 228

def version
  v = unpack[2] & 0b1111_0000_0000_0000
  v >> 12
end