diff --git a/include/faker-cxx/string.h b/include/faker-cxx/string.h index be6c2e784..d208ba334 100644 --- a/include/faker-cxx/string.h +++ b/include/faker-cxx/string.h @@ -19,10 +19,21 @@ #include "faker-cxx/export.h" #include "helpers/ulid/ulid.h" #include "random_generator.h" -#include "types/uuid.h" namespace faker::string { + +enum class Uuid +{ + V1, // Version 1: UUIDs using a timestamp and monotonic counter. + V3, // Version 3: UUIDs based on the MD5 hash of some data. + V4, // Version 4: UUIDs with random data. + V5, // Version 5: UUIDs based on the SHA1 hash of some data. + V6, // Version 6: UUIDs using a timestamp and monotonic counter (sortable). + V7, // Version 7: UUIDs using a Unix timestamp (sortable). + V8 // Version 8: UUIDs using user-defined data. +}; + enum class StringCasing { Mixed, @@ -73,141 +84,6 @@ FAKER_CXX_EXPORT bool isValidGuarantee(GuaranteeMap& guarantee, std::set& */ FAKER_CXX_EXPORT std::string generateAtLeastString(const GuaranteeMap& guarantee); -#pragma region UUID_IMPLEMENTATIONS - -template -std::string uuidV1(RandomGenerator gen = RandomGenerator{}) -{ - // Get current timestamp in 100-nanosecond intervals since UUID epoch (15 Oct 1582) - const uint64_t UUID_EPOCH_OFFSET = - 0x01B21DD213814000ULL; // Number of 100-ns intervals between UUID epoch and Unix epoch - auto now = std::chrono::system_clock::now(); - auto since_epoch = now.time_since_epoch(); - - uint64_t timestamp = - UUID_EPOCH_OFFSET + - static_cast(std::chrono::duration_cast(since_epoch).count() * 10); - - // Generate clock sequence (14 bits) - std::uniform_int_distribution clock_seq_dist(0, 0x3FFF); - uint16_t clock_seq = gen(clock_seq_dist); - - // Generate node identifier (48 bits) - std::uniform_int_distribution node_dist(0, 0xFFFFFFFFFFFFULL); - uint64_t node = gen(node_dist) & 0xFFFFFFFFFFFFULL; - - uint32_t time_low = static_cast(timestamp & 0xFFFFFFFFULL); - uint16_t time_mid = static_cast((timestamp >> 32) & 0xFFFFULL); - uint16_t time_hi_and_version = static_cast((timestamp >> 48) & 0x0FFFULL); - time_hi_and_version |= (1 << 12); // Set the version number to 1 - - uint8_t clock_seq_low = clock_seq & 0xFF; - uint8_t clock_seq_hi_and_reserved = ((clock_seq >> 8) & 0x3F) | 0x80; // Set the variant to '10' - - std::ostringstream ss; - ss << std::hex << std::setfill('0'); - ss << std::setw(8) << time_low << '-'; - ss << std::setw(4) << time_mid << '-'; - ss << std::setw(4) << time_hi_and_version << '-'; - ss << std::setw(2) << static_cast(clock_seq_hi_and_reserved); - ss << std::setw(2) << static_cast(clock_seq_low) << '-'; - ss << std::setw(12) << std::setw(12) << node; - - return ss.str(); -} - -template -std::string uuidV3(RandomGenerator gen = RandomGenerator{}) -{ - // FAking MD5 hash with random data from the generator is enough for this purpose - std::array hash; - std::uniform_int_distribution dist(0, 255); - - for (auto& byte : hash) - { - byte = gen(dist); - } - - hash[6] = (hash[6] & 0x0F) | 0x30; // Set the version to 3 - hash[8] = (hash[8] & 0x3F) | 0x80; // Set the variant to '10' - - std::ostringstream ss; - ss << std::hex << std::setfill('0'); - for (size_t i = 0; i < hash.size(); ++i) - { - ss << std::setw(2) << static_cast(hash[i]); - // Add hyphens at the appropriate positions - if (i == 3 || i == 5 || i == 7 || i == 9) - ss << '-'; - } - - return ss.str(); -} - -template -std::string uuidV4(RandomGenerator gen = RandomGenerator{}) -{ - static std::uniform_int_distribution<> dist(0, 15); - static std::uniform_int_distribution<> dist2(8, 11); - static std::string_view hexCharacters{"0123456789abcdef"}; - - std::string result; - result.reserve(36); - - for (int i = 0; i < 8; i++) - { - result.append(1, hexCharacters[static_cast(gen(dist))]); - } - result.append(1, '-'); - - for (int i = 0; i < 4; i++) - { - result.append(1, hexCharacters[static_cast(gen(dist))]); - } - result.append(1, '-'); - - result.append(1, '4'); - for (int i = 0; i < 3; i++) - { - result.append(1, hexCharacters[static_cast(gen(dist))]); - } - result.append(1, '-'); - - result.append(1, hexCharacters[static_cast(gen(dist2))]); - - for (int i = 0; i < 3; i++) - { - result.append(1, hexCharacters[static_cast(gen(dist))]); - } - result.append(1, '-'); - - for (int i = 0; i < 12; i++) - { - result.append(1, hexCharacters[static_cast(gen(dist))]); - } - - return result; -} - -#pragma endregion - -/** - * @brief Generates an Universally Unique Identifier, defaults to V4. - * - * @param gen A random number generator (type RandomGenerator) - * - * @returns UUID. - * - * @code - * faker::string::uuid() // V4: "27666229-cedb-4a45-8018-98b1e1d921e2" - * @endcode - */ -template -std::string uuid(RandomGenerator gen) -{ - return uuid(Uuid::V4, gen); -} - /** * @brief Generates an Universally Unique Identifier, defaults to V4. * @@ -226,29 +102,7 @@ std::string uuid(RandomGenerator gen) * faker::string::uuid(Uuid::V8) // "27666229-cedb-4a45-8018-98b1e1d921e2" * @endcode */ -template -std::string uuid(Uuid uuid = Uuid::V4, RandomGenerator gen = RandomGenerator{}) -{ - switch (uuid) - { - case Uuid::V1: - return uuidV1(gen); - case Uuid::V3: - return uuidV3(gen); - case Uuid::V4: - return uuidV4(gen); - case Uuid::V5: - return uuidV4(gen); - case Uuid::V6: - return uuidV4(gen); - case Uuid::V7: - return uuidV4(gen); - case Uuid::V8: - return uuidV4(gen); - default: - return uuidV4(gen); - } -} +FAKER_CXX_EXPORT std::string uuid(Uuid uuid = Uuid::V4); /** * @brief Generates an Universally Unique Lexicographically Sortable Identifier. diff --git a/include/faker-cxx/types/uuid.h b/include/faker-cxx/types/uuid.h deleted file mode 100644 index 3b21ca098..000000000 --- a/include/faker-cxx/types/uuid.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -namespace faker::string -{ -enum class Uuid -{ - /* - * Version 1: UUIDs using a timestamp and monotonic counter. - * Version 3: UUIDs based on the MD5 hash of some data. - * Version 4: UUIDs with random data. - * Version 5: UUIDs based on the SHA1 hash of some data. - * Version 6: UUIDs using a timestamp and monotonic counter (sortable). - * Version 7: UUIDs using a Unix timestamp (sortable). - * Version 8: UUIDs using user-defined data. - */ - V1, - V3, - V4, - V5, - V6, - V7, - V8 -}; -} diff --git a/src/modules/string.cpp b/src/modules/string.cpp index 29b73bf28..025a32cb2 100644 --- a/src/modules/string.cpp +++ b/src/modules/string.cpp @@ -10,7 +10,6 @@ #include "common/algo_helper.h" #include "faker-cxx/helper.h" #include "faker-cxx/number.h" -#include "faker-cxx/types/uuid.h" #include "helpers/ulid/ulid.h" #include "string_data.h" @@ -367,4 +366,145 @@ std::string ulid(time_t refDate) return std::string(data); } +#pragma region UUID_IMPLEMENTATIONS + +std::string uuidV1() +{ + RandomGenerator gen = RandomGenerator{}; + // Get current timestamp in 100-nanosecond intervals since UUID epoch (15 Oct 1582) + const uint64_t UUID_EPOCH_OFFSET = + 0x01B21DD213814000ULL; // Number of 100-ns intervals between UUID epoch and Unix epoch + auto now = std::chrono::system_clock::now(); + auto since_epoch = now.time_since_epoch(); + + uint64_t timestamp = + UUID_EPOCH_OFFSET + + static_cast(std::chrono::duration_cast(since_epoch).count() * 10); + + // Generate clock sequence (14 bits) + std::uniform_int_distribution clock_seq_dist(0, 0x3FFF); + uint16_t clock_seq = gen(clock_seq_dist); + + // Generate node identifier (48 bits) + std::uniform_int_distribution node_dist(0, 0xFFFFFFFFFFFFULL); + uint64_t node = gen(node_dist) & 0xFFFFFFFFFFFFULL; + + uint32_t time_low = static_cast(timestamp & 0xFFFFFFFFULL); + uint16_t time_mid = static_cast((timestamp >> 32) & 0xFFFFULL); + uint16_t time_hi_and_version = static_cast((timestamp >> 48) & 0x0FFFULL); + time_hi_and_version |= (1 << 12); // Set the version number to 1 + + uint8_t clock_seq_low = clock_seq & 0xFF; + uint8_t clock_seq_hi_and_reserved = ((clock_seq >> 8) & 0x3F) | 0x80; // Set the variant to '10' + + std::ostringstream ss; + ss << std::hex << std::setfill('0'); + ss << std::setw(8) << time_low << '-'; + ss << std::setw(4) << time_mid << '-'; + ss << std::setw(4) << time_hi_and_version << '-'; + ss << std::setw(2) << static_cast(clock_seq_hi_and_reserved); + ss << std::setw(2) << static_cast(clock_seq_low) << '-'; + ss << std::setw(12) << std::setw(12) << node; + + return ss.str(); +} + +std::string uuidV3() +{ + RandomGenerator gen = RandomGenerator{}; + // FAking MD5 hash with random data from the generator is enough for this purpose + std::array hash; + std::uniform_int_distribution dist(0, 255); + + for (auto& byte : hash) + { + byte = gen(dist); + } + + hash[6] = (hash[6] & 0x0F) | 0x30; // Set the version to 3 + hash[8] = (hash[8] & 0x3F) | 0x80; // Set the variant to '10' + + std::ostringstream ss; + ss << std::hex << std::setfill('0'); + for (size_t i = 0; i < hash.size(); ++i) + { + ss << std::setw(2) << static_cast(hash[i]); + // Add hyphens at the appropriate positions + if (i == 3 || i == 5 || i == 7 || i == 9) + ss << '-'; + } + + return ss.str(); +} + +std::string uuidV4() +{ + RandomGenerator gen = RandomGenerator{}; + static std::uniform_int_distribution<> dist(0, 15); + static std::uniform_int_distribution<> dist2(8, 11); + static std::string_view hexCharacters{"0123456789abcdef"}; + + std::string result; + result.reserve(36); + + for (int i = 0; i < 8; i++) + { + result.append(1, hexCharacters[static_cast(gen(dist))]); + } + result.append(1, '-'); + + for (int i = 0; i < 4; i++) + { + result.append(1, hexCharacters[static_cast(gen(dist))]); + } + result.append(1, '-'); + + result.append(1, '4'); + for (int i = 0; i < 3; i++) + { + result.append(1, hexCharacters[static_cast(gen(dist))]); + } + result.append(1, '-'); + + result.append(1, hexCharacters[static_cast(gen(dist2))]); + + for (int i = 0; i < 3; i++) + { + result.append(1, hexCharacters[static_cast(gen(dist))]); + } + result.append(1, '-'); + + for (int i = 0; i < 12; i++) + { + result.append(1, hexCharacters[static_cast(gen(dist))]); + } + + return result; +} + +std::string uuid(Uuid uuid) +{ + switch (uuid) + { + case Uuid::V1: + return uuidV1(); + case Uuid::V3: + return uuidV3(); + case Uuid::V4: + return uuidV4(); + case Uuid::V5: + return uuidV4(); + case Uuid::V6: + return uuidV4(); + case Uuid::V7: + return uuidV4(); + case Uuid::V8: + return uuidV4(); + default: + return uuidV4(); + } +} + +#pragma endregion + } diff --git a/tests/modules/string_test.cpp b/tests/modules/string_test.cpp index e4cb2cc8a..27e9aee10 100644 --- a/tests/modules/string_test.cpp +++ b/tests/modules/string_test.cpp @@ -20,27 +20,6 @@ class StringTest : public Test const int runCount{100}; }; -TEST_F(StringTest, shouldUseCustomRandomGeneratorForUuid1) -{ - RandomGenerator gen1{}; - const auto generatedUuid1 = uuid(Uuid::V1, gen1); - - ASSERT_EQ(generatedUuid1[8], '-'); - ASSERT_EQ(generatedUuid1[13], '-'); - ASSERT_EQ(generatedUuid1[18], '-'); - ASSERT_EQ(generatedUuid1[23], '-'); - ASSERT_EQ(generatedUuid1[14], '1'); // Check for version 1 - - RandomGenerator gen2{}; - const auto generatedUuid2 = uuid(Uuid::V1, gen2); - - ASSERT_EQ(generatedUuid2[8], '-'); - ASSERT_EQ(generatedUuid2[13], '-'); - ASSERT_EQ(generatedUuid2[18], '-'); - ASSERT_EQ(generatedUuid2[23], '-'); - ASSERT_EQ(generatedUuid2[14], '1'); -} - TEST_F(StringTest, shouldGenerateUuid1) { const auto generatedUuid = uuid(Uuid::V1); @@ -55,29 +34,6 @@ TEST_F(StringTest, shouldGenerateUuid1) ASSERT_EQ(generatedUuid[14], '1'); } -TEST_F(StringTest, shouldUseCustomRandomGeneratorForUuid3) -{ - RandomGenerator gen1{}; - const auto generatedUuid1 = uuid(Uuid::V3, gen1); - - ASSERT_EQ(generatedUuid1.size(), 36); - ASSERT_EQ(generatedUuid1[8], '-'); - ASSERT_EQ(generatedUuid1[13], '-'); - ASSERT_EQ(generatedUuid1[18], '-'); - ASSERT_EQ(generatedUuid1[23], '-'); - ASSERT_EQ(generatedUuid1[14], '3'); // Check for version 3 - - RandomGenerator gen2{}; - const auto generatedUuid2 = uuid(Uuid::V3, gen2); - - ASSERT_EQ(generatedUuid2.size(), 36); - ASSERT_EQ(generatedUuid2[8], '-'); - ASSERT_EQ(generatedUuid2[13], '-'); - ASSERT_EQ(generatedUuid2[18], '-'); - ASSERT_EQ(generatedUuid2[23], '-'); - ASSERT_EQ(generatedUuid2[14], '3'); -} - TEST_F(StringTest, shouldGenerateUuid3) { const auto generatedUuid = uuid(Uuid::V3); @@ -92,100 +48,19 @@ TEST_F(StringTest, shouldGenerateUuid3) ASSERT_EQ(generatedUuid[14], '3'); } -TEST_F(StringTest, shouldUseCustomRandomGeneratorForUuid4) +TEST_F(StringTest, shouldGenerateUuid4) { - RandomGenerator gen1{}; - const auto generatedUuid1 = uuid(gen1); - - ASSERT_EQ(generatedUuid1[8], '-'); - ASSERT_EQ(generatedUuid1[13], '-'); - ASSERT_EQ(generatedUuid1[14], '4'); - ASSERT_EQ(generatedUuid1[18], '-'); - ASSERT_EQ(generatedUuid1[23], '-'); - - RandomGenerator gen2{}; - const auto generatedUuid2 = uuid(gen2); - - ASSERT_EQ(generatedUuid2[8], '-'); - ASSERT_EQ(generatedUuid2[13], '-'); - ASSERT_EQ(generatedUuid2[14], '4'); - ASSERT_EQ(generatedUuid2[18], '-'); - ASSERT_EQ(generatedUuid2[23], '-'); - - RandomGenerator gen3{}; - const auto generatedUuid3 = uuid(gen3); - - ASSERT_EQ(generatedUuid3[8], '-'); - ASSERT_EQ(generatedUuid3[13], '-'); - ASSERT_EQ(generatedUuid3[14], '4'); - ASSERT_EQ(generatedUuid3[18], '-'); - ASSERT_EQ(generatedUuid3[23], '-'); - - RandomGenerator gen4{}; - const auto generatedUuid4 = uuid(gen4); - - ASSERT_EQ(generatedUuid4[8], '-'); - ASSERT_EQ(generatedUuid4[13], '-'); - ASSERT_EQ(generatedUuid4[14], '4'); - ASSERT_EQ(generatedUuid4[18], '-'); - ASSERT_EQ(generatedUuid4[23], '-'); - - RandomGenerator gen5{}; - const auto generatedUuid5 = uuid(gen5); - - ASSERT_EQ(generatedUuid5[8], '-'); - ASSERT_EQ(generatedUuid5[13], '-'); - ASSERT_EQ(generatedUuid5[14], '4'); - ASSERT_EQ(generatedUuid5[18], '-'); - ASSERT_EQ(generatedUuid5[23], '-'); - - RandomGenerator gen6{}; - const auto generatedUuid6 = uuid(gen6); - - ASSERT_EQ(generatedUuid6[8], '-'); - ASSERT_EQ(generatedUuid6[13], '-'); - ASSERT_EQ(generatedUuid6[14], '4'); - ASSERT_EQ(generatedUuid6[18], '-'); - ASSERT_EQ(generatedUuid6[23], '-'); - - RandomGenerator gen7{}; - const auto generatedUuid7 = uuid(gen7); - - ASSERT_EQ(generatedUuid7[8], '-'); - ASSERT_EQ(generatedUuid7[13], '-'); - ASSERT_EQ(generatedUuid7[14], '4'); - ASSERT_EQ(generatedUuid7[18], '-'); - ASSERT_EQ(generatedUuid7[23], '-'); - - RandomGenerator gen8{}; - const auto generatedUuid8 = uuid(gen8); - - ASSERT_EQ(generatedUuid8[8], '-'); - ASSERT_EQ(generatedUuid8[13], '-'); - ASSERT_EQ(generatedUuid8[14], '4'); - ASSERT_EQ(generatedUuid8[18], '-'); - ASSERT_EQ(generatedUuid8[23], '-'); - - RandomGenerator gen9{}; - const auto generatedUuid9 = uuid(gen9); - - ASSERT_EQ(generatedUuid9[8], '-'); - ASSERT_EQ(generatedUuid9[13], '-'); - ASSERT_EQ(generatedUuid9[14], '4'); - ASSERT_EQ(generatedUuid9[18], '-'); - ASSERT_EQ(generatedUuid9[23], '-'); - - RandomGenerator gen10{}; - const auto generatedUuid10 = uuid(gen10); - - ASSERT_EQ(generatedUuid10[8], '-'); - ASSERT_EQ(generatedUuid10[13], '-'); - ASSERT_EQ(generatedUuid10[14], '4'); - ASSERT_EQ(generatedUuid10[18], '-'); - ASSERT_EQ(generatedUuid10[23], '-'); + const auto generatedUuid = uuid(Uuid::V4); + + ASSERT_EQ(generatedUuid[8], '-'); + ASSERT_EQ(generatedUuid[13], '-'); + ASSERT_EQ(generatedUuid[14], '4'); + ASSERT_EQ(generatedUuid[18], '-'); + ASSERT_EQ(generatedUuid[23], '-'); } -TEST_F(StringTest, shouldGenerateUuid4) + +TEST_F(StringTest, shouldGenerateUuid4Default) { const auto generatedUuid = uuid();