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;
|
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.
|
||||||
|
@ -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)); }
|
||||||
|
@ -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";
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user