UUID.cpp

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

Generated on Fri Jun 16 15:39:09 2006 for blocxx by  doxygen 1.4.6