blocxx
|
00001 /******************************************************************************* 00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved. 00003 * Copyright (C) 2006, Novell, Inc. All rights reserved. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * 00008 * * Redistributions of source code must retain the above copyright notice, 00009 * this list of conditions and the following disclaimer. 00010 * * Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * * Neither the name of 00014 * Vintela, Inc., 00015 * nor Novell, Inc., 00016 * nor the names of its contributors or employees may be used to 00017 * endorse or promote products derived from this software without 00018 * specific prior written permission. 00019 * 00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00030 * POSSIBILITY OF SUCH DAMAGE. 00031 *******************************************************************************/ 00032 00033 00038 #include "blocxx/BLOCXX_config.h" 00039 #include "blocxx/UUID.hpp" 00040 #include "blocxx/NonRecursiveMutex.hpp" 00041 #include "blocxx/NonRecursiveMutexLock.hpp" 00042 #include "blocxx/Types.hpp" 00043 #include "blocxx/Format.hpp" 00044 #include "blocxx/SecureRand.hpp" 00045 #include "blocxx/ExceptionIds.hpp" 00046 00047 #if !defined(BLOCXX_WIN32) 00048 #include <sys/time.h> // for gettimeofday 00049 #endif 00050 00051 #ifdef BLOCXX_WIN32 00052 #include <winsock2.h> // for timeval 00053 #endif 00054 00055 #include <string.h> // for memcmp 00056 #include <stdlib.h> // for rand 00057 #include <ctype.h> // for isxdigit 00058 00059 namespace BLOCXX_NAMESPACE 00060 { 00061 00062 BLOCXX_DEFINE_EXCEPTION_WITH_ID(UUID); 00063 00064 namespace { 00065 // typedefs 00066 typedef UInt64 uuid_time_t; 00067 struct uuid_node_t 00068 { 00069 unsigned char nodeId[6]; 00070 }; 00071 struct uuid_state 00072 { 00073 uuid_time_t timestamp; 00074 uuid_node_t nodeId; 00075 UInt16 clockSequence; 00076 }; 00077 // static generator state 00078 uuid_state g_state; 00079 NonRecursiveMutex g_guard; 00080 00081 #ifdef BLOCXX_WIN32 00082 00083 // FILETIME of Jan 1 1970 00:00:00. 00084 static const unsigned __int64 epoch = 116444736000000000L; 00085 00086 int gettimeofday(struct timeval * tp, int bogusParm) 00087 { 00088 FILETIME file_time; 00089 SYSTEMTIME system_time; 00090 ULARGE_INTEGER ularge; 00091 00092 GetSystemTime(&system_time); 00093 SystemTimeToFileTime(&system_time, &file_time); 00094 ularge.LowPart = file_time.dwLowDateTime; 00095 ularge.HighPart = file_time.dwHighDateTime; 00096 00097 tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L); 00098 tp->tv_usec = (long) (system_time.wMilliseconds * 1000); 00099 00100 return 0; 00101 } 00102 #endif 00103 00105 void getSystemTime(uuid_time_t *uuid_time) 00106 { 00107 struct timeval tp; 00108 gettimeofday(&tp, 0); 00109 // Offset between UUID formatted times and Unix formatted times. 00110 // UUID UTC base time is October 15, 1582. 00111 // Unix base time is January 1, 1970. 00112 *uuid_time = 00113 (static_cast<unsigned long long>(tp.tv_sec) * 10000000) + 00114 (tp.tv_usec * 10) + 00115 ((static_cast<unsigned long long>(0x01B21DD2)) << 32) + 00116 0x13814000; 00117 } 00119 // these globals are protected by the mutex locked in UUID::UUID() 00120 uuid_time_t timeLast; 00121 UInt16 uuidsThisTick; 00122 bool currentTimeInited = false; 00123 void getCurrentTime(uuid_time_t * timestamp) 00124 { 00125 uuid_time_t timeNow; 00126 if (!currentTimeInited) 00127 { 00128 getSystemTime(&timeLast); 00129 uuidsThisTick = 0; 00130 currentTimeInited = true; 00131 } 00132 getSystemTime(&timeNow); 00133 if (timeLast != timeNow) 00134 { 00135 uuidsThisTick = 0; 00136 timeLast = timeNow; 00137 } 00138 else 00139 { 00140 uuidsThisTick++; 00141 } 00142 // add the count of uuids to low order bits of the clock reading 00143 *timestamp = timeNow + uuidsThisTick; 00144 } 00146 // these globals are protected by the mutex locked in UUID::UUID() 00147 unsigned char nodeId[6]; 00148 bool nodeIdInitDone = false; 00149 void getNodeIdentifier(uuid_node_t *node) 00150 { 00151 // If we ever get a portable (or ported) method of acquiring the MAC 00152 // address, we should use that. Until then, we'll just use random 00153 // numbers. 00154 if (!nodeIdInitDone) 00155 { 00156 Secure::rand(nodeId, sizeof(nodeId)); 00157 // Set multicast bit, to prevent conflicts with MAC addressses. 00158 nodeId[0] |= 0x80; 00159 nodeIdInitDone = true; 00160 } 00161 memcpy(node->nodeId, nodeId, sizeof(node->nodeId)); 00162 } 00164 inline unsigned char decodeHex(char c) 00165 { 00166 if (isdigit(c)) 00167 { 00168 return c - '0'; 00169 } 00170 else 00171 { 00172 c = toupper(c); 00173 return c - 'A' + 0xA; 00174 } 00175 } 00177 inline unsigned char fromHexStr(char c1, char c2, const String& uuidStr) 00178 { 00179 if (!isxdigit(c1) || !isxdigit(c2)) 00180 { 00181 BLOCXX_THROW(UUIDException, Format("Invalid UUID: %1", uuidStr).c_str()); 00182 } 00183 return (decodeHex(c1) << 4) | decodeHex(c2); 00184 } 00186 inline char toHexHi(unsigned char c) 00187 { 00188 unsigned char t = c >> 4; 00189 return t >= 10 ? t - 10 + 'a' : t + '0'; 00190 } 00192 inline char toHexLow(unsigned char c) 00193 { 00194 unsigned char t = c & 0xF; 00195 return t >= 10 ? t - 10 + 'a' : t + '0'; 00196 } 00197 } // end anonymous namespace 00199 UUID::UUID() 00200 { 00201 NonRecursiveMutexLock l(g_guard); 00202 uuid_time_t timestamp; 00203 getCurrentTime(×tamp); 00204 uuid_node_t node; 00205 getNodeIdentifier(&node); 00206 uuid_time_t last_time = g_state.timestamp; 00207 UInt16 clockseq = g_state.clockSequence; 00208 uuid_node_t last_node = g_state.nodeId; 00209 // If clock went backwards (can happen if system clock resolution is low), change clockseq 00210 if (timestamp < last_time) 00211 { 00212 ++clockseq; 00213 } 00214 // save the state for next time 00215 g_state.timestamp = last_time; 00216 g_state.clockSequence = clockseq; 00217 g_state.nodeId = last_node; 00218 l.release(); 00219 // stuff fields into the UUID 00220 // do time_low 00221 UInt32 tmp = static_cast<UInt32>(timestamp & 0xFFFFFFFF); 00222 m_uuid[3] = static_cast<UInt8>(tmp); 00223 tmp >>= 8; 00224 m_uuid[2] = static_cast<UInt8>(tmp); 00225 tmp >>= 8; 00226 m_uuid[1] = static_cast<UInt8>(tmp); 00227 tmp >>= 8; 00228 m_uuid[0] = static_cast<UInt8>(tmp); 00229 // do time_mid 00230 tmp = static_cast<UInt16>((timestamp >> 32) & 0xFFFF); 00231 m_uuid[5] = static_cast<UInt8>(tmp); 00232 tmp >>= 8; 00233 m_uuid[4] = static_cast<UInt8>(tmp); 00234 // do time_hi_and_version 00235 tmp = static_cast<UInt16>(((timestamp >> 48) & 0x0FFF) | (1 << 12)); 00236 m_uuid[7] = static_cast<UInt8>(tmp); 00237 tmp >>= 8; 00238 m_uuid[6] = static_cast<UInt8>(tmp); 00239 // do clk_seq_low 00240 tmp = clockseq & 0xFF; 00241 m_uuid[9] = static_cast<UInt8>(tmp); 00242 // do clk_seq_hi_res 00243 tmp = (clockseq & 0x3F00) >> 8 | 0x80; 00244 m_uuid[8] = static_cast<UInt8>(tmp); 00245 memcpy(m_uuid+10, &node, 6); 00246 } 00248 UUID::UUID(const String& uuidStr) 00249 { 00250 const char* s = uuidStr.c_str(); 00251 if (uuidStr.length() != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-') 00252 { 00253 BLOCXX_THROW(UUIDException, Format("Invalid UUID: %1", uuidStr).c_str()); 00254 } 00255 m_uuid[0] = fromHexStr(s[0], s[1], uuidStr); 00256 m_uuid[1] = fromHexStr(s[2], s[3], uuidStr); 00257 m_uuid[2] = fromHexStr(s[4], s[5], uuidStr); 00258 m_uuid[3] = fromHexStr(s[6], s[7], uuidStr); 00259 m_uuid[4] = fromHexStr(s[9], s[10], uuidStr); 00260 m_uuid[5] = fromHexStr(s[11], s[12], uuidStr); 00261 m_uuid[6] = fromHexStr(s[14], s[15], uuidStr); 00262 m_uuid[7] = fromHexStr(s[16], s[17], uuidStr); 00263 m_uuid[8] = fromHexStr(s[19], s[20], uuidStr); 00264 m_uuid[9] = fromHexStr(s[21], s[22], uuidStr); 00265 m_uuid[10] = fromHexStr(s[24], s[25], uuidStr); 00266 m_uuid[11] = fromHexStr(s[26], s[27], uuidStr); 00267 m_uuid[12] = fromHexStr(s[28], s[29], uuidStr); 00268 m_uuid[13] = fromHexStr(s[30], s[31], uuidStr); 00269 m_uuid[14] = fromHexStr(s[32], s[33], uuidStr); 00270 m_uuid[15] = fromHexStr(s[34], s[35], uuidStr); 00271 } 00273 String 00274 UUID::toString() const 00275 { 00276 // This will return a string like this: 00277 // 6ba7b810-9dad-11d1-80b4-00c04fd430c8 00278 const size_t uuidlen = 37; 00279 char* buf = new char[uuidlen]; 00280 buf[0] = toHexHi(m_uuid[0]); buf[1] = toHexLow(m_uuid[0]); 00281 buf[2] = toHexHi(m_uuid[1]); buf[3] = toHexLow(m_uuid[1]); 00282 buf[4] = toHexHi(m_uuid[2]); buf[5] = toHexLow(m_uuid[2]); 00283 buf[6] = toHexHi(m_uuid[3]); buf[7] = toHexLow(m_uuid[3]); 00284 buf[8] = '-'; 00285 buf[9] = toHexHi(m_uuid[4]); buf[10] = toHexLow(m_uuid[4]); 00286 buf[11] = toHexHi(m_uuid[5]); buf[12] = toHexLow(m_uuid[5]); 00287 buf[13] = '-'; 00288 buf[14] = toHexHi(m_uuid[6]); buf[15] = toHexLow(m_uuid[6]); 00289 buf[16] = toHexHi(m_uuid[7]); buf[17] = toHexLow(m_uuid[7]); 00290 buf[18] = '-'; 00291 buf[19] = toHexHi(m_uuid[8]); buf[20] = toHexLow(m_uuid[8]); 00292 buf[21] = toHexHi(m_uuid[9]); buf[22] = toHexLow(m_uuid[9]); 00293 buf[23] = '-'; 00294 buf[24] = toHexHi(m_uuid[10]); buf[25] = toHexLow(m_uuid[10]); 00295 buf[26] = toHexHi(m_uuid[11]); buf[27] = toHexLow(m_uuid[11]); 00296 buf[28] = toHexHi(m_uuid[12]); buf[29] = toHexLow(m_uuid[12]); 00297 buf[30] = toHexHi(m_uuid[13]); buf[31] = toHexLow(m_uuid[13]); 00298 buf[32] = toHexHi(m_uuid[14]); buf[33] = toHexLow(m_uuid[14]); 00299 buf[34] = toHexHi(m_uuid[15]); buf[35] = toHexLow(m_uuid[15]); 00300 buf[36] = '\0'; 00301 00302 return String(String::E_TAKE_OWNERSHIP, buf, uuidlen-1); 00303 } 00305 bool operator==(const UUID& x, const UUID& y) 00306 { 00307 return memcmp(x.m_uuid, y.m_uuid, sizeof(x.m_uuid)) == 0; 00308 } 00310 bool operator<(const UUID& x, const UUID& y) 00311 { 00312 return memcmp(x.m_uuid, y.m_uuid, sizeof(x.m_uuid)) < 0; 00313 } 00315 bool operator!=(const UUID& x, const UUID& y) 00316 { 00317 return !(x == y); 00318 } 00319 00320 } // end namespace BLOCXX_NAMESPACE 00321