220 lines
8.1 KiB
C++
220 lines
8.1 KiB
C++
/*
|
||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||
*/
|
||
|
||
/*
|
||
ver: 2.x.x - build: 2026-04-28
|
||
*/
|
||
|
||
/*
|
||
The zlib License
|
||
|
||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||
|
||
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.
|
||
*/
|
||
|
||
#pragma once
|
||
|
||
#include <array>
|
||
#include <chrono>
|
||
#include <format>
|
||
#include <iomanip>
|
||
#include <random>
|
||
#include <sstream>
|
||
#include <string>
|
||
|
||
namespace sdi_toolBox::desktop::utils::uuid
|
||
{
|
||
//--------------------------------------------------------------
|
||
/**
|
||
* @brief Generate a unique identifier based on the current time (microseconds).
|
||
*
|
||
* Produces a string identifier derived from the current timestamp in microseconds.
|
||
* If @p moreEntropy is true, additional random bytes are appended to increase
|
||
* uniqueness (useful when multiple identifiers are generated in quick succession).
|
||
*
|
||
* @param moreEntropy If true, append extra random entropy to the identifier.
|
||
* @return std::string Generated identifier (ASCII string, format not strictly defined).
|
||
*/
|
||
std::string uniqid(bool moreEntropy = false);
|
||
|
||
/**
|
||
* @brief Generate a random UUID (version 4).
|
||
*
|
||
* Produces a RFC 4122 compliant UUID version 4 using random data.
|
||
* The returned string uses the canonical textual representation:
|
||
* "8-4-4-4-12" hexadecimal digits.
|
||
*
|
||
* @return std::string UUID v4 as a text string.
|
||
*/
|
||
std::string v4();
|
||
|
||
/**
|
||
* @brief Generate a time-ordered UUID (version 7).
|
||
*
|
||
* Produces a UUID v7 combining a timestamp and random bits to allow
|
||
* roughly time-ordered identifiers while keeping sufficient entropy for uniqueness.
|
||
* The returned string uses the canonical textual representation:
|
||
* "8-4-4-4-12" hexadecimal digits.
|
||
*
|
||
* @note UUID v7 is a newer specification intended for sortable UUIDs.
|
||
*
|
||
* @return std::string UUID v7 as a text string.
|
||
*/
|
||
std::string v7();
|
||
//--------------------------------------------------------------
|
||
|
||
//--------------------------------------------------------------
|
||
/* Generates a unique identifier based on the current time in
|
||
* microseconds. If the `moreEntropy` parameter is set to
|
||
* `true`, additional random data is appended to the identifier
|
||
* to increase its uniqueness. The generated identifier is
|
||
* returned as a string. */
|
||
inline std::string uniqid(const bool moreEntropy)
|
||
{
|
||
const auto now = std::chrono::system_clock::now(); // Get current time point
|
||
const auto epoch = now.time_since_epoch(); // Get duration since epoch
|
||
const auto us_since_epoch = std::chrono::duration_cast<std::chrono::microseconds>(epoch).count();
|
||
|
||
// PHP format: "%08x%05x" -> 8 hex chars (sec) + 5 hex chars (usec fraction)
|
||
// sec = full seconds since epoch
|
||
// usec = microseconds fraction within the current second (0..999999)
|
||
const auto sec = static_cast<uint32_t>(us_since_epoch / 1'000'000);
|
||
const auto usec = static_cast<uint32_t>(us_since_epoch % 1'000'000);
|
||
std::string result = std::format("{:08x}{:05x}", sec, usec);
|
||
|
||
// PHP moreEntropy: appends a random float in [0, 10) with 8 decimal places
|
||
// Example output: "5f68b2056fbac7.12345678"
|
||
if (moreEntropy)
|
||
{
|
||
thread_local auto rng = std::mt19937{ std::random_device{}() };
|
||
auto f_dist = std::uniform_real_distribution<double>(0.0, 10.0);
|
||
|
||
// Append the random data in hexadecimal format
|
||
result = result + std::format("{:.8f}", f_dist(rng));
|
||
}
|
||
|
||
return result;
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Generates a random UUID (version 4) and returns it as a
|
||
* string. The UUID is generated using random numbers, and it
|
||
* follows the standard format of 8-4-4-4-12 hexadecimal
|
||
* characters. */
|
||
inline std::string v4()
|
||
{
|
||
/*
|
||
* UUID version 4 is a randomly generated UUID.
|
||
* It's structured as follows:
|
||
* - 122 bits are random
|
||
* - 6 bits are fixed to indicate the version and variant
|
||
* The total is 128 bits, formatted as 8-4-4-4-12 hexadecimal characters.
|
||
*
|
||
* The version (4) is set in the 7th byte (index 6) and the variant
|
||
* is set in the 9th byte (index 8).
|
||
*/
|
||
std::array<uint8_t, 16> bytes;
|
||
|
||
// Fast thread-local RNG seeded from random_device once per thread
|
||
thread_local auto rng = std::mt19937{ std::random_device{}() };
|
||
auto dist = std::uniform_int_distribution<uint32_t>(0, 0xFF);
|
||
|
||
// Fill the random bytes
|
||
for (auto &byte : bytes)
|
||
byte = static_cast<uint8_t>(dist(rng)); // Generate random bytes (8 bits each)
|
||
|
||
// Set version to 4 -> xxxx0100 in the 7th byte (index 6)
|
||
bytes[6] = static_cast<uint8_t>((bytes[6] & 0x0F) | 0x40);
|
||
|
||
// Set variant to 10xxxxxx in the 9th byte (index 8)
|
||
bytes[8] = static_cast<uint8_t>((bytes[8] & 0x3F) | 0x80);
|
||
|
||
// Format as 8-4-4-4-12 hex
|
||
std::ostringstream oss;
|
||
oss << std::hex << std::nouppercase << std::setfill('0');
|
||
for (int i = 0; i < 16; ++i)
|
||
{
|
||
oss << std::setw(2) << static_cast<int>(bytes[i]);
|
||
if (i == 3 || i == 5 || i == 7 || i == 9)
|
||
oss << '-';
|
||
}
|
||
return oss.str();
|
||
}
|
||
//--------------------------------------------------------------
|
||
/* Generates a UUID (version 7) based on the current timestamp
|
||
* and random data. The UUID is generated using a combination of
|
||
* the current time and random numbers, and it follows the
|
||
* standard format of 8-4-4-4-12 hexadecimal characters. */
|
||
inline std::string v7()
|
||
{
|
||
/*
|
||
* UUID version 7 is a time-ordered UUID that combines a timestamp with random bits.
|
||
* It's structured as follows:
|
||
* - 48 bits for the timestamp (milliseconds since Unix epoch)
|
||
* - 12 bits for the version (7)
|
||
* - 62 bits for random data (to ensure uniqueness)
|
||
* The total is 128 bits, formatted as 8-4-4-4-12 hexadecimal characters.
|
||
*
|
||
* The version (7) is set in the 7th byte (index 6) and the variant
|
||
* is set in the 9th byte (index 8).
|
||
*/
|
||
std::array<uint8_t, 16> bytes;
|
||
|
||
// Fast thread-local RNG seeded from random_device once per thread
|
||
thread_local auto rng = std::mt19937{ std::random_device{}() };
|
||
auto dist = std::uniform_int_distribution<uint32_t>(0, 0xFF);
|
||
|
||
const auto now = std::chrono::system_clock::now(); // Get current time point
|
||
const auto epoch = now.time_since_epoch(); // Get duration since epoch
|
||
const uint64_t ms_since_epoch = std::chrono::duration_cast<std::chrono::milliseconds>(epoch).count();
|
||
|
||
// Fill timestamp (Big-endian)
|
||
bytes[0] = static_cast<uint8_t>((ms_since_epoch >> 40) & 0xFF);
|
||
bytes[1] = static_cast<uint8_t>((ms_since_epoch >> 32) & 0xFF);
|
||
bytes[2] = static_cast<uint8_t>((ms_since_epoch >> 24) & 0xFF);
|
||
bytes[3] = static_cast<uint8_t>((ms_since_epoch >> 16) & 0xFF);
|
||
bytes[4] = static_cast<uint8_t>((ms_since_epoch >> 8) & 0xFF);
|
||
bytes[5] = static_cast<uint8_t>(ms_since_epoch & 0xFF);
|
||
|
||
// Generate randomness for the rest
|
||
for (size_t i = 6; i < 16; ++i)
|
||
bytes[i] = static_cast<uint8_t>(dist(rng)); // Generate random bytes (8 bits each)
|
||
|
||
// Set Version 7 (bits 48-51 -> 0111)
|
||
bytes[6] = static_cast<uint8_t>((bytes[6] & 0x0F) | 0x70);
|
||
|
||
// Set Variant (bits 64-65 -> 10)
|
||
bytes[8] = static_cast<uint8_t>((bytes[8] & 0x3F) | 0x80);
|
||
|
||
// Format as 8-4-4-4-12 hex
|
||
std::ostringstream oss;
|
||
oss << std::hex << std::nouppercase << std::setfill('0');
|
||
for (int i = 0; i < 16; ++i)
|
||
{
|
||
oss << std::setw(2) << static_cast<int>(bytes[i]);
|
||
if (i == 3 || i == 5 || i == 7 || i == 9)
|
||
oss << '-';
|
||
}
|
||
return oss.str();
|
||
}
|
||
//--------------------------------------------------------------
|
||
} // namespace sdi_toolBox::desktop::utils::uuid
|