QUuid: add a trivial structure to support exactly 128 bits

This is inspired by QBluetoothUuid's quint128, but with a better
name. It also matches systemd's sd_id128.

Change-Id: Id8e48e8f498c4a029619fffd172893dc1545adda
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
Thiago Macieira 2022-11-17 20:37:15 -08:00 committed by Ivan Solovev
parent c3c5d2cab0
commit 0f932b9a5d
3 changed files with 117 additions and 40 deletions

View File

@ -13,6 +13,9 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// ensure QList of this is efficient
static_assert(QTypeInfo<QUuid::Id128Bytes>::isRelocatable);
// 16 bytes (a uint, two shorts and a uchar[8]), each represented by two hex // 16 bytes (a uint, two shorts and a uchar[8]), each represented by two hex
// digits; plus four dashes and a pair of enclosing brace: 16*2 + 4 + 2 = 38. // digits; plus four dashes and a pair of enclosing brace: 16*2 + 4 + 2 = 38.
enum { MaxStringUuidLength = 38 }; enum { MaxStringUuidLength = 38 };
@ -286,6 +289,46 @@ static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCrypto
cannot parse this back again as input. cannot parse this back again as input.
*/ */
/*!
\class QUuid::Id128Bytes
\inmodule QtCore
\since 6.6
This trivial structure is 128 bits (16 bytes) in size and holds the binary
representation of a UUID. Applications can \c{memcpy()} its contents to and
from many other libraries' UUID or GUID structures that take 128-bit
values.
*/
/*!
\fn QUuid::QUuid(Id128Bytes id128) noexcept
\since 6.6
Creates a QUuid based on the integral \a id128 parameter.
\sa fromBytes(), toBytes(), toRfc4122()
*/
/*!
\fn QUuid::Id128Bytes QUuid::toBytes() const noexcept
\since 6.6
Returns an 128-bit ID created from this QUuid. The binary content of this
function is the same as toRfc4122(). See that function for more details.
\sa toRfc4122(), fromBytes(), QUuid()
*/
/*!
\fn QUuid QUuid::fromBytes(const void *bytes) noexcept
\since 6.6
Reads 128 bits (16 bytes) from \a bytes and returns the QUuid corresponding
to those bytes. This function does the same as fromRfc4122().
\sa fromRfc4122()
*/
/*! /*!
\fn QUuid::QUuid(const GUID &guid) \fn QUuid::QUuid(const GUID &guid)
@ -468,32 +511,13 @@ QUuid QUuid::createUuidV5(const QUuid &ns, const QByteArray &baseData)
\since 4.8 \since 4.8
\sa toRfc4122(), QUuid() \sa toRfc4122(), QUuid(), fromBytes()
*/ */
QUuid QUuid::fromRfc4122(QByteArrayView bytes) noexcept QUuid QUuid::fromRfc4122(QByteArrayView bytes) noexcept
{ {
if (bytes.isEmpty() || bytes.size() != 16) if (bytes.isEmpty() || bytes.size() != 16)
return QUuid(); return QUuid();
return fromBytes(bytes.data());
uint d1;
ushort d2, d3;
uchar d4[8];
const uchar *data = reinterpret_cast<const uchar *>(bytes.data());
d1 = qFromBigEndian<quint32>(data);
data += sizeof(quint32);
d2 = qFromBigEndian<quint16>(data);
data += sizeof(quint16);
d3 = qFromBigEndian<quint16>(data);
data += sizeof(quint16);
for (int i = 0; i < 8; ++i) {
d4[i] = *(data);
data++;
}
return QUuid(d1, d2, d3, d4[0], d4[1], d4[2], d4[3], d4[4], d4[5], d4[6], d4[7]);
} }
/*! /*!
@ -623,27 +647,16 @@ QByteArray QUuid::toByteArray(QUuid::StringFormat mode) const
\endtable \endtable
The bytes in the byte array returned by this function contains the same
binary content as toBytes().
\sa toBytes()
\since 4.8 \since 4.8
*/ */
QByteArray QUuid::toRfc4122() const QByteArray QUuid::toRfc4122() const
{ {
// we know how many bytes a UUID has, I hope :) Id128Bytes bytes = toBytes();
QByteArray bytes(16, Qt::Uninitialized); return QByteArrayView(bytes).toByteArray();
uchar *data = reinterpret_cast<uchar *>(bytes.data());
qToBigEndian(data1, data);
data += sizeof(quint32);
qToBigEndian(data2, data);
data += sizeof(quint16);
qToBigEndian(data3, data);
data += sizeof(quint16);
for (int i = 0; i < 8; ++i) {
*(data) = data4[i];
data++;
}
return bytes;
} }
#ifndef QT_NO_DATASTREAM #ifndef QT_NO_DATASTREAM

