Class: UUID

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/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

.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/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/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/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/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/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/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/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/uuid.rb', line 74

def prand # :nodoc:
	rand 0x100000000
end

.read_state(fp) ⇒ Object

:nodoc:



110
111
112
113
# File 'lib/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/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/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/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/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/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/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/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/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/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/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/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/uuid.rb', line 206

def unpack
	raw_bytes.unpack "NnnCCa6"
end

#versionObject

The version of this UUID



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

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