QByteArray: add STL-style assign()

Implemented assign() methods for QByteArray to align with the
criteria of std::basic_string, addressing the previously missing
functionality. This is a subset of the overloads provided by the
standard.

Reference:
https://en.cppreference.com/w/cpp/string/basic_string/assign

[ChangeLog][QtCore][QByteArray] Added assign().

Fixes: QTBUG-106199
Change-Id: I899b14d74e8f774face8690303efb8610ead95b5
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Dennis Oberst 2023-05-17 10:31:38 +02:00 committed by Marc Mutz
parent 1ee7aa741a
commit 18a2c62c07
4 changed files with 279 additions and 0 deletions

View File

@ -2104,6 +2104,73 @@ QByteArray& QByteArray::append(char ch)
return *this; 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 <typename InputIterator, if_input_iterator<InputIterator>> 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 Inserts \a data at index position \a i and returns a
reference to this byte array. reference to this byte array.

View File

@ -40,6 +40,8 @@ namespace emscripten {
} }
#endif #endif
class tst_QByteArray;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QString; class QString;
@ -60,6 +62,11 @@ private:
DataPointer d; DataPointer d;
static const char _empty; static const char _empty;
friend class ::tst_QByteArray;
template <typename InputIterator>
using if_input_iterator = QtPrivate::IfIsInputIterator<InputIterator>;
public: public:
enum Base64Option { enum Base64Option {
@ -227,6 +234,20 @@ public:
QByteArray &append(QByteArrayView a) QByteArray &append(QByteArrayView a)
{ return insert(size(), a); } { return insert(size(), a); }
QByteArray &assign(QByteArrayView v);
QByteArray &assign(qsizetype n, char c)
{
Q_ASSERT(n >= 0);
return fill(c, n);
}
template <typename InputIterator, if_input_iterator<InputIterator> = true>
QByteArray &assign(InputIterator first, InputIterator last)
{
d.assign(first, last);
d.data()[d.size] = '\0';
return *this;
}
QByteArray &insert(qsizetype i, QByteArrayView data); QByteArray &insert(qsizetype i, QByteArrayView data);
inline QByteArray &insert(qsizetype i, const char *s) inline QByteArray &insert(qsizetype i, const char *s)
{ return insert(i, QByteArrayView(s)); } { return insert(i, QByteArrayView(s)); }

View File

@ -12,6 +12,9 @@
#include "../shared/test_number_shared.h" #include "../shared/test_number_shared.h"
#include <QtCore/q20iterator.h>
#include <sstream>
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
class tst_QByteArray : public QObject class tst_QByteArray : public QObject
@ -50,6 +53,9 @@ private slots:
void append(); void append();
void appendExtended_data(); void appendExtended_data();
void appendExtended(); void appendExtended();
void assign();
void assignShared();
void assignUsesPrependBuffer();
void insert(); void insert();
void insertExtended_data(); void insertExtended_data();
void insertExtended(); void insertExtended();
@ -930,6 +936,190 @@ void tst_QByteArray::appendExtended()
QCOMPARE(array.size(), 11); 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<char> 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<char>{ss}, std::istream_iterator<char>{});
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<char>{ss}, std::istream_iterator<char>{});
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<char>{ss}, std::istream_iterator<char>{});
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() void tst_QByteArray::insert()
{ {
const char data[] = "data"; const char data[] = "data";

View File

@ -339,6 +339,7 @@ private Q_SLOTS:
void assign_std_vector() { assign_impl<std::vector<int>>(); }; void assign_std_vector() { assign_impl<std::vector<int>>(); };
void assign_QVarLengthArray() { assign_impl<QVarLengthArray<int, 4>>(); }; void assign_QVarLengthArray() { assign_impl<QVarLengthArray<int, 4>>(); };
void assign_QList() { assign_impl<QList<int>>(); } void assign_QList() { assign_impl<QList<int>>(); }
void assign_QByteArray() { assign_impl<QByteArray>(); }
private: private:
template <typename Container> template <typename Container>