View File

@ -4,6 +4,7 @@
#ifndef QUUID_H #ifndef QUUID_H
#define QUUID_H #define QUUID_H
#include <QtCore/qendian.h>
#include <QtCore/qstring.h> #include <QtCore/qstring.h>
#if defined(Q_OS_WIN) || defined(Q_QDOC) #if defined(Q_OS_WIN) || defined(Q_QDOC)
@ -26,7 +27,6 @@ Q_FORWARD_DECLARE_OBJC_CLASS(NSUUID);
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QUuid class Q_CORE_EXPORT QUuid
{ {
QUuid(Qt::Initialization) {} QUuid(Qt::Initialization) {}
@ -55,11 +55,24 @@ public:
Id128 = 3 Id128 = 3
}; };
union Id128Bytes {
quint8 data[16];
quint16 data16[8];
quint32 data32[4];
quint64 data64[2];
constexpr explicit operator QByteArrayView() const noexcept
{
return QByteArrayView(data, sizeof(data));
}
};
constexpr QUuid() noexcept {} constexpr QUuid() noexcept {}
constexpr QUuid(uint l, ushort w1, ushort w2, uchar b1, uchar b2, uchar b3, constexpr QUuid(uint l, ushort w1, ushort w2, uchar b1, uchar b2, uchar b3,
uchar b4, uchar b5, uchar b6, uchar b7, uchar b8) noexcept uchar b4, uchar b5, uchar b6, uchar b7, uchar b8) noexcept
: data1(l), data2(w1), data3(w2), data4{b1, b2, b3, b4, b5, b6, b7, b8} {} : data1(l), data2(w1), data3(w2), data4{b1, b2, b3, b4, b5, b6, b7, b8} {}
QUuid(Id128Bytes id128) noexcept;
explicit QUuid(QAnyStringView string) noexcept explicit QUuid(QAnyStringView string) noexcept
: QUuid{fromString(string)} {} : QUuid{fromString(string)} {}
@ -73,11 +86,15 @@ public:
#endif #endif
QString toString(StringFormat mode = WithBraces) const; QString toString(StringFormat mode = WithBraces) const;
QByteArray toByteArray(StringFormat mode = WithBraces) const; QByteArray toByteArray(StringFormat mode = WithBraces) const;
Id128Bytes toBytes() const noexcept;
QByteArray toRfc4122() const; QByteArray toRfc4122() const;
static QUuid fromBytes(const void *bytes) noexcept;
#if QT_CORE_REMOVED_SINCE(6, 3) #if QT_CORE_REMOVED_SINCE(6, 3)
static QUuid fromRfc4122(const QByteArray &); static QUuid fromRfc4122(const QByteArray &);
#endif #endif
static QUuid fromRfc4122(QByteArrayView) noexcept; static QUuid fromRfc4122(QByteArrayView) noexcept;
bool isNull() const noexcept; bool isNull() const noexcept;
constexpr bool operator==(const QUuid &orig) const noexcept constexpr bool operator==(const QUuid &orig) const noexcept
@ -177,6 +194,31 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const QUuid &);
Q_CORE_EXPORT size_t qHash(const QUuid &uuid, size_t seed = 0) noexcept; Q_CORE_EXPORT size_t qHash(const QUuid &uuid, size_t seed = 0) noexcept;
inline QUuid::QUuid(Id128Bytes uuid) noexcept
{
data1 = qFromBigEndian<quint32>(&uuid.data[0]);
data2 = qFromBigEndian<quint16>(&uuid.data[4]);
data3 = qFromBigEndian<quint16>(&uuid.data[6]);
memcpy(data4, &uuid.data[8], sizeof(data4));
}
inline QUuid::Id128Bytes QUuid::toBytes() const noexcept
{
Id128Bytes result = {};
qToBigEndian(data1, &result.data[0]);
qToBigEndian(data2, &result.data[4]);
qToBigEndian(data3, &result.data[6]);
memcpy(&result.data[8], data4, sizeof(data4));
return result;
}
inline QUuid QUuid::fromBytes(const void *bytes) noexcept
{
Id128Bytes result = {};
memcpy(result.data, bytes, sizeof(result));
return QUuid(result);
}
inline bool operator<=(const QUuid &lhs, const QUuid &rhs) noexcept inline bool operator<=(const QUuid &lhs, const QUuid &rhs) noexcept
{ return !(rhs < lhs); } { return !(rhs < lhs); }
inline bool operator>=(const QUuid &lhs, const QUuid &rhs) noexcept inline bool operator>=(const QUuid &lhs, const QUuid &rhs) noexcept

