So I have some code which needs to use UUID for database IDs. I’ve gone with v4 (random) for simplicity’s sake, and I don’t see any real reason to use any of the other less random version of UUID. My UUID class is approximately defined like this (simplified):
class uuid {
public:
static uuid create_v4();
public:
// cut out for simplification...
public:
uint8_t bytes[16];
};
where the actual generation code looks like this:
namespace {
uint32_t rand32() {
// we need to do this, because there is no
// gaurantee that RAND_MAX is >= 0xffffffff
// in fact, it is LIKELY to be 0x7fffffff
const uint32_t r1 = rand() & 0x0ff;
const uint32_t r2 = rand() & 0xfff;
const uint32_t r3 = rand() & 0xfff;
return (r3 << 20) | (r2 << 8) | r1;
}
}
uuid uuid::create_v4() {
static const uint16_t c[] = {
0x8000,
0x9000,
0xa000,
0xb000,
};
uuid uuid;
const uint32_t rand_1 = (rand32() & 0xffffffff);
const uint32_t rand_2 = (rand32() & 0xffff0fff) | 0x4000;
const uint32_t rand_3 = (rand32() & 0xffff0fff) | c[rand() & 0x03];
const uint32_t rand_4 = (rand32() & 0xffffffff);
uuid.bytes[0x00] = (rand_1 >> 24) & 0xff;
uuid.bytes[0x01] = (rand_1 >> 16) & 0xff;
uuid.bytes[0x02] = (rand_1 >> 8 ) & 0xff;
uuid.bytes[0x03] = (rand_1 ) & 0xff;
uuid.bytes[0x04] = (rand_2 >> 24) & 0xff;
uuid.bytes[0x05] = (rand_2 >> 16) & 0xff;
uuid.bytes[0x06] = (rand_2 >> 8 ) & 0xff;
uuid.bytes[0x07] = (rand_2 ) & 0xff;
uuid.bytes[0x08] = (rand_3 >> 24) & 0xff;
uuid.bytes[0x09] = (rand_3 >> 16) & 0xff;
uuid.bytes[0x0a] = (rand_3 >> 8 ) & 0xff;
uuid.bytes[0x0b] = (rand_3 ) & 0xff;
uuid.bytes[0x0c] = (rand_4 >> 24) & 0xff;
uuid.bytes[0x0d] = (rand_4 >> 16) & 0xff;
uuid.bytes[0x0e] = (rand_4 >> 8 ) & 0xff;
uuid.bytes[0x0f] = (rand_4 ) & 0xff;
return uuid;
}
This looks correct to me, but I recently got an error from the DB saying that the UUID I tried to insert was a duplicate. Since this is supposed to be highly improbable, I have to assume that there may be an issue with my code. So anyone see anything wrong? Is my random UUID generation, not quite random enough?
NOTE: I cannot use boost’s random number generation or it’s UUID library. I wish I could, but I am tied to a particular system with particular versions of libraries installed and getting a new enough version of boost to have those features is pretty much not possible.
The code appears to be reasonable to me. As mentioned in the comments, there is some question as to whether rand() is a good choice for this task, but your usage of it seems like a reasonable way to produce 32-bits of data assuming a newer version of the library is being used that ensures the lower bits are as random as the higher bits (also mentioned in the comments by you).
So as long as the rand() function is doing even a moderately good job, it seems very unlikely that you should get a duplicate. So my guess is that there was a different kind of failure. Some possibilities that come to mind:
Just wild guesses. Of these, the third one is my favorite, but the 4th would be the one I would suspect first if I were reviewing my own code.