Add QArrayDataPointer::freeSpace*() functions

Added functions that tell how much free space is available at the
beginning and at the end of the storage

Updated preconditions of operations to use freeSpace* functions

Also, changed casts uint(this->size) to size_t(this->size)

Task-number: QTBUG-84320
Change-Id: Iad94c1060a00f62068da9d1327e332a00d4f4109
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
This commit is contained in:
Andrei Golubev 2020-07-13 12:26:36 +03:00
parent 0bd647fa4f
commit 4bf8e82d41
5 changed files with 88 additions and 27 deletions

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@ -191,16 +191,15 @@ void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype al
qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, options);
QArrayData *header = allocateData(allocSize, options);
quintptr data = 0;
void *data = nullptr;
if (header) {
// find where offset should point to so that data() is aligned to alignment bytes
data = (quintptr(header) + sizeof(QArrayData) + alignment - 1)
& ~(alignment - 1);
data = QTypedArrayData<void>::dataStart(header, alignment);
header->alloc = qsizetype(capacity);
}
*dptr = header;
return reinterpret_cast<void *>(data);
return data;
}
QPair<QArrayData *, void *>

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2019 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@ -235,6 +235,15 @@ struct QTypedArrayData
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
QArrayData::deallocate(data, sizeof(T), alignof(AlignmentDummy));
}
static T *dataStart(QArrayData *data, qsizetype alignment) noexcept
{
// Alignment is a power of two
Q_ASSERT(alignment >= qsizetype(alignof(QArrayData)) && !(alignment & (alignment - 1)));
void *start = reinterpret_cast<void *>(
(quintptr(data) + sizeof(QArrayData) + alignment - 1) & ~(alignment - 1));
return static_cast<T *>(start);
}
};
namespace QtPrivate {

View File

@ -233,11 +233,11 @@ public:
{
Q_ASSERT(this->isMutable());
Q_ASSERT(!this->isShared());
Q_ASSERT(newSize > uint(this->size));
Q_ASSERT(newSize <= this->allocatedCapacity());
Q_ASSERT(newSize > size_t(this->size));
Q_ASSERT(newSize - this->size <= size_t(this->freeSpaceAtEnd()));
::memset(static_cast<void *>(this->end()), 0, (newSize - this->size) * sizeof(T));
this->size = int(newSize);
this->size = qsizetype(newSize);
}
template<typename iterator>
@ -245,7 +245,7 @@ public:
{
Q_ASSERT(this->isMutable() || b == e);
Q_ASSERT(!this->isShared() || b == e);
Q_ASSERT(std::distance(b, e) >= 0 && size_t(std::distance(b, e)) <= this->allocatedCapacity() - this->size);
Q_ASSERT(std::distance(b, e) >= 0 && qsizetype(std::distance(b, e)) <= this->freeSpaceAtEnd());
T *iter = this->end();
for (; b != e; ++iter, ++b) {
@ -272,7 +272,7 @@ public:
Q_ASSERT(!this->isShared());
Q_ASSERT(newSize < size_t(this->size));
this->size = int(newSize);
this->size = qsizetype(newSize);
}
void destroyAll() // Call from destructors, ONLY!
@ -291,7 +291,7 @@ public:
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(b <= e);
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
Q_ASSERT(size_t(e - b) <= this->allocatedCapacity() - this->size);
Q_ASSERT((e - b) <= this->freeSpaceAtEnd());
::memmove(static_cast<void *>(where + (e - b)), static_cast<void *>(where),
(static_cast<const T*>(this->end()) - where) * sizeof(T));
@ -303,11 +303,11 @@ public:
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(this->allocatedCapacity() - this->size >= n);
Q_ASSERT(size_t(this->freeSpaceAtEnd()) >= n);
::memmove(static_cast<void *>(where + n), static_cast<void *>(where),
(static_cast<const T*>(this->end()) - where) * sizeof(T));
this->size += int(n); // PODs can't throw on copy
this->size += qsizetype(n); // PODs can't throw on copy
while (n--)
*where++ = t;
}
@ -320,7 +320,7 @@ public:
{
Q_ASSERT(!this->isShared());
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(this->allocatedCapacity() - this->size >= 1);
Q_ASSERT(this->freeSpaceAtEnd() >= 1);
if (where == this->end()) {
new (this->end()) T(std::forward<Args>(args)...);
@ -394,13 +394,13 @@ struct QGenericArrayOps
{
Q_ASSERT(this->isMutable());
Q_ASSERT(!this->isShared());
Q_ASSERT(newSize > uint(this->size));
Q_ASSERT(newSize <= this->allocatedCapacity());
Q_ASSERT(newSize > size_t(this->size));
Q_ASSERT(newSize - this->size <= size_t(this->freeSpaceAtEnd()));
T *const b = this->begin();
do {
new (b + this->size) T;
} while (uint(++this->size) != newSize);
} while (size_t(++this->size) != newSize);
}
template<typename iterator>
@ -408,7 +408,7 @@ struct QGenericArrayOps
{
Q_ASSERT(this->isMutable() || b == e);
Q_ASSERT(!this->isShared() || b == e);
Q_ASSERT(std::distance(b, e) >= 0 && size_t(std::distance(b, e)) <= this->allocatedCapacity() - this->size);
Q_ASSERT(std::distance(b, e) >= 0 && qsizetype(std::distance(b, e)) <= this->freeSpaceAtEnd());
T *iter = this->end();
for (; b != e; ++iter, ++b) {
@ -425,7 +425,7 @@ struct QGenericArrayOps
Q_ASSERT(this->isMutable() || b == e);
Q_ASSERT(!this->isShared() || b == e);
Q_ASSERT(b <= e);
Q_ASSERT(size_t(e - b) <= this->allocatedCapacity() - this->size);
Q_ASSERT((e - b) <= this->freeSpaceAtEnd());
typedef typename QArrayExceptionSafetyPrimitives<T>::Constructor CopyConstructor;
@ -454,7 +454,7 @@ struct QGenericArrayOps
const T *const b = this->begin();
do {
(b + --this->size)->~T();
} while (uint(this->size) != newSize);
} while (size_t(this->size) != newSize);
}
void destroyAll() // Call from destructors, ONLY
@ -479,7 +479,7 @@ struct QGenericArrayOps
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(b <= e);
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
Q_ASSERT(size_t(e - b) <= this->allocatedCapacity() - this->size);
Q_ASSERT((e - b) <= this->freeSpaceAtEnd());
typedef typename QArrayExceptionSafetyPrimitives<T>::template Destructor<T *> Destructor;
@ -527,7 +527,7 @@ struct QGenericArrayOps
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(this->allocatedCapacity() - this->size >= n);
Q_ASSERT(size_t(this->freeSpaceAtEnd()) >= n);
typedef typename QArrayExceptionSafetyPrimitives<T>::template Destructor<T *> Destructor;
@ -578,7 +578,7 @@ struct QGenericArrayOps
{
Q_ASSERT(!this->isShared());
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(this->allocatedCapacity() - this->size >= 1);
Q_ASSERT(this->freeSpaceAtEnd() >= 1);
createInPlace(this->end(), std::forward<Args>(args)...);
++this->size;
@ -651,7 +651,7 @@ struct QMovableArrayOps
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(b <= e);
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
Q_ASSERT(size_t(e - b) <= this->allocatedCapacity() - this->size);
Q_ASSERT((e - b) <= this->freeSpaceAtEnd());
typedef typename QArrayExceptionSafetyPrimitives<T>::Displacer ReversibleDisplace;
typedef typename QArrayExceptionSafetyPrimitives<T>::Constructor CopyConstructor;
@ -670,7 +670,7 @@ struct QMovableArrayOps
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(this->allocatedCapacity() - this->size >= n);
Q_ASSERT(size_t(this->freeSpaceAtEnd()) >= n);
typedef typename QArrayExceptionSafetyPrimitives<T>::Displacer ReversibleDisplace;
typedef typename QArrayExceptionSafetyPrimitives<T>::Constructor CopyConstructor;

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -194,6 +194,20 @@ public:
Data *d_ptr() noexcept { return d; }
qsizetype freeSpaceAtBegin() const noexcept
{
if (d == nullptr)
return 0;
return this->ptr - Data::dataStart(d, alignof(typename Data::AlignmentDummy));
}
qsizetype freeSpaceAtEnd() const noexcept
{
if (d == nullptr)
return 0;
return d->constAllocatedCapacity() - freeSpaceAtBegin() - this->size;
}
private:
Q_REQUIRED_RESULT QPair<Data *, T *> clone(QArrayData::ArrayOptions options) const
{

View File

@ -76,6 +76,8 @@ private slots:
void variadicLiterals();
void rValueReferences();
void grow();
void freeSpace_data();
void freeSpace();
#ifndef QT_NO_EXCEPTIONS
void exceptionSafetyPrimitives_constructor();
void exceptionSafetyPrimitives_destructor();
@ -1814,6 +1816,43 @@ void tst_QArrayData::grow()
}
}
void tst_QArrayData::freeSpace_data()
{
QTest::addColumn<QArrayData::ArrayOptions>("allocationOptions");
QTest::addColumn<size_t>("n");
for (const size_t n : {1, 3, 5, 7, 16, 25}) {
QString suffix = QString::number(n) + QLatin1String("-elements");
QTest::newRow(qPrintable(QLatin1String("default-alloc-") + suffix))
<< QArrayData::ArrayOptions(QArrayData::DefaultAllocationFlags) << n;
QTest::newRow(qPrintable(QLatin1String("grows-forward-") + suffix))
<< QArrayData::ArrayOptions(QArrayData::GrowsForward) << n;
QTest::newRow(qPrintable(QLatin1String("grows-bidirectional-") + suffix))
<< QArrayData::ArrayOptions(QArrayData::GrowsForward | QArrayData::GrowsBackwards) << n;
}
}
void tst_QArrayData::freeSpace()
{
QFETCH(QArrayData::ArrayOptions, allocationOptions);
QFETCH(size_t, n);
const auto testFreeSpace = [] (auto dummy, auto options, size_t n) {
using Type = std::decay_t<decltype(dummy)>;
using Data = QTypedArrayData<Type>;
using DataPointer = QArrayDataPointer<Type>;
Q_UNUSED(dummy);
DataPointer ptr(Data::allocate(n, options));
const auto alloc = qsizetype(ptr.constAllocatedCapacity());
QVERIFY(size_t(alloc) >= n);
QCOMPARE(ptr.freeSpaceAtBegin() + ptr.freeSpaceAtEnd(), alloc);
};
RUN_TEST_FUNC(testFreeSpace, char(0), allocationOptions, n);
RUN_TEST_FUNC(testFreeSpace, char16_t(0), allocationOptions, n);
RUN_TEST_FUNC(testFreeSpace, int(0), allocationOptions, n);
RUN_TEST_FUNC(testFreeSpace, QString(), allocationOptions, n);
RUN_TEST_FUNC(testFreeSpace, CountedObject(), allocationOptions, n);
}
#ifndef QT_NO_EXCEPTIONS
struct ThrowingTypeWatcher
{