blocxx

UUID.cpp

Go to the documentation of this file.
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(&timestamp);
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