QUuid: add the ability to specify the byte order for 128-bit IDs

Some more modern protocols like Bluetooth LE transmit data in little
endian. QtBluetooth will benefit from this.

Change-Id: Id8e48e8f498c4a029619fffd1728c94ddd444537
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
Thiago Macieira 2022-11-18 12:56:38 -08:00 committed by Ivan Solovev
parent 0f932b9a5d
commit 686c02224c
3 changed files with 41 additions and 15 deletions

View File

@ -301,30 +301,33 @@ static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCrypto
*/
/*!
\fn QUuid::QUuid(Id128Bytes id128) noexcept
\fn QUuid::QUuid(Id128Bytes id128, QSysInfo::Endian order) noexcept
\since 6.6
Creates a QUuid based on the integral \a id128 parameter.
Creates a QUuid based on the integral \a id128 parameter and respecting the
byte order \a order.
\sa fromBytes(), toBytes(), toRfc4122()
*/
/*!
\fn QUuid::Id128Bytes QUuid::toBytes() const noexcept
\fn QUuid::Id128Bytes QUuid::toBytes(QSysInfo::Endian order) 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.
Returns an 128-bit ID created from this QUuid on the byte order specified
by \a order. The binary content of this function is the same as toRfc4122()
if the order is QSysInfo::BigEndian. See that function for more details.
\sa toRfc4122(), fromBytes(), QUuid()
*/
/*!
\fn QUuid QUuid::fromBytes(const void *bytes) noexcept
\fn QUuid QUuid::fromBytes(const void *bytes, QSysInfo::Endian order) 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().
Reads 128 bits (16 bytes) from \a bytes using byte order \a order and
returns the QUuid corresponding to those bytes. This function does the same
as fromRfc4122() if the byte order \a order is QSysInfo::BigEndian.
\sa fromRfc4122()
*/
@ -674,6 +677,9 @@ QDataStream &operator<<(QDataStream &s, const QUuid &id)
bytes = QByteArray(16, Qt::Uninitialized);
uchar *data = reinterpret_cast<uchar *>(bytes.data());
// for historical reasons, our little-endian serialization format
// stores each of the UUID fields in little endian, instead of storing
// a little endian Id128
qToLittleEndian(id.data1, data);
data += sizeof(quint32);
qToLittleEndian(id.data2, data);

View File

@ -72,7 +72,7 @@ public:
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
: data1(l), data2(w1), data3(w2), data4{b1, b2, b3, b4, b5, b6, b7, b8} {}
QUuid(Id128Bytes id128) noexcept;
QUuid(Id128Bytes id128, QSysInfo::Endian order = QSysInfo::BigEndian) noexcept;
explicit QUuid(QAnyStringView string) noexcept
: QUuid{fromString(string)} {}
@ -86,10 +86,10 @@ public:
#endif
QString toString(StringFormat mode = WithBraces) const;
QByteArray toByteArray(StringFormat mode = WithBraces) const;
Id128Bytes toBytes() const noexcept;
Id128Bytes toBytes(QSysInfo::Endian order = QSysInfo::BigEndian) const noexcept;
QByteArray toRfc4122() const;
static QUuid fromBytes(const void *bytes) noexcept;
static QUuid fromBytes(const void *bytes, QSysInfo::Endian order = QSysInfo::BigEndian) noexcept;
#if QT_CORE_REMOVED_SINCE(6, 3)
static QUuid fromRfc4122(const QByteArray &);
#endif
@ -179,6 +179,16 @@ public:
ushort data2 = 0;
ushort data3 = 0;
uchar data4[8] = {};
private:
static constexpr Id128Bytes bswap(Id128Bytes b)
{
// 128-bit byte swap
b.data64[0] = qbswap(b.data64[0]);
b.data64[1] = qbswap(b.data64[1]);
qSwap(b.data64[0], b.data64[1]);
return b;
}
};
Q_DECLARE_TYPEINFO(QUuid, Q_PRIMITIVE_TYPE);
@ -194,29 +204,33 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const QUuid &);
Q_CORE_EXPORT size_t qHash(const QUuid &uuid, size_t seed = 0) noexcept;
inline QUuid::QUuid(Id128Bytes uuid) noexcept
inline QUuid::QUuid(Id128Bytes uuid, QSysInfo::Endian order) noexcept
{
if (order == QSysInfo::LittleEndian)
uuid = bswap(uuid);
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
inline QUuid::Id128Bytes QUuid::toBytes(QSysInfo::Endian order) 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));
if (order == QSysInfo::LittleEndian)
return bswap(result);
return result;
}
inline QUuid QUuid::fromBytes(const void *bytes) noexcept
inline QUuid QUuid::fromBytes(const void *bytes, QSysInfo::Endian order) noexcept
{
Id128Bytes result = {};
memcpy(result.data, bytes, sizeof(result));
return QUuid(result);
return QUuid(result, order);
}
inline bool operator<=(const QUuid &lhs, const QUuid &rhs) noexcept

View File

@ -237,6 +237,12 @@ void tst_QUuid::id128()
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);
QUuid::Id128Bytes leBytesA = {};
for (int i = 0; i < 16; i++)
leBytesA.data[15 - i] = bytesA.data[i];
QCOMPARE(QUuid(leBytesA, QSysInfo::LittleEndian), uuidA);
QVERIFY(memcmp(uuidA.toBytes(QSysInfo::LittleEndian).data, leBytesA.data, sizeof(leBytesA)) == 0);
}
void tst_QUuid::createUuidV3OrV5()