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()));
|
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)
|
\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
|
byte array to a function accepting a \c{const char *} expected to be
|
||||||
'\\0'-terminated will fail.
|
'\\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
|
This function can be used instead of fromRawData() to re-use
|
||||||
existing QByteArray objects to save memory re-allocations.
|
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)
|
QByteArray &QByteArray::setRawData(const char *data, qsizetype size)
|
||||||
{
|
{
|
||||||
|
@ -511,6 +511,30 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
explicit inline QByteArray(DataPointer &&dd) : d(std::move(dd)) {}
|
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:
|
private:
|
||||||
friend bool comparesEqual(const QByteArray &lhs, const QByteArrayView &rhs) noexcept
|
friend bool comparesEqual(const QByteArray &lhs, const QByteArrayView &rhs) noexcept
|
||||||
{ return QByteArrayView(lhs) == rhs; }
|
{ return QByteArrayView(lhs) == rhs; }
|
||||||
|
@ -7063,6 +7063,31 @@ const ushort *QString::utf16() const
|
|||||||
return reinterpret_cast<const ushort *>(d.data());
|
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
|
Returns a string of size \a width that contains this string
|
||||||
padded by the \a fill character.
|
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
|
'\\0'-terminated string (although utf16() does, at the cost of
|
||||||
copying the raw data).
|
copying the raw data).
|
||||||
|
|
||||||
\sa fromUtf16(), setRawData()
|
\sa fromUtf16(), setRawData(), data(), constData(),
|
||||||
|
nullTerminate(), nullTerminated()
|
||||||
*/
|
*/
|
||||||
QString QString::fromRawData(const QChar *unicode, qsizetype size)
|
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
|
This function can be used instead of fromRawData() to re-use
|
||||||
existings QString objects to save memory re-allocations.
|
existings QString objects to save memory re-allocations.
|
||||||
|
|
||||||
\sa fromRawData()
|
\sa fromRawData(), nullTerminate(), nullTerminated()
|
||||||
*/
|
*/
|
||||||
QString &QString::setRawData(const QChar *unicode, qsizetype size)
|
QString &QString::setRawData(const QChar *unicode, qsizetype size)
|
||||||
{
|
{
|
||||||
|
@ -699,6 +699,29 @@ public:
|
|||||||
[[nodiscard]] QString repeated(qsizetype times) const;
|
[[nodiscard]] QString repeated(qsizetype times) const;
|
||||||
|
|
||||||
const ushort *utf16() const; // ### Qt 7 char16_t
|
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)
|
#if !defined(Q_QDOC)
|
||||||
[[nodiscard]] QByteArray toLatin1() const &
|
[[nodiscard]] QByteArray toLatin1() const &
|
||||||
|
@ -60,6 +60,7 @@ private slots:
|
|||||||
void appendFromRawData();
|
void appendFromRawData();
|
||||||
void appendExtended_data();
|
void appendExtended_data();
|
||||||
void appendExtended();
|
void appendExtended();
|
||||||
|
void nullTerminated();
|
||||||
void appendEmptyNull();
|
void appendEmptyNull();
|
||||||
void assign();
|
void assign();
|
||||||
void assignShared();
|
void assignShared();
|
||||||
@ -983,6 +984,42 @@ void tst_QByteArray::appendExtended()
|
|||||||
QCOMPARE(array.size(), 11);
|
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()
|
void tst_QByteArray::appendEmptyNull()
|
||||||
{
|
{
|
||||||
QByteArray a;
|
QByteArray a;
|
||||||
|
@ -370,6 +370,7 @@ private slots:
|
|||||||
void check_QDataStream();
|
void check_QDataStream();
|
||||||
void fromRawData();
|
void fromRawData();
|
||||||
void setRawData();
|
void setRawData();
|
||||||
|
void nullTerminated();
|
||||||
void setUnicode();
|
void setUnicode();
|
||||||
void endsWith();
|
void endsWith();
|
||||||
void startsWith();
|
void startsWith();
|
||||||
@ -5934,6 +5935,42 @@ void tst_QString::setRawData()
|
|||||||
QVERIFY(cstr.data_ptr() != csd);
|
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()
|
void tst_QString::setUnicode()
|
||||||
{
|
{
|
||||||
const QChar ptr[] = { u'ሴ', QChar(0x0000) };
|
const QChar ptr[] = { u'ሴ', QChar(0x0000) };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user