diff --git a/include/uuid.h b/include/uuid.h index a6921e6..5b42520 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -12,11 +12,20 @@ #include #include #include -#include +#include +#include #include #ifdef _WIN32 #include + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include +#include +#pragma comment(lib, "IPHLPAPI.lib") + #elif defined(__linux__) || defined(__unix__) #include #elif defined(__APPLE__) @@ -750,6 +759,94 @@ namespace uuids uuid nsuuid; detail::sha1 hasher; }; + + // this implementation is currently unreliable for good uuids + class uuid_time_generator + { + using mac_address = std::array; + + std::optional device_address; + std::mt19937 generator; + std::uniform_int_distribution dis; + + bool get_mac_address() + { + if (device_address.has_value()) + { + return true; + } + +#ifdef _WIN32 + DWORD len = 0; + auto ret = GetAdaptersInfo(nullptr, &len); + if (ret != ERROR_BUFFER_OVERFLOW) return false; + std::vector buf(len); + auto pips = reinterpret_cast(&buf.front()); + ret = GetAdaptersInfo(pips, &len); + if (ret != ERROR_SUCCESS) return false; + mac_address addr; + std::copy(pips->Address, pips->Address + 6, std::begin(addr)); + device_address = addr; +#endif + + return device_address.has_value(); + } + + long long get_time_intervals() + { + auto start = std::chrono::system_clock::from_time_t(-12219292800); + auto diff = std::chrono::system_clock::now() - start; + auto ns = std::chrono::duration_cast(diff).count(); + return ns / 100; + } + + public: + uuid_time_generator():dis(-32768, 32767) + { + std::random_device rd; + auto seed_data = std::array {}; + std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + generator.seed(seq); + } + + uuid operator()() + { + if (get_mac_address()) + { + std::array data; + + auto tm = get_time_intervals(); + + short clock_seq = dis(generator); + + clock_seq &= 0x3FFF; + + auto ptm = reinterpret_cast(&tm); + ptm[0] &= 0x0F; + + memcpy(&data[0], ptm + 4, 4); + memcpy(&data[4], ptm + 2, 2); + memcpy(&data[6], ptm, 2); + + memcpy(&data[8], reinterpret_cast(&clock_seq), 2); + + // variant must be 0b10xxxxxx + data[8] &= 0xBF; + data[8] |= 0x80; + + // version must be 0b0001xxxx + data[6] &= 0x5F; + data[6] |= 0x10; + + memcpy(&data[10], &device_address.value()[0], 6); + + return uuids::uuid{std::cbegin(data), std::cend(data)}; + } + + return {}; + } + }; } namespace std diff --git a/test/test_generators.cpp b/test/test_generators.cpp index 64c0ec8..2271f8a 100644 --- a/test/test_generators.cpp +++ b/test/test_generators.cpp @@ -210,4 +210,26 @@ TEST_CASE("Test name generator (std::string)", "[gen][name]") REQUIRE(id1 != id2); REQUIRE(id2 == id3); REQUIRE(id3 != id4); +} + +TEST_CASE("Test time generator", "[gen][time]") +{ + uuid_time_generator gen; + auto id1 = gen(); + auto id2 = gen(); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.variant() == uuids::uuid_variant::rfc); + REQUIRE(id1.version() == uuids::uuid_version::time_based); + + REQUIRE(!id2.is_nil()); + REQUIRE(id2.variant() == uuids::uuid_variant::rfc); + REQUIRE(id2.version() == uuids::uuid_version::time_based); + + REQUIRE(id1 != id2); + + std::set ids; + for (int i = 0; i < 1000; ++i) + ids.insert(gen()); + + REQUIRE(ids.size() == 1000); } \ No newline at end of file