Introduce the Mutable flag and move QArrayDataPointer::needsDetach
The Mutable flag now contains the information on whether the data this QArrayData points to is mutable. This decouples the mutability / immutability setting from the allocation and from the type of data, opening the way for mutable raw or foreign data. There are still plenty of places in the source code that check the size of the allocation when it actually wants d->isMutable(). Fixing this will require reviewing all the code, so is left for later. The needsDetach() function is moved to QArrayData and de-constified. It returns true when a reallocation is necessary if the data is to be modified. Change-Id: I17e2bc5a3f6ef1f3eba8a205acd9852b95524f57 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>bb10
parent
b6aa133cf5
commit
a3aa2fcfa7
|
|
@ -63,7 +63,7 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define IS_RAW_DATA(d) (!(d)->isMutable())
|
||||
#define IS_RAW_DATA(d) ((d)->flags & QArrayData::RawDataType)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -1208,7 +1208,7 @@ QByteArray &QByteArray::operator=(const char *str)
|
|||
} else {
|
||||
const int len = int(strlen(str));
|
||||
const int fullLen = len + 1;
|
||||
if (d->ref.isShared() || fullLen > int(d->allocatedCapacity())
|
||||
if (d->needsDetach() || fullLen > int(d->allocatedCapacity())
|
||||
|| (len < d->size && fullLen < int(d->allocatedCapacity() >> 1)))
|
||||
reallocData(fullLen, d->detachFlags());
|
||||
x = d;
|
||||
|
|
@ -1755,7 +1755,7 @@ void QByteArray::resize(int size)
|
|||
if (size < 0)
|
||||
size = 0;
|
||||
|
||||
if (IS_RAW_DATA(d) && !d->ref.isShared() && size < d->size) {
|
||||
if (!d->ref.isShared() && !d->isMutable() && size < d->size) {
|
||||
d->size = size;
|
||||
return;
|
||||
}
|
||||
|
|
@ -1775,9 +1775,9 @@ void QByteArray::resize(int size)
|
|||
x->data()[size] = '\0';
|
||||
d = x;
|
||||
} else {
|
||||
if (d->ref.isShared() || size > capacity())
|
||||
if (d->needsDetach() || size > capacity())
|
||||
reallocData(uint(size) + 1u, d->detachFlags() | Data::GrowsForward);
|
||||
if (d->allocatedCapacity()) {
|
||||
if (d->isMutable()) {
|
||||
d->size = size;
|
||||
d->data()[size] = '\0';
|
||||
}
|
||||
|
|
@ -1805,7 +1805,7 @@ QByteArray &QByteArray::fill(char ch, int size)
|
|||
|
||||
void QByteArray::reallocData(uint alloc, Data::ArrayOptions options)
|
||||
{
|
||||
if (d->ref.isShared() || IS_RAW_DATA(d)) {
|
||||
if (d->needsDetach()) {
|
||||
Data *x = Data::allocate(alloc, options);
|
||||
Q_CHECK_PTR(x);
|
||||
x->size = qMin(int(alloc) - 1, d->size);
|
||||
|
|
@ -1900,7 +1900,7 @@ QByteArray &QByteArray::prepend(const char *str)
|
|||
QByteArray &QByteArray::prepend(const char *str, int len)
|
||||
{
|
||||
if (str) {
|
||||
if (d->ref.isShared() || d->size + len > capacity())
|
||||
if (d->needsDetach() || d->size + len > capacity())
|
||||
reallocData(uint(d->size + len) + 1u, d->detachFlags() | Data::GrowsForward);
|
||||
memmove(d->data()+len, d->data(), d->size);
|
||||
memcpy(d->data(), str, len);
|
||||
|
|
@ -1926,7 +1926,7 @@ QByteArray &QByteArray::prepend(const char *str, int len)
|
|||
|
||||
QByteArray &QByteArray::prepend(char ch)
|
||||
{
|
||||
if (d->ref.isShared() || d->size + 1 > capacity())
|
||||
if (d->needsDetach() || d->size + 1 > capacity())
|
||||
reallocData(uint(d->size) + 2u, d->detachFlags() | Data::GrowsForward);
|
||||
memmove(d->data()+1, d->data(), d->size);
|
||||
d->data()[0] = ch;
|
||||
|
|
@ -1964,7 +1964,7 @@ QByteArray &QByteArray::append(const QByteArray &ba)
|
|||
if (d->size == 0 && d->ref.isStatic() && !IS_RAW_DATA(ba.d)) {
|
||||
*this = ba;
|
||||
} else if (ba.d->size != 0) {
|
||||
if (d->ref.isShared() || d->size + ba.d->size > capacity())
|
||||
if (d->needsDetach() || d->size + ba.d->size > capacity())
|
||||
reallocData(uint(d->size + ba.d->size) + 1u, d->detachFlags() | Data::GrowsForward);
|
||||
memcpy(d->data() + d->size, ba.d->data(), ba.d->size);
|
||||
d->size += ba.d->size;
|
||||
|
|
@ -1996,7 +1996,7 @@ QByteArray& QByteArray::append(const char *str)
|
|||
{
|
||||
if (str) {
|
||||
const int len = int(strlen(str));
|
||||
if (d->ref.isShared() || d->size + len > capacity())
|
||||
if (d->needsDetach() || d->size + len > capacity())
|
||||
reallocData(uint(d->size + len) + 1u, d->detachFlags() | Data::GrowsForward);
|
||||
memcpy(d->data() + d->size, str, len + 1); // include null terminator
|
||||
d->size += len;
|
||||
|
|
@ -2021,7 +2021,7 @@ QByteArray &QByteArray::append(const char *str, int len)
|
|||
if (len < 0)
|
||||
len = qstrlen(str);
|
||||
if (str && len) {
|
||||
if (d->ref.isShared() || d->size + len > capacity())
|
||||
if (d->needsDetach() || d->size + len > capacity())
|
||||
reallocData(uint(d->size + len) + 1u, d->detachFlags() | Data::GrowsForward);
|
||||
memcpy(d->data() + d->size, str, len); // include null terminator
|
||||
d->size += len;
|
||||
|
|
@ -2049,7 +2049,7 @@ QByteArray &QByteArray::append(const char *str, int len)
|
|||
|
||||
QByteArray& QByteArray::append(char ch)
|
||||
{
|
||||
if (d->ref.isShared() || d->size + 1 > capacity())
|
||||
if (d->needsDetach() || d->size + 1 > capacity())
|
||||
reallocData(uint(d->size) + 2u, d->detachFlags() | Data::GrowsForward);
|
||||
d->data()[d->size++] = ch;
|
||||
d->data()[d->size] = '\0';
|
||||
|
|
|
|||
|
|
@ -495,7 +495,7 @@ inline const char *QByteArray::data() const
|
|||
inline const char *QByteArray::constData() const
|
||||
{ return d->data(); }
|
||||
inline void QByteArray::detach()
|
||||
{ if (d->ref.isShared() || !d->isMutable()) reallocData(uint(d->size) + 1u, d->detachFlags()); }
|
||||
{ if (d->needsDetach()) reallocData(uint(d->size) + 1u, d->detachFlags()); }
|
||||
inline bool QByteArray::isDetached() const
|
||||
{ return !d->ref.isShared(); }
|
||||
inline QByteArray::QByteArray(const QByteArray &a) noexcept : d(a.d)
|
||||
|
|
@ -506,7 +506,7 @@ inline int QByteArray::capacity() const
|
|||
|
||||
inline void QByteArray::reserve(int asize)
|
||||
{
|
||||
if (d->ref.isShared() || asize > capacity()) {
|
||||
if (d->needsDetach() || asize > capacity()) {
|
||||
reallocData(qMax(uint(size()), uint(asize)) + 1u, d->detachFlags() | Data::CapacityReserved);
|
||||
} else {
|
||||
d->flags |= Data::CapacityReserved;
|
||||
|
|
@ -515,7 +515,9 @@ inline void QByteArray::reserve(int asize)
|
|||
|
||||
inline void QByteArray::squeeze()
|
||||
{
|
||||
if (d->ref.isShared() || d->size < capacity()) {
|
||||
if ((d->flags & Data::CapacityReserved) == 0)
|
||||
return;
|
||||
if (d->needsDetach() || d->size < capacity()) {
|
||||
reallocData(uint(d->size) + 1u, d->detachFlags() & ~Data::CapacityReserved);
|
||||
} else {
|
||||
d->flags &= ~Data::CapacityReserved;
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@
|
|||
#define ULLONG_MAX quint64_C(18446744073709551615)
|
||||
#endif
|
||||
|
||||
#define IS_RAW_DATA(d) (!(d)->isMutable())
|
||||
#define IS_RAW_DATA(d) ((d)->flags & QArrayData::RawDataType)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -2265,14 +2265,14 @@ void QString::resize(int size)
|
|||
if (size < 0)
|
||||
size = 0;
|
||||
|
||||
if (IS_RAW_DATA(d) && !d->ref.isShared() && size < d->size) {
|
||||
if (!d->ref.isShared() && !d->isMutable() && size < d->size) {
|
||||
d->size = size;
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->ref.isShared() || uint(size) + 1u > d->allocatedCapacity())
|
||||
if (d->needsDetach() || size > capacity())
|
||||
reallocData(uint(size) + 1u, true);
|
||||
if (d->flags & Data::AllocatedDataType) {
|
||||
if (d->isMutable()) {
|
||||
d->size = size;
|
||||
d->data()[size] = '\0';
|
||||
}
|
||||
|
|
@ -2353,7 +2353,7 @@ void QString::reallocData(uint alloc, bool grow)
|
|||
if (grow)
|
||||
allocOptions |= QArrayData::GrowsForward;
|
||||
|
||||
if (d->ref.isShared() || IS_RAW_DATA(d)) {
|
||||
if (d->needsDetach()) {
|
||||
Data *x = Data::allocate(alloc, allocOptions);
|
||||
Q_CHECK_PTR(x);
|
||||
x->size = qMin(int(alloc) - 1, d->size);
|
||||
|
|
@ -2658,7 +2658,7 @@ QString &QString::append(const QString &str)
|
|||
if (d == Data::sharedNull()) {
|
||||
operator=(str);
|
||||
} else {
|
||||
if (d->ref.isShared() || d->size + str.d->size > capacity())
|
||||
if (d->needsDetach() || d->size + str.d->size > capacity())
|
||||
reallocData(uint(d->size + str.d->size) + 1u, true);
|
||||
memcpy(d->data() + d->size, str.d->data(), str.d->size * sizeof(QChar));
|
||||
d->size += str.d->size;
|
||||
|
|
@ -2696,7 +2696,7 @@ QString &QString::append(QLatin1String str)
|
|||
const char *s = str.latin1();
|
||||
if (s) {
|
||||
int len = str.size();
|
||||
if (d->ref.isShared() || d->size + len > capacity())
|
||||
if (d->needsDetach() || d->size + len > capacity())
|
||||
reallocData(uint(d->size + len) + 1u, true);
|
||||
ushort *i = d->data() + d->size;
|
||||
qt_from_latin1(i, s, uint(len));
|
||||
|
|
@ -2743,7 +2743,7 @@ QString &QString::append(QLatin1String str)
|
|||
*/
|
||||
QString &QString::append(QChar ch)
|
||||
{
|
||||
if (d->ref.isShared() || d->size + 1 > capacity())
|
||||
if (d->needsDetach() || d->size + 1 > capacity())
|
||||
reallocData(uint(d->size) + 2u, true);
|
||||
d->data()[d->size++] = ch.unicode();
|
||||
d->data()[d->size] = '\0';
|
||||
|
|
@ -6457,7 +6457,7 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1,
|
|||
|
||||
const ushort *QString::utf16() const
|
||||
{
|
||||
if (IS_RAW_DATA(d)) {
|
||||
if (!d->isMutable()) {
|
||||
// ensure '\0'-termination for ::fromRawData strings
|
||||
const_cast<QString*>(this)->reallocData(uint(d->size) + 1u);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -542,7 +542,7 @@ public:
|
|||
inline QString &prepend(QLatin1String s) { return insert(0, s); }
|
||||
|
||||
inline QString &operator+=(QChar c) {
|
||||
if (d->ref.isShared() || d->size + 1 > capacity())
|
||||
if (d->needsDetach() || d->size + 1 > capacity())
|
||||
reallocData(uint(d->size) + 2u, true);
|
||||
d->data()[d->size++] = c.unicode();
|
||||
d->data()[d->size] = '\0';
|
||||
|
|
@ -1041,7 +1041,7 @@ inline QChar *QString::data()
|
|||
inline const QChar *QString::constData() const
|
||||
{ return reinterpret_cast<const QChar*>(d->data()); }
|
||||
inline void QString::detach()
|
||||
{ if (d->ref.isShared() || !d->isMutable()) reallocData(uint(d->size) + 1u); }
|
||||
{ if (d->needsDetach()) reallocData(uint(d->size) + 1u); }
|
||||
inline bool QString::isDetached() const
|
||||
{ return !d->ref.isShared(); }
|
||||
inline void QString::clear()
|
||||
|
|
@ -1263,7 +1263,7 @@ inline QString::~QString() { if (!d->ref.deref()) Data::deallocate(d); }
|
|||
|
||||
inline void QString::reserve(int asize)
|
||||
{
|
||||
if (d->ref.isShared() || asize >= capacity())
|
||||
if (d->needsDetach() || asize >= capacity())
|
||||
reallocData(qMax(asize, size()) + 1u);
|
||||
|
||||
// we're not shared anymore, for sure
|
||||
|
|
@ -1272,7 +1272,9 @@ inline void QString::reserve(int asize)
|
|||
|
||||
inline void QString::squeeze()
|
||||
{
|
||||
if (d->ref.isShared() || d->size < capacity())
|
||||
if ((d->flags & Data::CapacityReserved) == 0)
|
||||
return;
|
||||
if (d->needsDetach() || d->size < capacity())
|
||||
reallocData(uint(d->size) + 1u);
|
||||
|
||||
// we're not shared anymore, for sure
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
|
|||
return nullptr;
|
||||
|
||||
size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options);
|
||||
options |= ArrayOption(AllocatedDataType);
|
||||
options |= AllocatedDataType | Mutable;
|
||||
QArrayData *header = allocateData(allocSize, options);
|
||||
if (header) {
|
||||
quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1)
|
||||
|
|
@ -252,6 +252,7 @@ QArrayData *QArrayData::reallocateUnaligned(QArrayData *data, size_t objectSize,
|
|||
options |= ArrayOption(AllocatedDataType);
|
||||
size_t headerSize = sizeof(QArrayData);
|
||||
size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options);
|
||||
options |= AllocatedDataType | Mutable;
|
||||
QArrayData *header = reallocateData(data, allocSize, options);
|
||||
if (header)
|
||||
header->alloc = capacity;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ struct Q_CORE_EXPORT QArrayData
|
|||
CapacityReserved = 0x0010, //!< the capacity was reserved by the user, try to keep it
|
||||
GrowsForward = 0x0020, //!< allocate with eyes towards growing through append()
|
||||
GrowsBackwards = 0x0040, //!< allocate with eyes towards growing through prepend()
|
||||
Mutable = 0x0080, //!< the data can be changed; doesn't say anything about the header
|
||||
|
||||
/// this option is used by the Q_ARRAY_LITERAL and similar macros
|
||||
StaticDataFlags = RawDataType,
|
||||
|
|
@ -102,7 +103,16 @@ struct Q_CORE_EXPORT QArrayData
|
|||
// follow COW principles.
|
||||
bool isMutable() const
|
||||
{
|
||||
return flags & AllocatedDataType;
|
||||
return flags & Mutable;
|
||||
}
|
||||
|
||||
// Returns true if a detach is necessary before modifying the data
|
||||
// This method is intentionally not const: if you want to know whether
|
||||
// detaching is necessary, you should be in a non-const function already
|
||||
bool needsDetach()
|
||||
{
|
||||
// ### optimize me -- this currently requires 3 conditionals!
|
||||
return !isMutable() || ref.isShared();
|
||||
}
|
||||
|
||||
size_t detachCapacity(size_t newSize) const
|
||||
|
|
|
|||
|
|
@ -126,11 +126,6 @@ public:
|
|||
return d;
|
||||
}
|
||||
|
||||
bool needsDetach() const
|
||||
{
|
||||
return (!d->isMutable() || d->ref.isShared());
|
||||
}
|
||||
|
||||
void swap(QArrayDataPointer &other) noexcept
|
||||
{
|
||||
qSwap(d, other.d);
|
||||
|
|
@ -144,7 +139,7 @@ public:
|
|||
|
||||
bool detach()
|
||||
{
|
||||
if (needsDetach()) {
|
||||
if (d->needsDetach()) {
|
||||
Data *copy = clone(d->detachFlags());
|
||||
QArrayDataPointer old(d);
|
||||
d = copy;
|
||||
|
|
|
|||
|
|
@ -390,10 +390,11 @@ inline QVector<T>::QVector(const QVector<T> &v)
|
|||
template <typename T>
|
||||
void QVector<T>::detach()
|
||||
{
|
||||
// ### check whether this is still required
|
||||
if (d->ref.isStatic())
|
||||
return;
|
||||
|
||||
if (!isDetached())
|
||||
if (d->needsDetach())
|
||||
realloc(d->allocatedCapacity(), d->detachFlags());
|
||||
Q_ASSERT(isDetached());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2163,7 +2163,7 @@ void tst_QByteArray::movablity()
|
|||
const int newSize = size + 2;
|
||||
const bool newIsEmpty = false;
|
||||
const bool newIsNull = false;
|
||||
const int newCapacity = 16;
|
||||
const int newCapacity = memSpace.capacity();
|
||||
|
||||
// move back memSpace -> array
|
||||
array.~QByteArray();
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ public:
|
|||
if (size() == newSize)
|
||||
return;
|
||||
|
||||
if (d.needsDetach() || newSize > capacity()) {
|
||||
if (d->needsDetach() || newSize > capacity()) {
|
||||
SimpleVector detached(Data::allocate(
|
||||
d->detachCapacity(newSize), d->detachFlags()));
|
||||
if (newSize) {
|
||||
|
|
@ -195,7 +195,7 @@ public:
|
|||
return;
|
||||
|
||||
T *const begin = d->begin();
|
||||
if (d.needsDetach()
|
||||
if (d->needsDetach()
|
||||
|| capacity() - size() < size_t(last - first)) {
|
||||
SimpleVector detached(Data::allocate(
|
||||
d->detachCapacity(size() + (last - first)),
|
||||
|
|
@ -216,7 +216,7 @@ public:
|
|||
if (first == last)
|
||||
return;
|
||||
|
||||
if (d.needsDetach()
|
||||
if (d->needsDetach()
|
||||
|| capacity() - size() < size_t(last - first)) {
|
||||
SimpleVector detached(Data::allocate(
|
||||
d->detachCapacity(size() + (last - first)),
|
||||
|
|
@ -256,7 +256,7 @@ public:
|
|||
const iterator begin = d->begin();
|
||||
const iterator where = begin + position;
|
||||
const iterator end = begin + d->size;
|
||||
if (d.needsDetach()
|
||||
if (d->needsDetach()
|
||||
|| capacity() - size() < size_t(last - first)) {
|
||||
SimpleVector detached(Data::allocate(
|
||||
d->detachCapacity(size() + (last - first)),
|
||||
|
|
@ -294,7 +294,7 @@ public:
|
|||
const T *const begin = d->begin();
|
||||
const T *const end = begin + d->size;
|
||||
|
||||
if (d.needsDetach()) {
|
||||
if (d->needsDetach()) {
|
||||
SimpleVector detached(Data::allocate(
|
||||
d->detachCapacity(size() - (last - first)),
|
||||
d->detachFlags()));
|
||||
|
|
|
|||
Loading…
Reference in New Issue