views:

614

answers:

7

Guys,

I wanted a number that would remain unique for a day (24 hours). Following is the code that I came up with; I was wondering about its fallacies/possible risks; 'I believe' this guarantees a 12 digit unique number for a day atleast.

Logic is to get the current date/time (hhmmssmmm) and concat the first four bytes of query performance counter result.

__forceinline bool GetUniqueID(char caUID[MAX_STRING_LENGTH])
{
    //Logic: Add HHMMSSmmm with mid 3 bytes of performance counter.
    //Guarantees that in a single milli second band (0 to 999) the three bytes 
    //of performance counter would always be unique.
    //1. Get system time, and use
    bool bStatus = false;
    try
    {

     SYSTEMTIME localtime;
     GetLocalTime(&localtime);//Get local time, so that we may pull out HHMMSSmmm

     LARGE_INTEGER li;
     char cNT[MAX_STRING_LENGTH];//new time.
     memset(cNT, '\0', sizeof(cNT));
     try
     {
      //Try to get the performance counter,
      //if one is provided by the OEM.

      QueryPerformanceCounter(&li);//This function retrieves the current value of the 
              //high-resolution performance counter if one is provided by the OEM
              //We use the first four bytes only of it.
      sprintf(cNT, "%u", li.QuadPart);
     }
     catch(...)
     {
      //Not provided by OEM.
      //Lets go with the GetTickCounts();
      //ddHHMMSS + 4 bytes of dwTicks
      sprintf(cNT,"%04d", GetTickCount());
     }


     //Get the first four bytes.
     int iSkipTo  = 0;//This is incase we'd decide to pull out next four bytes, rather than first four bytes.
     int iGetChars = 4;//Number of chars to get.
     char *pSub = (char*) malloc(iGetChars+1);//Clear memory
     strncpy(pSub, cNT + iSkipTo, iGetChars);//Get string
     pSub[iGetChars] = '\0'; //Mark end.

     //Prepare unique id
     sprintf(caUID, "%02d%02d%02d%3d%s", 
            localtime.wHour, 
            localtime.wMinute, 
            localtime.wSecond, 
            localtime.wMilliseconds, 
            pSub); //First four characters concat.

     bStatus = true;
    }
    catch(...)
    {
     //Couldnt prepare. There was some problem.
     bStatus = false;
    }

    return bStatus;
}

Following is the output that I get:

Unique:[125907 462224] Unique:[125907 462225] Unique:[125907 462226] Unique:[125907 462227] Unique:[125907 462228] Unique:[125907 462230] Unique:[125907 462231] Unique:[125907 462232] Unique:[125907 462233] Unique:[125907 462234] Unique:[125907 462235] Unique:[125907 462237] Unique:[125907 462238] Unique:[125907 462239] Unique:[125907 462240] Unique:[125907 462241] Unique:[125907 462243] Unique:[125907 462244] Unique:[125907 462245] Unique:[125907 462246] Unique:[125907 462247] Unique:[125907 462248] Unique:[125907 462249] Unique:[125907 462251] Unique:[125907 462252] Unique:[125907 462253] Unique:[125907 462254] Unique:[125907 462255] Unique:[125907 462256] Unique:[125907 462257] Unique:[125907 462258] Millisecond changed, 46 Unique:[125907 622261] Unique:[125907 622262] Unique:[125907 622263] Unique:[125907 622264] Unique:[125907 622265] Unique:[125907 622267] Unique:[125907 622268] Unique:[125907 622269] Unique:[125907 622270] Unique:[125907 622271] Unique:[125907 622273] Unique:[125907 622274] Unique:[125907 622275] Unique:[125907 622276] Unique:[125907 622277] Unique:[125907 622278] Unique:[125907 622279] Unique:[125907 622281] Unique:[125907 622282] Unique:[125907 622283] Unique:[125907 622284] Unique:[125907 622285] Unique:[125907 622286] Unique:[125907 622288] Unique:[125907 622289] Unique:[125907 622290] Unique:[125907 622291] Unique:[125907 622292] Unique:[125907 622293] Unique:[125907 622295] Unique:[125907 622296] Unique:[125907 622297] Unique:[125907 622298] Unique:[125907 622299] Unique:[125907 622300] Unique:[125907 622301] Unique:[125907 622302] Unique:[125907 622304] Unique:[125907 622305] Unique:[125907 622306] Millisecond changed, 62 Unique:[125907 782308] Unique:[125907 782310] Unique:[125907 782311] Unique:[125907 782312] Unique:[125907 782313] Unique:[125907 782314] Unique:[125907 782316] Unique:[125907 782317] Unique:[125907 782318] Unique:[125907 782319] Millisecond changed, 125 Unique:[1259071402495] Unique:[1259071402497] Unique:[1259071402498] Unique:[1259071402499] Unique:[1259071402500] Unique:[1259071402502] Unique:[1259071402503] Unique:[1259071402504] Unique:[1259071402505] Unique:[1259071402507]

