lnuid.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2019 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #if defined(__linux__) || defined(__APPLE__)
  14. #include <sys/time.h>
  15. #elif _WIN32
  16. #include <windows.h>
  17. #include <wincrypt.h>
  18. #include <chrono>
  19. #endif
  20. /**
  21. * This is an implementation of Globally Unique Transaction ID’s.
  22. * Note: This class currently generates unique id on OS's that supports device /dev/urandom.
  23. * @author - Amol Patwardhan
  24. * Version: 1.0
  25. */
  26. #include <iostream>
  27. #include <stdio.h>
  28. #include <string.h>
  29. #include <vector>
  30. #include <assert.h>
  31. #include <stdio.h>
  32. #include <time.h>
  33. #include <sstream>
  34. #include <ctime>
  35. #include "lnuid.h"
  36. #include "jlog.hpp"
  37. using namespace std;
  38. namespace ln_uid {
  39. /** All alphanumeric characters except for "0", "I", "O", and "l" */
  40. const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
  41. //Random byte count
  42. const unsigned int random_byte_count = 11;
  43. //Random byte count
  44. const unsigned int time_byte_count = 5;
  45. //NOTE: This variable is used to offset dropped 41st bit from time component.
  46. // This variable adds time till last 41st bit flip on 03-Nov-2004.
  47. // This value should be updated later to handle next bit flip on 07-Sep-2039.
  48. unsigned long long int OFFSET = (unsigned long long int) 1099511627776;
  49. ln_uid_t &createUniqueId(ln_uid_t &out)
  50. {
  51. #if defined(__linux__) || defined(__APPLE__)
  52. struct timeval tv;
  53. gettimeofday(&tv, NULL);
  54. unsigned long long int millisecondsSinceEpochTrim =
  55. (unsigned long long int)(tv.tv_sec) * 1000 +
  56. (unsigned long long int)(tv.tv_usec) / 1000;
  57. #elif _WIN32
  58. unsigned long long int millisecondsSinceEpochTrim =
  59. std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1);
  60. #else
  61. #error "Unimplemented"
  62. #endif
  63. unsigned char timedata[time_byte_count];
  64. timedata[0] = (int)((millisecondsSinceEpochTrim >> 32) & 0xFF);
  65. timedata[1] = (int)((millisecondsSinceEpochTrim >> 24) & 0xFF);
  66. timedata[2] = (int)((millisecondsSinceEpochTrim >> 16) & 0xFF);
  67. timedata[3] = (int)((millisecondsSinceEpochTrim >> 8) & 0XFF);
  68. timedata[4] = (int)((millisecondsSinceEpochTrim & 0XFF));
  69. unsigned char randomdata[random_byte_count];
  70. #if defined(__linux__) || defined(__APPLE__)
  71. FILE *fp;
  72. fp = fopen("/dev/urandom", "r");
  73. if (!fp || fread(&randomdata, 1, random_byte_count, fp) != random_byte_count)
  74. {
  75. // Should never happen, but if it does log it and ignore
  76. OERRLOG("Could not read data from /dev/urandom");
  77. }
  78. if (fp)
  79. fclose(fp);
  80. #elif _WIN32
  81. HCRYPTPROV hProvider;
  82. CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
  83. CryptGenRandom(hProvider, random_byte_count, randomdata);
  84. #else
  85. #error "Unimplemented"
  86. #endif
  87. for(unsigned int i=0; i <time_byte_count; i++)
  88. out[i] = timedata[i];
  89. for(unsigned int i=5, j=0; i <uid_size; i++, j++)
  90. out[i] = randomdata[j];
  91. return out;
  92. }
  93. string createUniqueIdString()
  94. {
  95. ln_uid_t ln_uid = {};
  96. createUniqueId(ln_uid);
  97. return uniqueIdToString(ln_uid);
  98. }
  99. string uniqueIdToString(const ln_uid_t &uid)
  100. {
  101. // Skip and count leading zeroes.
  102. int zeroes = 0;
  103. int length = 0;
  104. int counter = 0;
  105. while (counter != uid_size && uid[counter] == 0)
  106. {
  107. counter++;
  108. zeroes++;
  109. }
  110. // Allocate enough space in big-endian base58 representation.
  111. int size = (uid_size) * 138 / 100 + 1; // log(256) / log(58), rounded up.
  112. vector<unsigned char> b58(size);
  113. // Process the bytes.
  114. while (counter != uid_size)
  115. {
  116. int carry = uid[counter];
  117. int i = 0;
  118. // Apply "b58 = b58 * 256 + ch".
  119. for (vector<unsigned char>::reverse_iterator it = b58.rbegin(); (carry != 0 || i < length) && (it != b58.rend()); it++, i++)
  120. {
  121. carry += 256 * (*it);
  122. *it = carry % 58;
  123. carry /= 58;
  124. }
  125. assert(carry == 0);
  126. length = i;
  127. counter++;
  128. }
  129. // Skip leading zeroes in base58 result.
  130. vector<unsigned char>::iterator it = b58.begin() + (size - length);
  131. while (it != b58.end() && *it == 0)
  132. it++;
  133. // Translate the result into a string.
  134. string str;
  135. str.reserve(zeroes + (b58.end() - it));
  136. str.assign(zeroes, '1');
  137. while (it != b58.end())
  138. str += pszBase58[*(it++)];
  139. return str;
  140. }
  141. ln_uid_t &uniqueIdFromString(const char* uid, ln_uid_t &out)
  142. {
  143. // Skip leading spaces. psz
  144. while (*uid && isspace(*uid))
  145. uid++;
  146. // Skip and count leading '1's.
  147. int zeroes = 0;
  148. while (*uid == '1')
  149. {
  150. zeroes++;
  151. uid++;
  152. }
  153. // Allocate enough space in big-endian base256 representation.
  154. vector<unsigned char> b256(strlen(uid) * 733 / 1000 + 1); // log(58) / log(256), rounded up.
  155. // Process the characters.
  156. while (*uid && !isspace(*uid))
  157. {
  158. // Decode base58 character
  159. const char* ch = strchr(pszBase58, *uid);
  160. if (ch == NULL)
  161. return out;
  162. // Apply "b256 = b256 * 58 + ch".
  163. int carry = ch - pszBase58;
  164. for (vector<unsigned char>::reverse_iterator it = b256.rbegin(); it != b256.rend(); it++)
  165. {
  166. carry += 58 * (*it);
  167. *it = carry % 256;
  168. carry /= 256;
  169. }
  170. assert(carry == 0);
  171. uid++;
  172. }
  173. // Skip trailing spaces.
  174. while (isspace(*uid))
  175. uid++;
  176. if (*uid != 0)
  177. return out;
  178. // Skip leading zeroes in b256.
  179. vector<unsigned char>::iterator it = b256.begin();
  180. while (it != b256.end() && *it == 0)
  181. it++;
  182. vector<unsigned char> vch;
  183. // Copy result into output vector.
  184. vch.reserve(zeroes + (b256.end() - it));
  185. vch.assign(zeroes, 0x00);
  186. while (it != b256.end())
  187. vch.push_back(*(it++));
  188. for(unsigned int i=0; i <vch.size(); i++)
  189. out[i] = vch[i];
  190. return out;
  191. }
  192. time_t timeFromUniqueId(const char* uid)
  193. {
  194. ln_uid_t uid_ch = {};
  195. uniqueIdFromString(uid, uid_ch);
  196. return timeFromUniqueId(uid_ch);
  197. }
  198. time_t timeFromUniqueId(const ln_uid_t &uid)
  199. {
  200. unsigned long long int timepart = 0;
  201. for(unsigned int i=0; i< time_byte_count; i++ )
  202. timepart = (timepart << 8) + (uid[i] & 0xff);
  203. timepart = timepart + OFFSET;
  204. time_t seconds = (time_t)(timepart/1000);
  205. return seconds;
  206. }
  207. void getUniqueIdRange(time_t start, time_t end, ln_uid_t &uid_start, ln_uid_t &uid_end)
  208. {
  209. unsigned long long int startMilliSecondsSinceEpochTrim = (unsigned long long int)(start) * 1000;
  210. unsigned long long int endMilliSecondsSinceEpochTrim = (unsigned long long int)(end) * 1000;
  211. unsigned char startTimeData[time_byte_count], endTimeData[time_byte_count];
  212. startTimeData[0] = (int)((startMilliSecondsSinceEpochTrim >> 32) & 0xFF) ;
  213. startTimeData[1] = (int)((startMilliSecondsSinceEpochTrim >> 24) & 0xFF) ;
  214. startTimeData[2] = (int)((startMilliSecondsSinceEpochTrim >> 16) & 0xFF) ;
  215. startTimeData[3] = (int)((startMilliSecondsSinceEpochTrim >> 8) & 0XFF);
  216. startTimeData[4] = (int)((startMilliSecondsSinceEpochTrim & 0XFF));
  217. endTimeData[0] = (int)((endMilliSecondsSinceEpochTrim >> 32) & 0xFF) ;
  218. endTimeData[1] = (int)((endMilliSecondsSinceEpochTrim >> 24) & 0xFF) ;
  219. endTimeData[2] = (int)((endMilliSecondsSinceEpochTrim >> 16) & 0xFF) ;
  220. endTimeData[3] = (int)((endMilliSecondsSinceEpochTrim >> 8) & 0XFF);
  221. endTimeData[4] = (int)((endMilliSecondsSinceEpochTrim & 0XFF));
  222. unsigned char blankedRandomdata[random_byte_count], filledRandomData[random_byte_count];
  223. for(unsigned int i=0; i <random_byte_count; i++)
  224. {
  225. blankedRandomdata[i] = 0x00;
  226. filledRandomData[i] = 0xFF;
  227. }
  228. for(unsigned int i=0; i <time_byte_count; i++)
  229. uid_start[i] = startTimeData[i] ;
  230. for(unsigned int i=time_byte_count, j=0; i <uid_size; i++, j++)
  231. uid_start[i] = blankedRandomdata[j] ;
  232. for(unsigned int i=0; i <time_byte_count; i++)
  233. uid_end[i] = endTimeData[i] ;
  234. for(unsigned int i=time_byte_count, j=0; i <uid_size; i++, j++)
  235. uid_end[i] = filledRandomData[j] ;
  236. }
  237. void getUniqueIdDateRange(const char *start, const char *end, ln_uid_t &uid_start, ln_uid_t &uid_end)
  238. {
  239. tm startDateTime;
  240. tm endDateTime;
  241. time_t t1, t2, start_time, end_time;
  242. t1 = time(NULL);
  243. t2 = time(NULL);
  244. #if defined(__linux__) || defined(__APPLE__)
  245. localtime_r(&t1, &startDateTime);
  246. strptime(start, "%Y-%m-%d %H:%M:%S", &startDateTime);
  247. #elif _WIN32
  248. int Y1, m1, d1, H1, M1, S1;
  249. localtime_s( &startDateTime, &t1);
  250. sscanf_s(start, "%4d-%2d-%2d %2d:%2d:%2d", &Y1, &m1, &d1, &H1, &M1, &S1);
  251. startDateTime.tm_year = Y1 - 1900;
  252. startDateTime.tm_mon = m1 - 1;
  253. startDateTime.tm_mday = d1;
  254. startDateTime.tm_hour = H1;
  255. startDateTime.tm_min = M1;
  256. startDateTime.tm_sec = S1;
  257. #else
  258. #error "Unimplemented"
  259. #endif
  260. #if defined(__linux__) || defined(__APPLE__)
  261. localtime_r(&t2, &endDateTime);
  262. strptime(end, "%Y-%m-%d %H:%M:%S", &endDateTime);
  263. #elif _WIN32
  264. localtime_s(&endDateTime, &t2);
  265. int Y2, m2, d2, H2, M2, S2;
  266. sscanf_s(end, "%4d-%2d-%2d %2d:%2d:%2d", &Y2, &m2, &d2, &H2, &M2, &S2);
  267. endDateTime.tm_year = Y2 - 1900;
  268. endDateTime.tm_mon = m2 - 1;
  269. endDateTime.tm_mday = d2;
  270. endDateTime.tm_hour = H2;
  271. endDateTime.tm_min = M2;
  272. endDateTime.tm_sec = S2;
  273. #else
  274. #error "Unimplemented"
  275. #endif
  276. start_time = mktime(&startDateTime) ;
  277. end_time = mktime(&endDateTime) ;
  278. cout << "Start time:" << "UTC OFFSET:"<< get_utc_offset () << ":" <<start << ":" << start_time << endl;
  279. cout << "End time:" << end << ":" << end_time << endl;
  280. struct tm tm;
  281. char sb[100];
  282. #if defined(__linux__) || defined(__APPLE__)
  283. localtime_r(&start_time, &tm);
  284. sprintf(sb, "now: %4d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
  285. tm.tm_hour, tm.tm_min, tm.tm_sec);
  286. #elif _WIN32
  287. localtime_s(&tm, &start_time);
  288. sprintf_s(sb, "now: %4d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
  289. tm.tm_hour, tm.tm_min, tm.tm_sec);
  290. #else
  291. #error "Unimplemented"
  292. #endif
  293. cout << "DateTime is " << sb << endl;
  294. getUniqueIdRange(start_time, end_time, uid_start, uid_end);
  295. }
  296. bool sameUniqueId(const ln_uid_t &uid1, const ln_uid_t &uid2)
  297. {
  298. return memcmp(uid1, uid2, sizeof uid2)==0;
  299. }
  300. void copyUniqueId(ln_uid_t &to, const ln_uid_t &from)
  301. {
  302. memcpy (to, from, sizeof from);
  303. }
  304. /* returns the utc timezone offset (e.g. -8 hours for PST)
  305. */
  306. int get_utc_offset()
  307. {
  308. time_t zero = 24 * 60 * 60L;
  309. struct tm timeptr;
  310. int gmtime_hours;
  311. /* get the local time for Jan 2, 1900 00:00 UTC */
  312. #if defined(__linux__) || defined(__APPLE__)
  313. localtime_r(&zero, &timeptr);
  314. #elif _WIN32
  315. localtime_s(&timeptr, &zero);
  316. #else
  317. #error "Unimplemented"
  318. #endif
  319. gmtime_hours = timeptr.tm_hour;
  320. /* if the local time is the "day before" the UTC, subtract 24 hours
  321. from the hours to get the UTC offset
  322. */
  323. if (timeptr.tm_mday < 2)
  324. gmtime_hours -= 24;
  325. return gmtime_hours;
  326. }
  327. };