diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp index 4e6cce49c0..267d8a3371 100644 --- a/src/corelib/text/qbytearray.cpp +++ b/src/corelib/text/qbytearray.cpp @@ -2104,6 +2104,73 @@ QByteArray& QByteArray::append(char ch) return *this; } +/*! + \fn QByteArray &QByteArray::assign(QByteArrayView v) + \since 6.6 + + Replaces the contents of this byte array with a copy of \a v and returns a + reference to this byte array. + + The size of this byte array will be equal to the size of \a v. + + This function only allocates memory if the size of \a v exceeds the capacity + of this byte array or this byte array is shared. +*/ + +/*! + \fn QByteArray &QByteArray::assign(qsizetype n, char c) + \since 6.6 + + Replaces the contents of this byte array with \a n copies of \a c and + returns a reference to this byte array. + + The size of this byte array will be equal to \a n, which has to be non-negative. + + This function will only allocate memory if \a n exceeds the capacity of this + byte array or this byte array is shared. + + \sa fill() +*/ + +/*! + \fn template > QByteArray &QByteArray::assign(InputIterator first, InputIterator last) + \since 6.6 + + Replaces the contents of this byte array with a copy of the elements in the + iterator range [\a first, \a last) and returns a reference to this + byte array. + + The size of this byte array will be equal to the number of elements in the + range [\a first, \a last). + + This function will only allocate memory if the number of elements in the + range exceeds the capacity of this byte array or this byte array is shared. + + \note This function overload only participates in overload resolution if + \c InputIterator meets the requirements of a + \l {https://en.cppreference.com/w/cpp/named_req/InputIterator} {LegacyInputIterator}. + + \note The behavior is undefined if either argument is an iterator into *this or + [\a first, \a last) is not a valid range. +*/ + +QByteArray &QByteArray::assign(QByteArrayView v) +{ + const auto len = v.size(); + + if (len <= capacity() && isDetached()) { + const auto offset = d.freeSpaceAtBegin(); + if (offset) + d.setBegin(d.begin() - offset); + std::memcpy(d.begin(), v.data(), len); + d.size = len; + d.data()[d.size] = '\0'; + } else { + *this = v.toByteArray(); + } + return *this; +} + /*! Inserts \a data at index position \a i and returns a reference to this byte array. diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h index 2c15545ecd..972e2174d3 100644 --- a/src/corelib/text/qbytearray.h +++ b/src/corelib/text/qbytearray.h @@ -40,6 +40,8 @@ namespace emscripten { } #endif +class tst_QByteArray; + QT_BEGIN_NAMESPACE class QString; @@ -60,6 +62,11 @@ private: DataPointer d; static const char _empty; + + friend class ::tst_QByteArray; + + template + using if_input_iterator = QtPrivate::IfIsInputIterator; public: enum Base64Option { @@ -227,6 +234,20 @@ public: QByteArray &append(QByteArrayView a) { return insert(size(), a); } + QByteArray &assign(QByteArrayView v); + QByteArray &assign(qsizetype n, char c) + { + Q_ASSERT(n >= 0); + return fill(c, n); + } + template = true> + QByteArray &assign(InputIterator first, InputIterator last) + { + d.assign(first, last); + d.data()[d.size] = '\0'; + return *this; + } + QByteArray &insert(qsizetype i, QByteArrayView data); inline QByteArray &insert(qsizetype i, const char *s) { return insert(i, QByteArrayView(s)); } diff --git a/tests/auto/corelib/text/qbytearray/tst_qbytearray.cpp b/tests/auto/corelib/text/qbytearray/tst_qbytearray.cpp index 97fe367528..617737d9ed 100644 --- a/tests/auto/corelib/text/qbytearray/tst_qbytearray.cpp +++ b/tests/auto/corelib/text/qbytearray/tst_qbytearray.cpp @@ -12,6 +12,9 @@ #include "../shared/test_number_shared.h" +#include +#include + using namespace Qt::StringLiterals; class tst_QByteArray : public QObject @@ -50,6 +53,9 @@ private slots: void append(); void appendExtended_data(); void appendExtended(); + void assign(); + void assignShared(); + void assignUsesPrependBuffer(); void insert(); void insertExtended_data(); void insertExtended(); @@ -930,6 +936,190 @@ void tst_QByteArray::appendExtended() QCOMPARE(array.size(), 11); } +void tst_QByteArray::assign() +{ + // QByteArray &assign(QByteArrayView) + { + QByteArray ba; + QByteArray test("data"); + QCOMPARE(ba.assign(test), test); + QCOMPARE(ba.size(), test.size()); + test = "data\0data"; + QCOMPARE(ba.assign(test), test); + QCOMPARE(ba.size(), test.size()); + test = "data\0data"_ba; + QCOMPARE(ba.assign(test), test); + QCOMPARE(ba.size(), test.size()); + } + // QByteArray &assign(qsizetype, char); + { + QByteArray ba; + QByteArray test("ddd"); + QCOMPARE(ba.assign(3, 'd'), test); + QCOMPARE(ba.size(), test.size()); + test = "xx"; + QCOMPARE(ba.assign(20, 'd').assign(2, 'x'), test); + QCOMPARE(ba.size(), test.size()); + test = "ddddd"; + QCOMPARE(ba.assign(0, 'x').assign(5, 'd'), test); + QCOMPARE(ba.size(), test.size()); + test = "\0\0\0"_ba; + QCOMPARE(ba.assign(0, 'x').assign(3, '\0'), test); + QCOMPARE(ba.size(), test.size()); + } + // QByteArray &assign(InputIterator, InputIterator) + { + QByteArray ba; + QByteArrayView test; + + QList l = {'\0', 'T', 'E', 'S', 'T'}; + ba.assign(l.begin(), l.end()); + test = "\0TEST"_ba; + QCOMPARE(ba, test); + QCOMPARE(ba.size(), test.size()); + + const std::byte bytes[] = {std::byte('T'), std::byte(0), std::byte('S'), std::byte('T')}; + test = QByteArrayView::fromArray(bytes); + QCOMPARE(ba.assign(test.begin(), test.end()), test); + QCOMPARE(ba.size(), test.size()); + + std::stringstream ss; + ss << "T " << '\0' << ' ' << "S " << "T "; + ba.assign(std::istream_iterator{ss}, std::istream_iterator{}); + test = "T\0ST"_ba; + QCOMPARE(ba, test); + QCOMPARE(ba.size(), test.size()); + } + // Test chaining + { + QByteArray ba; + QByteArray test("TTTTT"); + char arr[] = {'T', 'E', 'S', 'T'}; + ba.assign(std::begin(arr), std::end(arr)).assign({"Hello World!"}).assign(5, 'T'); + QCOMPARE(ba, test); + QCOMPARE(ba.size(), test.size()); + test = "DATA"; + QCOMPARE(ba.assign(300, 'T').assign({"DATA"}), test); + QCOMPARE(ba.size(), test.size()); + test = QByteArray(arr, q20::ssize(arr)); + QCOMPARE(ba.assign(10, 'c').assign(std::begin(arr), std::end(arr)), test); + QCOMPARE(ba.size(), test.size()); + test = "TTT"; + QCOMPARE(ba.assign("data").assign(QByteArrayView::fromArray( + {std::byte('T'), std::byte('T'), std::byte('T')})), test); + QCOMPARE(ba.size(), test.size()); + test = "\0data"; + QCOMPARE(ba.assign("data").assign("\0data"), test); + QCOMPARE(ba.size(), test.size()); + } +} + +void tst_QByteArray::assignShared() +{ + { + QByteArray ba; + ba.assign({"DATA"}); + QVERIFY(ba.isDetached()); + QCOMPARE(ba, QByteArray("DATA")); + + auto baCopy = ba; + QVERIFY(!ba.isDetached()); + QVERIFY(!baCopy.isDetached()); + QVERIFY(ba.isSharedWith(baCopy)); + QVERIFY(baCopy.isSharedWith(ba)); + + ba.assign(10, 'D'); + QVERIFY(ba.isDetached()); + QVERIFY(baCopy.isDetached()); + QVERIFY(!ba.isSharedWith(baCopy)); + QVERIFY(!baCopy.isSharedWith(ba)); + QCOMPARE(ba, QByteArray("DDDDDDDDDD")); + QCOMPARE(baCopy, QByteArray("DATA")); + } + { + QByteArray ba("START"); + QByteArrayView bav("DATA"); + QVERIFY(ba.isDetached()); + QCOMPARE(ba, QByteArray("START")); + + auto copyForwardIt = ba; + QVERIFY(!ba.isDetached()); + QVERIFY(!copyForwardIt.isDetached()); + QVERIFY(ba.isSharedWith(copyForwardIt)); + QVERIFY(copyForwardIt.isSharedWith(ba)); + + ba.assign(bav.begin(), bav.end()); + QVERIFY(ba.isDetached()); + QVERIFY(copyForwardIt.isDetached()); + QVERIFY(!ba.isSharedWith(copyForwardIt)); + QVERIFY(!copyForwardIt.isSharedWith(ba)); + QCOMPARE(ba, QByteArray("DATA")); + QCOMPARE(copyForwardIt, QByteArray("START")); + + auto copyInputIt = ba; + QVERIFY(!ba.isDetached()); + QVERIFY(!copyInputIt.isDetached()); + QVERIFY(ba.isSharedWith(copyInputIt)); + QVERIFY(copyInputIt.isSharedWith(ba)); + + std::stringstream ss("1 2 3 4 5 6 "); + ba.assign(std::istream_iterator{ss}, std::istream_iterator{}); + QVERIFY(ba.isDetached()); + QVERIFY(copyInputIt.isDetached()); + QVERIFY(!ba.isSharedWith(copyInputIt)); + QVERIFY(!copyInputIt.isSharedWith(ba)); + QCOMPARE(ba, QByteArray("123456")); + QCOMPARE(copyInputIt, QByteArray("DATA")); + } +} + +void tst_QByteArray::assignUsesPrependBuffer() +{ + const auto capBegin = [](const QByteArray &ba) { + return ba.begin() - ba.d.freeSpaceAtBegin(); + }; + const auto capEnd = [](const QByteArray &ba) { + return ba.end() + ba.d.freeSpaceAtEnd(); + }; + // QByteArray &assign(QByteArrayView) + { + QByteArray withFreeSpaceAtBegin; + for (int i = 0; i < 100 && withFreeSpaceAtBegin.d.freeSpaceAtBegin() < 2; ++i) + withFreeSpaceAtBegin.prepend("data"); + QCOMPARE_GT(withFreeSpaceAtBegin.d.freeSpaceAtBegin(), 1); + + const auto oldCapBegin = capBegin(withFreeSpaceAtBegin); + const auto oldCapEnd = capEnd(withFreeSpaceAtBegin); + + std::string test(withFreeSpaceAtBegin.d.freeSpaceAtBegin(), 'd'); + withFreeSpaceAtBegin.assign(test); + + QCOMPARE_EQ(withFreeSpaceAtBegin.d.freeSpaceAtBegin(), 0); // we used the prepend buffer + QCOMPARE_EQ(capBegin(withFreeSpaceAtBegin), oldCapBegin); + QCOMPARE_EQ(capEnd(withFreeSpaceAtBegin), oldCapEnd); + QCOMPARE(withFreeSpaceAtBegin, test.data()); + } + // QByteArray &assign(InputIterator, InputIterator) + { + QByteArray withFreeSpaceAtBegin; + for (int i = 0; i < 100 && withFreeSpaceAtBegin.d.freeSpaceAtBegin() < 2; ++i) + withFreeSpaceAtBegin.prepend("data"); + QCOMPARE_GT(withFreeSpaceAtBegin.d.freeSpaceAtBegin(), 1); + + const auto oldCapBegin = capBegin(withFreeSpaceAtBegin); + const auto oldCapEnd = capEnd(withFreeSpaceAtBegin); + + std::stringstream ss; + for (qsizetype i = 0; i < withFreeSpaceAtBegin.d.freeSpaceAtBegin(); ++i) + ss << "d "; + + withFreeSpaceAtBegin.assign(std::istream_iterator{ss}, std::istream_iterator{}); + QCOMPARE_EQ(withFreeSpaceAtBegin.d.freeSpaceAtBegin(), 0); // we used the prepend buffer + QCOMPARE_EQ(capBegin(withFreeSpaceAtBegin), oldCapBegin); + QCOMPARE_EQ(capEnd(withFreeSpaceAtBegin), oldCapEnd); + } +} + void tst_QByteArray::insert() { const char data[] = "data"; diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp index c0fab0cc91..023a03f4a4 100644 --- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp +++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp @@ -339,6 +339,7 @@ private Q_SLOTS: void assign_std_vector() { assign_impl>(); }; void assign_QVarLengthArray() { assign_impl>(); }; void assign_QList() { assign_impl>(); } + void assign_QByteArray() { assign_impl(); } private: template