$(XTRA_LIBS)
tests_testMath_LDFLAGS = $(LIBADD_DL)
+## Tests of RandomUuid.h
+check_PROGRAMS += tests/testRandomUuid
+tests_testRandomUuid_SOURCES = \
+ tests/testRandomUuid.cc
+nodist_tests_testRandomUuid_SOURCES = \
+ RandomUuid.h \
+ tests/stub_debug.cc \
+ tests/stub_libmem.cc
+tests_testRandomUuid_LDADD = \
+ libsquid.la \
+ sbuf/libsbuf.la \
+ base/libbase.la \
+ $(LIBCPPUNIT_LIBS) \
+ $(COMPAT_LIB) \
+ $(XTRA_LIBS)
+tests_testRandomUuid_LDFLAGS = $(LIBADD_DL)
+
## Tests of mem/*
check_PROGRAMS += tests/testMem
Optional.h \
Packable.h \
PackableStream.h \
+ RandomUuid.cc \
+ RandomUuid.h \
Range.h \
Raw.cc \
Raw.h \
--- /dev/null
+/*
+ * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#include "squid.h"
+#include "base/IoManip.h"
+#include "base/RandomUuid.h"
+#include "base/TextException.h"
+#include "defines.h"
+
+#include <iostream>
+#include <random>
+
+static_assert(sizeof(RandomUuid) == 128/8, "RandomUuid has RFC 4122-prescribed 128-bit size");
+
+RandomUuid::RandomUuid()
+{
+ // Generate random bits for populating our UUID.
+ // STL implementation bugs notwithstanding (e.g., MinGW bug #338), this is
+ // our best chance of getting a non-deterministic seed value for the r.n.g.
+ static std::mt19937_64 rng(std::random_device {}()); // produces 64-bit sized values
+ const auto rnd1 = rng();
+ const auto rnd2 = rng();
+
+ // No real r.n.g. is perfect, but we assume that std::mt19937_64 quality is
+ // high enough to make any imperfections irrelevant to this specific code.
+
+ // bullet 3 of RFC 4122 Section 4.4 algorithm but setting _all_ bits (KISS)
+ static_assert(sizeof(rnd1) + sizeof(rnd2) == sizeof(*this), "random bits fill a UUID");
+ memcpy(raw(), &rnd1, sizeof(rnd1));
+ memcpy(raw() + sizeof(rnd1), &rnd2, sizeof(rnd2));
+
+ // bullet 2 of RFC 4122 Section 4.4 algorithm
+ EBIT_CLR(timeHiAndVersion, 12);
+ EBIT_CLR(timeHiAndVersion, 13);
+ EBIT_SET(timeHiAndVersion, 14);
+ EBIT_CLR(timeHiAndVersion, 15);
+
+ // bullet 1 of RFC 4122 Section 4.4 algorithm
+ EBIT_CLR(clockSeqHiAndReserved, 6);
+ EBIT_SET(clockSeqHiAndReserved, 7);
+
+ assert(sane());
+}
+
+RandomUuid::RandomUuid(const Serialized &bytes)
+{
+ static_assert(sizeof(*this) == sizeof(Serialized), "RandomUuid is deserialized with 128/8 bytes");
+ memcpy(raw(), bytes.data(), sizeof(*this));
+ timeLow = ntohl(timeLow);
+ timeMid = ntohs(timeMid);
+ timeHiAndVersion = ntohs(timeHiAndVersion);
+ if (!sane())
+ throw TextException("malformed version 4 variant 1 UUID", Here());
+}
+
+/// whether this (being constructed) object follows UUID version 4 variant 1 format
+bool
+RandomUuid::sane() const
+{
+ return (!EBIT_TEST(clockSeqHiAndReserved, 6) &&
+ EBIT_TEST(clockSeqHiAndReserved, 7) &&
+ !EBIT_TEST(timeHiAndVersion, 12) &&
+ !EBIT_TEST(timeHiAndVersion, 13) &&
+ EBIT_TEST(timeHiAndVersion, 14) &&
+ !EBIT_TEST(timeHiAndVersion, 15));
+}
+
+RandomUuid::Serialized
+RandomUuid::serialize() const
+{
+ assert(sane());
+ auto toNetwork = clone();
+ // Convert all multi-byte fields to network byte order so that the recipient
+ // will consider our ID sane() and print() the same text representation.
+ toNetwork.timeLow = htonl(timeLow);
+ toNetwork.timeMid = htons(timeMid);
+ toNetwork.timeHiAndVersion = htons(timeHiAndVersion);
+ return *reinterpret_cast<const Serialized *>(toNetwork.raw());
+}
+
+void
+RandomUuid::print(std::ostream &os) const
+{
+ const auto savedFlags = os.flags();
+ const auto savedFill = os.fill('0');
+
+ os << std::hex;
+
+ os <<
+ std::setw(8) << timeLow << '-' <<
+ std::setw(4) << timeMid << '-' <<
+ std::setw(4) << timeHiAndVersion << '-' <<
+ std::setw(2) << +clockSeqHiAndReserved << std::setw(2) << +clockSeqLow << '-';
+
+ for (size_t i = 0; i < sizeof(node); ++i)
+ os << std::setw(2) << +node[i];
+
+ os.fill(savedFill);
+ os.flags(savedFlags);
+}
+
+bool
+RandomUuid::operator ==(const RandomUuid &other) const
+{
+ return memcmp(raw(), other.raw(), sizeof(*this)) == 0;
+}
+
+std::ostream &
+operator<<(std::ostream &os, const RandomUuid &uuid)
+{
+ uuid.print(os);
+ return os;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#ifndef SQUID_SRC_BASE_RANDOMUUID_H
+#define SQUID_SRC_BASE_RANDOMUUID_H
+
+#include <array>
+#include <iosfwd>
+
+/// 128-bit Universally Unique IDentifier (UUID), version 4 (variant 1) as
+/// defined by RFC 4122. These UUIDs are generated from pseudo-random numbers.
+class RandomUuid
+{
+public:
+ /// UUID representation independent of machine byte-order architecture
+ using Serialized = std::array<uint8_t, 128/8>;
+
+ /// creates a new unique ID (i.e. not a "nil UUID" in RFC 4122 terminology)
+ RandomUuid();
+
+ /// imports a UUID value that was exported using the serialize() API
+ explicit RandomUuid(const Serialized &);
+
+ RandomUuid(RandomUuid &&) = default;
+ RandomUuid &operator=(RandomUuid &&) = default;
+
+ // (Implicit) public copying is prohibited to prevent accidental duplication
+ // of supposed-to-be-unique values. Use clone() when duplication is needed.
+ RandomUuid &operator=(const RandomUuid &) = delete;
+
+ /// exports UUID value; suitable for long-term storage
+ Serialized serialize() const;
+
+ bool operator ==(const RandomUuid &) const;
+ bool operator !=(const RandomUuid &other) const { return !(*this == other); }
+
+ /// creates a UUID object with the same value as this UUID
+ RandomUuid clone() const { return *this; }
+
+ /// writes a human-readable representation
+ void print(std::ostream &os) const;
+
+private:
+ RandomUuid(const RandomUuid &) = default;
+
+ /// read/write access to storage bytes
+ char *raw() { return reinterpret_cast<char*>(this); }
+
+ /// read-only access to storage bytes
+ const char *raw() const { return reinterpret_cast<const char*>(this); }
+
+ /// whether this (being constructed) object follows UUID version 4 variant 1 format
+ bool sane() const;
+
+ /*
+ * These field sizes and names come from RFC 4122 Section 4.1.2. They do not
+ * accurately represent the actual UUID version 4 structure which, the six
+ * version/variant bits aside, contains just random bits.
+ */
+ uint32_t timeLow;
+ uint16_t timeMid;
+ uint16_t timeHiAndVersion;
+ uint8_t clockSeqHiAndReserved;
+ uint8_t clockSeqLow;
+ uint8_t node[6];
+};
+
+std::ostream &operator<<(std::ostream &os, const RandomUuid &uuid);
+
+#endif /* SQUID_SRC_BASE_RANDOMUUID_H */
+
--- /dev/null
+/*
+ * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#include "squid.h"
+#include "base/RandomUuid.h"
+#include "compat/cppunit.h"
+#include "sbuf/SBuf.h"
+#include "sbuf/Stream.h"
+#include "unitTestMain.h"
+
+#include <map>
+#include <set>
+
+class TestRandomUuid: public CPPUNIT_NS::TestFixture
+{
+ CPPUNIT_TEST_SUITE( TestRandomUuid );
+ CPPUNIT_TEST( testUniqueness );
+ CPPUNIT_TEST( testSerialization );
+ CPPUNIT_TEST( testTextRepresentation );
+ CPPUNIT_TEST( testInvalidIds );
+ CPPUNIT_TEST_SUITE_END();
+
+protected:
+ void testUniqueness();
+ void testSerialization();
+ void testTextRepresentation();
+ void testInvalidIds();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION( TestRandomUuid );
+
+typedef std::map<SBuf, RandomUuid::Serialized> RandomIds;
+
+// Generated by https://www.uuidgenerator.net/version4
+// binary representation of the generated UUID in network byte order
+static const RandomIds ExternalIds {
+ { SBuf("bd1b1c07-f7fa-428a-b019-7e390133b0e5"), { 0xbd, 0x1b, 0x1c, 0x07, 0xf7, 0xfa, 0x42, 0x8a, 0xb0, 0x19, 0x7e, 0x39, 0x01, 0x33, 0xb0, 0xe5 } },
+ { SBuf("f63ccd5a-9d25-41a5-a36c-a7b0c6b5c678"), { 0xf6, 0x3c, 0xcd, 0x5a, 0x9d, 0x25, 0x41, 0xa5, 0xa3, 0x6c, 0xa7, 0xb0, 0xc6, 0xb5, 0xc6, 0x78 } },
+ { SBuf("9c8363ac-9c62-44e9-941f-86b7edc25dc7"), { 0x9c, 0x83, 0x63, 0xac, 0x9c, 0x62, 0x44, 0xe9, 0x94, 0x1f, 0x86, 0xb7, 0xed, 0xc2, 0x5d, 0xc7 } }
+};
+
+static const RandomIds InvalidIds {
+ { SBuf("00000000-0000-0000-0000-000000000000"), { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
+ { SBuf("ffffffff-ffff-ffff-ffff-ffffffffffff"), { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }
+};
+
+void
+TestRandomUuid::testUniqueness()
+{
+ std::set<SBuf> uniqueIds;
+ while (uniqueIds.size() < 1000) {
+ const auto inserted = uniqueIds.insert(ToSBuf(RandomUuid())).second;
+ CPPUNIT_ASSERT_MESSAGE("few generated UUIDs are unique", inserted);
+ }
+}
+
+void
+TestRandomUuid::testSerialization()
+{
+ RandomUuid uuid;
+ CPPUNIT_ASSERT_MESSAGE("original and deserialized UUIDs are equal", uuid == RandomUuid(uuid.serialize()));
+}
+
+void
+TestRandomUuid::testTextRepresentation()
+{
+ for (const auto &id: ExternalIds) {
+ CPPUNIT_ASSERT_MESSAGE("UUID text representation matches the expected one", ToSBuf(RandomUuid(id.second)) == id.first);
+ }
+}
+
+void
+TestRandomUuid::testInvalidIds()
+{
+ for (const auto &id: InvalidIds) {
+ try {
+ RandomUuid uuid(id.second);
+ std::cerr << std::endl
+ << "FAIL: " << id.first
+ << Debug::Extra << "error: should be rejected" << std::endl;
+ } catch (const TextException &e) {
+ continue; // success, caught a malformed UUID
+ }
+ CPPUNIT_FAIL("failed to reject an invalid UUID");
+ }
+}
+