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:
parent
0bd647fa4f
commit
4bf8e82d41
@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2019 The Qt Company Ltd.
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
** Copyright (C) 2016 Intel Corporation.
|
** Copyright (C) 2016 Intel Corporation.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** 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);
|
qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, options);
|
||||||
QArrayData *header = allocateData(allocSize, options);
|
QArrayData *header = allocateData(allocSize, options);
|
||||||
quintptr data = 0;
|
void *data = nullptr;
|
||||||
if (header) {
|
if (header) {
|
||||||
// find where offset should point to so that data() is aligned to alignment bytes
|
// find where offset should point to so that data() is aligned to alignment bytes
|
||||||
data = (quintptr(header) + sizeof(QArrayData) + alignment - 1)
|
data = QTypedArrayData<void>::dataStart(header, alignment);
|
||||||
& ~(alignment - 1);
|
|
||||||
header->alloc = qsizetype(capacity);
|
header->alloc = qsizetype(capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
*dptr = header;
|
*dptr = header;
|
||||||
return reinterpret_cast<void *>(data);
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPair<QArrayData *, void *>
|
QPair<QArrayData *, void *>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
** Copyright (C) 2019 Intel Corporation.
|
** Copyright (C) 2019 Intel Corporation.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
@ -235,6 +235,15 @@ struct QTypedArrayData
|
|||||||
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
|
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
|
||||||
QArrayData::deallocate(data, sizeof(T), alignof(AlignmentDummy));
|
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 {
|
namespace QtPrivate {
|
||||||
|
@ -233,11 +233,11 @@ public:
|
|||||||
{
|
{
|
||||||
Q_ASSERT(this->isMutable());
|
Q_ASSERT(this->isMutable());
|
||||||
Q_ASSERT(!this->isShared());
|
Q_ASSERT(!this->isShared());
|
||||||
Q_ASSERT(newSize > uint(this->size));
|
Q_ASSERT(newSize > size_t(this->size));
|
||||||
Q_ASSERT(newSize <= this->allocatedCapacity());
|
Q_ASSERT(newSize - this->size <= size_t(this->freeSpaceAtEnd()));
|
||||||
|
|
||||||
::memset(static_cast<void *>(this->end()), 0, (newSize - this->size) * sizeof(T));
|
::memset(static_cast<void *>(this->end()), 0, (newSize - this->size) * sizeof(T));
|
||||||
this->size = int(newSize);
|
this->size = qsizetype(newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename iterator>
|
template<typename iterator>
|
||||||
@ -245,7 +245,7 @@ public:
|
|||||||
{
|
{
|
||||||
Q_ASSERT(this->isMutable() || b == e);
|
Q_ASSERT(this->isMutable() || b == e);
|
||||||
Q_ASSERT(!this->isShared() || 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();
|
T *iter = this->end();
|
||||||
for (; b != e; ++iter, ++b) {
|
for (; b != e; ++iter, ++b) {
|
||||||
@ -272,7 +272,7 @@ public:
|
|||||||
Q_ASSERT(!this->isShared());
|
Q_ASSERT(!this->isShared());
|
||||||
Q_ASSERT(newSize < size_t(this->size));
|
Q_ASSERT(newSize < size_t(this->size));
|
||||||
|
|
||||||
this->size = int(newSize);
|
this->size = qsizetype(newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroyAll() // Call from destructors, ONLY!
|
void destroyAll() // Call from destructors, ONLY!
|
||||||
@ -291,7 +291,7 @@ public:
|
|||||||
Q_ASSERT(where >= this->begin() && where <= this->end());
|
Q_ASSERT(where >= this->begin() && where <= this->end());
|
||||||
Q_ASSERT(b <= e);
|
Q_ASSERT(b <= e);
|
||||||
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
|
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),
|
::memmove(static_cast<void *>(where + (e - b)), static_cast<void *>(where),
|
||||||
(static_cast<const T*>(this->end()) - where) * sizeof(T));
|
(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(!this->isShared() || (n == 0 && where == this->end()));
|
||||||
Q_ASSERT(where >= this->begin() && 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),
|
::memmove(static_cast<void *>(where + n), static_cast<void *>(where),
|
||||||
(static_cast<const T*>(this->end()) - where) * sizeof(T));
|
(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--)
|
while (n--)
|
||||||
*where++ = t;
|
*where++ = t;
|
||||||
}
|
}
|
||||||
@ -320,7 +320,7 @@ public:
|
|||||||
{
|
{
|
||||||
Q_ASSERT(!this->isShared());
|
Q_ASSERT(!this->isShared());
|
||||||
Q_ASSERT(where >= this->begin() && where <= this->end());
|
Q_ASSERT(where >= this->begin() && where <= this->end());
|
||||||
Q_ASSERT(this->allocatedCapacity() - this->size >= 1);
|
Q_ASSERT(this->freeSpaceAtEnd() >= 1);
|
||||||
|
|
||||||
if (where == this->end()) {
|
if (where == this->end()) {
|
||||||
new (this->end()) T(std::forward<Args>(args)...);
|
new (this->end()) T(std::forward<Args>(args)...);
|
||||||
@ -394,13 +394,13 @@ struct QGenericArrayOps
|
|||||||
{
|
{
|
||||||
Q_ASSERT(this->isMutable());
|
Q_ASSERT(this->isMutable());
|
||||||
Q_ASSERT(!this->isShared());
|
Q_ASSERT(!this->isShared());
|
||||||
Q_ASSERT(newSize > uint(this->size));
|
Q_ASSERT(newSize > size_t(this->size));
|
||||||
Q_ASSERT(newSize <= this->allocatedCapacity());
|
Q_ASSERT(newSize - this->size <= size_t(this->freeSpaceAtEnd()));
|
||||||
|
|
||||||
T *const b = this->begin();
|
T *const b = this->begin();
|
||||||
do {
|
do {
|
||||||
new (b + this->size) T;
|
new (b + this->size) T;
|
||||||
} while (uint(++this->size) != newSize);
|
} while (size_t(++this->size) != newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename iterator>
|
template<typename iterator>
|
||||||
@ -408,7 +408,7 @@ struct QGenericArrayOps
|
|||||||
{
|
{
|
||||||
Q_ASSERT(this->isMutable() || b == e);
|
Q_ASSERT(this->isMutable() || b == e);
|
||||||
Q_ASSERT(!this->isShared() || 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();
|
T *iter = this->end();
|
||||||
for (; b != e; ++iter, ++b) {
|
for (; b != e; ++iter, ++b) {
|
||||||
@ -425,7 +425,7 @@ struct QGenericArrayOps
|
|||||||
Q_ASSERT(this->isMutable() || b == e);
|
Q_ASSERT(this->isMutable() || b == e);
|
||||||
Q_ASSERT(!this->isShared() || b == e);
|
Q_ASSERT(!this->isShared() || b == e);
|
||||||
Q_ASSERT(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;
|
typedef typename QArrayExceptionSafetyPrimitives<T>::Constructor CopyConstructor;
|
||||||
|
|
||||||
@ -454,7 +454,7 @@ struct QGenericArrayOps
|
|||||||
const T *const b = this->begin();
|
const T *const b = this->begin();
|
||||||
do {
|
do {
|
||||||
(b + --this->size)->~T();
|
(b + --this->size)->~T();
|
||||||
} while (uint(this->size) != newSize);
|
} while (size_t(this->size) != newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroyAll() // Call from destructors, ONLY
|
void destroyAll() // Call from destructors, ONLY
|
||||||
@ -479,7 +479,7 @@ struct QGenericArrayOps
|
|||||||
Q_ASSERT(where >= this->begin() && where <= this->end());
|
Q_ASSERT(where >= this->begin() && where <= this->end());
|
||||||
Q_ASSERT(b <= e);
|
Q_ASSERT(b <= e);
|
||||||
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
|
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;
|
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(!this->isShared() || (n == 0 && where == this->end()));
|
||||||
Q_ASSERT(where >= this->begin() && 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;
|
typedef typename QArrayExceptionSafetyPrimitives<T>::template Destructor<T *> Destructor;
|
||||||
|
|
||||||
@ -578,7 +578,7 @@ struct QGenericArrayOps
|
|||||||
{
|
{
|
||||||
Q_ASSERT(!this->isShared());
|
Q_ASSERT(!this->isShared());
|
||||||
Q_ASSERT(where >= this->begin() && where <= this->end());
|
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)...);
|
createInPlace(this->end(), std::forward<Args>(args)...);
|
||||||
++this->size;
|
++this->size;
|
||||||
@ -651,7 +651,7 @@ struct QMovableArrayOps
|
|||||||
Q_ASSERT(where >= this->begin() && where <= this->end());
|
Q_ASSERT(where >= this->begin() && where <= this->end());
|
||||||
Q_ASSERT(b <= e);
|
Q_ASSERT(b <= e);
|
||||||
Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append
|
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>::Displacer ReversibleDisplace;
|
||||||
typedef typename QArrayExceptionSafetyPrimitives<T>::Constructor CopyConstructor;
|
typedef typename QArrayExceptionSafetyPrimitives<T>::Constructor CopyConstructor;
|
||||||
@ -670,7 +670,7 @@ struct QMovableArrayOps
|
|||||||
{
|
{
|
||||||
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
|
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
|
||||||
Q_ASSERT(where >= this->begin() && 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>::Displacer ReversibleDisplace;
|
||||||
typedef typename QArrayExceptionSafetyPrimitives<T>::Constructor CopyConstructor;
|
typedef typename QArrayExceptionSafetyPrimitives<T>::Constructor CopyConstructor;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the QtCore module of the Qt Toolkit.
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
@ -194,6 +194,20 @@ public:
|
|||||||
|
|
||||||
Data *d_ptr() noexcept { return d; }
|
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:
|
private:
|
||||||
Q_REQUIRED_RESULT QPair<Data *, T *> clone(QArrayData::ArrayOptions options) const
|
Q_REQUIRED_RESULT QPair<Data *, T *> clone(QArrayData::ArrayOptions options) const
|
||||||
{
|
{
|
||||||
|
@ -76,6 +76,8 @@ private slots:
|
|||||||
void variadicLiterals();
|
void variadicLiterals();
|
||||||
void rValueReferences();
|
void rValueReferences();
|
||||||
void grow();
|
void grow();
|
||||||
|
void freeSpace_data();
|
||||||
|
void freeSpace();
|
||||||
#ifndef QT_NO_EXCEPTIONS
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
void exceptionSafetyPrimitives_constructor();
|
void exceptionSafetyPrimitives_constructor();
|
||||||
void exceptionSafetyPrimitives_destructor();
|
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
|
#ifndef QT_NO_EXCEPTIONS
|
||||||
struct ThrowingTypeWatcher
|
struct ThrowingTypeWatcher
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user