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()