Now I am thinking of keeping the generated IDs in a list, and compare the new generated one with the existing ones in the list. If it already exist in the list, then well I can certainly skip the number and generate another, but certainly and clearly this logic would fail.

Would appreciate your comments/suggestions/updates/etc.

Thanks JT.

+1  A: 

The logic seems sound to me if you're running it from one machine on a single-core processor. I don't know if the same holds for a multi-core processor for successive calls, however. The generated numbers will definitely not be unique across machines, however.

Out of curiosity, is there a reason you're not using GUIDs?

Or since you are considering keeping the generated IDs in a list to compare, why can't you create a generator? Since you suggest storage is an option, if you store the last used number and increment it every use... You can even store a date and reset the counter every day if you wish.

lc
Thanks LC:Though you're idea of generator is good; and it may work as well. Reason I wanted a list is to see, to verify, if the new number is already occured during that day. Keeping a list of previous numbers, in production, would/could mean an unknown length of list, and possibly memory leaks.
KMan
And that's why if you can keep a local generator, you don't need to know anything but the last generated number. That is, if your machine is the only one generating numbers, of course.
lc
+1  A: 

My solution was to get the system time and add a counter to that (pseudo code):

static int counter = 0;
static Time lastTime;

String getNextId() {
    Time now = System.getTime();
    if (lastTime == now)
        counter ++;
    else
        counter = 0;
    return now+counter;
}

This would guarantee that I'd get a new ID even when I called the method more often than getTime() changes.

Aaron Digulla
Thanks Aaron: This is what LC pointed towards.
KMan
The advantage of my approach is that I only need to remember the last result.
Aaron Digulla
A: 

I have had problems with times in fast loops where they haven't changed despite it being common sense they should (you have no control over the OS so you can't assume that the time changes every x milliseconds etc.)

Adding the counter value as an extra few digits (not as an increment) and resetting it on restart or after 9999 should hide this enough to make it all but impossible to happen (famous last words).

format is then TTTTTToooo where T is your time figure and o your 4 digit offset.

Michael
A: 

Aaron: Thanks for your comments, I used your approach to get what I wanted.

__forceinline bool GetUniqueIDEx(char caUID[MAX_STRING_LENGTH])
{
    //Logic: Add HHMMSSmmm with 3 bytes counter.
    //Guarantees a unique number for a calendar date, 
    //that in a single milli second band (0 to 999) the three bytes 
    //of counter would always be unique.
    //1. Get system time, and use

    bool bStatus = false;
    try
    {
     GetLocalTime(&localtime);//Get local time, so that we may pull out HHMMSSmmm

     char cNT[MAX_STRING_LENGTH];//new time.
     memset(cNT, '\0', sizeof(cNT));
     if(m_nCounter> MAX_COUNTER_LIMIT)
     {
      m_nCounter= 0;
     }

     sprintf(cNT, "%03d", ++m_nCounter);

     //Prepare unique id
     sprintf(caUID, "%02d%02d%02d%03d%s", 
              localtime.wHour, 
              localtime.wMinute, 
              localtime.wSecond, 
              localtime.wMilliseconds, 
              cNT); 

     bStatus = true;
    }
    catch(...)
    {
     //Couldnt prepare. There was some problem.
     bStatus = false;
    }

    return bStatus;
}
KMan
A: 

I would use seconds since midnight and then a counter. This gives you up to ten millions hits per seconds. Should be pretty unique. Using Aaron's code above, you format the string as:

sprintf(idstr, "%05d%07d", secs_since_midnight, counter);

Of course, I'm also a firm believer in using base-36 (via itoa) when you really want to cram a counter into a few printable characters (and day of year rather than month/day, etc.)

jmucchiello
A: 

I'm not sure whether your process can sacrifice tiny performance issue and keep the logic simple.

We can guarantee unique number with HHMMSSmmm format itself if you put your process to sleep for 1 millisecond in between two calls. By this way you can eliminate the concatenation part and also the list you have to maintain to double check the uniqueness.

Manikanthan Velayutham // programmed to think BIG

Manikanthan Velayutham
What would happen when your application expects more than 1000 requests in a millisecond - or even a second? and the user is waiting for a response, which in turn is going to respond to other system - and so on. Do you believe adding a sleep would help? While your suggestion may work, but I believe no system "should" allow a sleep call just to get a unique timer. A not-so-good-design.
KMan
Btw, Aaron's idea worked for me.
KMan
A: 

My bad. I did not know your application's nature.

Manikanthan Velayutham // programmed to think BIG

Manikanthan Velayutham