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:
parent
1ee7aa741a
commit
18a2c62c07
@ -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 <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
|
||||
reference to this byte array.
|
||||
|
@ -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 <typename InputIterator>
|
||||
using if_input_iterator = QtPrivate::IfIsInputIterator<InputIterator>;
|
||||
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 <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);
|
||||
inline QByteArray &insert(qsizetype i, const char *s)
|
||||
{ return insert(i, QByteArrayView(s)); }
|
||||
|
@ -12,6 +12,9 @@
|
||||
|
||||
#include "../shared/test_number_shared.h"
|
||||
|
||||
#include <QtCore/q20iterator.h>
|
||||
#include <sstream>
|
||||
|
||||
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<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()
|
||||
{
|
||||
const char data[] = "data";
|
||||
|
@ -339,6 +339,7 @@ private Q_SLOTS:
|
||||
void assign_std_vector() { assign_impl<std::vector<int>>(); };
|
||||
void assign_QVarLengthArray() { assign_impl<QVarLengthArray<int, 4>>(); };
|
||||
void assign_QList() { assign_impl<QList<int>>(); }
|
||||
void assign_QByteArray() { assign_impl<QByteArray>(); }
|
||||
|
||||
private:
|
||||
template <typename Container>
|
||||
|
Loading…
Reference in New Issue
Block a user