Click here to Skip to main content
15,867,568 members
Articles / General Programming / Algorithms
Tip/Trick

Sequential GUIDs in C++ with Qt

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
17 May 2013CPOL1 min read 17.7K   7   1
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)


Written By
President Computer Magic And Software Design
United States United States
We specialize in custom software development for your business. With 10+ years of experience, you can be be sure that you are getting the best code that money can buy.

Comments and Discussions

 
SuggestionA better implementation for Qt users? Pin
Member 1091535630-Jun-14 17:10
Member 1091535630-Jun-14 17:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.