diff --git a/include/uuid.cpp b/include/uuid.cpp index 6b321ae..454e3b2 100644 --- a/include/uuid.cpp +++ b/include/uuid.cpp @@ -173,6 +173,21 @@ namespace uuids return uuid{}; #endif } + + basic_uuid_random_generator() + :generator(new UniformRandomNumberGenerator) + { + 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); + } + + explicit basic_uuid_random_generator(UniformRandomNumberGenerator& gen) : + generator(&gen, []() {}) {} + explicit basic_uuid_random_generator(UniformRandomNumberGenerator* pGen) : + generator(&gen, []() {}) {} } namespace std diff --git a/include/uuid.h b/include/uuid.h index 3453c40..69b540d 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -7,6 +7,15 @@ #include #include #include +#include + +#ifdef _WIN32 +#include +#elif defined(__linux__) || defined(__unix__) +#include +#elif defined(__APPLE__) +#include +#endif namespace uuids { @@ -145,30 +154,6 @@ namespace uuids iterator end() noexcept { return &data[0] + size(); } const_iterator end() const noexcept { return &data[0] + size(); } - template, - class Alloc = std::allocator> - std::basic_string string(Alloc const & a = Alloc()) const - { - std::basic_stringstream sstr; - sstr << *this; - return sstr.str(); - } - - std::string string() const - { - std::stringstream sstr; - sstr << *this; - return sstr.str(); - } - - std::wstring wstring() const - { - std::wstringstream sstr; - sstr << *this; - return sstr.str(); - } - private: std::array data{ { 0 } }; @@ -223,12 +208,113 @@ namespace uuids << std::setw(2) << (int)id.data[15]; } + std::string to_string(uuid const & id) + { + std::stringstream sstr; + sstr << id; + return sstr.str(); + } + + std::wstring to_wstring(uuid const & id) + { + std::wstringstream sstr; + sstr << id; + return sstr.str(); + } + class uuid_default_generator { public: typedef uuid result_type; - uuid operator()(); + uuid operator()() + { +#ifdef _WIN32 + + GUID newId; + ::CoCreateGuid(&newId); + + std::array bytes = + { { + (unsigned char)((newId.Data1 >> 24) & 0xFF), + (unsigned char)((newId.Data1 >> 16) & 0xFF), + (unsigned char)((newId.Data1 >> 8) & 0xFF), + (unsigned char)((newId.Data1) & 0xFF), + + (unsigned char)((newId.Data2 >> 8) & 0xFF), + (unsigned char)((newId.Data2) & 0xFF), + + (unsigned char)((newId.Data3 >> 8) & 0xFF), + (unsigned char)((newId.Data3) & 0xFF), + + newId.Data4[0], + newId.Data4[1], + newId.Data4[2], + newId.Data4[3], + newId.Data4[4], + newId.Data4[5], + newId.Data4[6], + newId.Data4[7] + } }; + + return uuid{ std::begin(bytes), std::end(bytes) }; + +#elif defined(__linux__) || defined(__unix__) + + uuid_t id; + uuid_generate(id); + + std::array bytes = + { { + id[0], + id[1], + id[2], + id[3], + id[4], + id[5], + id[6], + id[7], + id[8], + id[9], + id[10], + id[11], + id[12], + id[13], + id[14], + id[15] + } }; + + return uuid{ std::begin(bytes), std::end(bytes) }; + +#elif defined(__APPLE__) + auto newId = CFUUIDCreate(NULL); + auto bytes = CFUUIDGetUUIDBytes(newId); + CFRelease(newId); + + std::array bytes = + { { + bytes.byte0, + bytes.byte1, + bytes.byte2, + bytes.byte3, + bytes.byte4, + bytes.byte5, + bytes.byte6, + bytes.byte7, + bytes.byte8, + bytes.byte9, + bytes.byte10, + bytes.byte11, + bytes.byte12, + bytes.byte13, + bytes.byte14, + bytes.byte15 + } }; + return uuid{ std::begin(bytes), std::end(bytes) }; +#elif + return uuid{}; +#endif + } }; template @@ -237,23 +323,125 @@ namespace uuids public: typedef uuid result_type; - basic_uuid_random_generator() {} - explicit basic_uuid_random_generator(UniformRandomNumberGenerator& gen) {} - explicit basic_uuid_random_generator(UniformRandomNumberGenerator* pGen) {} + basic_uuid_random_generator() + :generator(new UniformRandomNumberGenerator) + { + std::random_device rd; + generator->seed(rd()); + } - uuid operator()() { return uuid{}; } + explicit basic_uuid_random_generator(UniformRandomNumberGenerator& gen) : + generator(&gen, [](auto) {}) {} + explicit basic_uuid_random_generator(UniformRandomNumberGenerator* gen) : + generator(gen, [](auto) {}) {} + + uuid operator()() + { + uint8_t bytes[16]; + for (int i = 0; i < 16; i += 4) + *reinterpret_cast(bytes + i) = distribution(*generator); + + // variant must be 10xxxxxx + bytes[8] &= 0xBF; + bytes[8] |= 0x80; + + // version must be 0100xxxx + bytes[6] &= 0x4F; + bytes[6] |= 0x40; + + return uuid{std::begin(bytes), std::end(bytes)}; + } private: - + std::uniform_int_distribution distribution; + std::shared_ptr generator; }; using uuid_random_generator = basic_uuid_random_generator; + + namespace detail + { + template + constexpr inline unsigned char hex2char(TChar const ch) + { + if (ch >= static_cast('0') && ch <= static_cast('9')) + return ch - static_cast('0'); + if (ch >= static_cast('a') && ch <= static_cast('f')) + return 10 + ch - static_cast('a'); + if (ch >= static_cast('A') && ch <= static_cast('F')) + return 10 + ch - static_cast('A'); + return 0; + } + + template + constexpr inline bool is_hex(TChar const ch) + { + return + (ch >= static_cast('0') && ch <= static_cast('9')) || + (ch >= static_cast('a') && ch <= static_cast('f')) || + (ch >= static_cast('A') && ch <= static_cast('F')); + } + + template + constexpr inline unsigned char hexpair2char(TChar const a, TChar const b) + { + return (hex2char(a) << 4) | hex2char(b); + } + } + + uuid::uuid(std::string_view str) + { + create(str.data(), str.size()); + } + + uuid::uuid(std::wstring_view str) + { + create(str.data(), str.size()); + } + + template + void uuid::create(TChar const * const str, size_t const size) + { + TChar digit = 0; + bool firstdigit = true; + size_t index = 0; + + for (size_t i = 0; i < size; ++i) + { + if (str[i] == static_cast('-')) continue; + + if (index >= 16 || !detail::is_hex(str[i])) + { + std::fill(std::begin(data), std::end(data), 0); + return; + } + + if (firstdigit) + { + digit = str[i]; + firstdigit = false; + } + else + { + data[index++] = detail::hexpair2char(digit, str[i]); + firstdigit = true; + } + } + + if (index < 16) + { + std::fill(std::begin(data), std::end(data), 0); + } + } } namespace std { template <> - void swap(uuids::uuid & lhs, uuids::uuid & rhs); + void swap(uuids::uuid & lhs, uuids::uuid & rhs) + { + lhs.swap(rhs); + } template <> struct hash @@ -264,7 +452,7 @@ namespace std result_type operator()(argument_type const &uuid) const { std::hash hasher; - return static_cast(hasher(uuid.string())); + return static_cast(hasher(uuids::to_string(uuid))); } }; } \ No newline at end of file diff --git a/test/test.cpp b/test/test.cpp index 9525eec..024ac18 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -25,13 +25,13 @@ int main() { auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; uuid guid(str); - assert(guid.string() == str); + assert(uuids::to_string(guid) == str); } { uuid guid("47183823-2574-4bfd-b411-99ed177d3e43"); - assert(guid.string() == "47183823-2574-4bfd-b411-99ed177d3e43"); - assert(guid.wstring() == L"47183823-2574-4bfd-b411-99ed177d3e43"); + assert(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); + assert(uuids::to_wstring(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); } } @@ -42,7 +42,7 @@ int main() auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s; uuid guid(str); - assert(guid.wstring() == str); + assert(uuids::to_wstring(guid) == str); } { @@ -59,7 +59,7 @@ int main() 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43} }; uuid guid(std::begin(arr), std::end(arr)); - assert(guid.string() == "47183823-2574-4bfd-b411-99ed177d3e43"s); + assert(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s); } { @@ -71,20 +71,10 @@ int main() 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 }; uuid guid(std::begin(arr), std::end(arr)); - assert(guid.string() == "47183823-2574-4bfd-b411-99ed177d3e43"s); + assert(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"s); } } - { - std::cout << "Test default generator" << std::endl; - - uuid const guid = uuids::uuid_default_generator{}(); - assert(!guid.nil()); - assert(guid.size() == 16); - assert(guid.version() == uuids::uuid_version::random_number_based); - assert(guid.variant() == uuids::uuid_variant::rfc); - } - { std::cout << "Test equality" << std::endl; @@ -163,8 +153,8 @@ int main() std::cout << "Test string conversion" << std::endl; uuid empty; - assert(empty.string() == "00000000-0000-0000-0000-000000000000"); - assert(empty.wstring() == L"00000000-0000-0000-0000-000000000000"); + assert(uuids::to_string(empty) == "00000000-0000-0000-0000-000000000000"); + assert(uuids::to_wstring(empty) == L"00000000-0000-0000-0000-000000000000"); } { @@ -183,7 +173,7 @@ int main() std::copy(std::cbegin(arr), std::cend(arr), std::begin(guid)); assert(!guid.nil()); - assert(guid.string() == "47183823-2574-4bfd-b411-99ed177d3e43"); + assert(uuids::to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); size_t i = 0; for (auto const & b : guid) @@ -202,19 +192,195 @@ int main() constexpr uuid_version version = empty.version(); } - {/* - auto id1 = make_uuid(); - uuid_default_generator dgen; - auto id2 = make_uuid(dgen); - auto id3 = make_uuid(dgen); + { + std::cout << "Test default generator" << std::endl; + + uuid const guid = uuids::uuid_default_generator{}(); + assert(!guid.nil()); + assert(guid.size() == 16); + assert(guid.version() == uuids::uuid_version::random_number_based); + assert(guid.variant() == uuids::uuid_variant::rfc); + } + + { + std::cout << "Test random generator (default ctor)" << std::endl; + + uuids::uuid_random_generator dgen; + auto id1 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + auto id2 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + assert(id1 != id2); + } + + { + std::cout << "Test random generator (conversion ctor w/ ptr)" << std::endl; std::random_device rd; - std::mt19937 mtgen(rd()); - uuid_random_generator rgen(mtgen); - auto id4 = make_uuid(rgen); - auto id5 = make_uuid(rgen); - */} + 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)); + std::mt19937 generator(seq); + + uuids::uuid_random_generator dgen(&generator); + auto id1 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + auto id2 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + assert(id1 != id2); + } + + { + std::cout << "Test random generator (conversion ctor w/ ptr)" << std::endl; + + 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)); + auto generator = std::make_unique(seq); + + uuids::uuid_random_generator dgen(generator.get()); + auto id1 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + auto id2 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + assert(id1 != id2); + } + + { + std::cout << "Test random generator (conversion ctor w/ ref)" << std::endl; + + 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)); + std::mt19937 generator(seq); + + uuids::uuid_random_generator dgen(generator); + auto id1 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + auto id2 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + assert(id1 != id2); + } + + { + std::cout << "Test basic random generator (default ctor) w/ ranlux48_base" << std::endl; + + uuids::basic_uuid_random_generator dgen; + auto id1 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + auto id2 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + assert(id1 != id2); + } + + { + std::cout << "Test basic random generator (conversion ctor w/ ptr) w/ ranlux48_base" << std::endl; + + std::random_device rd; + std::ranlux48_base generator(rd()); + + uuids::basic_uuid_random_generator dgen(&generator); + auto id1 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + auto id2 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + assert(id1 != id2); + } + + { + std::cout << "Test basic random generator (conversion ctor w/ ptr) w/ ranlux48_base" << std::endl; + + std::random_device rd; + auto generator = std::make_unique(rd()); + + uuids::basic_uuid_random_generator dgen(generator.get()); + auto id1 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + auto id2 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + assert(id1 != id2); + } + + { + std::cout << "Test basic random generator (conversion ctor w/ ref) w/ ranlux48_base" << std::endl; + + std::random_device rd; + std::ranlux48_base generator(rd()); + + uuids::basic_uuid_random_generator dgen(generator); + auto id1 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + auto id2 = dgen(); + assert(!id1.nil()); + assert(id1.size() == 16); + assert(id1.version() == uuids::uuid_version::random_number_based); + assert(id1.variant() == uuids::uuid_variant::rfc); + + assert(id1 != id2); + } std::cout << "ALL PASSED" << std::endl;