QString/QByteArray: add nullTerminate{,d}()

As requested in code review.

utf16() guarantees that the string will always be \0-terminated which
could mean allocating behind the scenes. On the other hand
nullTerminate{,d}() makes it obvious that the data may be deep-copied.

For QByteArray, this partially reverts
e0206fe9d47bfbf18a0d4c2e5e780504305f72e7. Adding the same method to
QByteArray was requested in code review for API symmetry.

[ChangeLog][QtCore][QString] Added nullTerminate() and nullTerminated()
methods (like chop() and chopped()), which are useful for strings
constructed with fromRawData() when calling methods that expect
\0-terminated strings.

[ChangeLog][QtCore][QByteArray] Added nullTerminate() and
nullTerminated() methods, which are useful for byte arrays constructed
with fromRawData() when calling methods that expect \0-terminated data.

Change-Id: Iec33b889a9ab62460b7a0df8f9b2189f3f794a54
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Ahmad Samir 2024-10-25 03:08:38 +03:00
parent 09fa335fb2
commit 3b95bfe7c7
6 changed files with 176 additions and 4 deletions

View File

@ -2011,6 +2011,31 @@ void QByteArray::expand(qsizetype i)
resize(qMax(i + 1, size()));
}
/*!
\fn QByteArray &QByteArray::nullTerminate() const
\since 6.9
If this byte array's data isn't null-terminated, this method will make
a deep-copy of the data and make it null-terminated.
A QByteArray is null-terminated by default, however in some cases
(e.g. when using fromRawData()), the data doesn't necessarily end with
a \c {\0} character, which could be a problem when calling methods that
expect a null-terminated string (for example, C API).
\sa nullTerminated(), fromRawData(), setRawData()
*/
/*!
\fn QByteArray QByteArray::nullTerminated() const
\since 6.9
Returns a copy of this byte array that is always null-terminated.
See nullTerminate().
\sa nullTerminate(), fromRawData(), setRawData()
*/
/*!
\fn QByteArray &QByteArray::prepend(QByteArrayView ba)
@ -4419,7 +4444,7 @@ QByteArray QByteArray::number(double n, char format, int precision)
byte array to a function accepting a \c{const char *} expected to be
'\\0'-terminated will fail.
\sa setRawData(), data(), constData()
\sa setRawData(), data(), constData(), nullTerminate(), nullTerminated()
*/
/*!
@ -4434,7 +4459,7 @@ QByteArray QByteArray::number(double n, char format, int precision)
This function can be used instead of fromRawData() to re-use
existing QByteArray objects to save memory re-allocations.
\sa fromRawData(), data(), constData()
\sa fromRawData(), data(), constData(), nullTermiante(), nullTerminated()
*/
QByteArray &QByteArray::setRawData(const char *data, qsizetype size)
{

View File

@ -511,6 +511,30 @@ public:
#endif
explicit inline QByteArray(DataPointer &&dd) : d(std::move(dd)) {}
[[nodiscard]] QByteArray nullTerminated() const &
{
// Ensure \0-termination for fromRawData() byte arrays
if (!d.isMutable())
return QByteArray{constData(), size()};
return *this;
}
[[nodiscard]] QByteArray nullTerminated() &&
{
// Ensure \0-termination for fromRawData() byte arrays
if (!d.isMutable())
return QByteArray{constData(), size()};
return std::move(*this);
}
QByteArray &nullTerminate()
{
// Ensure \0-termination for fromRawData() byte arrays
if (!d.isMutable())
*this = QByteArray{constData(), size()};
return *this;
}
private:
friend bool comparesEqual(const QByteArray &lhs, const QByteArrayView &rhs) noexcept
{ return QByteArrayView(lhs) == rhs; }

View File

@ -7063,6 +7063,31 @@ const ushort *QString::utf16() const
return reinterpret_cast<const ushort *>(d.data());
}
/*!
\fn QString nullTerminate() const
\since 6.9
If this string data isn't null-terminated, this method will make a deep
copy of the data and make it null-terminated().
A QString is null-terminated by default, however in some cases (e.g.
when using fromRawData()), the string data doesn't necessarily end
with a \c {\0} character, which could be a problem when calling methods
that expect a null-terminated string.
\sa nullTerminated(), fromRawData(), setRawData()
*/
/*!
\fn QString nullTerminated() const
\since 6.9
Returns a copy of this string that is always null-terminated.
See nullTerminate().
\sa nullTerminated(), fromRawData(), setRawData()
*/
/*!
Returns a string of size \a width that contains this string
padded by the \a fill character.
@ -9375,7 +9400,8 @@ QString::iterator QString::erase(QString::const_iterator first, QString::const_i
'\\0'-terminated string (although utf16() does, at the cost of
copying the raw data).
\sa fromUtf16(), setRawData()
\sa fromUtf16(), setRawData(), data(), constData(),
nullTerminate(), nullTerminated()
*/
QString QString::fromRawData(const QChar *unicode, qsizetype size)
{
@ -9394,7 +9420,7 @@ QString QString::fromRawData(const QChar *unicode, qsizetype size)
This function can be used instead of fromRawData() to re-use
existings QString objects to save memory re-allocations.
\sa fromRawData()
\sa fromRawData(), nullTerminate(), nullTerminated()
*/
QString &QString::setRawData(const QChar *unicode, qsizetype size)
{

View File

@ -699,6 +699,29 @@ public:
[[nodiscard]] QString repeated(qsizetype times) const;
const ushort *utf16() const; // ### Qt 7 char16_t
[[nodiscard]] QString nullTerminated() const &
{
// ensure '\0'-termination for ::fromRawData strings
if (!d->isMutable())
return QString{constData(), size()};
return *this;
}
[[nodiscard]] QString nullTerminated() &&
{
// ensure '\0'-termination for ::fromRawData strings
if (!d->isMutable())
return QString{constData(), size()};
return std::move(*this);
}
QString &nullTerminate()
{
// ensure '\0'-termination for ::fromRawData strings
if (!d->isMutable())
*this = QString{constData(), size()};
return *this;
}
#if !defined(Q_QDOC)
[[nodiscard]] QByteArray toLatin1() const &

View File

@ -60,6 +60,7 @@ private slots:
void appendFromRawData();
void appendExtended_data();
void appendExtended();
void nullTerminated();
void appendEmptyNull();
void assign();
void assignShared();
@ -983,6 +984,42 @@ void tst_QByteArray::appendExtended()
QCOMPARE(array.size(), 11);
}
void tst_QByteArray::nullTerminated()
{
const char ptr[] = {'A', 'B', 'C'};
QTest::ThrowOnFailEnabler throwOnFail;
auto check = [&ptr](const QByteArray &ba) {
QCOMPARE_NE(reinterpret_cast<const void *>(ba.constData()), ptr);
QCOMPARE(ba.constData()[0], ptr[0]);
QCOMPARE(ba.constData()[1], ptr[1]);
QCOMPARE(ba.constData()[2], '\0');
QCOMPARE(ba.size(), 2);
};
{
auto ba = QByteArray::fromRawData(ptr, 2);
QCOMPARE_EQ(reinterpret_cast<const void *>(ba.constData()), ptr);
QCOMPARE(ba.constData()[0], ptr[0]);
QCOMPARE(ba.constData()[1], ptr[1]);
QCOMPARE(ba.size(), 2);
check(ba.nullTerminated());
check(QByteArray::fromRawData(ptr, 2).nullTerminated()); // rvalue
}
{
auto ba = QByteArray::fromRawData(ptr, 2);
QCOMPARE_EQ(reinterpret_cast<const void *>(ba.constData()), ptr);
QCOMPARE(ba.constData()[0], ptr[0]);
QCOMPARE(ba.constData()[1], ptr[1]);
QCOMPARE(ba.size(), 2);
check(ba.nullTerminate());
}
}
void tst_QByteArray::appendEmptyNull()
{
QByteArray a;

View File

@ -370,6 +370,7 @@ private slots:
void check_QDataStream();
void fromRawData();
void setRawData();
void nullTerminated();
void setUnicode();
void endsWith();
void startsWith();
@ -5934,6 +5935,42 @@ void tst_QString::setRawData()
QVERIFY(cstr.data_ptr() != csd);
}
void tst_QString::nullTerminated()
{
const QChar ptr[] = { u'', u'ʎ', u'\0' };
QTest::ThrowOnFailEnabler thrower;
auto check = [ptr] (const QString &r) {
QVERIFY(r.constData() != ptr);
QCOMPARE(r.constData()[0], ptr[0]);
QCOMPARE(r.constData()[1], ptr[1]);
QCOMPARE(r.constData()[2], u'\0');
QCOMPARE(r.size(), 2);
};
{
QString str = QString::fromRawData(ptr, 2);
QCOMPARE(str.constData(), ptr);
QCOMPARE(str.constData()[0], ptr[0]);
QCOMPARE(str.constData()[1], ptr[1]);
QCOMPARE(str.size(), 2);
check(str.nullTerminated());
check(QString::fromRawData(ptr, 2).nullTerminated()); // rvalue
}
{
QString str = QString::fromRawData(ptr, 2);
QCOMPARE(str.constData(), ptr);
QCOMPARE(str.constData()[0], ptr[0]);
QCOMPARE(str.constData()[1], ptr[1]);
QCOMPARE(str.size(), 2);
check(str.nullTerminate());
}
}
void tst_QString::setUnicode()
{
const QChar ptr[] = { u'', QChar(0x0000) };