Introduction
Generate sequential GUIDs in C++ using the Qt framework. This is a rewrite of the existing C# article here (http://www.codeproject.com/Articles/388157/GUIDs-as-fast-primary-keys-under-multiple-database). That article contains an excellent description of why you would want sequential GUIDs.
This code is written specifically for Qt 5.
Background
Databases insert sequential information quickly. When you insert information out of order it requires the database to rebuild indexes which can be time consuming. Get the advantages of sequential numbering for you database records while getting the benefit of unique ids that a GUID gives.
Source Code - sequenatialguid.h
#ifndef SEQUENTIALGUID_H
#define SEQUENTIALGUID_H
#include <QObject>
#include <QUuid>
#include <QByteArray>
#include <QDateTime>
#include <QDataStream>
#include <QTime>
class SequentialGUID : public QObject
{
Q_OBJECT
public:
explicit SequentialGUID(QObject *parent = 0);
enum SequentialGUIDType {
SequentialAsString = 0,
SequentialAsBinary = 1,
SequentialAtEnd = 2
};
static QUuid GetSequentialGUID(SequentialGUIDType guid_type);
signals:
public slots:
private:
static void InitRand();
static int randInt(int low, int high);
static bool rand_init;
};
#endif // SEQUENTIALGUID_H
Source Code - sequentialguid.cpp
#include "sequentialguid.h"
bool SequentialGUID::rand_init = false;
SequentialGUID::SequentialGUID(QObject *parent) :
QObject(parent)
{
}
void SequentialGUID::InitRand() {
if (!rand_init) {
QTime t = QTime::currentTime();
qsrand((uint) t.msec());
rand_init = true;
}
}
int SequentialGUID::randInt(int low, int high) {
InitRand();
return qrand() % ((high+1) - low) + low;
}
QUuid SequentialGUID::GetSequentialGUID(SequentialGUIDType guid_type)
{
QByteArray randBytes;
for(int i=0; i<10; i++) {
char b = (char)randInt(0, 255);
randBytes.append(b);
}
qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
QByteArray timestampBytes;
timestampBytes.resize(sizeof(timestamp));
QByteArray timestampBytesRev;
timestampBytesRev.resize(sizeof(timestamp));
char *ptr = (char *)×tamp;
for(int i=0; i<8; i++) {
timestampBytes[i] = ptr[i];
timestampBytesRev[7-i] = ptr[i];
}
timestampBytes.remove(6, 2);
timestampBytesRev.remove(0,2);
QByteArray tsBytes;
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
tsBytes = timestampBytesRev;
#else
tsBytes = timestampBytes;
#endif
QByteArray guidBytes;
switch (guid_type) {
case SequentialAsString:
case SequentialAsBinary:
guidBytes.append(tsBytes);
guidBytes.append(randBytes);
break;
case SequentialAtEnd:
guidBytes.append(randBytes);
guidBytes.append(tsBytes);
break;
}
QUuid uid = QUuid::fromRfc4122(guidBytes);
return uid;
}
Using the code
Include the sequentialguid.h file so you can use the object in your application:
#include "sequentialguid.h"
Get a new GUID this way:
<span style="font-size: 9pt;">QUuid guid = SequentialGUID::GetSequentialGUID(SequentialGUID::SequentialAsString);</span><span style="font-size: 9pt;"> </span>
Points of Interest
Random numbers are NOT generated using a cryptographically secure method. For my particular case, this wasn't important. The original article used the .Net framework System.Security.Cryptography.RNGCryptoServiceProvider(); to do so. I didn't know the code to do so in C++ off the top of my head.
The parameter SequentialGUID::SequentialAsString can be replaced with SequentialAsBinary, or SequentialAtEnd.
Rule of thumb us to use SequentialAtEnd for MSSql servers, and the others for most other database systems (look under "Using The Code" section in original article for a description).
Here is an example of sequential GUIDs
History
5/17/2013 - Initial posting of code