QString: don't detach in remove(pos, len)

- If this string isn't shared, don't call detach, instead use ->erase() as
  needed
- If this string is shared, create a new string, and copy all elements
  except the ones that would be removed, see task for details

Update unittest to test both code paths.

Task-number: QTBUG-106181
Change-Id: I4c73ff17a6fa89ddcf6966f9c5bf789753f6d39e
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Ahmad Samir 2022-10-24 23:58:14 +02:00
parent dcfab7e28e
commit f41089ba3d
3 changed files with 43 additions and 9 deletions

View File

@ -3294,9 +3294,22 @@ QString &QString::remove(qsizetype pos, qsizetype len)
return *this;
len = std::min(len, size() - pos);
detach();
d->erase(d.begin() + pos, len);
d.data()[d.size] = u'\0';
if (!d->isShared()) {
d->erase(d.begin() + pos, len);
d.data()[d.size] = u'\0';
} else {
// TODO: either reserve "size()", which is bigger than needed, or
// modify the shrinking-erase docs of this method (since the size
// of "copy" won't have any extra capacity any more)
const qsizetype sz = size() - len;
QString copy{sz, Qt::Uninitialized};
auto begin = d.begin();
auto toRemove_start = d.begin() + pos;
copy.d->copyRanges({{begin, toRemove_start},
{toRemove_start + len, d.end()}});
swap(copy);
}
return *this;
}

View File

@ -213,6 +213,17 @@ public:
--this->size;
}
struct Span { T *begin; T *end; };
void copyRanges(const std::initializer_list<Span> &ranges)
{
auto it = this->begin();
std::for_each(ranges.begin(), ranges.end(), [&it](const auto &span) {
it = std::copy(span.begin, span.end, it);
});
this->size = std::distance(this->begin(), it);
}
void assign(T *b, T *e, parameter_type t) noexcept
{
Q_ASSERT(b <= e);

View File

@ -3318,13 +3318,23 @@ void tst_QString::remove_uint_uint()
QFETCH( int, index );
QFETCH( int, len );
QFETCH( QString, after );
QFETCH(QString, result);
if ( after.size() == 0 ) {
QString s1 = string;
s1.remove( (uint) index, (uint) len );
QTEST( s1, "result" );
} else
QCOMPARE( 0, 0 ); // shut Qt Test
// For the replace() unitests?
if ( after.size() != 0 ) {
return;
}
// Test when isShared() is true
QString s1 = string;
s1.remove((qsizetype)index, (qsizetype)len);
QCOMPARE(s1, result);
QString s2 = string;
// Test when isShared() is false
s2.detach();
s2.remove((qsizetype)index, (qsizetype)len);
QCOMPARE(s2, result);
}
void tst_QString::remove_string()