blocxx
UUID.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
3 * Copyright (C) 2006, Novell, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of
14 * Vintela, Inc.,
15 * nor Novell, Inc.,
16 * nor the names of its contributors or employees may be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *******************************************************************************/
32 
33 
38 #include "blocxx/BLOCXX_config.h"
39 #include "blocxx/UUID.hpp"
42 #include "blocxx/Types.hpp"
43 #include "blocxx/Format.hpp"
44 #include "blocxx/SecureRand.hpp"
45 #include "blocxx/ExceptionIds.hpp"
46 
47 #if !defined(BLOCXX_WIN32)
48 #include <sys/time.h> // for gettimeofday
49 #endif
50 
51 #ifdef BLOCXX_WIN32
52 #include <winsock2.h> // for timeval
53 #endif
54 
55 #include <string.h> // for memcmp
56 #include <stdlib.h> // for rand
57 #include <ctype.h> // for isxdigit
58 
59 namespace BLOCXX_NAMESPACE
60 {
61 
63 
64 namespace {
65 // typedefs
66 typedef UInt64 uuid_time_t;
67 struct uuid_node_t
68 {
69  unsigned char nodeId[6];
70 };
71 struct uuid_state
72 {
73  uuid_time_t timestamp;
74  uuid_node_t nodeId;
75  UInt16 clockSequence;
76 };
77 // static generator state
78 uuid_state g_state;
79 NonRecursiveMutex g_guard;
80 
81 #ifdef BLOCXX_WIN32
82 
83 // FILETIME of Jan 1 1970 00:00:00.
84 static const unsigned __int64 epoch = 116444736000000000L;
85 
86 int gettimeofday(struct timeval * tp, int bogusParm)
87 {
88  FILETIME file_time;
89  SYSTEMTIME system_time;
90  ULARGE_INTEGER ularge;
91 
92  GetSystemTime(&system_time);
93  SystemTimeToFileTime(&system_time, &file_time);
94  ularge.LowPart = file_time.dwLowDateTime;
95  ularge.HighPart = file_time.dwHighDateTime;
96 
97  tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
98  tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
99 
100  return 0;
101 }
102 #endif
103 
105 void getSystemTime(uuid_time_t *uuid_time)
106 {
107  struct timeval tp;
108  gettimeofday(&tp, 0);
109  // Offset between UUID formatted times and Unix formatted times.
110  // UUID UTC base time is October 15, 1582.
111  // Unix base time is January 1, 1970.
112  *uuid_time =
113  (static_cast<unsigned long long>(tp.tv_sec) * 10000000) +
114  (tp.tv_usec * 10) +
115  ((static_cast<unsigned long long>(0x01B21DD2)) << 32) +
116  0x13814000;
117 }
119 // these globals are protected by the mutex locked in UUID::UUID()
120 uuid_time_t timeLast;
121 UInt16 uuidsThisTick;
122 bool currentTimeInited = false;
123 void getCurrentTime(uuid_time_t * timestamp)
124 {
125  uuid_time_t timeNow;
126  if (!currentTimeInited)
127  {
128  getSystemTime(&timeLast);
129  uuidsThisTick = 0;
130  currentTimeInited = true;
131  }
132  getSystemTime(&timeNow);
133  if (timeLast != timeNow)
134  {
135  uuidsThisTick = 0;
136  timeLast = timeNow;
137  }
138  else
139  {
140  uuidsThisTick++;
141  }
142  // add the count of uuids to low order bits of the clock reading
143  *timestamp = timeNow + uuidsThisTick;
144 }
146 // these globals are protected by the mutex locked in UUID::UUID()
147 unsigned char nodeId[6];
148 bool nodeIdInitDone = false;
149 void getNodeIdentifier(uuid_node_t *node)
150 {
151  // If we ever get a portable (or ported) method of acquiring the MAC
152  // address, we should use that. Until then, we'll just use random
153  // numbers.
154  if (!nodeIdInitDone)
155  {
156  Secure::rand(nodeId, sizeof(nodeId));
157  // Set multicast bit, to prevent conflicts with MAC addressses.
158  nodeId[0] |= 0x80;
159  nodeIdInitDone = true;
160  }
161  memcpy(node->nodeId, nodeId, sizeof(node->nodeId));
162 }
164 inline unsigned char decodeHex(char c)
165 {
166  if (isdigit(c))
167  {
168  return c - '0';
169  }
170  else
171  {
172  c = toupper(c);
173  return c - 'A' + 0xA;
174  }
175 }
177 inline unsigned char fromHexStr(char c1, char c2, const String& uuidStr)
178 {
179  if (!isxdigit(c1) || !isxdigit(c2))
180  {
181  BLOCXX_THROW(UUIDException, Format("Invalid UUID: %1", uuidStr).c_str());
182  }
183  return (decodeHex(c1) << 4) | decodeHex(c2);
184 }
186 inline char toHexHi(unsigned char c)
187 {
188  unsigned char t = c >> 4;
189  return t >= 10 ? t - 10 + 'a' : t + '0';
190 }
192 inline char toHexLow(unsigned char c)
193 {
194  unsigned char t = c & 0xF;
195  return t >= 10 ? t - 10 + 'a' : t + '0';
196 }
197 } // end anonymous namespace
200 {
201  NonRecursiveMutexLock l(g_guard);
202  uuid_time_t timestamp;
203  getCurrentTime(&timestamp);
204  uuid_node_t node;
205  getNodeIdentifier(&node);
206  uuid_time_t last_time = g_state.timestamp;
207  UInt16 clockseq = g_state.clockSequence;
208  uuid_node_t last_node = g_state.nodeId;
209  // If clock went backwards (can happen if system clock resolution is low), change clockseq
210  if (timestamp < last_time)
211  {
212  ++clockseq;
213  }
214  // save the state for next time
215  g_state.timestamp = last_time;
216  g_state.clockSequence = clockseq;
217  g_state.nodeId = last_node;
218  l.release();
219  // stuff fields into the UUID
220  // do time_low
221  UInt32 tmp = static_cast<UInt32>(timestamp & 0xFFFFFFFF);
222  m_uuid[3] = static_cast<UInt8>(tmp);
223  tmp >>= 8;
224  m_uuid[2] = static_cast<UInt8>(tmp);
225  tmp >>= 8;
226  m_uuid[1] = static_cast<UInt8>(tmp);
227  tmp >>= 8;
228  m_uuid[0] = static_cast<UInt8>(tmp);
229  // do time_mid
230  tmp = static_cast<UInt16>((timestamp >> 32) & 0xFFFF);
231  m_uuid[5] = static_cast<UInt8>(tmp);
232  tmp >>= 8;
233  m_uuid[4] = static_cast<UInt8>(tmp);
234  // do time_hi_and_version
235  tmp = static_cast<UInt16>(((timestamp >> 48) & 0x0FFF) | (1 << 12));
236  m_uuid[7] = static_cast<UInt8>(tmp);
237  tmp >>= 8;
238  m_uuid[6] = static_cast<UInt8>(tmp);
239  // do clk_seq_low
240  tmp = clockseq & 0xFF;
241  m_uuid[9] = static_cast<UInt8>(tmp);
242  // do clk_seq_hi_res
243  tmp = (clockseq & 0x3F00) >> 8 | 0x80;
244  m_uuid[8] = static_cast<UInt8>(tmp);
245  memcpy(m_uuid+10, &node, 6);
246 }
248 UUID::UUID(const String& uuidStr)
249 {
250  const char* s = uuidStr.c_str();
251  if (uuidStr.length() != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
252  {
253  BLOCXX_THROW(UUIDException, Format("Invalid UUID: %1", uuidStr).c_str());
254  }
255  m_uuid[0] = fromHexStr(s[0], s[1], uuidStr);
256  m_uuid[1] = fromHexStr(s[2], s[3], uuidStr);
257  m_uuid[2] = fromHexStr(s[4], s[5], uuidStr);
258  m_uuid[3] = fromHexStr(s[6], s[7], uuidStr);
259  m_uuid[4] = fromHexStr(s[9], s[10], uuidStr);
260  m_uuid[5] = fromHexStr(s[11], s[12], uuidStr);
261  m_uuid[6] = fromHexStr(s[14], s[15], uuidStr);
262  m_uuid[7] = fromHexStr(s[16], s[17], uuidStr);
263  m_uuid[8] = fromHexStr(s[19], s[20], uuidStr);
264  m_uuid[9] = fromHexStr(s[21], s[22], uuidStr);
265  m_uuid[10] = fromHexStr(s[24], s[25], uuidStr);
266  m_uuid[11] = fromHexStr(s[26], s[27], uuidStr);
267  m_uuid[12] = fromHexStr(s[28], s[29], uuidStr);
268  m_uuid[13] = fromHexStr(s[30], s[31], uuidStr);
269  m_uuid[14] = fromHexStr(s[32], s[33], uuidStr);
270  m_uuid[15] = fromHexStr(s[34], s[35], uuidStr);
271 }
273 String
275 {
276  // This will return a string like this:
277  // 6ba7b810-9dad-11d1-80b4-00c04fd430c8
278  const size_t uuidlen = 37;
279  char* buf = new char[uuidlen];
280  buf[0] = toHexHi(m_uuid[0]); buf[1] = toHexLow(m_uuid[0]);
281  buf[2] = toHexHi(m_uuid[1]); buf[3] = toHexLow(m_uuid[1]);
282  buf[4] = toHexHi(m_uuid[2]); buf[5] = toHexLow(m_uuid[2]);
283  buf[6] = toHexHi(m_uuid[3]); buf[7] = toHexLow(m_uuid[3]);
284  buf[8] = '-';
285  buf[9] = toHexHi(m_uuid[4]); buf[10] = toHexLow(m_uuid[4]);
286  buf[11] = toHexHi(m_uuid[5]); buf[12] = toHexLow(m_uuid[5]);
287  buf[13] = '-';
288  buf[14] = toHexHi(m_uuid[6]); buf[15] = toHexLow(m_uuid[6]);
289  buf[16] = toHexHi(m_uuid[7]); buf[17] = toHexLow(m_uuid[7]);
290  buf[18] = '-';
291  buf[19] = toHexHi(m_uuid[8]); buf[20] = toHexLow(m_uuid[8]);
292  buf[21] = toHexHi(m_uuid[9]); buf[22] = toHexLow(m_uuid[9]);
293  buf[23] = '-';
294  buf[24] = toHexHi(m_uuid[10]); buf[25] = toHexLow(m_uuid[10]);
295  buf[26] = toHexHi(m_uuid[11]); buf[27] = toHexLow(m_uuid[11]);
296  buf[28] = toHexHi(m_uuid[12]); buf[29] = toHexLow(m_uuid[12]);
297  buf[30] = toHexHi(m_uuid[13]); buf[31] = toHexLow(m_uuid[13]);
298  buf[32] = toHexHi(m_uuid[14]); buf[33] = toHexLow(m_uuid[14]);
299  buf[34] = toHexHi(m_uuid[15]); buf[35] = toHexLow(m_uuid[15]);
300  buf[36] = '\0';
301 
302  return String(String::E_TAKE_OWNERSHIP, buf, uuidlen-1);
303 }
305 bool operator==(const UUID& x, const UUID& y)
306 {
307  return memcmp(x.m_uuid, y.m_uuid, sizeof(x.m_uuid)) == 0;
308 }
310 bool operator<(const UUID& x, const UUID& y)
311 {
312  return memcmp(x.m_uuid, y.m_uuid, sizeof(x.m_uuid)) < 0;
313 }
315 bool operator!=(const UUID& x, const UUID& y)
316 {
317  return !(x == y);
318 }
319 
320 } // end namespace BLOCXX_NAMESPACE
321