123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2019 HPCC Systems®.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- #if defined(__linux__) || defined(__APPLE__)
- #include <sys/time.h>
- #elif _WIN32
- #include <windows.h>
- #include <wincrypt.h>
- #include <chrono>
- #endif
- /**
- * This is an implementation of Globally Unique Transaction ID’s.
- * Note: This class currently generates unique id on OS's that supports device /dev/urandom.
- * @author - Amol Patwardhan
- * Version: 1.0
- */
- #include <iostream>
- #include <stdio.h>
- #include <string.h>
- #include <vector>
- #include <assert.h>
- #include <stdio.h>
- #include <time.h>
- #include <sstream>
- #include <ctime>
- #include "lnuid.h"
- #include "jlog.hpp"
- using namespace std;
- namespace ln_uid {
- /** All alphanumeric characters except for "0", "I", "O", and "l" */
- const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
- //Random byte count
- const unsigned int random_byte_count = 11;
- //Random byte count
- const unsigned int time_byte_count = 5;
- //NOTE: This variable is used to offset dropped 41st bit from time component.
- // This variable adds time till last 41st bit flip on 03-Nov-2004.
- // This value should be updated later to handle next bit flip on 07-Sep-2039.
- unsigned long long int OFFSET = (unsigned long long int) 1099511627776;
- ln_uid_t &createUniqueId(ln_uid_t &out)
- {
- #if defined(__linux__) || defined(__APPLE__)
- struct timeval tv;
- gettimeofday(&tv, NULL);
- unsigned long long int millisecondsSinceEpochTrim =
- (unsigned long long int)(tv.tv_sec) * 1000 +
- (unsigned long long int)(tv.tv_usec) / 1000;
- #elif _WIN32
- unsigned long long int millisecondsSinceEpochTrim =
- std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1);
- #else
- #error "Unimplemented"
- #endif
- unsigned char timedata[time_byte_count];
- timedata[0] = (int)((millisecondsSinceEpochTrim >> 32) & 0xFF);
- timedata[1] = (int)((millisecondsSinceEpochTrim >> 24) & 0xFF);
- timedata[2] = (int)((millisecondsSinceEpochTrim >> 16) & 0xFF);
- timedata[3] = (int)((millisecondsSinceEpochTrim >> 8) & 0XFF);
- timedata[4] = (int)((millisecondsSinceEpochTrim & 0XFF));
- unsigned char randomdata[random_byte_count];
- #if defined(__linux__) || defined(__APPLE__)
- FILE *fp;
- fp = fopen("/dev/urandom", "r");
- if (!fp || fread(&randomdata, 1, random_byte_count, fp) != random_byte_count)
- {
- // Should never happen, but if it does log it and ignore
- OERRLOG("Could not read data from /dev/urandom");
- }
- if (fp)
- fclose(fp);
- #elif _WIN32
- HCRYPTPROV hProvider;
- CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
- CryptGenRandom(hProvider, random_byte_count, randomdata);
- #else
- #error "Unimplemented"
- #endif
- for(unsigned int i=0; i <time_byte_count; i++)
- out[i] = timedata[i];
- for(unsigned int i=5, j=0; i <uid_size; i++, j++)
- out[i] = randomdata[j];
- return out;
- }
- string createUniqueIdString()
- {
- ln_uid_t ln_uid = {};
- createUniqueId(ln_uid);
- return uniqueIdToString(ln_uid);
- }
- string uniqueIdToString(const ln_uid_t &uid)
- {
- // Skip and count leading zeroes.
- int zeroes = 0;
- int length = 0;
- int counter = 0;
- while (counter != uid_size && uid[counter] == 0)
- {
- counter++;
- zeroes++;
- }
- // Allocate enough space in big-endian base58 representation.
- int size = (uid_size) * 138 / 100 + 1; // log(256) / log(58), rounded up.
- vector<unsigned char> b58(size);
- // Process the bytes.
- while (counter != uid_size)
- {
- int carry = uid[counter];
- int i = 0;
- // Apply "b58 = b58 * 256 + ch".
- for (vector<unsigned char>::reverse_iterator it = b58.rbegin(); (carry != 0 || i < length) && (it != b58.rend()); it++, i++)
- {
- carry += 256 * (*it);
- *it = carry % 58;
- carry /= 58;
- }
- assert(carry == 0);
- length = i;
- counter++;
- }
- // Skip leading zeroes in base58 result.
- vector<unsigned char>::iterator it = b58.begin() + (size - length);
- while (it != b58.end() && *it == 0)
- it++;
- // Translate the result into a string.
- string str;
- str.reserve(zeroes + (b58.end() - it));
- str.assign(zeroes, '1');
- while (it != b58.end())
- str += pszBase58[*(it++)];
- return str;
- }
- ln_uid_t &uniqueIdFromString(const char* uid, ln_uid_t &out)
- {
- // Skip leading spaces. psz
- while (*uid && isspace(*uid))
- uid++;
- // Skip and count leading '1's.
- int zeroes = 0;
- while (*uid == '1')
- {
- zeroes++;
- uid++;
- }
- // Allocate enough space in big-endian base256 representation.
- vector<unsigned char> b256(strlen(uid) * 733 / 1000 + 1); // log(58) / log(256), rounded up.
- // Process the characters.
- while (*uid && !isspace(*uid))
- {
- // Decode base58 character
- const char* ch = strchr(pszBase58, *uid);
- if (ch == NULL)
- return out;
- // Apply "b256 = b256 * 58 + ch".
- int carry = ch - pszBase58;
- for (vector<unsigned char>::reverse_iterator it = b256.rbegin(); it != b256.rend(); it++)
- {
- carry += 58 * (*it);
- *it = carry % 256;
- carry /= 256;
- }
- assert(carry == 0);
- uid++;
- }
- // Skip trailing spaces.
- while (isspace(*uid))
- uid++;
- if (*uid != 0)
- return out;
- // Skip leading zeroes in b256.
- vector<unsigned char>::iterator it = b256.begin();
- while (it != b256.end() && *it == 0)
- it++;
- vector<unsigned char> vch;
- // Copy result into output vector.
- vch.reserve(zeroes + (b256.end() - it));
- vch.assign(zeroes, 0x00);
- while (it != b256.end())
- vch.push_back(*(it++));
- for(unsigned int i=0; i <vch.size(); i++)
- out[i] = vch[i];
- return out;
- }
- time_t timeFromUniqueId(const char* uid)
- {
- ln_uid_t uid_ch = {};
- uniqueIdFromString(uid, uid_ch);
- return timeFromUniqueId(uid_ch);
- }
- time_t timeFromUniqueId(const ln_uid_t &uid)
- {
- unsigned long long int timepart = 0;
- for(unsigned int i=0; i< time_byte_count; i++ )
- timepart = (timepart << 8) + (uid[i] & 0xff);
- timepart = timepart + OFFSET;
- time_t seconds = (time_t)(timepart/1000);
- return seconds;
- }
- void getUniqueIdRange(time_t start, time_t end, ln_uid_t &uid_start, ln_uid_t &uid_end)
- {
- unsigned long long int startMilliSecondsSinceEpochTrim = (unsigned long long int)(start) * 1000;
- unsigned long long int endMilliSecondsSinceEpochTrim = (unsigned long long int)(end) * 1000;
- unsigned char startTimeData[time_byte_count], endTimeData[time_byte_count];
- startTimeData[0] = (int)((startMilliSecondsSinceEpochTrim >> 32) & 0xFF) ;
- startTimeData[1] = (int)((startMilliSecondsSinceEpochTrim >> 24) & 0xFF) ;
- startTimeData[2] = (int)((startMilliSecondsSinceEpochTrim >> 16) & 0xFF) ;
- startTimeData[3] = (int)((startMilliSecondsSinceEpochTrim >> 8) & 0XFF);
- startTimeData[4] = (int)((startMilliSecondsSinceEpochTrim & 0XFF));
- endTimeData[0] = (int)((endMilliSecondsSinceEpochTrim >> 32) & 0xFF) ;
- endTimeData[1] = (int)((endMilliSecondsSinceEpochTrim >> 24) & 0xFF) ;
- endTimeData[2] = (int)((endMilliSecondsSinceEpochTrim >> 16) & 0xFF) ;
- endTimeData[3] = (int)((endMilliSecondsSinceEpochTrim >> 8) & 0XFF);
- endTimeData[4] = (int)((endMilliSecondsSinceEpochTrim & 0XFF));
- unsigned char blankedRandomdata[random_byte_count], filledRandomData[random_byte_count];
- for(unsigned int i=0; i <random_byte_count; i++)
- {
- blankedRandomdata[i] = 0x00;
- filledRandomData[i] = 0xFF;
- }
- for(unsigned int i=0; i <time_byte_count; i++)
- uid_start[i] = startTimeData[i] ;
- for(unsigned int i=time_byte_count, j=0; i <uid_size; i++, j++)
- uid_start[i] = blankedRandomdata[j] ;
- for(unsigned int i=0; i <time_byte_count; i++)
- uid_end[i] = endTimeData[i] ;
- for(unsigned int i=time_byte_count, j=0; i <uid_size; i++, j++)
- uid_end[i] = filledRandomData[j] ;
- }
- void getUniqueIdDateRange(const char *start, const char *end, ln_uid_t &uid_start, ln_uid_t &uid_end)
- {
- tm startDateTime;
- tm endDateTime;
- time_t t1, t2, start_time, end_time;
- t1 = time(NULL);
- t2 = time(NULL);
- #if defined(__linux__) || defined(__APPLE__)
- localtime_r(&t1, &startDateTime);
- strptime(start, "%Y-%m-%d %H:%M:%S", &startDateTime);
- #elif _WIN32
- int Y1, m1, d1, H1, M1, S1;
- localtime_s( &startDateTime, &t1);
- sscanf_s(start, "%4d-%2d-%2d %2d:%2d:%2d", &Y1, &m1, &d1, &H1, &M1, &S1);
- startDateTime.tm_year = Y1 - 1900;
- startDateTime.tm_mon = m1 - 1;
- startDateTime.tm_mday = d1;
- startDateTime.tm_hour = H1;
- startDateTime.tm_min = M1;
- startDateTime.tm_sec = S1;
- #else
- #error "Unimplemented"
- #endif
- #if defined(__linux__) || defined(__APPLE__)
- localtime_r(&t2, &endDateTime);
- strptime(end, "%Y-%m-%d %H:%M:%S", &endDateTime);
- #elif _WIN32
- localtime_s(&endDateTime, &t2);
- int Y2, m2, d2, H2, M2, S2;
- sscanf_s(end, "%4d-%2d-%2d %2d:%2d:%2d", &Y2, &m2, &d2, &H2, &M2, &S2);
- endDateTime.tm_year = Y2 - 1900;
- endDateTime.tm_mon = m2 - 1;
- endDateTime.tm_mday = d2;
- endDateTime.tm_hour = H2;
- endDateTime.tm_min = M2;
- endDateTime.tm_sec = S2;
- #else
- #error "Unimplemented"
- #endif
- start_time = mktime(&startDateTime) ;
- end_time = mktime(&endDateTime) ;
- cout << "Start time:" << "UTC OFFSET:"<< get_utc_offset () << ":" <<start << ":" << start_time << endl;
- cout << "End time:" << end << ":" << end_time << endl;
- struct tm tm;
- char sb[100];
- #if defined(__linux__) || defined(__APPLE__)
- localtime_r(&start_time, &tm);
- sprintf(sb, "now: %4d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
- #elif _WIN32
- localtime_s(&tm, &start_time);
- sprintf_s(sb, "now: %4d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
- #else
- #error "Unimplemented"
- #endif
- cout << "DateTime is " << sb << endl;
- getUniqueIdRange(start_time, end_time, uid_start, uid_end);
- }
- bool sameUniqueId(const ln_uid_t &uid1, const ln_uid_t &uid2)
- {
- return memcmp(uid1, uid2, sizeof uid2)==0;
- }
- void copyUniqueId(ln_uid_t &to, const ln_uid_t &from)
- {
- memcpy (to, from, sizeof from);
- }
- /* returns the utc timezone offset (e.g. -8 hours for PST)
- */
- int get_utc_offset()
- {
- time_t zero = 24 * 60 * 60L;
- struct tm timeptr;
- int gmtime_hours;
- /* get the local time for Jan 2, 1900 00:00 UTC */
- #if defined(__linux__) || defined(__APPLE__)
- localtime_r(&zero, &timeptr);
- #elif _WIN32
- localtime_s(&timeptr, &zero);
- #else
- #error "Unimplemented"
- #endif
- gmtime_hours = timeptr.tm_hour;
- /* if the local time is the "day before" the UTC, subtract 24 hours
- from the hours to get the UTC offset
- */
- if (timeptr.tm_mday < 2)
- gmtime_hours -= 24;
- return gmtime_hours;
- }
- };
|