Remove '\0' space reservation logic in QString
Changed QString to use implicit element reserved by QArrayData Task-number: QTBUG-84320 Change-Id: If517500b3f0e71bb8d2989c64815a634aa8dd554 Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
parent
04d78df344
commit
ec7e680c50
@ -2232,7 +2232,7 @@ QString::QString(const QChar *unicode, qsizetype size)
|
|||||||
if (!size) {
|
if (!size) {
|
||||||
d = DataPointer::fromRawData(&_empty, 0);
|
d = DataPointer::fromRawData(&_empty, 0);
|
||||||
} else {
|
} else {
|
||||||
d = DataPointer(Data::allocate(size + 1), size);
|
d = DataPointer(Data::allocate(size), size);
|
||||||
memcpy(d.data(), unicode, size * sizeof(QChar));
|
memcpy(d.data(), unicode, size * sizeof(QChar));
|
||||||
d.data()[size] = '\0';
|
d.data()[size] = '\0';
|
||||||
}
|
}
|
||||||
@ -2250,7 +2250,7 @@ QString::QString(qsizetype size, QChar ch)
|
|||||||
if (size <= 0) {
|
if (size <= 0) {
|
||||||
d = DataPointer::fromRawData(&_empty, 0);
|
d = DataPointer::fromRawData(&_empty, 0);
|
||||||
} else {
|
} else {
|
||||||
d = DataPointer(Data::allocate(size + 1), size);
|
d = DataPointer(Data::allocate(size), size);
|
||||||
d.data()[size] = '\0';
|
d.data()[size] = '\0';
|
||||||
char16_t *i = d.data() + size;
|
char16_t *i = d.data() + size;
|
||||||
char16_t *b = d.data();
|
char16_t *b = d.data();
|
||||||
@ -2268,8 +2268,12 @@ QString::QString(qsizetype size, QChar ch)
|
|||||||
*/
|
*/
|
||||||
QString::QString(qsizetype size, Qt::Initialization)
|
QString::QString(qsizetype size, Qt::Initialization)
|
||||||
{
|
{
|
||||||
d = DataPointer(Data::allocate(size + 1), size);
|
if (size <= 0) {
|
||||||
|
d = DataPointer::fromRawData(&_empty, 0);
|
||||||
|
} else {
|
||||||
|
d = DataPointer(Data::allocate(size), size);
|
||||||
d.data()[size] = '\0';
|
d.data()[size] = '\0';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \fn QString::QString(QLatin1String str)
|
/*! \fn QString::QString(QLatin1String str)
|
||||||
@ -2284,7 +2288,7 @@ QString::QString(qsizetype size, Qt::Initialization)
|
|||||||
*/
|
*/
|
||||||
QString::QString(QChar ch)
|
QString::QString(QChar ch)
|
||||||
{
|
{
|
||||||
d = DataPointer(Data::allocate(2), 1);
|
d = DataPointer(Data::allocate(1), 1);
|
||||||
d.data()[0] = ch.unicode();
|
d.data()[0] = ch.unicode();
|
||||||
d.data()[1] = '\0';
|
d.data()[1] = '\0';
|
||||||
}
|
}
|
||||||
@ -2380,7 +2384,7 @@ void QString::resize(qsizetype size)
|
|||||||
|
|
||||||
const auto capacityAtEnd = capacity() - d.freeSpaceAtBegin();
|
const auto capacityAtEnd = capacity() - d.freeSpaceAtBegin();
|
||||||
if (d->needsDetach() || size > capacityAtEnd)
|
if (d->needsDetach() || size > capacityAtEnd)
|
||||||
reallocData(size_t(size) + 1u, d->detachFlags() | Data::GrowsForward);
|
reallocData(size_t(size), d->detachFlags() | Data::GrowsForward);
|
||||||
d.size = size;
|
d.size = size;
|
||||||
if (d->allocatedCapacity())
|
if (d->allocatedCapacity())
|
||||||
d.data()[size] = 0;
|
d.data()[size] = 0;
|
||||||
@ -2460,13 +2464,18 @@ void QString::resize(qsizetype size, QChar fillChar)
|
|||||||
|
|
||||||
void QString::reallocData(size_t alloc, Data::ArrayOptions allocOptions)
|
void QString::reallocData(size_t alloc, Data::ArrayOptions allocOptions)
|
||||||
{
|
{
|
||||||
|
if (!alloc) {
|
||||||
|
d = DataPointer::fromRawData(&_empty, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// there's a case of slow reallocate path where we need to memmove the data
|
// there's a case of slow reallocate path where we need to memmove the data
|
||||||
// before a call to ::realloc(), meaning that there's an extra "heavy"
|
// before a call to ::realloc(), meaning that there's an extra "heavy"
|
||||||
// operation. just prefer ::malloc() branch in this case
|
// operation. just prefer ::malloc() branch in this case
|
||||||
const bool slowReallocatePath = d.freeSpaceAtBegin() > 0;
|
const bool slowReallocatePath = d.freeSpaceAtBegin() > 0;
|
||||||
|
|
||||||
if (d->needsDetach() || slowReallocatePath) {
|
if (d->needsDetach() || slowReallocatePath) {
|
||||||
DataPointer dd(Data::allocate(alloc, allocOptions), qMin(qsizetype(alloc) - 1, d.size));
|
DataPointer dd(Data::allocate(alloc, allocOptions), qMin(qsizetype(alloc), d.size));
|
||||||
if (dd.size > 0)
|
if (dd.size > 0)
|
||||||
::memcpy(dd.data(), d.data(), dd.size * sizeof(QChar));
|
::memcpy(dd.data(), d.data(), dd.size * sizeof(QChar));
|
||||||
dd.data()[dd.size] = 0;
|
dd.data()[dd.size] = 0;
|
||||||
@ -2478,8 +2487,11 @@ void QString::reallocData(size_t alloc, Data::ArrayOptions allocOptions)
|
|||||||
|
|
||||||
void QString::reallocGrowData(size_t alloc, Data::ArrayOptions options)
|
void QString::reallocGrowData(size_t alloc, Data::ArrayOptions options)
|
||||||
{
|
{
|
||||||
|
if (!alloc) // expected to always allocate
|
||||||
|
alloc = 1;
|
||||||
|
|
||||||
if (d->needsDetach()) {
|
if (d->needsDetach()) {
|
||||||
const auto newSize = qMin(qsizetype(alloc) - 1, d.size);
|
const auto newSize = qMin(qsizetype(alloc), d.size);
|
||||||
DataPointer dd(DataPointer::allocateGrow(d, alloc, newSize, options));
|
DataPointer dd(DataPointer::allocateGrow(d, alloc, newSize, options));
|
||||||
dd->copyAppend(d.data(), d.data() + newSize);
|
dd->copyAppend(d.data(), d.data() + newSize);
|
||||||
dd.data()[dd.size] = 0;
|
dd.data()[dd.size] = 0;
|
||||||
@ -2695,7 +2707,7 @@ QString& QString::insert(qsizetype i, const QChar *unicode, qsizetype size)
|
|||||||
|
|
||||||
// ### optimize me
|
// ### optimize me
|
||||||
if (d->needsDetach() || newSize > capacity() || shouldGrow)
|
if (d->needsDetach() || newSize > capacity() || shouldGrow)
|
||||||
reallocGrowData(newSize + 1, flags);
|
reallocGrowData(newSize, flags);
|
||||||
|
|
||||||
if (i > oldSize) // set spaces in the uninitialized gap
|
if (i > oldSize) // set spaces in the uninitialized gap
|
||||||
d->copyAppend(i - oldSize, u' ');
|
d->copyAppend(i - oldSize, u' ');
|
||||||
@ -2729,7 +2741,7 @@ QString& QString::insert(qsizetype i, QChar ch)
|
|||||||
|
|
||||||
// ### optimize me
|
// ### optimize me
|
||||||
if (d->needsDetach() || newSize > capacity() || shouldGrow)
|
if (d->needsDetach() || newSize > capacity() || shouldGrow)
|
||||||
reallocGrowData(newSize + 1, flags);
|
reallocGrowData(newSize, flags);
|
||||||
|
|
||||||
if (i > oldSize) // set spaces in the uninitialized gap
|
if (i > oldSize) // set spaces in the uninitialized gap
|
||||||
d->copyAppend(i - oldSize, u' ');
|
d->copyAppend(i - oldSize, u' ');
|
||||||
@ -2765,7 +2777,7 @@ QString &QString::append(const QString &str)
|
|||||||
} else {
|
} else {
|
||||||
const bool shouldGrow = d->shouldGrowBeforeInsert(d.end(), str.d.size);
|
const bool shouldGrow = d->shouldGrowBeforeInsert(d.end(), str.d.size);
|
||||||
if (d->needsDetach() || size() + str.size() > capacity() || shouldGrow)
|
if (d->needsDetach() || size() + str.size() > capacity() || shouldGrow)
|
||||||
reallocGrowData(uint(size() + str.size()) + 1u,
|
reallocGrowData(uint(size() + str.size()),
|
||||||
d->detachFlags() | Data::GrowsForward);
|
d->detachFlags() | Data::GrowsForward);
|
||||||
d->copyAppend(str.d.data(), str.d.data() + str.d.size);
|
d->copyAppend(str.d.data(), str.d.data() + str.d.size);
|
||||||
d.data()[d.size] = '\0';
|
d.data()[d.size] = '\0';
|
||||||
@ -2785,7 +2797,7 @@ QString &QString::append(const QChar *str, qsizetype len)
|
|||||||
if (str && len > 0) {
|
if (str && len > 0) {
|
||||||
const bool shouldGrow = d->shouldGrowBeforeInsert(d.end(), len);
|
const bool shouldGrow = d->shouldGrowBeforeInsert(d.end(), len);
|
||||||
if (d->needsDetach() || size() + len > capacity() || shouldGrow)
|
if (d->needsDetach() || size() + len > capacity() || shouldGrow)
|
||||||
reallocGrowData(uint(size() + len) + 1u, d->detachFlags() | Data::GrowsForward);
|
reallocGrowData(uint(size() + len), d->detachFlags() | Data::GrowsForward);
|
||||||
static_assert(sizeof(QChar) == sizeof(char16_t), "Unexpected difference in sizes");
|
static_assert(sizeof(QChar) == sizeof(char16_t), "Unexpected difference in sizes");
|
||||||
// the following should be safe as QChar uses char16_t as underlying data
|
// the following should be safe as QChar uses char16_t as underlying data
|
||||||
const char16_t *char16String = reinterpret_cast<const char16_t *>(str);
|
const char16_t *char16String = reinterpret_cast<const char16_t *>(str);
|
||||||
@ -2807,7 +2819,7 @@ QString &QString::append(QLatin1String str)
|
|||||||
qsizetype len = str.size();
|
qsizetype len = str.size();
|
||||||
const bool shouldGrow = d->shouldGrowBeforeInsert(d.end(), len);
|
const bool shouldGrow = d->shouldGrowBeforeInsert(d.end(), len);
|
||||||
if (d->needsDetach() || size() + len > capacity() || shouldGrow)
|
if (d->needsDetach() || size() + len > capacity() || shouldGrow)
|
||||||
reallocGrowData(size_t(size() + len) + 1u, d->detachFlags() | Data::GrowsForward);
|
reallocGrowData(size_t(size() + len), d->detachFlags() | Data::GrowsForward);
|
||||||
|
|
||||||
if (d.freeSpaceAtBegin() == 0) { // fast path
|
if (d.freeSpaceAtBegin() == 0) { // fast path
|
||||||
char16_t *i = d.data() + d.size;
|
char16_t *i = d.data() + d.size;
|
||||||
@ -2860,7 +2872,7 @@ QString &QString::append(QChar ch)
|
|||||||
{
|
{
|
||||||
const bool shouldGrow = d->shouldGrowBeforeInsert(d.end(), 1);
|
const bool shouldGrow = d->shouldGrowBeforeInsert(d.end(), 1);
|
||||||
if (d->needsDetach() || size() + 1 > capacity() || shouldGrow)
|
if (d->needsDetach() || size() + 1 > capacity() || shouldGrow)
|
||||||
reallocGrowData(d.size + 2u, d->detachFlags() | Data::GrowsForward);
|
reallocGrowData(d.size + 1u, d->detachFlags() | Data::GrowsForward);
|
||||||
d->copyAppend(1, ch.unicode());
|
d->copyAppend(1, ch.unicode());
|
||||||
d.data()[d.size] = '\0';
|
d.data()[d.size] = '\0';
|
||||||
return *this;
|
return *this;
|
||||||
@ -2963,8 +2975,9 @@ QString &QString::remove(qsizetype pos, qsizetype len)
|
|||||||
} else if (len > 0) {
|
} else if (len > 0) {
|
||||||
detach();
|
detach();
|
||||||
memmove(d.data() + pos, d.data() + pos + len,
|
memmove(d.data() + pos, d.data() + pos + len,
|
||||||
(d.size - pos - len + 1) * sizeof(QChar));
|
(d.size - pos - len) * sizeof(QChar));
|
||||||
d.size -= len;
|
d.size -= len;
|
||||||
|
d.data()[d.size] = '\0';
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -3998,7 +4011,7 @@ QString &QString::replace(const QRegularExpression &re, const QString &after)
|
|||||||
if (!iterator.hasNext()) // no matches at all
|
if (!iterator.hasNext()) // no matches at all
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
reallocData(size_t(d.size) + 1u, d->detachFlags());
|
reallocData(size_t(d.size), d->detachFlags());
|
||||||
|
|
||||||
qsizetype numCaptures = re.captureCount();
|
qsizetype numCaptures = re.captureCount();
|
||||||
|
|
||||||
@ -5105,10 +5118,9 @@ QString::DataPointer QString::fromLatin1_helper(const char *str, qsizetype size)
|
|||||||
} else {
|
} else {
|
||||||
if (size < 0)
|
if (size < 0)
|
||||||
size = qstrlen(str);
|
size = qstrlen(str);
|
||||||
d = DataPointer(Data::allocate(size + 1), size);
|
d = DataPointer(Data::allocate(size), size);
|
||||||
d.data()[size] = '\0';
|
d.data()[size] = '\0';
|
||||||
char16_t *dst = d.data();
|
char16_t *dst = d.data();
|
||||||
|
|
||||||
qt_from_latin1(dst, str, size_t(size));
|
qt_from_latin1(dst, str, size_t(size));
|
||||||
}
|
}
|
||||||
return d;
|
return d;
|
||||||
@ -6089,7 +6101,7 @@ const ushort *QString::utf16() const
|
|||||||
{
|
{
|
||||||
if (!d->isMutable()) {
|
if (!d->isMutable()) {
|
||||||
// ensure '\0'-termination for ::fromRawData strings
|
// ensure '\0'-termination for ::fromRawData strings
|
||||||
const_cast<QString*>(this)->reallocData(size_t(d.size) + 1u, d->detachFlags());
|
const_cast<QString*>(this)->reallocData(size_t(d.size), d->detachFlags());
|
||||||
}
|
}
|
||||||
return reinterpret_cast<const ushort *>(d.data());
|
return reinterpret_cast<const ushort *>(d.data());
|
||||||
}
|
}
|
||||||
|
@ -1094,15 +1094,14 @@ inline QChar *QString::data()
|
|||||||
inline const QChar *QString::constData() const
|
inline const QChar *QString::constData() const
|
||||||
{ return data(); }
|
{ return data(); }
|
||||||
inline void QString::detach()
|
inline void QString::detach()
|
||||||
{ if (d->needsDetach()) reallocData(d.size + 1u, d->detachFlags()); }
|
{ if (d->needsDetach()) reallocData(d.size, d->detachFlags()); }
|
||||||
inline bool QString::isDetached() const
|
inline bool QString::isDetached() const
|
||||||
{ return !d->isShared(); }
|
{ return !d->isShared(); }
|
||||||
inline void QString::clear()
|
inline void QString::clear()
|
||||||
{ if (!isNull()) *this = QString(); }
|
{ if (!isNull()) *this = QString(); }
|
||||||
inline QString::QString(const QString &other) noexcept : d(other.d)
|
inline QString::QString(const QString &other) noexcept : d(other.d)
|
||||||
{ }
|
{ }
|
||||||
inline qsizetype QString::capacity() const
|
inline qsizetype QString::capacity() const { return qsizetype(d->constAllocatedCapacity()); }
|
||||||
{ const auto realCapacity = d->constAllocatedCapacity(); return realCapacity ? int(realCapacity) - 1 : 0; }
|
|
||||||
inline QString &QString::setNum(short n, int base)
|
inline QString &QString::setNum(short n, int base)
|
||||||
{ return setNum(qlonglong(n), base); }
|
{ return setNum(qlonglong(n), base); }
|
||||||
inline QString &QString::setNum(ushort n, int base)
|
inline QString &QString::setNum(ushort n, int base)
|
||||||
@ -1167,22 +1166,22 @@ inline QString::~QString() {}
|
|||||||
|
|
||||||
inline void QString::reserve(qsizetype asize)
|
inline void QString::reserve(qsizetype asize)
|
||||||
{
|
{
|
||||||
if (d->needsDetach() || asize >= capacity() - d.freeSpaceAtBegin())
|
if (d->needsDetach() || asize >= capacity() - d.freeSpaceAtBegin()) {
|
||||||
reallocData(uint(qMax(asize, size())) + 1u, d->detachFlags());
|
reallocData(size_t(qMax(asize, size())), d->detachFlags() | Data::CapacityReserved);
|
||||||
|
} else {
|
||||||
// we're not shared anymore, for sure
|
|
||||||
d->setFlag(Data::CapacityReserved);
|
d->setFlag(Data::CapacityReserved);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void QString::squeeze()
|
inline void QString::squeeze()
|
||||||
{
|
{
|
||||||
if ((d->flags() & Data::CapacityReserved) == 0)
|
if ((d->flags() & Data::CapacityReserved) == 0)
|
||||||
return;
|
return;
|
||||||
if (d->needsDetach() || int(d.size) < capacity())
|
if (d->needsDetach() || int(d.size) < capacity()) {
|
||||||
reallocData(uint(d.size) + 1u, d->detachFlags());
|
reallocData(size_t(d.size), d->detachFlags() & ~Data::CapacityReserved);
|
||||||
|
} else {
|
||||||
// we're not shared anymore, for sure
|
|
||||||
d->clearFlag(Data::CapacityReserved);
|
d->clearFlag(Data::CapacityReserved);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QString &QString::setUtf16(const ushort *autf16, qsizetype asize)
|
inline QString &QString::setUtf16(const ushort *autf16, qsizetype asize)
|
||||||
|
@ -4822,6 +4822,7 @@ void tst_QString::capacity()
|
|||||||
s2 = s1; // share again
|
s2 = s1; // share again
|
||||||
s2.reserve( res * 2 );
|
s2.reserve( res * 2 );
|
||||||
QVERIFY( (int)s2.capacity() >= res * 2 );
|
QVERIFY( (int)s2.capacity() >= res * 2 );
|
||||||
|
if (res != 0) // can both point to QString::_empty when empty
|
||||||
QVERIFY(s2.constData() != s1.constData());
|
QVERIFY(s2.constData() != s1.constData());
|
||||||
QCOMPARE( s2, s1 );
|
QCOMPARE( s2, s1 );
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user