feat(Foundation): Poco::UUID/UUIDGenerator: add support for Version 6 and 7 UUIDs (#4580)

This commit is contained in:
Günter Obiltschnig 2024-09-26 13:27:47 +02:00
parent db1cc9507b
commit 072e980e1d
8 changed files with 204 additions and 16 deletions

View File

@ -40,6 +40,8 @@ class Foundation_API UUID
/// draft by Leach/Salz from February, 1998
/// (http://www.ics.uci.edu/~ejw/authoring/uuid-guid/draft-leach-uuids-guids-01.txt)
/// and also http://tools.ietf.org/html/draft-mealling-uuid-urn-05
///
/// Version 6 and 7 UUIDs are based on RFC 9562.
{
public:
enum Version
@ -48,8 +50,9 @@ public:
UUID_DCE_UID = 0x02,
UUID_NAME_BASED = 0x03,
UUID_RANDOM = 0x04,
UUID_NAME_BASED_SHA1 = 0x05
UUID_NAME_BASED_SHA1 = 0x05,
UUID_TIME_BASED_V6 = 0x06,
UUID_TIME_BASED_V7 = 0x07
};
UUID();

View File

@ -39,6 +39,8 @@ class Foundation_API UUIDGenerator
/// RFC 2518 (WebDAV), section 6.4.1 and the UUIDs and GUIDs internet
/// draft by Leach/Salz from February, 1998
/// (http://ftp.ics.uci.edu/pub/ietf/webdav/uuid-guid/draft-leach-uuids-guids-01.txt)
///
/// Version 6 and 7 UUIDs are based on RFC 9562.
{
public:
UUIDGenerator();
@ -78,6 +80,13 @@ public:
/// The UUID::version() method can be used to determine the actual kind of
/// the UUID generated.
UUID createV6();
/// Creates a time-based version 6 UUID (according to RFC 9562) with a MAC address.
/// If no MAC address is available, a random MAC address will be generated.
UUID createV7();
/// Creates a time-based version 7 UUID (according to RFC 9652).
void seed(UInt32 n);
/// Seeds the internal pseudo random generator for time-based UUIDs with the given seed.
@ -97,6 +106,7 @@ private:
Random _random;
Timestamp _lastTime;
int _ticks;
Poco::UInt16 _counter;
Environment::NodeId _node;
bool _haveNode;

View File

@ -33,6 +33,10 @@ int main(int argc, char** argv)
{
if (arg == "-random")
uuid = UUIDGenerator::defaultGenerator().createRandom();
else if (arg == "-v6")
uuid = UUIDGenerator::defaultGenerator().createV6();
else if (arg == "-v7")
uuid = UUIDGenerator::defaultGenerator().createV7();
else if (arg.empty())
uuid = UUIDGenerator::defaultGenerator().create();
else

View File

@ -24,8 +24,12 @@
namespace Poco {
UUIDGenerator::UUIDGenerator(): _ticks(0), _haveNode(false)
UUIDGenerator::UUIDGenerator():
_ticks(0),
_counter(0),
_haveNode(false)
{
seed();
}
@ -38,6 +42,18 @@ UUID UUIDGenerator::create()
{
FastMutex::ScopedLock lock(_mutex);
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | time_low |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | time_mid | ver | time_high |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |var| clock_seq | node |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | node |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
if (!_haveNode)
{
Environment::nodeId(_node);
@ -99,6 +115,88 @@ UUID UUIDGenerator::createRandom()
}
UUID UUIDGenerator::createOne()
{
try
{
return create();
}
catch (Exception&)
{
return createRandom();
}
}
UUID UUIDGenerator::createV6()
{
FastMutex::ScopedLock lock(_mutex);
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | time_high |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | time_mid | ver | time_low |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |var| clock_seq | node |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | node |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
if (!_haveNode)
{
try
{
Environment::nodeId(_node);
_haveNode = true;
}
catch (Poco::Exception&)
{
RandomInputStream ris;
ris.read(reinterpret_cast<char*>(_node), sizeof(_node));
}
}
Timestamp::UtcTimeVal tv = timeStamp();
UInt32 timeHigh = UInt32((tv >> 28) & 0xFFFFFFFF);
UInt16 timeMid = UInt16((tv >> 12) & 0xFFFF);
UInt16 timeLoAndVersion = UInt16(tv & 0x0FFF) + (UUID::UUID_TIME_BASED_V6 << 12);
UInt16 clockSeq = (UInt16(_random.next() >> 4) & 0x3FFF) | 0x8000;
return UUID(timeHigh, timeMid, timeLoAndVersion, clockSeq, _node);
}
UUID UUIDGenerator::createV7()
{
FastMutex::ScopedLock lock(_mutex);
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | unix_ts_ms |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | unix_ts_ms | ver | rand_a |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |var| rand_b |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | rand_b |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
if (_counter == 0) _counter = static_cast<Poco::UInt16>(_random.next(4096));
Timestamp::TimeVal tv = Poco::Timestamp().epochMicroseconds()/1000;
UInt32 timeHigh = UInt32((tv >> 16) & 0xFFFFFFFF);
UInt16 timeMid = UInt16(tv & 0xFFFF);
UInt16 randAVersion = (_counter++ & 0x0FFF) + (UUID::UUID_TIME_BASED_V7 << 12);
UInt16 randBHiVar = (UInt16(_random.next() >> 4) & 0x3FFF) | 0x8000;
Poco::UInt8 randBLow[6];
RandomInputStream ris;
ris.read(reinterpret_cast<char*>(randBLow), sizeof(randBLow));
return UUID(timeHigh, timeMid, randAVersion, randBHiVar, randBLow);
}
Timestamp::UtcTimeVal UUIDGenerator::timeStamp()
{
Timestamp now;
@ -122,19 +220,6 @@ Timestamp::UtcTimeVal UUIDGenerator::timeStamp()
}
UUID UUIDGenerator::createOne()
{
try
{
return create();
}
catch (Exception&)
{
return createRandom();
}
}
void UUIDGenerator::seed()
{
Poco::FastMutex::ScopedLock lock(_mutex);

View File

@ -91,6 +91,58 @@ void UUIDGeneratorTest::testNameBased()
}
void UUIDGeneratorTest::testV6()
{
UUIDGenerator& gen = UUIDGenerator::defaultGenerator();
Poco::UUID uuid1 = gen.createV6();
assertTrue (uuid1.version() == Poco::UUID::UUID_TIME_BASED_V6);
assertTrue (uuid1.variant() == 2);
}
void UUIDGeneratorTest::testV7()
{
UUIDGenerator& gen = UUIDGenerator::defaultGenerator();
Poco::UUID uuid1 = gen.createV7();
assertTrue (uuid1.version() == Poco::UUID::UUID_TIME_BASED_V7);
assertTrue (uuid1.variant() == 2);
}
void UUIDGeneratorTest::testV6Uniqueness()
{
UUIDGenerator& gen = UUIDGenerator::defaultGenerator();
const int ROUNDS = 1000;
std::set<Poco::UUID> uuids;
for (int i = 0; i < ROUNDS; i++)
{
uuids.insert(gen.createV6());
}
assertTrue (uuids.size() == ROUNDS);
}
void UUIDGeneratorTest::testV7Uniqueness()
{
UUIDGenerator& gen = UUIDGenerator::defaultGenerator();
const int ROUNDS = 1000;
std::set<Poco::UUID> uuids;
for (int i = 0; i < ROUNDS; i++)
{
uuids.insert(gen.createV7());
}
assertTrue (uuids.size() == ROUNDS);
}
void UUIDGeneratorTest::setUp()
{
}
@ -108,6 +160,10 @@ CppUnit::Test* UUIDGeneratorTest::suite()
CppUnit_addTest(pSuite, UUIDGeneratorTest, testTimeBased);
CppUnit_addTest(pSuite, UUIDGeneratorTest, testRandom);
CppUnit_addTest(pSuite, UUIDGeneratorTest, testNameBased);
CppUnit_addTest(pSuite, UUIDGeneratorTest, testV6);
CppUnit_addTest(pSuite, UUIDGeneratorTest, testV7);
CppUnit_addTest(pSuite, UUIDGeneratorTest, testV6Uniqueness);
CppUnit_addTest(pSuite, UUIDGeneratorTest, testV7Uniqueness);
return pSuite;
}

View File

@ -27,6 +27,10 @@ public:
void testTimeBased();
void testRandom();
void testNameBased();
void testV6();
void testV7();
void testV6Uniqueness();
void testV7Uniqueness();
void setUp();
void tearDown();

View File

@ -204,6 +204,7 @@ void UUIDTest::testSwap()
assertTrue (uuid2.toString() == "db4fa7e9-9e62-4597-99e0-b1ec0b59800e");
}
void UUIDTest::testTryParse()
{
Poco::UUID uuid;
@ -215,6 +216,27 @@ void UUIDTest::testTryParse()
assertTrue (notUuid.isNull());
}
void UUIDTest::testV6()
{
Poco::UUID uuid("1EC9414C-232A-6B00-B3C8-9F6BDECED846");
int ver = uuid.version();
assertTrue (ver == 6);
int var = uuid.variant();
assertTrue (var == 2);
}
void UUIDTest::testV7()
{
Poco::UUID uuid("017F22E2-79B0-7CC3-98C4-DC0C0C07398F");
int ver = uuid.version();
assertTrue (ver == 7);
int var = uuid.variant();
assertTrue (var == 2);
}
void UUIDTest::setUp()
{
}
@ -236,6 +258,8 @@ CppUnit::Test* UUIDTest::suite()
CppUnit_addTest(pSuite, UUIDTest, testVariant);
CppUnit_addTest(pSuite, UUIDTest, testSwap);
CppUnit_addTest(pSuite, UUIDTest, testTryParse);
CppUnit_addTest(pSuite, UUIDTest, testV6);
CppUnit_addTest(pSuite, UUIDTest, testV7);
return pSuite;
}

View File

@ -31,6 +31,8 @@ public:
void testVariant();
void testSwap();
void testTryParse();
void testV6();
void testV7();
void setUp();
void tearDown();