5 changed files with 1049 additions and 0 deletions
-
17core/utils/uuid/sole/LICENSE
-
77core/utils/uuid/sole/README.md
-
23core/utils/uuid/sole/RELEASE
-
903core/utils/uuid/sole/sole.hpp
-
29core/utils/uuid/uuid.hpp
@ -0,0 +1,17 @@ |
|||
Copyright (c) 2015 r-lyeh (https://github.com/r-lyeh) |
|||
|
|||
This software is provided 'as-is', without any express or implied |
|||
warranty. In no event will the authors be held liable for any damages |
|||
arising from the use of this software. |
|||
|
|||
Permission is granted to anyone to use this software for any purpose, |
|||
including commercial applications, and to alter it and redistribute it |
|||
freely, subject to the following restrictions: |
|||
|
|||
1. The origin of this software must not be misrepresented; you must not |
|||
claim that you wrote the original software. If you use this software |
|||
in a product, an acknowledgment in the product documentation would be |
|||
appreciated but is not required. |
|||
2. Altered source versions must be plainly marked as such, and must not be |
|||
misrepresented as being the original software. |
|||
3. This notice may not be removed or altered from any source distribution. |
@ -0,0 +1,77 @@ |
|||
sole <a href="https://travis-ci.org/r-lyeh/sole"><img src="https://api.travis-ci.org/r-lyeh/sole.svg?branch=master" align="right" /></a> |
|||
==== |
|||
|
|||
- Sole is a lightweight C++11 library to generate universally unique identificators (UUID). |
|||
- Sole provides interface for UUID versions 0, 1 and 4. |
|||
- Sole rebuilds UUIDs from hexadecimal and base62 cooked strings as well. |
|||
- Sole is tiny, header-only, cross-platform. |
|||
- Sole is zlib/libpng licensed. |
|||
|
|||
### Some theory |
|||
- UUID version 1 (48-bit MAC address + 60-bit clock with a resolution of 100 ns) |
|||
- UUID version 4 (122-bits of randomness) |
|||
- Use v1 if you are worried about leaving it up to probabilities. |
|||
- Use v4 if you are worried about security issues and determinism. |
|||
|
|||
### About custom version 0 |
|||
- UUID version 0 (16-bit PID + 48-bit MAC address + 60-bit clock with a resolution of 100ns since Unix epoch) |
|||
- Format is EPOCH_LOW-EPOCH_MID-VERSION(0)|EPOCH_HI-PID-MAC |
|||
|
|||
### Public API |
|||
- `sole::uuid` 128-bit UUID base type that allows comparison and sorting. `std::ostream <<` friendly. `.str()` to get a cooked hex string. `.base62()` to get a cooked base62 string. `.pretty()` to get a pretty decomposed report. |
|||
- `sole::uuid0()` creates an UUID v0. |
|||
- `sole::uuid1()` creates an UUID v1. |
|||
- `sole::uuid4()` creates an UUID v4. |
|||
- `sole::rebuild()` rebuilds an UUID from given string or 64-bit tuple. |
|||
|
|||
### Showcase |
|||
```c++ |
|||
~sole> cat sample.cc |
|||
#include <iostream> |
|||
#include "sole.hpp" |
|||
|
|||
int main() { |
|||
sole::uuid u0 = sole::uuid0(), u1 = sole::uuid1(), u4 = sole::uuid4(); |
|||
|
|||
std::cout << "uuid v0 string : " << u0 << std::endl; |
|||
std::cout << "uuid v0 base62 : " << u0.base62() << std::endl; |
|||
std::cout << "uuid v0 pretty : " << u0.pretty() << std::endl << std::endl; |
|||
|
|||
std::cout << "uuid v1 string : " << u1 << std::endl; |
|||
std::cout << "uuid v1 base62 : " << u1.base62() << std::endl; |
|||
std::cout << "uuid v1 pretty : " << u1.pretty() << std::endl << std::endl; |
|||
|
|||
std::cout << "uuid v4 string : " << u4 << std::endl; |
|||
std::cout << "uuid v4 base62 : " << u4.base62() << std::endl; |
|||
std::cout << "uuid v4 pretty : " << u4.pretty() << std::endl << std::endl; |
|||
|
|||
u1 = sole::rebuild("F81D4FAE-7DEC-11D0-A765-00A0C91E6BF6"); |
|||
std::cout << "uuid v1 rebuilt: " << u1 << " -> " << u1.pretty() << std::endl; |
|||
|
|||
u4 = sole::rebuild("GITheR4tLlg-BagIW20DGja"); |
|||
std::cout << "uuid v4 rebuilt: " << u4 << " -> " << u4.pretty() << std::endl; |
|||
} |
|||
|
|||
~sole> g++ sample.cc -std=c++11 -lrt && ./a.out |
|||
uuid v0 string : 00aed2f9-c5f8-0030-0fd8-00ffb77bd832 |
|||
uuid v0 base62 : 3dNJHWv0aW-1MKpXy7mEmf |
|||
uuid v0 pretty : version=0,timestamp="03/07/2013 12:19:43",mac=00ffb77bd832,pid=4056, |
|||
|
|||
uuid v1 string : 14314b83-e3ca-11e2-8b83-00ffb77bd832 |
|||
uuid v1 base62 : 1jU2TXBD9t4-BycINxiP5Jh |
|||
uuid v1 pretty : version=1,timestamp="03/07/2013 12:19:43",mac=00ffb77bd832,clock_seq=2947, |
|||
|
|||
uuid v4 string : fa237b32-d580-42db-aeb9-b09a1d90067e |
|||
uuid v4 base62 : LTTsO5t3jMR-F03eZqkMchC |
|||
uuid v4 pretty : version=4,randbits=fa237b32d58002db2eb9b09a1d90067e, |
|||
|
|||
uuid v1 rebuilt : f81d4fae-7dec-11d0-a765-00a0c91e6bf6 -> version=1,timestamp="03/02/1997 18:43:12",mac=00a0c91e6bf6,clock_seq=10085, |
|||
uuid v4 rebuilt : bdd55e2f-6f6b-4088-8703-ddedba9456a2 -> version=4,randbits=bdd55e2f6f6b0088703ddedba9456a2, |
|||
``` |
|||
|
|||
### Special notes |
|||
- clang/g++ users: both `-std=c++11` and `-lrt` may be required when compiling `sole.cpp` |
|||
|
|||
### Changelog |
|||
- v1.0.1 (2017/05/16): Improve UUID4 and base62 performance; Fix warnings |
|||
- v1.0.0 (2016/02/03): Initial semver adherence; Switch to header-only; Remove warnings |
@ -0,0 +1,23 @@ |
|||
commit 89f9c68bba69a53bcb7c3f6e4980312a86cf21b0 |
|||
Merge: ad2d25c 7e5c0ed |
|||
Author: r-lyeh <35402248+r-lyeh@users.noreply.github.com> |
|||
Date: Sat May 18 10:28:20 2019 +0200 |
|||
|
|||
Merge pull request #25 from MisterTea/patch-2 |
|||
|
|||
localtime_r() doesn't exist on mingw so use localtime() |
|||
|
|||
commit 7e5c0eddc3877d93be401bb50f7c51022055f232 |
|||
Author: Jason Gauci <jgmath2000@gmail.com> |
|||
Date: Fri May 17 22:26:50 2019 -0700 |
|||
|
|||
localtime_r() doesn't exist on mingw so use localtime() |
|||
|
|||
commit ad2d25cc0a622f006b65fb449606edf457dd2566 |
|||
Merge: 653a25a ff14f0b |
|||
Author: r-lyeh <35402248+r-lyeh@users.noreply.github.com> |
|||
Date: Sun May 5 10:46:33 2019 +0200 |
|||
|
|||
Merge pull request #24 from MisterTea/patch-1 |
|||
|
|||
Change "class hash" to "struct hash" to avoid compiler warnings on Xcode |
@ -0,0 +1,903 @@ |
|||
/* Sole is a lightweight C++11 library to generate universally unique identificators.
|
|||
* Sole provides interface for UUID versions 0, 1 and 4. |
|||
|
|||
* https://github.com/r-lyeh/sole
|
|||
* Copyright (c) 2013,2014,2015 r-lyeh. zlib/libpng licensed. |
|||
|
|||
* Based on code by Dmitri Bouianov, Philip O'Toole, Poco C++ libraries and anonymous |
|||
* code found on the net. Thanks guys! |
|||
|
|||
* Theory: (see Hoylen's answer at [1]) |
|||
* - UUID version 1 (48-bit MAC address + 60-bit clock with a resolution of 100ns) |
|||
* Clock wraps in 3603 A.D. |
|||
* Up to 10000000 UUIDs per second. |
|||
* MAC address revealed. |
|||
* |
|||
* - UUID Version 4 (122-bits of randomness) |
|||
* See [2] or other analysis that describe how very unlikely a duplicate is. |
|||
* |
|||
* - Use v1 if you need to sort or classify UUIDs per machine. |
|||
* Use v1 if you are worried about leaving it up to probabilities (e.g. your are the |
|||
* type of person worried about the earth getting destroyed by a large asteroid in your |
|||
* lifetime). Just use a v1 and it is guaranteed to be unique till 3603 AD. |
|||
* |
|||
* - Use v4 if you are worried about security issues and determinism. That is because |
|||
* v1 UUIDs reveal the MAC address of the machine it was generated on and they can be |
|||
* predictable. Use v4 if you need more than 10 million uuids per second, or if your |
|||
* application wants to live past 3603 A.D. |
|||
|
|||
* Additionally a custom UUID v0 is provided: |
|||
* - 16-bit PID + 48-bit MAC address + 60-bit clock with a resolution of 100ns since Unix epoch |
|||
* - Format is EPOCH_LOW-EPOCH_MID-VERSION(0)|EPOCH_HI-PID-MAC |
|||
* - Clock wraps in 3991 A.D. |
|||
* - Up to 10000000 UUIDs per second. |
|||
* - MAC address and PID revealed. |
|||
|
|||
* References: |
|||
* - [1] http://stackoverflow.com/questions/1155008/how-unique-is-uuid
|
|||
* - [2] http://en.wikipedia.org/wiki/UUID#Random%5FUUID%5Fprobability%5Fof%5Fduplicates
|
|||
* - http://en.wikipedia.org/wiki/Universally_unique_identifier
|
|||
* - http://en.cppreference.com/w/cpp/numeric/random/random_device
|
|||
* - http://www.itu.int/ITU-T/asn1/uuid.html f81d4fae-7dec-11d0-a765-00a0c91e6bf6
|
|||
|
|||
* - rlyeh ~~ listening to Hedon Cries / Until The Sun Goes up |
|||
*/ |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////
|
|||
|
|||
#pragma once
|
|||
#include <stdint.h>
|
|||
#include <stdio.h> // for size_t; should be stddef.h instead; however, clang+archlinux fails when compiling it (@Travis-Ci)
|
|||
#include <sys/types.h> // for uint32_t; should be stdint.h instead; however, GCC 5 on OSX fails when compiling it (See issue #11)
|
|||
#include <functional>
|
|||
#include <string>
|
|||
|
|||
// public API
|
|||
|
|||
#define SOLE_VERSION "1.0.1" /* (2017/05/16): Improve UUID4 and base62 performance; fix warnings
|
|||
#define SOLE_VERSION "1.0.0" // (2016/02/03): Initial semver adherence; Switch to header-only; Remove warnings */
|
|||
|
|||
namespace sole |
|||
{ |
|||
// 128-bit basic UUID type that allows comparison and sorting.
|
|||
// Use .str() for printing and .pretty() for pretty printing.
|
|||
// Also, ostream friendly.
|
|||
struct uuid |
|||
{ |
|||
uint64_t ab; |
|||
uint64_t cd; |
|||
|
|||
bool operator==( const uuid &other ) const; |
|||
bool operator!=( const uuid &other ) const; |
|||
bool operator <( const uuid &other ) const; |
|||
|
|||
std::string pretty() const; |
|||
std::string base62() const; |
|||
std::string str() const; |
|||
|
|||
template<typename ostream> |
|||
inline friend ostream &operator<<( ostream &os, const uuid &self ) { |
|||
return os << self.str(), os; |
|||
} |
|||
}; |
|||
|
|||
// Generators
|
|||
uuid uuid0(); // UUID v0, pro: unique; cons: MAC revealed, pid revealed, predictable.
|
|||
uuid uuid1(); // UUID v1, pro: unique; cons: MAC revealed, predictable.
|
|||
uuid uuid4(); // UUID v4, pros: anonymous, fast; con: uuids "can clash"
|
|||
|
|||
// Rebuilders
|
|||
uuid rebuild( uint64_t ab, uint64_t cd ); |
|||
uuid rebuild( const std::string &uustr ); |
|||
} |
|||
|
|||
#ifdef _MSC_VER
|
|||
#pragma warning(push)
|
|||
#pragma warning(disable:4127)
|
|||
#endif
|
|||
|
|||
namespace std { |
|||
template<> |
|||
struct hash< sole::uuid > { |
|||
public: |
|||
// hash functor: hash uuid to size_t value by pseudorandomizing transform
|
|||
size_t operator()( const sole::uuid &uuid ) const { |
|||
if( sizeof(size_t) > 4 ) { |
|||
return size_t( uuid.ab ^ uuid.cd ); |
|||
} else { |
|||
uint64_t hash64 = uuid.ab ^ uuid.cd; |
|||
return size_t( uint32_t( hash64 >> 32 ) ^ uint32_t( hash64 ) ); |
|||
} |
|||
} |
|||
}; |
|||
} |
|||
|
|||
#ifdef _MSC_VER
|
|||
#pragma warning(pop)
|
|||
#endif
|
|||
|
|||
// implementation
|
|||
|
|||
#include <memory.h>
|
|||
#include <stdint.h>
|
|||
#include <stdio.h>
|
|||
#include <time.h>
|
|||
|
|||
#include <cstring>
|
|||
#include <ctime>
|
|||
|
|||
#include <iomanip>
|
|||
#include <random>
|
|||
#include <sstream>
|
|||
#include <string>
|
|||
#include <vector>
|
|||
|
|||
#if defined(_WIN32)
|
|||
# include <winsock2.h>
|
|||
# include <process.h>
|
|||
# include <iphlpapi.h>
|
|||
# ifdef _MSC_VER
|
|||
# pragma comment(lib,"iphlpapi.lib")
|
|||
# endif
|
|||
# define $windows $yes
|
|||
#elif defined(__FreeBSD__) || defined(__NetBSD__) || \
|
|||
defined(__OpenBSD__) || defined(__MINT__) || defined(__bsdi__) |
|||
# include <ifaddrs.h>
|
|||
# include <net/if_dl.h>
|
|||
# include <sys/socket.h>
|
|||
# include <sys/time.h>
|
|||
# include <sys/types.h>
|
|||
# include <unistd.h>
|
|||
# define $bsd $yes
|
|||
#elif (defined(__APPLE__) && defined(__MACH__))
|
|||
# include <ifaddrs.h>
|
|||
# include <net/if_dl.h>
|
|||
# include <sys/socket.h>
|
|||
# include <sys/time.h>
|
|||
# include <sys/types.h>
|
|||
# include <unistd.h>
|
|||
# pragma clang diagnostic push
|
|||
# pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
|
|||
# define $osx $yes
|
|||
#elif defined(__linux__)
|
|||
# include <arpa/inet.h>
|
|||
# include <net/if.h>
|
|||
# include <netinet/in.h>
|
|||
# include <sys/ioctl.h>
|
|||
# include <sys/socket.h>
|
|||
# include <sys/time.h>
|
|||
# include <unistd.h>
|
|||
# define $linux $yes
|
|||
#else //elif defined(__unix__)
|
|||
# if defined(__VMS)
|
|||
# include <ioctl.h>
|
|||
# include <inet.h>
|
|||
# else
|
|||
# include <sys/ioctl.h>
|
|||
# include <arpa/inet.h>
|
|||
# endif
|
|||
# if defined(sun) || defined(__sun)
|
|||
# include <sys/sockio.h>
|
|||
# endif
|
|||
# include <net/if.h>
|
|||
# include <net/if_arp.h>
|
|||
# include <netdb.h>
|
|||
# include <netinet/in.h>
|
|||
# include <sys/socket.h>
|
|||
# include <sys/time.h>
|
|||
# include <sys/types.h>
|
|||
# include <unistd.h>
|
|||
# if defined(__VMS)
|
|||
namespace { enum { MAXHOSTNAMELEN = 64 }; } |
|||
# endif
|
|||
# define $unix $yes
|
|||
#endif
|
|||
|
|||
#ifdef _MSC_VER
|
|||
# define $msvc $yes
|
|||
#endif
|
|||
|
|||
#if defined(__GNUC__) && (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 < 50100)
|
|||
namespace std |
|||
{ |
|||
static inline std::string put_time( const std::tm* tmb, const char* fmt ) { |
|||
std::string s( 128, '\0' ); |
|||
while( !strftime( &s[0], s.size(), fmt, tmb ) ) |
|||
s.resize( s.size() + 128 ); |
|||
return s; |
|||
} |
|||
} |
|||
#endif
|
|||
|
|||
////////////////////////////////////////////////////////////////////////////////////
|
|||
|
|||
#ifdef $windows
|
|||
#define $welse $no
|
|||
#else
|
|||
#define $windows $no
|
|||
#define $welse $yes
|
|||
#endif
|
|||
|
|||
#ifdef $bsd
|
|||
#define $belse $no
|
|||
#else
|
|||
#define $bsd $no
|
|||
#define $belse $yes
|
|||
#endif
|
|||
|
|||
#ifdef $linux
|
|||
#define $lelse $no
|
|||
#else
|
|||
#define $linux $no
|
|||
#define $lelse $yes
|
|||
#endif
|
|||
|
|||
#ifdef $unix
|
|||
#define $uelse $no
|
|||
#else
|
|||
#define $unix $no
|
|||
#define $uelse $yes
|
|||
#endif
|
|||
|
|||
#ifdef $osx
|
|||
#define $oelse $no
|
|||
#else
|
|||
#define $osx $no
|
|||
#define $oelse $yes
|
|||
#endif
|
|||
|
|||
#ifdef $msvc
|
|||
#define $melse $no
|
|||
#else
|
|||
#define $msvc $no
|
|||
#define $melse $yes
|
|||
#endif
|
|||
|
|||
#define $yes(...) __VA_ARGS__
|
|||
#define $no(...)
|
|||
|
|||
inline bool sole::uuid::operator==( const sole::uuid &other ) const { |
|||
return ab == other.ab && cd == other.cd; |
|||
} |
|||
inline bool sole::uuid::operator!=( const sole::uuid &other ) const { |
|||
return !operator==(other); |
|||
} |
|||
inline bool sole::uuid::operator<( const sole::uuid &other ) const { |
|||
if( ab < other.ab ) return true; |
|||
if( ab > other.ab ) return false; |
|||
if( cd < other.cd ) return true; |
|||
return false; |
|||
} |
|||
|
|||
namespace sole { |
|||
|
|||
inline std::string printftime( uint64_t timestamp_secs = 0, const std::string &locale = std::string() ) { |
|||
std::string timef; |
|||
try { |
|||
// Taken from parameter
|
|||
//std::string locale; // = "es-ES", "Chinese_China.936", "en_US.UTF8", etc...
|
|||
std::time_t t = timestamp_secs; |
|||
std::tm tm; |
|||
$msvc( |
|||
localtime_s( &tm, &t ); |
|||
) |
|||
$melse( |
|||
$windows(tm = *localtime( &t ); ) |
|||
$welse( localtime_r(&t, &tm); ) |
|||
) |
|||
|
|||
std::stringstream ss; |
|||
$melse( |
|||
std::locale lc( locale.c_str() ); |
|||
ss.imbue( lc ); |
|||
) |
|||
ss << std::put_time( &tm, "\"%c\"" ); |
|||
|
|||
timef = ss.str(); |
|||
} |
|||
catch(...) { |
|||
timef = "\"\""; |
|||
} |
|||
return timef; |
|||
} |
|||
|
|||
inline std::string uuid::pretty() const { |
|||
std::stringstream ss; |
|||
|
|||
uint64_t a = (ab >> 32); |
|||
uint64_t b = (ab & 0xFFFFFFFF); |
|||
uint64_t c = (cd >> 32); |
|||
uint64_t d = (cd & 0xFFFFFFFF); |
|||
|
|||
int version = (b & 0xF000) >> 12; |
|||
uint64_t timestamp = ((b & 0x0FFF) << 48 ) | (( b >> 16 ) << 32) | a; // in 100ns units
|
|||
|
|||
ss << "version=" << (version) << ','; |
|||
|
|||
if( version == 1 ) |
|||
timestamp = timestamp - 0x01b21dd213814000ULL; // decrement Gregorian calendar
|
|||
|
|||
ss << std::hex << std::nouppercase << std::setfill('0'); |
|||
version <= 1 && ss << "timestamp=" << printftime(timestamp/10000000) << ','; |
|||
version <= 1 && ss << "mac=" << std::setw(4) << (c & 0xFFFF) << std::setw(8) << d << ','; |
|||
version == 4 && ss << "randbits=" << std::setw(8) << (ab & 0xFFFFFFFFFFFF0FFFULL) << std::setw(8) << (cd & 0x3FFFFFFFFFFFFFFFULL) << ','; |
|||
|
|||
ss << std::dec; |
|||
version == 0 && ss << "pid=" << std::setw(4) << (c >> 16 ) << ','; |
|||
version == 1 && ss << "clock_seq=" << std::setw(4) << ((c >> 16) & 0x3FFF) << ','; |
|||
|
|||
return ss.str(); |
|||
} |
|||
|
|||
inline std::string uuid::str() const { |
|||
std::stringstream ss; |
|||
ss << std::hex << std::nouppercase << std::setfill('0'); |
|||
|
|||
uint32_t a = (ab >> 32); |
|||
uint32_t b = (ab & 0xFFFFFFFF); |
|||
uint32_t c = (cd >> 32); |
|||
uint32_t d = (cd & 0xFFFFFFFF); |
|||
|
|||
ss << std::setw(8) << (a) << '-'; |
|||
ss << std::setw(4) << (b >> 16) << '-'; |
|||
ss << std::setw(4) << (b & 0xFFFF) << '-'; |
|||
ss << std::setw(4) << (c >> 16 ) << '-'; |
|||
ss << std::setw(4) << (c & 0xFFFF); |
|||
ss << std::setw(8) << d; |
|||
|
|||
return ss.str(); |
|||
} |
|||
|
|||
inline std::string uuid::base62() const { |
|||
int base62len = 10 + 26 + 26; |
|||
const char base62[] = |
|||
"0123456789" |
|||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
|||
"abcdefghijklmnopqrstuvwxyz"; |
|||
char res[24], *end = &res[24]; *(--end) = '\0'; |
|||
uint64_t rem, AB = ab, CD = cd; |
|||
do { |
|||
rem = CD % base62len; |
|||
*--end = base62[int(rem)]; |
|||
CD /= base62len; |
|||
} while (CD > 0); |
|||
*--end = '-'; |
|||
do { |
|||
rem = AB % base62len; |
|||
*--end = base62[int(rem)]; |
|||
AB /= base62len; |
|||
} while (AB > 0); |
|||
return end; |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////
|
|||
// multiplatform clock_gettime()
|
|||
|
|||
$windows( |
|||
struct timespec { |
|||
uint64_t tv_sec; |
|||
uint64_t tv_nsec; |
|||
}; |
|||
struct timezone { |
|||
int tz_minuteswest; /* minutes W of Greenwich */ |
|||
int tz_dsttime; /* type of dst correction */ |
|||
}; |
|||
inline int gettimeofday( struct timeval *tv, struct timezone *tz ) { |
|||
FILETIME ft; |
|||
uint64_t tmpres = 0; |
|||
|
|||
if( NULL != tv ) { |
|||
GetSystemTimeAsFileTime(&ft); |
|||
|
|||
// The GetSystemTimeAsFileTime returns the number of 100 nanosecond
|
|||
// intervals since Jan 1, 1601 in a structure. Copy the high bits to
|
|||
// the 64 bit tmpres, shift it left by 32 then or in the low 32 bits.
|
|||
tmpres |= ft.dwHighDateTime; |
|||
tmpres <<= 32; |
|||
tmpres |= ft.dwLowDateTime; |
|||
|
|||
// Convert to microseconds by dividing by 10
|
|||
tmpres /= 10; |
|||
|
|||
// The Unix epoch starts on Jan 1 1970. Need to subtract the difference
|
|||
// in seconds from Jan 1 1601.
|
|||
tmpres -= 11644473600000000ULL; |
|||
|
|||
// Finally change microseconds to seconds and place in the seconds value.
|
|||
// The modulus picks up the microseconds.
|
|||
tv->tv_sec = static_cast<long>(tmpres / 1000000UL); |
|||
tv->tv_usec = (tmpres % 1000000UL); |
|||
} |
|||
|
|||
if( NULL != tz ) { |
|||
static bool once = true; |
|||
if( once ) { |
|||
once = false; |
|||
_tzset(); |
|||
} |
|||
|
|||
long timezoneSecs = 0; |
|||
int daylight = 0; |
|||
|
|||
$msvc( |
|||
_get_timezone(&timezoneSecs); |
|||
_get_daylight(&daylight); |
|||
) |
|||
$melse( |
|||
timezoneSecs = _timezone; |
|||
daylight = _daylight; |
|||
) |
|||
|
|||
tz->tz_minuteswest = timezoneSecs / 60; |
|||
tz->tz_dsttime = daylight; |
|||
} |
|||
|
|||
return 0; |
|||
} |
|||
) |
|||
$lelse( $belse( // if not linux, if not bsd... valid for apple/win32
|
|||
inline int clock_gettime( int /*clk_id*/, struct timespec* t ) { |
|||
struct timeval now; |
|||
int rv = gettimeofday(&now, NULL); |
|||
if( rv ) return rv; |
|||
t->tv_sec = now.tv_sec; |
|||
t->tv_nsec = now.tv_usec * 1000; |
|||
return 0; |
|||
} |
|||
)) |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////
|
|||
// Timestamp and MAC interfaces
|
|||
|
|||
// Returns number of 100ns intervals
|
|||
inline uint64_t get_time( uint64_t offset ) { |
|||
struct timespec tp; |
|||
clock_gettime(0 /*CLOCK_REALTIME*/, &tp); |
|||
|
|||
// Convert to 100-nanosecond intervals
|
|||
uint64_t uuid_time; |
|||
uuid_time = tp.tv_sec * 10000000; |
|||
uuid_time = uuid_time + (tp.tv_nsec / 100); |
|||
uuid_time = uuid_time + offset; |
|||
|
|||
// If the clock looks like it went backwards, or is the same, increment it.
|
|||
static uint64_t last_uuid_time = 0; |
|||
if( last_uuid_time > uuid_time ) |
|||
last_uuid_time = uuid_time; |
|||
else |
|||
last_uuid_time = ++uuid_time; |
|||
|
|||
return uuid_time; |
|||
} |
|||
|
|||
// Looks for first MAC address of any network device, any size.
|
|||
inline bool get_any_mac( std::vector<unsigned char> &_node ) { |
|||
$windows({ |
|||
PIP_ADAPTER_INFO pAdapterInfo; |
|||
PIP_ADAPTER_INFO pAdapter = 0; |
|||
ULONG len = sizeof(IP_ADAPTER_INFO); |
|||
pAdapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(new char[len]); |
|||
|
|||
// Make an initial call to GetAdaptersInfo to get
|
|||
// the necessary size into len
|
|||
DWORD rc = GetAdaptersInfo(pAdapterInfo, &len); |
|||
if (rc == ERROR_BUFFER_OVERFLOW) |
|||
{ |
|||
delete [] reinterpret_cast<char*>(pAdapterInfo); |
|||
pAdapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(new char[len]); |
|||
} |
|||
else if (rc != ERROR_SUCCESS) |
|||
{ |
|||
return $no("cannot get network adapter list") false; |
|||
} |
|||
|
|||
bool found = false, gotten = false; |
|||
if (GetAdaptersInfo(pAdapterInfo, &len) == NO_ERROR) |
|||
{ |
|||
gotten = true; |
|||
|
|||
pAdapter = pAdapterInfo; |
|||
while (pAdapter && !found) |
|||
{ |
|||
if (pAdapter->Type == MIB_IF_TYPE_ETHERNET && pAdapter->AddressLength > 0 ) |
|||
{ |
|||
_node.resize( pAdapter->AddressLength ); |
|||
std::memcpy(_node.data(), pAdapter->Address, _node.size() ); |
|||
found = true; |
|||
} |
|||
pAdapter = pAdapter->Next; |
|||
} |
|||
} |
|||
|
|||
delete [] reinterpret_cast<char*>(pAdapterInfo); |
|||
|
|||
if( !gotten ) |
|||
return $no("cannot get network adapter list") false; |
|||
|
|||
if (!found) |
|||
return $no("no Ethernet adapter found") false; |
|||
|
|||
return true; |
|||
}) |
|||
|
|||
$bsd({ |
|||
struct ifaddrs* ifaphead; |
|||
int rc = getifaddrs(&ifaphead); |
|||
if (rc) return $no("cannot get network adapter list") false; |
|||
|
|||
bool foundAdapter = false; |
|||
for (struct ifaddrs* ifap = ifaphead; ifap; ifap = ifap->ifa_next) |
|||
{ |
|||
if (ifap->ifa_addr && ifap->ifa_addr->sa_family == AF_LINK) |
|||
{ |
|||
struct sockaddr_dl* sdl = reinterpret_cast<struct sockaddr_dl*>(ifap->ifa_addr); |
|||
caddr_t ap = (caddr_t) (sdl->sdl_data + sdl->sdl_nlen); |
|||
int alen = sdl->sdl_alen; |
|||
if (ap && alen > 0) |
|||
{ |
|||
_node.resize( alen ); |
|||
std::memcpy(_node.data(), ap, _node.size() ); |
|||
foundAdapter = true; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
freeifaddrs(ifaphead); |
|||
if (!foundAdapter) return $no("cannot determine MAC address (no suitable network adapter found)") false; |
|||
return true; |
|||
}) |
|||
|
|||
$osx({ |
|||
struct ifaddrs* ifaphead; |
|||
int rc = getifaddrs(&ifaphead); |
|||
if (rc) return $no("cannot get network adapter list") false; |
|||
|
|||
bool foundAdapter = false; |
|||
for (struct ifaddrs* ifap = ifaphead; ifap; ifap = ifap->ifa_next) |
|||
{ |
|||
if (ifap->ifa_addr && ifap->ifa_addr->sa_family == AF_LINK) |
|||
{ |
|||
struct sockaddr_dl* sdl = reinterpret_cast<struct sockaddr_dl*>(ifap->ifa_addr); |
|||
caddr_t ap = (caddr_t) (sdl->sdl_data + sdl->sdl_nlen); |
|||
int alen = sdl->sdl_alen; |
|||
if (ap && alen > 0) |
|||
{ |
|||
_node.resize( alen ); |
|||
std::memcpy(_node.data(), ap, _node.size() ); |
|||
foundAdapter = true; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
freeifaddrs(ifaphead); |
|||
if (!foundAdapter) return $no("cannot determine MAC address (no suitable network adapter found)") false; |
|||
return true; |
|||
}) |
|||
|
|||
$linux({ |
|||
struct ifreq ifr; |
|||
|
|||
int s = socket(PF_INET, SOCK_DGRAM, 0); |
|||
if (s == -1) return $no("cannot open socket") false; |
|||
|
|||
std::strcpy(ifr.ifr_name, "eth0"); |
|||
int rc = ioctl(s, SIOCGIFHWADDR, &ifr); |
|||
close(s); |
|||
if (rc < 0) return $no("cannot get MAC address") false; |
|||
struct sockaddr* sa = reinterpret_cast<struct sockaddr*>(&ifr.ifr_addr); |
|||
_node.resize( sizeof(sa->sa_data) ); |
|||
std::memcpy(_node.data(), sa->sa_data, _node.size() ); |
|||
return true; |
|||
}) |
|||
|
|||
$unix({ |
|||
char name[MAXHOSTNAMELEN]; |
|||
if (gethostname(name, sizeof(name))) |
|||
return $no("cannot get host name") false; |
|||
|
|||
struct hostent* pHost = gethostbyname(name); |
|||
if (!pHost) return $no("cannot get host IP address") false; |
|||
|
|||
int s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); |
|||
if (s == -1) return $no("cannot open socket") false; |
|||
|
|||
struct arpreq ar; |
|||
std::memset(&ar, 0, sizeof(ar)); |
|||
struct sockaddr_in* pAddr = reinterpret_cast<struct sockaddr_in*>(&ar.arp_pa); |
|||
pAddr->sin_family = AF_INET; |
|||
std::memcpy(&pAddr->sin_addr, *pHost->h_addr_list, sizeof(struct in_addr)); |
|||
int rc = ioctl(s, SIOCGARP, &ar); |
|||
close(s); |
|||
if (rc < 0) return $no("cannot get MAC address") false; |
|||
_node.resize( sizeof(ar.arp_ha.sa_data) ); |
|||
std::memcpy(_node.data(), ar.arp_ha.sa_data, _node.size()); |
|||
return true; |
|||
}) |
|||
} |
|||
|
|||
// Looks for first MAC address of any network device, size truncated to 48bits.
|
|||
inline uint64_t get_any_mac48() { |
|||
std::vector<unsigned char> node; |
|||
if( get_any_mac(node) ) { |
|||
std::stringstream ss; |
|||
ss << std::hex << std::setfill('0'); |
|||
node.resize(6); |
|||
for( unsigned i = 0; i < 6; ++i ) |
|||
ss << std::setw(2) << int(node[i]); |
|||
uint64_t t; |
|||
if( ss >> t ) |
|||
return t; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////////////////
|
|||
// UUID implementations
|
|||
|
|||
inline uuid uuid4() { |
|||
static std::random_device rd; |
|||
static std::uniform_int_distribution<uint64_t> dist(0, (uint64_t)(~0)); |
|||
|
|||
uuid my; |
|||
|
|||
my.ab = dist(rd); |
|||
my.cd = dist(rd); |
|||
|
|||
my.ab = (my.ab & 0xFFFFFFFFFFFF0FFFULL) | 0x0000000000004000ULL; |
|||
my.cd = (my.cd & 0x3FFFFFFFFFFFFFFFULL) | 0x8000000000000000ULL; |
|||
|
|||
return my; |
|||
} |
|||
|
|||
inline uuid uuid1() { |
|||
// Number of 100-ns intervals since 00:00:00.00 15 October 1582; [ref] uuid.py
|
|||
uint64_t ns100_intervals = get_time( 0x01b21dd213814000ULL ); |
|||
uint16_t clock_seq = (uint16_t)( ns100_intervals & 0x3fff ); // 14-bits max
|
|||
uint64_t mac = get_any_mac48(); // 48-bits max
|
|||
|
|||
uint32_t time_low = ns100_intervals & 0xffffffff; |
|||
uint16_t time_mid = (ns100_intervals >> 32) & 0xffff; |
|||
uint16_t time_hi_version = (ns100_intervals >> 48) & 0xfff; |
|||
uint8_t clock_seq_low = clock_seq & 0xff; |
|||
uint8_t clock_seq_hi_variant = (clock_seq >> 8) & 0x3f; |
|||
|
|||
uuid u; |
|||
uint64_t &upper_ = u.ab; |
|||
uint64_t &lower_ = u.cd; |
|||
|
|||
// Build the high 32 bytes
|
|||
upper_ = (uint64_t) time_low << 32; |
|||
upper_ |= (uint64_t) time_mid << 16; |
|||
upper_ |= (uint64_t) time_hi_version; |
|||
|
|||
// Build the low 32 bytes, using the clock sequence number
|
|||
lower_ = (uint64_t) ((clock_seq_hi_variant << 8) | clock_seq_low) << 48; |
|||
lower_ |= mac; |
|||
|
|||
// Set the variant to RFC 4122.
|
|||
lower_ &= ~((uint64_t)0xc000 << 48); |
|||
lower_ |= (uint64_t)0x8000 << 48; |
|||
|
|||
// Set the version number.
|
|||
enum { version = 1 }; |
|||
upper_ &= ~0xf000; |
|||
upper_ |= version << 12; |
|||
|
|||
return u; |
|||
} |
|||
|
|||
inline uuid uuid0() { |
|||
// Number of 100-ns intervals since Unix epoch time
|
|||
uint64_t ns100_intervals = get_time( 0 ); |
|||
uint64_t pid = $windows( _getpid() ) $welse( getpid() ); |
|||
uint16_t pid16 = (uint16_t)( pid & 0xffff ); // 16-bits max
|
|||
uint64_t mac = get_any_mac48(); // 48-bits max
|
|||
|
|||
uint32_t time_low = ns100_intervals & 0xffffffff; |
|||
uint16_t time_mid = (ns100_intervals >> 32) & 0xffff; |
|||
uint16_t time_hi_version = (ns100_intervals >> 48) & 0xfff; |
|||
uint8_t pid_low = pid16 & 0xff; |
|||
uint8_t pid_hi = (pid16 >> 8) & 0xff; |
|||
|
|||
uuid u; |
|||
uint64_t &upper_ = u.ab; |
|||
uint64_t &lower_ = u.cd; |
|||
|
|||
// Build the high 32 bytes.
|
|||
upper_ = (uint64_t) time_low << 32; |
|||
upper_ |= (uint64_t) time_mid << 16; |
|||
upper_ |= (uint64_t) time_hi_version; |
|||
|
|||
// Build the low 32 bytes, using the mac and pid number.
|
|||
lower_ = (uint64_t) ((pid_hi << 8) | pid_low) << 48; |
|||
lower_ |= mac; |
|||
|
|||
// Set the version number.
|
|||
enum { version = 0 }; |
|||
upper_ &= ~0xf000; |
|||
upper_ |= version << 12; |
|||
|
|||
return u; |
|||
} |
|||
|
|||
inline uuid rebuild( uint64_t ab, uint64_t cd ) { |
|||
uuid u; |
|||
u.ab = ab; u.cd = cd; |
|||
return u; |
|||
} |
|||
|
|||
inline uuid rebuild( const std::string &uustr ) { |
|||
char sep; |
|||
uint64_t a,b,c,d,e; |
|||
uuid u = { 0, 0 }; |
|||
auto idx = uustr.find_first_of("-"); |
|||
if( idx != std::string::npos ) { |
|||
// single separator, base62 notation
|
|||
if( uustr.find_first_of("-",idx+1) == std::string::npos ) { |
|||
auto rebase62 = [&]( const char *input, size_t limit ) -> uint64_t { |
|||
int base62len = 10 + 26 + 26; |
|||
auto strpos = []( char ch ) -> size_t { |
|||
if( ch >= 'a' ) return ch - 'a' + 10 + 26; |
|||
if( ch >= 'A' ) return ch - 'A' + 10; |
|||
return ch - '0'; |
|||
}; |
|||
uint64_t res = strpos( input[0] ); |
|||
for( size_t i = 1; i < limit; ++i ) |
|||
res = base62len * res + strpos( input[i] ); |
|||
return res; |
|||
}; |
|||
u.ab = rebase62( &uustr[0], idx ); |
|||
u.cd = rebase62( &uustr[idx+1], uustr.size() - (idx+1) ); |
|||
} |
|||
// else classic hex notation
|
|||
else { |
|||
std::stringstream ss( uustr ); |
|||
if( ss >> std::hex >> a >> sep >> b >> sep >> c >> sep >> d >> sep >> e ) { |
|||
if( ss.eof() ) { |
|||
u.ab = (a << 32) | (b << 16) | c; |
|||
u.cd = (d << 48) | e; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return u; |
|||
} |
|||
|
|||
} // ::sole
|
|||
|
|||
#undef $bsd
|
|||
#undef $belse
|
|||
#undef $linux
|
|||
#undef $lelse
|
|||
#undef $osx
|
|||
#undef $oelse
|
|||
#undef $unix
|
|||
#undef $uelse
|
|||
#undef $windows
|
|||
#undef $welse
|
|||
#undef $yes
|
|||
#undef $no
|
|||
|
|||
// Pop disabled warnings
|
|||
#if (defined(__APPLE__) && defined(__MACH__))
|
|||
#pragma clang diagnostic pop
|
|||
#endif
|
|||
|
|||
#ifdef SOLE_BUILD_DEMO
|
|||
// g++ users: `g++ demo.cc -std=c++11 -lrt -o sample`
|
|||
// visual studio: `cl.exe demo.cc sole.cpp`
|
|||
|
|||
#include <iostream>
|
|||
|
|||
int main() { |
|||
sole::uuid u0 = sole::uuid0(), u1 = sole::uuid1(), u4 = sole::uuid4(); |
|||
|
|||
std::cout << "uuid v0 string : " << u0 << std::endl; |
|||
std::cout << "uuid v0 base62 : " << u0.base62() << std::endl; |
|||
std::cout << "uuid v0 pretty : " << u0.pretty() << std::endl << std::endl; |
|||
|
|||
std::cout << "uuid v1 string : " << u1 << std::endl; |
|||
std::cout << "uuid v1 base62 : " << u1.base62() << std::endl; |
|||
std::cout << "uuid v1 pretty : " << u1.pretty() << std::endl << std::endl; |
|||
|
|||
std::cout << "uuid v4 string : " << u4 << std::endl; |
|||
std::cout << "uuid v4 base62 : " << u4.base62() << std::endl; |
|||
std::cout << "uuid v4 pretty : " << u4.pretty() << std::endl << std::endl; |
|||
|
|||
u1 = sole::rebuild("F81D4FAE-7DEC-11D0-A765-00A0C91E6BF6"); |
|||
u4 = sole::rebuild("GITheR4tLlg-BagIW20DGja"); |
|||
|
|||
std::cout << "uuid v1 rebuilt : " << u1 << " -> " << u1.pretty() << std::endl; |
|||
std::cout << "uuid v4 rebuilt : " << u4 << " -> " << u4.pretty() << std::endl; |
|||
} |
|||
|
|||
#endif
|
|||
|
|||
#ifdef SOLE_BUILD_TESTS
|
|||
// g++ users: `g++ sole.cxx -std=c++11 -lrt -o tests`
|
|||
// visual studio: `cl.exe sole.cxx`
|
|||
|
|||
#include <cassert>
|
|||
#include <set>
|
|||
#include <ratio>
|
|||
#include <chrono>
|
|||
#include <iostream>
|
|||
|
|||
using namespace sole; |
|||
|
|||
namespace run |
|||
{ |
|||
auto epoch = [](){ |
|||
return std::chrono::system_clock::to_time_t( std::chrono::system_clock::now() ); |
|||
}; |
|||
|
|||
template<typename FN> |
|||
void benchmark( const FN &fn, const std::string &name ) { |
|||
std::cout << "Benchmarking " << name << "... " << std::flush; |
|||
|
|||
auto then = epoch(); |
|||
|
|||
while( epoch() == then ); |
|||
then = epoch(); |
|||
|
|||
unsigned c = 0; |
|||
while( epoch() == then ) c = ( fn(), ++c ); |
|||
|
|||
std::cout << (c) << " uuids/sec" << std::endl; |
|||
} |
|||
|
|||
template<typename FN> |
|||
void tests( const FN &fn ) { |
|||
unsigned numtests = ~0; |
|||
std::cout << "Testing for " << numtests << " collisions... " << std::endl; |
|||
|
|||
auto then = epoch(); |
|||
|
|||
std::set<uuid> all; |
|||
for( unsigned i = 0; i < numtests; ++i ) { |
|||
auto now = epoch(); |
|||
if( now != then ) { |
|||
then = now; |
|||
double pct6digs = ( int( ( double(i) / (unsigned)(~0) ) * 1e4 ) / double(1e4) ); |
|||
std::cout << '\r' << i << " uuids generated, no collision (" << pct6digs << "%)" << std::flush; |
|||
} |
|||
sole::uuid my_uuid = fn(); |
|||
assert( all.find(my_uuid) == all.end() && "error: UUIDs just collided! is std::random_device a real random generator?" ); |
|||
all.insert( my_uuid ); |
|||
} |
|||
} |
|||
|
|||
template<typename FN> |
|||
void verify( const FN &fn ) { |
|||
std::cout << "Verifying serialization of 1 million UUIDs... " << std::flush; |
|||
|
|||
for( unsigned i = 0; i < 1000000; ++i ) { |
|||
sole::uuid uuid = fn(); |
|||
sole::uuid rebuilt1 = sole::rebuild( uuid.str() ); |
|||
sole::uuid rebuilt2 = sole::rebuild( uuid.base62() ); |
|||
assert( rebuilt1 == uuid && "error: rebuild() or .str() failed" ); |
|||
assert( rebuilt2 == uuid && "error: rebuild() or .base62() failed" ); |
|||
} |
|||
|
|||
std::cout << "ok" << std::endl; |
|||
} |
|||
} |
|||
|
|||
int main() { |
|||
assert( sizeof(sole::uuid ) * 8 == 128 ); |
|||
assert( sizeof(sole::uuid0().ab) * 8 == 64 ); |
|||
assert( sizeof(sole::uuid0().cd) * 8 == 64 ); |
|||
|
|||
run::benchmark(uuid0, "v0"); |
|||
run::benchmark(uuid1, "v1"); |
|||
run::benchmark(uuid4, "v4"); |
|||
|
|||
run::verify(uuid4); // use fastest implementation
|
|||
|
|||
// run::tests(uuid0); // not applicable
|
|||
// run::tests(uuid1); // not applicable
|
|||
run::tests(uuid4); |
|||
} |
|||
|
|||
#endif
|
|||
|
@ -0,0 +1,29 @@ |
|||
#pragma once
|
|||
#include <fstream>
|
|||
#include <functional>
|
|||
#include <iostream>
|
|||
#include <list>
|
|||
#include <map>
|
|||
#include <memory>
|
|||
#include <set>
|
|||
#include <sstream>
|
|||
#include <string>
|
|||
#include <vector>
|
|||
#include "iflytopcpp/core/utils/uuid/sole/sole.hpp"
|
|||
namespace iflytop { |
|||
namespace core { |
|||
using namespace std; |
|||
|
|||
class UUID { |
|||
public: |
|||
UUID() { |
|||
sole::uuid u4 = sole::uuid4(); |
|||
value = u4.str(); |
|||
}; |
|||
~UUID(){}; |
|||
const std::string &toString() { return value; }; |
|||
private: |
|||
std::string value; |
|||
}; |
|||
} // namespace core
|
|||
} // namespace iflytop
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue