Move insert() operation into QArrayDataOps

This allows us to unify and simplify the code base between QList,
QString and QByteArray.

Change-Id: Idc8f360d78f508a68f38eb3ef0ed6e5d37f90574
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Lars Knoll 2020-10-30 13:01:56 +01:00
parent b76fbb48fb
commit 9ee50a14b7
4 changed files with 104 additions and 100 deletions

View File

@ -1922,39 +1922,34 @@ QByteArray& QByteArray::append(char ch)
QByteArray &QByteArray::insert(qsizetype i, QByteArrayView data)
{
const char *str = data.data();
qsizetype len = data.size();
if (i < 0 || str == nullptr || len <= 0)
qsizetype size = data.size();
if (i < 0 || size <= 0)
return *this;
if (points_into_range(str, d.data(), d.data() + d.size)) {
QVarLengthArray a(str, str + len);
// handle this specially, as QArrayDataOps::insert() doesn't handle out of
// bounds positions
if (i >= d->size) {
// In case when data points into the range or is == *this, we need to
// defer a call to free() so that it comes after we copied the data from
// the old memory:
DataPointer detached{}; // construction is free
if (d->needsDetach() || i + size - d->size > d.freeSpaceAtEnd()) {
detached = DataPointer::allocateGrow(d, i + size - d->size, Data::AllocateAtEnd);
detached->copyAppend(d.constBegin(), d.constEnd());
d.swap(detached);
}
d->copyAppend(i - d->size, ' ');
d->copyAppend(str, str + size);
d.data()[d.size] = '\0';
return *this;
}
if (!d->needsDetach() && points_into_range(str, d.data(), d.data() + d.size)) {
QVarLengthArray a(str, str + size);
return insert(i, a);
}
const auto oldSize = this->size();
qsizetype sizeToGrow = len;
if (i > oldSize)
sizeToGrow += i - oldSize;
if (d->needsDetach() || (sizeToGrow > d.freeSpaceAtBegin() && sizeToGrow > d.freeSpaceAtEnd())) {
QArrayData::AllocationPosition pos = QArrayData::AllocateAtEnd;
if (oldSize != 0 && i <= (oldSize >> 1))
pos = QArrayData::AllocateAtBeginning;
DataPointer detached(DataPointer::allocateGrow(d, sizeToGrow, pos));
auto where = d.constBegin() + qMin(i, d->size);
detached->copyAppend(d.constBegin(), where);
if (i > oldSize)
detached->copyAppend(i - oldSize, u' ');
detached->copyAppend(str, str + len);
detached->copyAppend(where, d.constEnd());
d.swap(detached);
} else {
if (i > oldSize) // set spaces in the uninitialized gap
d->copyAppend(i - oldSize, ' ');
d->insert(d.begin() + i, str, str + len);
}
d->insert(i, str, size);
d.data()[d.size] = '\0';
return *this;
}
@ -1995,30 +1990,20 @@ QByteArray &QByteArray::insert(qsizetype i, qsizetype count, char ch)
if (i < 0 || count <= 0)
return *this;
const auto oldSize = this->size();
qsizetype sizeToGrow = count;
if (i > oldSize)
sizeToGrow += i - oldSize;
if (d->needsDetach() || (sizeToGrow > d.freeSpaceAtBegin() && sizeToGrow > d.freeSpaceAtEnd())) {
QArrayData::AllocationPosition pos = QArrayData::AllocateAtEnd;
if (oldSize != 0 && i <= (oldSize >> 1))
pos = QArrayData::AllocateAtBeginning;
DataPointer detached(DataPointer::allocateGrow(d, sizeToGrow, pos));
auto where = d.constBegin() + qMin(i, d->size);
detached->copyAppend(d.constBegin(), where);
if (i > oldSize)
detached->copyAppend(i - oldSize, ' ');
detached->copyAppend(count, ch);
detached->copyAppend(where, d.constEnd());
d.swap(detached);
} else {
if (i > oldSize) // set spaces in the uninitialized gap
d->copyAppend(i - oldSize, u' ');
d->insert(d.begin() + i, count, ch);
if (i >= d->size) {
// handle this specially, as QArrayDataOps::insert() doesn't handle out of bounds positions
if (d->needsDetach() || i + count - d->size > d.freeSpaceAtEnd()) {
DataPointer detached(DataPointer::allocateGrow(d, i + count - d->size, Data::AllocateAtEnd));
detached->copyAppend(d.constBegin(), d.constEnd());
d.swap(detached);
}
d->copyAppend(i - d->size, ' ');
d->copyAppend(count, ch);
d.data()[d.size] = '\0';
return *this;
}
d->insert(i, count, ch);
d.data()[d.size] = '\0';
return *this;
}

View File

@ -2728,34 +2728,30 @@ QString& QString::insert(qsizetype i, const QChar *unicode, qsizetype size)
return *this;
const auto s = reinterpret_cast<const char16_t *>(unicode);
if (points_into_range(s, d.data(), d.data() + d.size))
// handle this specially, as QArrayDataOps::insert() doesn't handle out of
// bounds positions
if (i >= d->size) {
// In case when data points into the range or is == *this, we need to
// defer a call to free() so that it comes after we copied the data from
// the old memory:
DataPointer detached{}; // construction is free
if (d->needsDetach() || i + size - d->size > d.freeSpaceAtEnd()) {
detached = DataPointer::allocateGrow(d, i + size - d->size, Data::AllocateAtEnd);
detached->copyAppend(d.constBegin(), d.constEnd());
d.swap(detached);
}
d->copyAppend(i - d->size, u' ');
d->copyAppend(s, s + size);
d.data()[d.size] = u'\0';
return *this;
}
if (!d->needsDetach() && points_into_range(s, d.data(), d.data() + d.size))
return insert(i, QStringView{QVarLengthArray(s, s + size)});
const auto oldSize = this->size();
qsizetype sizeToGrow = size;
if (i > oldSize)
sizeToGrow += i - oldSize;
if (d->needsDetach() || (sizeToGrow > d.freeSpaceAtBegin() && sizeToGrow > d.freeSpaceAtEnd())) {
QArrayData::AllocationPosition pos = QArrayData::AllocateAtEnd;
if (oldSize != 0 && i <= (oldSize >> 1))
pos = QArrayData::AllocateAtBeginning;
DataPointer detached(DataPointer::allocateGrow(d, sizeToGrow, pos));
auto where = d.constBegin() + qMin(i, d->size);
detached->copyAppend(d.constBegin(), where);
if (i > oldSize)
detached->copyAppend(i - oldSize, u' ');
detached->copyAppend(s, s + size);
detached->copyAppend(where, d.constEnd());
d.swap(detached);
} else {
if (i > oldSize) // set spaces in the uninitialized gap
d->copyAppend(i - oldSize, u' ');
d->insert(d.begin() + i, s, s + size);
}
d.data()[d.size] = '\0';
d->insert(i, s, size);
d.data()[d.size] = u'\0';
return *this;
}

