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
37
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"
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
59namespace BLOCXX_NAMESPACE
60{
61
63
64namespace {
65// typedefs
66typedef UInt64 uuid_time_t;
67struct uuid_node_t
68{
69 unsigned char nodeId[6];
70};
71struct uuid_state
72{
73 uuid_time_t timestamp;
74 uuid_node_t nodeId;
75 UInt16 clockSequence;
76};
77// static generator state
78uuid_state g_state;
79NonRecursiveMutex g_guard;
80
81#ifdef BLOCXX_WIN32
82
83// FILETIME of Jan 1 1970 00:00:00.
84static const unsigned __int64 epoch = 116444736000000000L;
85
86int 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
105void 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()
120uuid_time_t timeLast;
121UInt16 uuidsThisTick;
122bool currentTimeInited = false;
123void 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()
147unsigned char nodeId[6];
148bool nodeIdInitDone = false;
149void 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}
164inline 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}
177inline 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}
186inline char toHexHi(unsigned char c)
187{
188 unsigned char t = c >> 4;
189 return t >= 10 ? t - 10 + 'a' : t + '0';
190}
192inline 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}
247
248UUID::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}
272
273String
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}
304
305bool operator==(const UUID& x, const UUID& y)
306{
307 return memcmp(x.m_uuid, y.m_uuid, sizeof(x.m_uuid)) == 0;
308}
309
310bool operator<(const UUID& x, const UUID& y)
311{
312 return memcmp(x.m_uuid, y.m_uuid, sizeof(x.m_uuid)) < 0;
313}
314
315bool operator!=(const UUID& x, const UUID& y)
316{
317 return !(x == y);
318}
319
320} // end namespace BLOCXX_NAMESPACE
321
#define BLOCXX_DEFINE_EXCEPTION_WITH_ID(NAME)
Define a new exception class named <NAME>Exception that derives from Exception.
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
Note that descriptions of what exceptions may be thrown assumes that object is used correctly,...
This String class is an abstract data type that represents as NULL terminated string of characters.
Definition String.hpp:67
const char * c_str() const
Definition String.cpp:905
size_t length() const
Definition String.cpp:354
UUIDs (Universally Unique IDentifiers), also known as GUIDs (Globally Unique IDentifiers),...
Definition UUID.hpp:63
UUID()
Create a new UUID.
Definition UUID.cpp:199
String toString() const
Get the string representation of this UUID.
Definition UUID.cpp:274
unsigned char m_uuid[16]
Definition UUID.hpp:104
Taken from RFC 1321.
unsigned char UInt8
Definition Types.hpp:66
bool operator<(const Array< T > &x, const Array< T > &y)
bool operator==(const Array< T > &x, const Array< T > &y)
bool operator!=(const Array< T > &x, const Array< T > &y)
Determine two Arrays are not equal.
Definition Array.hpp:446