Class: SimpleUUID::UUID

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/simple_uuid.rb

Overview

UUID format version 1, as specified in RFC 4122, with jitter in place of the mac address and sequence counter.

Defined Under Namespace

Classes: InvalidVersion

Constant Summary collapse

GREGORIAN_EPOCH_OFFSET =

Oct 15, 1582

0x01B2_1DD2_1381_4000
VARIANT =
0b1000_0000_0000_0000

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bytes = nil, opts = {}) ⇒ UUID

Returns a new instance of UUID.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/simple_uuid.rb', line 23

def initialize(bytes = nil, opts = {})
  case bytes
  when self.class # UUID
    @bytes = bytes.to_s
  when String
    case bytes.size
    when 16 # Raw byte array
      @bytes = bytes.respond_to?(:force_encoding) ? bytes.force_encoding("ASCII-8BIT") : bytes
    when 36 # Human-readable UUID representation; inverse of #to_guid
      elements = bytes.split("-")
      raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (malformed UUID representation)" if elements.size != 5
      @bytes = [elements.join].pack('H32')
    else
      raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (invalid bytecount)"
    end

  when Integer
    raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (integer out of range)" if bytes < 0 or bytes > 2**128
    @bytes = [
      (bytes >> 96) & 0xFFFF_FFFF,
      (bytes >> 64) & 0xFFFF_FFFF,
      (bytes >> 32) & 0xFFFF_FFFF,
      bytes & 0xFFFF_FFFF
    ].pack("NNNN")

  when NilClass, Time
    time = (bytes || Time).stamp * 10 + GREGORIAN_EPOCH_OFFSET
    # See http://github.com/spectra/ruby-uuid/
    byte_array = [
      time & 0xFFFF_FFFF,
      time >> 32,
      ((time >> 48) & 0x0FFF) | 0x1000,
    ]

    # Top 3 bytes reserved
    if opts[:randomize] == false
      byte_array += if !opts[:salt].nil?
        clock_h, clock_l, node_h, node_l =
          opts[:salt].to_s.unpack("CCnN")

        clock = [
          clock_h | VARIANT,
          clock_l
        ].pack("n").unpack("n").first

        [ clock, node_h, node_l ]
      else
        [ 0 | VARIANT, 0, 0 ]
      end
    else
      byte_array += [
        rand(2**13) | VARIANT,
        rand(2**16),
        rand(2**32)
      ]
    end

    @bytes = byte_array.pack("NnnnnN")
  else
    raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (unknown source class)"
  end
end

Class Method Details

.to_time(bytes) ⇒ Object

Given raw bytes, return a time object



155
156
157
158
# File 'lib/simple_uuid.rb', line 155

def self.to_time(bytes)
  usecs = total_usecs(bytes)
  Time.at(usecs / 1_000_000, usecs % 1_000_000)
end

.total_usecs(bytes) ⇒ Object

Given raw bytes, return the total_usecs



166
167
168
169
# File 'lib/simple_uuid.rb', line 166

def self.total_usecs(bytes)
  elements = bytes.unpack("Nnn")
  (elements[0] + (elements[1] << 32) + ((elements[2] & 0x0FFF) << 48) - GREGORIAN_EPOCH_OFFSET) / 10
end

Instance Method Details

#<=>(other) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
# File 'lib/simple_uuid.rb', line 119

def <=>(other)
  me = to_s.unpack('Nn2C*')
  him = other.to_s.unpack('Nn2C*')
  # swap most significant time bits to front
  me[0], me[2], him[0], him[2] = me[2], me[0], him[2], him[0]
  me.zip(him) do |m, h|
    comp = m <=> h
    return comp if comp != 0
  end
  return 0
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


150
151
152
# File 'lib/simple_uuid.rb', line 150

def eql?(other)
  other.respond_to?(:bytes) && bytes == other.bytes
end

#hashObject



146
147
148
# File 'lib/simple_uuid.rb', line 146

def hash
  @bytes.hash
end

#inspect(long = false) ⇒ Object



131
132
133
134
135
136
137
138
139
# File 'lib/simple_uuid.rb', line 131

def inspect(long = false)
  "<UUID##{object_id} time: #{
    to_time.inspect
  }, usecs: #{
    usecs
  } jitter: #{
    @bytes.unpack('QQ')[1]
  }" + (long ? ", version: #{version}, variant: #{variant}, guid: #{to_guid}>" :  ">")
end

#secondsObject



111
112
113
# File 'lib/simple_uuid.rb', line 111

def seconds
  total_usecs / 1_000_000
end

#to_guidObject



104
105
106
107
108
109
# File 'lib/simple_uuid.rb', line 104

def to_guid
  elements = @bytes.unpack("NnnCCa6")
  node = elements[-1].unpack('C*')
  elements[-1] = '%02x%02x%02x%02x%02x%02x' % node
  "%08x-%04x-%04x-%02x%02x-%s" % elements
end

#to_iObject



86
87
88
89
90
91
92
# File 'lib/simple_uuid.rb', line 86

def to_i
  ints = @bytes.unpack("NNNN")
  (ints[0] << 96) +
  (ints[1] << 64) +
  (ints[2] << 32) +
  ints[3]
end

#to_sObject Also known as: bytes



141
142
143
# File 'lib/simple_uuid.rb', line 141

def to_s
  @bytes
end

#to_timeObject

Return a time object



161
162
163
# File 'lib/simple_uuid.rb', line 161

def to_time
  Time.at(total_usecs / 1_000_000, total_usecs % 1_000_000)
end

#usecsObject



115
116
117
# File 'lib/simple_uuid.rb', line 115

def usecs
  total_usecs % 1_000_000
end

#variantObject



100
101
102
# File 'lib/simple_uuid.rb', line 100

def variant
  @bytes.unpack('QnnN')[1] >> 13
end

#versionObject



94
95
96
97
98
# File 'lib/simple_uuid.rb', line 94

def version
  time_high = @bytes.unpack("NnnQ")[2]
  version = (time_high & 0xF000).to_s(16)[0].chr.to_i
  version > 0 and version < 6 ? version : -1
end