summaryrefslogtreecommitdiff
path: root/cpp/src/IceUtil/UUID.cpp
blob: 169dcf32eef17490a6104d959e193308cc2687e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// **********************************************************************
//
// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
//
// This copy of Ice is licensed to you under the terms described in the
// ICE_LICENSE file included in this distribution.
//
// **********************************************************************

#include <IceUtil/UUID.h>

// On Windows, we use Windows's RPC UUID generator.
// On other platforms, we use a high quality random number generator 
// (/dev/random) to generate "version 4" UUIDs, as described in 
// http://www.ietf.org/internet-drafts/draft-mealling-uuid-urn-00.txt

#ifdef _WIN32
#   include <rpc.h>
#else
#   include <IceUtil/Random.h>
#endif

using namespace std;

// Helper char to hex functions
//
inline void halfByteToHex(unsigned char hb, char*& hexBuffer)
{
    if(hb < 10)
    {
	*hexBuffer++ = '0' + hb;
    }
    else
    {
	*hexBuffer++ = 'A' + (hb - 10);
    } 
}

inline void bytesToHex(unsigned char* bytes, size_t len, char*& hexBuffer)
{
    for(size_t i = 0; i < len; i++)
    {
	halfByteToHex((bytes[i] & 0xF0) >> 4, hexBuffer);
	halfByteToHex((bytes[i] & 0x0F), hexBuffer);
    }
}

string
IceUtil::generateUUID()
{
#ifdef _WIN32

    UUID uuid;
    UuidCreate(&uuid);

    unsigned char* str;

    UuidToString(&uuid, &str);

    string result = reinterpret_cast<char*>(str);

    RpcStringFree(&str);
    return result;
    
#else
    struct UUID
    {
	unsigned char timeLow[4];
	unsigned char timeMid[2];
	unsigned char timeHighAndVersion[2];
	unsigned char clockSeqHiAndReserved;
	unsigned char clockSeqLow;
	unsigned char node[6];
    };
    UUID uuid;

    assert(sizeof(UUID) == 16);

    // 
    // Get a random sequence of bytes and the pid of the current
    // process. Instead of using 122 random bits that could be
    // duplicated (because of a bug with some Linux kernels and
    // potentially other Unix platforms -- see comment in Random.cpp),
    // we replace the last 15 bits of all "random" Randoms by the last
    // 15 bits of the process id.
    //
    char* buffer = reinterpret_cast<char*>(&uuid);
    char pid[2];
    generateRandomAndGetPid(buffer, sizeof(UUID), pid);

    //
    // Adjust the bits that say "version 4" UUID
    //
    uuid.timeHighAndVersion[0] &= 0x0F;
    uuid.timeHighAndVersion[0] |= (4 << 4);
    uuid.clockSeqHiAndReserved &= 0x3F;
    uuid.clockSeqHiAndReserved |= 0x80;

    //
    // Replace the end of the node by myPid (15 bits) 
    //
    uuid.node[4] = (uuid.node[4] & 0x80) | pid[0];
    uuid.node[5] = pid[1];

    //
    // Convert to a UUID string
    //
    char uuidString[16 * 2 + 4 + 1]; // 16 bytes, 4 '-' and a final '\0'
    char* uuidIndex = uuidString;
    bytesToHex(uuid.timeLow, sizeof(uuid.timeLow), uuidIndex);
    *uuidIndex++ = '-';
    bytesToHex(uuid.timeMid, sizeof(uuid.timeMid), uuidIndex);
    *uuidIndex++ = '-';
    bytesToHex(uuid.timeHighAndVersion, sizeof(uuid.timeHighAndVersion), uuidIndex);
    *uuidIndex++ = '-';
    bytesToHex(&uuid.clockSeqHiAndReserved, sizeof(uuid.clockSeqHiAndReserved), uuidIndex);
    bytesToHex(&uuid.clockSeqLow, sizeof(uuid.clockSeqLow), uuidIndex);
    *uuidIndex++ = '-';
    bytesToHex(uuid.node, sizeof(uuid.node), uuidIndex);
    *uuidIndex = '\0';

    return uuidString;

#endif
}