From 072e980e1d87bd0c88169a0deda2c76404e030c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Obiltschnig?= Date: Thu, 26 Sep 2024 13:27:47 +0200 Subject: [PATCH] feat(Foundation): Poco::UUID/UUIDGenerator: add support for Version 6 and 7 UUIDs (#4580) --- Foundation/include/Poco/UUID.h | 7 +- Foundation/include/Poco/UUIDGenerator.h | 10 ++ Foundation/samples/uuidgen/src/uuidgen.cpp | 4 + Foundation/src/UUIDGenerator.cpp | 113 +++++++++++++++--- .../testsuite/src/UUIDGeneratorTest.cpp | 56 +++++++++ Foundation/testsuite/src/UUIDGeneratorTest.h | 4 + Foundation/testsuite/src/UUIDTest.cpp | 24 ++++ Foundation/testsuite/src/UUIDTest.h | 2 + 8 files changed, 204 insertions(+), 16 deletions(-) diff --git a/Foundation/include/Poco/UUID.h b/Foundation/include/Poco/UUID.h index 0e72f31c2..3ffda7368 100644 --- a/Foundation/include/Poco/UUID.h +++ b/Foundation/include/Poco/UUID.h @@ -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(); diff --git a/Foundation/include/Poco/UUIDGenerator.h b/Foundation/include/Poco/UUIDGenerator.h index b91c2d895..296837eba 100644 --- a/Foundation/include/Poco/UUIDGenerator.h +++ b/Foundation/include/Poco/UUIDGenerator.h @@ -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; diff --git a/Foundation/samples/uuidgen/src/uuidgen.cpp b/Foundation/samples/uuidgen/src/uuidgen.cpp index 33554b0d0..04826ffd3 100644 --- a/Foundation/samples/uuidgen/src/uuidgen.cpp +++ b/Foundation/samples/uuidgen/src/uuidgen.cpp @@ -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 diff --git a/Foundation/src/UUIDGenerator.cpp b/Foundation/src/UUIDGenerator.cpp index 078e669fd..d728436e1 100644 --- a/Foundation/src/UUIDGenerator.cpp +++ b/Foundation/src/UUIDGenerator.cpp @@ -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(_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(_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(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); diff --git a/Foundation/testsuite/src/UUIDGeneratorTest.cpp b/Foundation/testsuite/src/UUIDGeneratorTest.cpp index 513de8821..b22b2f826 100644 --- a/Foundation/testsuite/src/UUIDGeneratorTest.cpp +++ b/Foundation/testsuite/src/UUIDGeneratorTest.cpp @@ -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 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 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; } diff --git a/Foundation/testsuite/src/UUIDGeneratorTest.h b/Foundation/testsuite/src/UUIDGeneratorTest.h index 210eef4cc..380d46d82 100644 --- a/Foundation/testsuite/src/UUIDGeneratorTest.h +++ b/Foundation/testsuite/src/UUIDGeneratorTest.h @@ -27,6 +27,10 @@ public: void testTimeBased(); void testRandom(); void testNameBased(); + void testV6(); + void testV7(); + void testV6Uniqueness(); + void testV7Uniqueness(); void setUp(); void tearDown(); diff --git a/Foundation/testsuite/src/UUIDTest.cpp b/Foundation/testsuite/src/UUIDTest.cpp index 52ec13c5b..c3d7b1848 100644 --- a/Foundation/testsuite/src/UUIDTest.cpp +++ b/Foundation/testsuite/src/UUIDTest.cpp @@ -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; } diff --git a/Foundation/testsuite/src/UUIDTest.h b/Foundation/testsuite/src/UUIDTest.h index 7299ebb61..2d4422934 100644 --- a/Foundation/testsuite/src/UUIDTest.h +++ b/Foundation/testsuite/src/UUIDTest.h @@ -31,6 +31,8 @@ public: void testVariant(); void testSwap(); void testTryParse(); + void testV6(); + void testV7(); void setUp(); void tearDown();