Class: UUID

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/cast_off/compile/namespace/uuid.rb

Overview

Pure ruby UUID generator, which is compatible with RFC4122

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

.new(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 :)



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
186
187
188
189
190
# File 'lib/cast_off/compile/namespace/uuid.rb', line 134

def new 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
      str = sha1.digest
      r = rand 34 # 40-6
      node = str[r, 6] || str
      node = node.bytes.to_a
      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

.new_md5(str, namespace) ⇒ Object

UUID generation using MD5 (for backward compat.)



109
110
111
112
113
114
115
116
# File 'lib/cast_off/compile/namespace/uuid.rb', line 109

def new_md5 str, namespace
  md5 = Digest::MD5.new
  md5.update namespace.raw_bytes
  md5.update str
  sum = md5.digest
  raw = mask 3, sum[0..16]
  create raw
end

.new_randomObject

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



121
122
123
124
125
# File 'lib/cast_off/compile/namespace/uuid.rb', line 121

def new_random
  rnd = [prand, prand, prand, prand].pack "N4"
  raw = mask 4, rnd
  create raw
end

.new_sha1(str, namespace) ⇒ Object

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



99
100
101
102
103
104
105
106
# File 'lib/cast_off/compile/namespace/uuid.rb', line 99

def new_sha1 str, namespace
  sha1 = Digest::SHA1.new
  sha1.update namespace.raw_bytes
  sha1.update str
  sum = sha1.digest
  raw = mask 5, sum[0..15]
  create raw
end

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

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



203
204
205
206
# File 'lib/cast_off/compile/namespace/uuid.rb', line 203

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

.parse(obj) ⇒ Object

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



194
195
196
197
198
199
# File 'lib/cast_off/compile/namespace/uuid.rb', line 194

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

Instance Method Details

#<=>(other) ⇒ Object

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



293
294
295
# File 'lib/cast_off/compile/namespace/uuid.rb', line 293

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.



281
282
283
# File 'lib/cast_off/compile/namespace/uuid.rb', line 281

def == other
  to_i == other.to_i
end

#clockObject

The clock sequence of this UUID



239
240
241
242
243
244
245
246
# File 'lib/cast_off/compile/namespace/uuid.rb', line 239

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



287
288
289
# File 'lib/cast_off/compile/namespace/uuid.rb', line 287

def hash
  to_i
end

#new_md5(str) ⇒ Object

shortcut too



303
304
305
# File 'lib/cast_off/compile/namespace/uuid.rb', line 303

def new_md5 str
  self.class.new_md5 str, self
end

#new_sha1(str) ⇒ Object

shortcut



298
299
300
# File 'lib/cast_off/compile/namespace/uuid.rb', line 298

def new_sha1 str
  self.class.new_sha1 str, self
end

#nodeObject Also known as: mac_address, ieee802

The IEEE 802 address in a hexadecimal format



249
250
251
252
# File 'lib/cast_off/compile/namespace/uuid.rb', line 249

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

#raw_bytesObject



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/cast_off/compile/namespace/uuid.rb', line 47

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



217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/cast_off/compile/namespace/uuid.rb', line 217

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.



273
274
275
# File 'lib/cast_off/compile/namespace/uuid.rb', line 273

def to_int
  @num
end

#to_sObject Also known as: guid

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



257
258
259
260
261
# File 'lib/cast_off/compile/namespace/uuid.rb', line 257

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



265
266
267
# File 'lib/cast_off/compile/namespace/uuid.rb', line 265

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

#unpackObject

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



211
212
213
# File 'lib/cast_off/compile/namespace/uuid.rb', line 211

def unpack
  raw_bytes.unpack "NnnCCa6"
end

#versionObject

The version of this UUID



233
234
235
236
# File 'lib/cast_off/compile/namespace/uuid.rb', line 233

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