From f4101f9953ec63ee09e73b4b72bf6ed561d7568f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 25 Sep 2023 11:08:51 -0700 Subject: [PATCH] QString/QBA: add lvalue and rvalue overloads to first/last/sliced/chopped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those ought to have been the original implementation, when they were added in commit 38096a3d7040edac4f769270d2402ff4e39d7694, for Qt 6.0. Because these classes are exported, we need to provide the previous only implementations for MSVC. All other compilers would provide inline or emit local, out-of-line copies. Change-Id: Ifeb6206a9fa04424964bfffd178836a2ae56157d Reviewed-by: MÃ¥rten Nordheim --- src/corelib/compat/removed_api.cpp | 32 ++++++++ src/corelib/text/qbytearray.cpp | 23 ++++-- src/corelib/text/qbytearray.h | 35 +++++++-- src/corelib/text/qstring.cpp | 23 ++++-- src/corelib/text/qstring.h | 33 ++++++-- src/corelib/tools/qarraydatapointer.h | 20 +++++ .../auto/corelib/text/qstring/tst_qstring.cpp | 77 +++++++++++++++++++ 7 files changed, 223 insertions(+), 20 deletions(-) diff --git a/src/corelib/compat/removed_api.cpp b/src/corelib/compat/removed_api.cpp index 591ef0fc68..5c3a587f6e 100644 --- a/src/corelib/compat/removed_api.cpp +++ b/src/corelib/compat/removed_api.cpp @@ -618,6 +618,22 @@ QStringView QXmlStreamAttributes::value(QLatin1StringView qualifiedName) const #if QT_CORE_REMOVED_SINCE(6, 7) +#include "qbytearray.h" + +#ifdef Q_CC_MSVC +// previously inline methods, only needed for MSVC compat +QByteArray QByteArray::first(qsizetype n) const +{ return sliced(0, n); } +QByteArray QByteArray::last(qsizetype n) const +{ return sliced(size() - n, n); } +QByteArray QByteArray::sliced(qsizetype pos) const +{ return sliced(pos, size() - pos); } +QByteArray QByteArray::sliced(qsizetype pos, qsizetype n) const +{ verify(pos, n); return QByteArray(d.data() + pos, n); } +QByteArray QByteArray::chopped(qsizetype n) const +{ return sliced(0, size() - n); } +#endif + #include "qdatetime.h" QDateTime::QDateTime(QDate date, QTime time, const QTimeZone &timeZone) @@ -726,6 +742,22 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase * return invokeMethodImpl(object, slot, type, 1, &ret, nullptr, nullptr); } +#include "qstring.h" + +#ifdef Q_CC_MSVC +// previously inline methods, only needed for MSVC compat +QString QString::first(qsizetype n) const +{ return sliced(0, n); } +QString QString::last(qsizetype n) const +{ return sliced(size() - n, n); } +QString QString::sliced(qsizetype pos) const +{ return sliced(pos, size() - pos); } +QString QString::sliced(qsizetype pos, qsizetype n) const +{ verify(pos, n); return QString(begin() + pos, n); } +QString QString::chopped(qsizetype n) const +{ return sliced(0, size() - n); } +#endif + #include "qurl.h" QUrl QUrl::fromEncoded(const QByteArray &input, ParsingMode mode) diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp index 9135dd8523..1821dd2c26 100644 --- a/src/corelib/text/qbytearray.cpp +++ b/src/corelib/text/qbytearray.cpp @@ -3102,7 +3102,8 @@ QByteArray QByteArray::mid(qsizetype pos, qsizetype len) const } /*! - \fn QByteArray QByteArray::first(qsizetype n) const + \fn QByteArray QByteArray::first(qsizetype n) const & + \fn QByteArray QByteArray::first(qsizetype n) && \since 6.0 Returns the first \a n bytes of the byte array. @@ -3116,7 +3117,8 @@ QByteArray QByteArray::mid(qsizetype pos, qsizetype len) const */ /*! - \fn QByteArray QByteArray::last(qsizetype n) const + \fn QByteArray QByteArray::last(qsizetype n) const & + \fn QByteArray QByteArray::last(qsizetype n) && \since 6.0 Returns the last \a n bytes of the byte array. @@ -3130,7 +3132,8 @@ QByteArray QByteArray::mid(qsizetype pos, qsizetype len) const */ /*! - \fn QByteArray QByteArray::sliced(qsizetype pos, qsizetype n) const + \fn QByteArray QByteArray::sliced(qsizetype pos, qsizetype n) const & + \fn QByteArray QByteArray::sliced(qsizetype pos, qsizetype n) && \since 6.0 Returns a byte array containing the \a n bytes of this object starting @@ -3144,9 +3147,18 @@ QByteArray QByteArray::mid(qsizetype pos, qsizetype len) const \sa first(), last(), chopped(), chop(), truncate() */ +QByteArray QByteArray::sliced_helper(QByteArray &a, qsizetype pos, qsizetype n) +{ + if (n == 0) + return fromRawData(&_empty, 0); + DataPointer d = std::move(a.d).sliced(pos, n); + d.data()[n] = 0; + return QByteArray(std::move(d)); +} /*! - \fn QByteArray QByteArray::sliced(qsizetype pos) const + \fn QByteArray QByteArray::sliced(qsizetype pos) const & + \fn QByteArray QByteArray::sliced(qsizetype pos) && \since 6.0 \overload @@ -3159,7 +3171,8 @@ QByteArray QByteArray::mid(qsizetype pos, qsizetype len) const */ /*! - \fn QByteArray QByteArray::chopped(qsizetype len) const + \fn QByteArray QByteArray::chopped(qsizetype len) const & + \fn QByteArray QByteArray::chopped(qsizetype len) && \since 5.10 Returns a byte array that contains the leftmost size() - \a len bytes of diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h index 36abd53618..4251e6e20e 100644 --- a/src/corelib/text/qbytearray.h +++ b/src/corelib/text/qbytearray.h @@ -155,17 +155,40 @@ public: [[nodiscard]] QByteArray right(qsizetype len) const; [[nodiscard]] QByteArray mid(qsizetype index, qsizetype len = -1) const; - [[nodiscard]] QByteArray first(qsizetype n) const +#if QT_CORE_REMOVED_SINCE(6, 7) + QByteArray first(qsizetype n) const; + QByteArray last(qsizetype n) const; + QByteArray sliced(qsizetype pos) const; + QByteArray sliced(qsizetype pos, qsizetype n) const; + QByteArray chopped(qsizetype len) const; +#else + [[nodiscard]] QByteArray first(qsizetype n) const & { verify(0, n); return sliced(0, n); } - [[nodiscard]] QByteArray last(qsizetype n) const + [[nodiscard]] QByteArray last(qsizetype n) const & { verify(0, n); return sliced(size() - n, n); } - [[nodiscard]] QByteArray sliced(qsizetype pos) const + [[nodiscard]] QByteArray sliced(qsizetype pos) const & { verify(pos, 0); return QByteArray(data() + pos, size() - pos); } - [[nodiscard]] QByteArray sliced(qsizetype pos, qsizetype n) const + [[nodiscard]] QByteArray sliced(qsizetype pos, qsizetype n) const & { verify(pos, n); return QByteArray(d.data() + pos, n); } - [[nodiscard]] QByteArray chopped(qsizetype len) const + [[nodiscard]] QByteArray chopped(qsizetype len) const & { verify(0, len); return sliced(0, size() - len); } + [[nodiscard]] QByteArray first(qsizetype n) && + { + verify(0, n); + resize(n); // may detach and allocate memory + return std::move(*this); + } + [[nodiscard]] QByteArray last(qsizetype n) && + { verify(0, n); return sliced_helper(*this, size() - n, n); } + [[nodiscard]] QByteArray sliced(qsizetype pos) && + { verify(pos, 0); return sliced_helper(*this, pos, size() - pos); } + [[nodiscard]] QByteArray sliced(qsizetype pos, qsizetype n) && + { verify(pos, n); return sliced_helper(*this, pos, n); } + [[nodiscard]] QByteArray chopped(qsizetype len) && + { verify(0, len); return std::move(*this).first(size() - len); } +#endif + bool startsWith(QByteArrayView bv) const { return QtPrivate::startsWith(qToByteArrayViewIgnoringNull(*this), bv); } bool startsWith(char c) const { return size() > 0 && front() == c; } @@ -479,6 +502,7 @@ public: QT_CORE_INLINE_SINCE(6, 4) bool isNull() const noexcept; + inline const DataPointer &data_ptr() const { return d; } inline DataPointer &data_ptr() { return d; } #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) explicit inline QByteArray(const DataPointer &dd) : d(dd) {} @@ -499,6 +523,7 @@ private: Q_ASSERT(n <= d.size - pos); } + static QByteArray sliced_helper(QByteArray &a, qsizetype pos, qsizetype n); static QByteArray toLower_helper(const QByteArray &a); static QByteArray toLower_helper(QByteArray &a); static QByteArray toUpper_helper(const QByteArray &a); diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index 7f7c85492b..b5b9af55e2 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -5222,7 +5222,8 @@ QString QString::mid(qsizetype position, qsizetype n) const } /*! - \fn QString QString::first(qsizetype n) const + \fn QString QString::first(qsizetype n) const & + \fn QString QString::first(qsizetype n) && \since 6.0 Returns a string that contains the first \a n characters @@ -5236,7 +5237,8 @@ QString QString::mid(qsizetype position, qsizetype n) const */ /*! - \fn QString QString::last(qsizetype n) const + \fn QString QString::last(qsizetype n) const & + \fn QString QString::last(qsizetype n) && \since 6.0 Returns the string that contains the last \a n characters of this string. @@ -5249,7 +5251,8 @@ QString QString::mid(qsizetype position, qsizetype n) const */ /*! - \fn QString QString::sliced(qsizetype pos, qsizetype n) const + \fn QString QString::sliced(qsizetype pos, qsizetype n) const & + \fn QString QString::sliced(qsizetype pos, qsizetype n) && \since 6.0 Returns a string that contains \a n characters of this string, @@ -5262,9 +5265,18 @@ QString QString::mid(qsizetype position, qsizetype n) const \sa first(), last(), chopped(), chop(), truncate() */ +QString QString::sliced_helper(QString &str, qsizetype pos, qsizetype n) +{ + if (n == 0) + return QString(DataPointer::fromRawData(&_empty, 0)); + DataPointer d = std::move(str.d).sliced(pos, n); + d.data()[n] = 0; + return QString(std::move(d)); +} /*! - \fn QString QString::sliced(qsizetype pos) const + \fn QString QString::sliced(qsizetype pos) const & + \fn QString QString::sliced(qsizetype pos) && \since 6.0 \overload @@ -5277,7 +5289,8 @@ QString QString::mid(qsizetype position, qsizetype n) const */ /*! - \fn QString QString::chopped(qsizetype len) const + \fn QString QString::chopped(qsizetype len) const & + \fn QString QString::chopped(qsizetype len) && \since 5.10 Returns a string that contains the size() - \a len leftmost characters diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index 63432e5c5e..e2f5d99d90 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -335,17 +335,39 @@ public: [[nodiscard]] QString right(qsizetype n) const; [[nodiscard]] QString mid(qsizetype position, qsizetype n = -1) const; - [[nodiscard]] QString first(qsizetype n) const +#if QT_CORE_REMOVED_SINCE(6, 7) + QString first(qsizetype n) const; + QString last(qsizetype n) const; + QString sliced(qsizetype pos) const; + QString sliced(qsizetype pos, qsizetype n) const; + QString chopped(qsizetype n) const; +#else + [[nodiscard]] QString first(qsizetype n) const & { verify(0, n); return sliced(0, n); } - [[nodiscard]] QString last(qsizetype n) const + [[nodiscard]] QString last(qsizetype n) const & { verify(0, n); return sliced(size() - n, n); } - [[nodiscard]] QString sliced(qsizetype pos) const + [[nodiscard]] QString sliced(qsizetype pos) const & { verify(pos, 0); return QString(data() + pos, size() - pos); } - [[nodiscard]] QString sliced(qsizetype pos, qsizetype n) const + [[nodiscard]] QString sliced(qsizetype pos, qsizetype n) const & { verify(pos, n); return QString(begin() + pos, n); } - [[nodiscard]] QString chopped(qsizetype n) const + [[nodiscard]] QString chopped(qsizetype n) const & { verify(0, n); return sliced(0, size() - n); } + [[nodiscard]] QString first(qsizetype n) && + { + verify(0, n); + resize(n); // may detach and allocate memory + return std::move(*this); + } + [[nodiscard]] QString last(qsizetype n) && + { verify(0, n); return sliced_helper(*this, size() - n, n); } + [[nodiscard]] QString sliced(qsizetype pos) && + { verify(pos, 0); return sliced_helper(*this, pos, size() - pos); } + [[nodiscard]] QString sliced(qsizetype pos, qsizetype n) && + { verify(pos, n); return sliced_helper(*this, pos, n); } + [[nodiscard]] QString chopped(qsizetype n) && + { verify(0, n); return std::move(*this).first(size() - n); } +#endif bool startsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; [[nodiscard]] bool startsWith(QStringView s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept { return QtPrivate::startsWith(*this, s, cs); } @@ -951,6 +973,7 @@ private: Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; static int localeAwareCompare_helper(const QChar *data1, qsizetype length1, const QChar *data2, qsizetype length2); + static QString sliced_helper(QString &str, qsizetype pos, qsizetype n); static QString toLower_helper(const QString &str); static QString toLower_helper(QString &str); static QString toUpper_helper(const QString &str); diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 201f3e665d..2fbd6c7505 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -413,6 +413,26 @@ public: size = dst - begin(); } + QArrayDataPointer sliced(qsizetype pos, qsizetype n) const & + { + QArrayDataPointer result(n); + std::uninitialized_copy_n(begin() + pos, n, result.begin()); + result.size = n; + return result; + } + + QArrayDataPointer sliced(qsizetype pos, qsizetype n) && + { + if (needsDetach()) + return sliced(pos, n); + T *newBeginning = begin() + pos; + std::destroy(begin(), newBeginning); + std::destroy(newBeginning + n, end()); + setBegin(newBeginning); + size = n; + return std::move(*this); + } + // forwards from QArrayData qsizetype allocatedCapacity() noexcept { return d ? d->allocatedCapacity() : 0; } qsizetype constAllocatedCapacity() const noexcept { return d ? d->constAllocatedCapacity() : 0; } diff --git a/tests/auto/corelib/text/qstring/tst_qstring.cpp b/tests/auto/corelib/text/qstring/tst_qstring.cpp index 77fb85d80f..49c923eb61 100644 --- a/tests/auto/corelib/text/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/text/qstring/tst_qstring.cpp @@ -44,6 +44,15 @@ using namespace Qt::StringLiterals; namespace { +template String detached(String s) +{ + if (!s.isNull()) { // detaching loses nullness, but we need to preserve it + auto d = s.data(); + Q_UNUSED(d); + } + return s; +} + // this wraps an argument to a QString function, as well as how to apply // the argument to a given QString member function. template @@ -697,6 +706,8 @@ private slots: void rawData(); void clear(); + void first(); + void last(); void sliced(); void chopped(); void removeIf(); @@ -8850,6 +8861,50 @@ void tst_QString::clear() QVERIFY(s.isEmpty()); } +void tst_QString::first() +{ + QString a; + + QVERIFY(a.first(0).isEmpty()); + QVERIFY(!a.isDetached()); + + a = u"ABCDEFGHIEfGEFG"_s; // 15 chars + + // lvalue + QCOMPARE(a.first(5), u"ABCDE"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); + + // rvalue, not detached + QCOMPARE(QString(a).first(5), u"ABCDE"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); + + // rvalue, detached + QCOMPARE(detached(a).first(5), u"ABCDE"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); +} + +void tst_QString::last() +{ + QString a; + + QVERIFY(a.last(0).isEmpty()); + QVERIFY(!a.isDetached()); + + a = u"ABCDEFGHIEfGEFG"_s; // 15 chars + + // lvalue + QCOMPARE(a.last(10), u"FGHIEfGEFG"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); + + // rvalue, not detached + QCOMPARE(QString(a).last(10), u"FGHIEfGEFG"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); + + // rvalue, detached + QCOMPARE(detached(a).last(10), u"FGHIEfGEFG"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); +} + void tst_QString::sliced() { QString a; @@ -8860,8 +8915,20 @@ void tst_QString::sliced() a = u"ABCDEFGHIEfGEFG"_s; // 15 chars + // lvalue QCOMPARE(a.sliced(5), u"FGHIEfGEFG"); QCOMPARE(a.sliced(5, 3), u"FGH"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); + + // rvalue, not detached + QCOMPARE(QString(a).sliced(5), u"FGHIEfGEFG"); + QCOMPARE(QString(a).sliced(5, 3), u"FGH"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); + + // rvalue, detached + QCOMPARE(detached(a).sliced(5), u"FGHIEfGEFG"); + QCOMPARE(detached(a).sliced(5, 3), u"FGH"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); } void tst_QString::chopped() @@ -8873,7 +8940,17 @@ void tst_QString::chopped() a = u"ABCDEFGHIEfGEFG"_s; // 15 chars + // lvalue QCOMPARE(a.chopped(10), u"ABCDE"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); + + // rvalue, not detached + QCOMPARE(QString(a).chopped(10), u"ABCDE"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); + + // rvalue, detached + QCOMPARE(detached(a).chopped(10), u"ABCDE"); + QCOMPARE(a, u"ABCDEFGHIEfGEFG"); } void tst_QString::removeIf()