这个UUID生成代码有问题吗?

时间:2012-09-07 17:46:39

标签: c++ random uuid

所以我有一些代码需要使用UUID来存储数据库ID。为了简单起见,我已经使用了v4(随机),并且我没有看到任何真正的理由使用任何其他较少随机版本的UUID。我的UUID类大致定义如下(简化):

class uuid {
public:
    static uuid create_v4();
public:
    // cut out for simplification...
public:
    uint8_t bytes[16];
};

实际生成代码如下所示:

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;
}

这个看起来对我来说是正确的,但我最近收到了来自数据库的错误,说我尝试插入的UUID是重复的。由于这应该是非常不可能的,我不得不假设我的代码可能存在问题。所以任何人都看错了什么?是我的随机UUID生成,不够随意吗?

注意:我不能使用boost的随机数生成或它的UUID库。我希望我可以,但是我与安装了特定版本库的特定系统绑定并获得足够新版本的boost以使这些功能几乎不可能。

1 个答案:

答案 0 :(得分:3)

代码对我来说似乎是合理的。正如评论中所提到的,关于rand()是否是这项任务的一个很好的选择存在一些问题,但是你使用它似乎是一种产生32位数据的合理方法,假设有一个更新版本的库正在用于确保低位与高位一样随机(在您的评论中也提到)。

因此,只要rand()函数即使是一个中等程度的好工作,你似乎也不太可能得到重复。所以我的猜测是有一种不同的失败。想到的一些可能性:

  1. 时间(0)失败。这似乎不太可能。如果它返回-1表示两次不同运行中的错误,则可能导致问题。但是,它应该能够失败的唯一方法是,如果给它一个无效的地址(这绝对不是这种情况)。
  2. 多线程使用。我不认为rand()是线程安全的。如果在多线程情况下使用此代码,可能会导致意外行为。
  3. Cron造成了困难。如果工作站上的时钟不准确并且它被自动设置(例如,通过rdate)以与某个服务器同步,那么它可能导致在某个时间在cron作业中重复。我只是通过创建一个cron作业来模仿这种行为,每分钟将当前日期转储到一个文件,然后重复设置日期...它最终写入相同的日期/时间(到第二个)到文件更多不止一次。使用时间函数的一秒分辨率,这很容易导致重复的种子。
  4. 将UUID写入数据库的代码不正确。即使UUID生成器工作正常,也可能存在将同一UUID两次写入数据库的不同错误。
  5. 只是疯狂的猜测。其中,第三个是我最喜欢的,但如果我正在审查自己的代码,第四个将是我首先怀疑的那个。