View File

@ -25,6 +25,7 @@ private slots:
void fromByteArray(); void fromByteArray();
void toRfc4122(); void toRfc4122();
void fromRfc4122(); void fromRfc4122();
void id128();
void createUuidV3OrV5(); void createUuidV3OrV5();
void check_QDataStream(); void check_QDataStream();
void isNull(); void isNull();
@ -98,7 +99,7 @@ void tst_QUuid::fromChar()
QCOMPARE(QUuid(), QUuid("fc69b59e-cc34-")); QCOMPARE(QUuid(), QUuid("fc69b59e-cc34-"));
QCOMPARE(QUuid(), QUuid("fc69b59e-cc34")); QCOMPARE(QUuid(), QUuid("fc69b59e-cc34"));
QCOMPARE(QUuid(), QUuid("cc34")); QCOMPARE(QUuid(), QUuid("cc34"));
QCOMPARE(QUuid(), QUuid(NULL)); QCOMPARE(QUuid(), QUuid(nullptr));
QCOMPARE(uuidB, QUuid(QString("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}"))); QCOMPARE(uuidB, QUuid(QString("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}")));
} }
@ -217,6 +218,27 @@ void tst_QUuid::fromRfc4122()
QCOMPARE(uuidB, QUuid::fromRfc4122(QByteArray::fromHex("1ab6e93ab1cb4a87ba47ec7e99039a7b"))); QCOMPARE(uuidB, QUuid::fromRfc4122(QByteArray::fromHex("1ab6e93ab1cb4a87ba47ec7e99039a7b")));
} }
void tst_QUuid::id128()
{
constexpr QUuid::Id128Bytes bytesA = { {
0xfc, 0x69, 0xb5, 0x9e,
0xcc, 0x34,
0x44, 0x36,
0xa4, 0x3c, 0xee, 0x95, 0xd1, 0x28, 0xb8, 0xc5,
} };
constexpr QUuid::Id128Bytes bytesB = { {
0x1a, 0xb6, 0xe9, 0x3a,
0xb1, 0xcb,
0x4a, 0x87,
0xba, 0x47, 0xec, 0x7e, 0x99, 0x03, 0x9a, 0x7b,
} };
QCOMPARE(QUuid(bytesA), uuidA);
QCOMPARE(QUuid(bytesB), uuidB);
QVERIFY(memcmp(uuidA.toBytes().data, bytesA.data, sizeof(QUuid::Id128Bytes)) == 0);
QVERIFY(memcmp(uuidB.toBytes().data, bytesB.data, sizeof(QUuid::Id128Bytes)) == 0);
}
void tst_QUuid::createUuidV3OrV5() void tst_QUuid::createUuidV3OrV5()
{ {
//"www.widgets.com" is also from RFC4122 //"www.widgets.com" is also from RFC4122