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:
parent
09fa335fb2
commit
3b95bfe7c7
@ -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)
|
||||
{
|
||||
|
@ -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; }
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 &
|
||||
|
@ -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;
|
||||
|
@ -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) };
|
||||
|
Loading…
x
Reference in New Issue
Block a user