View File

@ -1024,6 +1024,7 @@ struct QCommonArrayOps : QArrayOpsSelector<T>::Type
{
using Base = typename QArrayOpsSelector<T>::Type;
using Data = QTypedArrayData<T>;
using DataPointer = QArrayDataPointer<T>;
using parameter_type = typename Base::parameter_type;
using iterator = typename Base::iterator;
using const_iterator = typename Base::const_iterator;
@ -1216,6 +1217,50 @@ public:
Base::insert(GrowsForwardTag{}, where, qsizetype(n) - beginSize, t);
}
void insert(qsizetype i, qsizetype n, parameter_type t)
{
if (this->needsDetach() || (n > this->freeSpaceAtBegin() && n > this->freeSpaceAtEnd())) {
typename Data::AllocationPosition pos = Data::AllocateAtEnd;
if (this->size != 0 && i <= (this->size >> 1))
pos = Data::AllocateAtBeginning;
DataPointer detached(DataPointer::allocateGrow(*this, n, pos));
const_iterator where = this->constBegin() + i;
detached->copyAppend(this->constBegin(), where);
detached->copyAppend(n, t);
detached->copyAppend(where, this->constEnd());
this->swap(detached);
} else {
// we're detached and we can just move data around
if (i == this->size && n <= this->freeSpaceAtEnd()) {
copyAppend(n, t);
} else {
T copy(t);
insert(this->begin() + i, n, copy);
}
}
}
void insert(qsizetype i, const T *data, qsizetype n)
{
if (this->needsDetach() || (n > this->freeSpaceAtBegin() && n > this->freeSpaceAtEnd())) {
typename Data::AllocationPosition pos = Data::AllocateAtEnd;
if (this->size != 0 && i <= (this->size >> 1))
pos = Data::AllocateAtBeginning;
DataPointer detached(DataPointer::allocateGrow(*this, n, pos));
auto where = this->constBegin() + i;
detached->copyAppend(this->constBegin(), where);
detached->copyAppend(data, data + n);
detached->copyAppend(where, this->constEnd());
this->swap(detached);
} else {
insert(this->begin() + i, data, data + n);
}
}
template <typename iterator, typename ...Args>
void emplace(iterator where, Args&&... args)
{

View File

@ -732,29 +732,7 @@ QList<T>::insert(qsizetype i, qsizetype n, parameter_type t)
{
Q_ASSERT_X(size_t(i) <= size_t(d->size), "QList<T>::insert", "index out of range");
// we don't have a quick exit for n == 0
// it's not worth wasting CPU cycles for that
if (d->needsDetach() || (n > d.freeSpaceAtBegin() && n > d.freeSpaceAtEnd())) {
typename QArrayData::AllocationPosition pos = QArrayData::AllocateAtEnd;
if (d.size != 0 && i <= (d.size >> 1))
pos = QArrayData::AllocateAtBeginning;
DataPointer detached(DataPointer::allocateGrow(d, n, pos));
const_iterator where = constBegin() + i;
detached->copyAppend(constBegin(), where);
detached->copyAppend(n, t);
detached->copyAppend(where, constEnd());
d.swap(detached);
} else {
// we're detached and we can just move data around
if (i == size() && n <= d.freeSpaceAtEnd()) {
d->copyAppend(n, t);
} else {
T copy(t);
d->insert(d.begin() + i, n, copy);
}
}
d->insert(i, n, t);
return d.begin() + i;
}