Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / algorithm

Sequential GUIDs in C++ with Qt

0.00/5 (No votes)
17 May 2013CPOL1 min read 18.3K  
Generating sequential GUIDs in C++ and Qt 5

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"
 
// Declare static variables
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;
    // Get some random bytes
    for(int i=0; i<10; i++) {
        char b = (char)randInt(0, 255);
        randBytes.append(b);
    }
    // Would prefer a cryptographically secure rng
    // _rng.GetBytes(randBytes);

 
    //long timestamp = QDateTime::Now.Ticks / 10000L;
    qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
    QByteArray timestampBytes;
    timestampBytes.resize(sizeof(timestamp));
    QByteArray timestampBytesRev;
    timestampBytesRev.resize(sizeof(timestamp));
 
    char *ptr = (char *)&timestamp;
    for(int i=0; i<8; i++) {
        timestampBytes[i] = ptr[i];
        timestampBytesRev[7-i] = ptr[i];
    }
    // We only want 48 bytes, cut off high bytes which should be 0's anyway
    // unless this is the future...
    timestampBytes.remove(6, 2);
    timestampBytesRev.remove(0,2);
 
    QByteArray tsBytes;
 
    // Use the correct endian so we get a proper sequence (e.g. counting up digits
    // on the right so it sorts properly in the database)
#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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)