Class: JSON::Util::UUID

Inherits:
Struct
  • Object
show all
Includes:
Comparable
Defined in:
lib/json-schema/util/uuid.rb,
lib/json-schema/util/uuid.rb

Overview

Pure ruby UUID generator, which is compatible with RFC4122

Constant Summary collapse

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'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#raw_bytesObject

Returns the value of attribute raw_bytes

Returns:

  • (Object)

    the current value of raw_bytes



33
34
35
# File 'lib/json-schema/util/uuid.rb', line 33

def raw_bytes
  @raw_bytes
end

Class Method Details

.create(clock = nil, time = nil, mac_addr = nil) ⇒ Object Also known as: create_v1

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



120
121
122
123
124
125
126
127
128
129
130
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
# File 'lib/json-schema/util/uuid.rb', line 120

def create clock = nil, time = nil, 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 = [rand(0x100000000)].pack 'N'
        sha1.update r
      end
      str = sha1.digest
      r = rand 14 # 20-6
      node = str[r, 6] || str
      nnode = node.bytes.to_a
      nnode[0] |= 0x01
      node = ''
      nnode.each { |s| node << s.chr }
      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 = clock % 0x4000 if clock
      m = mac_addr if mac_addr
      t = time
      if t.nil? then
        # UUID epoch is 1582/Oct/15
        tt = Time.now
        t = (tt.to_i * 10000000) + (tt.tv_usec * 10) + 0x01B21DD213814000
      end
      c = c.succ # important; increment here
      write_state fp, c, m
    end
  end

  tl = t & 0xFFFF_FFFF
  tm = t >> 32
  tm = tm & 0xFFFF
  th = t >> 48
  th = th & 0x0FFF
  th = th | 0x1000
  cl = c & 0xFF
  ch = c & 0x3F00
  ch = ch >> 8
  ch = ch | 0x80
  pack tl, tm, th, cl, ch, m
end

.create_md5(str, namespace) ⇒ Object Also known as: create_v3

UUID generation using MD5 (for backward compat.)



70
71
72
73
74
75
76
77
78
79
# File 'lib/json-schema/util/uuid.rb', line 70

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]
  ret = new raw
  ret.freeze
  ret
end

.create_randomObject Also known as: create_v4

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



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/json-schema/util/uuid.rb', line 85

def create_random
  rnd = [
    rand(0x100000000),
    rand(0x100000000),
    rand(0x100000000),
    rand(0x100000000),
  ].pack 'N4'
  raw = mask 4, rnd
  ret = new raw
  ret.freeze
  ret
end

.create_sha1(str, namespace) ⇒ Object Also known as: create_v5

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



57
58
59
60
61
62
63
64
65
66
# File 'lib/json-schema/util/uuid.rb', line 57

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]
  ret = new raw
  ret.freeze
  ret
end

.mask(v, str) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/json-schema/util/uuid.rb', line 39

def mask v, str
  nstr = str.bytes.to_a
  version = [0, 16, 32, 48, 64, 80][v]
  nstr[6] &= 0b00001111
  nstr[6] |= version
  # nstr[7] &= 0b00001111
  # nstr[7] |= 0b01010000
  nstr[8] &= 0b00111111
  nstr[8] |= 0b10000000
  str = +''
  nstr.each { |s| str << s.chr }
  str
end

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

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



190
191
192
193
194
195
# File 'lib/json-schema/util/uuid.rb', line 190

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

.parse(obj) ⇒ Object

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



179
180
181
182
183
184
185
186
# File 'lib/json-schema/util/uuid.rb', line 179

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

.read_state(fp) ⇒ Object

:nodoc:



99
100
101
102
# File 'lib/json-schema/util/uuid.rb', line 99

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

.write_state(fp, c, m) ⇒ Object

:nodoc:



104
105
106
107
108
# File 'lib/json-schema/util/uuid.rb', line 104

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



247
248
249
# File 'lib/json-schema/util/uuid.rb', line 247

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

#==(other) ⇒ Object

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



241
242
243
# File 'lib/json-schema/util/uuid.rb', line 241

def == other
  to_i == other.to_i
end

#to_intObject Also known as: to_i

Convert into 128-bit unsigned integer



220
221
222
223
224
225
# File 'lib/json-schema/util/uuid.rb', line 220

def to_int
  tmp = raw_bytes.unpack 'C*'
  tmp.inject do |r, i|
    (r * 256) | i
  end
end

#to_sObject Also known as: guid

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



205
206
207
208
209
210
# File 'lib/json-schema/util/uuid.rb', line 205

def to_s
  a = unpack
  tmp = a[-1].unpack 'C*'
  a[-1] = format '%02x%02x%02x%02x%02x%02x', *tmp
  '%08x-%04x-%04x-%02x%02x-%s' % a
end

#to_uriObject Also known as: urn

Convert into a RFC4122-comforming URN representation



214
215
216
# File 'lib/json-schema/util/uuid.rb', line 214

def to_uri
  'urn:uuid:' + to_s
end

#unpackObject

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



200
201
202
# File 'lib/json-schema/util/uuid.rb', line 200

def unpack
  raw_bytes.unpack 'NnnCCa6'
end

#versionObject

Gets the version of this UUID returns nil if bad version



230
231
232
233
234
235
236
# File 'lib/json-schema/util/uuid.rb', line 230

def version
  a = unpack
  v = (a[2] & 0xF000).to_s(16)[0].chr.to_i
  return v if (1..5).include? v

  nil
end