Introduce GrowsBackwards case operations

Added operation overloads based on GrowsBackwards flag to array data ops

"New" operations can be considered as mirrored to original special cases
where near-begin (free)space is used instead of near-end space

The newly added functions are not used anywhere in this commit. Yet there
is enough code to consider a separate review for the operations along with
tag dispatch approach used

Task-number: QTBUG-84320
Change-Id: Ie57d97fcc6beeaf5cce6820b38702e376c431c0e
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Andrei Golubev 2020-08-10 15:46:27 +02:00
parent ff52d95e29
commit 9260950552

View File

@ -215,6 +215,10 @@ struct QArrayExceptionSafetyPrimitives
};
};
// Tags for compile-time dispatch based on backwards vs forward growing policy
struct GrowsForwardTag {};
struct GrowsBackwardsTag {};
QT_WARNING_PUSH
#if defined(Q_CC_GNU) && Q_CC_GNU >= 700
QT_WARNING_DISABLE_GCC("-Wstringop-overflow")
@ -285,6 +289,9 @@ public:
}
void insert(T *where, const T *b, const T *e)
{ insert(GrowsForwardTag{}, where, b, e); }
void insert(GrowsForwardTag, T *where, const T *b, const T *e)
{
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
@ -299,7 +306,28 @@ public:
this->size += (e - b);
}
void insert(GrowsBackwardsTag, T *where, const T *b, const T *e)
{
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
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((e - b) <= this->freeSpaceAtBegin());
auto oldBegin = this->begin();
this->ptr -= (e - b);
::memmove(static_cast<void *>(this->begin()), static_cast<void *>(oldBegin),
(where - static_cast<const T*>(oldBegin)) * sizeof(T));
::memcpy(static_cast<void *>(where - (e - b)), static_cast<const void *>(b),
(e - b) * sizeof(T));
this->size += (e - b);
}
void insert(T *where, size_t n, parameter_type t)
{ insert(GrowsForwardTag{}, where, n, t); }
void insert(GrowsForwardTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
@ -312,11 +340,31 @@ public:
*where++ = t;
}
void insert(GrowsBackwardsTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(size_t(this->freeSpaceAtBegin()) >= n);
auto oldBegin = this->begin();
this->ptr -= n;
::memmove(static_cast<void *>(this->begin()), static_cast<void *>(oldBegin),
(where - static_cast<const T*>(oldBegin)) * sizeof(T));
this->size += qsizetype(n); // PODs can't throw on copy
where -= n;
while (n--)
*where++ = t;
}
template <typename ...Args>
void createInPlace(T *where, Args&&... args) { new (where) T(std::forward<Args>(args)...); }
template <typename ...Args>
void emplace(T *where, Args&&... args)
{ emplace(GrowsForwardTag{}, where, std::forward<Args>(args)...); }
template <typename ...Args>
void emplace(GrowsForwardTag, T *where, Args&&... args)
{
Q_ASSERT(!this->isShared());
Q_ASSERT(where >= this->begin() && where <= this->end());
@ -336,8 +384,34 @@ public:
++this->size;
}
template <typename ...Args>
void emplace(GrowsBackwardsTag, T *where, Args&&... args)
{
Q_ASSERT(!this->isShared());
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(this->freeSpaceAtBegin() >= 1);
if (where == this->begin()) {
new (this->begin() - 1) T(std::forward<Args>(args)...);
--this->ptr;
} else {
// Preserve the value, because it might be a reference to some part of the moved chunk
T t(std::forward<Args>(args)...);
auto oldBegin = this->begin();
--this->ptr;
::memmove(static_cast<void *>(this->begin()), static_cast<void *>(oldBegin),
(where - oldBegin) * sizeof(T));
*(where - 1) = t;
}
++this->size;
}
void erase(T *b, T *e)
{ erase(GrowsForwardTag{}, b, e); }
void erase(GrowsForwardTag, T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
@ -349,6 +423,20 @@ public:
this->size -= (e - b);
}
void erase(GrowsBackwardsTag, T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
Q_ASSERT(b >= this->begin() && b < this->end());
Q_ASSERT(e > this->begin() && e <= this->end());
const auto oldBegin = this->begin();
this->ptr += (e - b);
::memmove(static_cast<void *>(this->begin()), static_cast<void *>(oldBegin),
(b - static_cast<T *>(oldBegin)) * sizeof(T));
this->size -= (e - b);
}
void assign(T *b, T *e, parameter_type t)
{
Q_ASSERT(b <= e);
@ -473,6 +561,9 @@ struct QGenericArrayOps
}
void insert(T *where, const T *b, const T *e)
{ insert(GrowsForwardTag{}, where, b, e); }
void insert(GrowsForwardTag, T *where, const T *b, const T *e)
{
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
@ -523,7 +614,56 @@ struct QGenericArrayOps
}
}
void insert(GrowsBackwardsTag, T *where, const T *b, const T *e)
{
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
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((e - b) <= this->freeSpaceAtBegin());
typedef typename QArrayExceptionSafetyPrimitives<T>::template Destructor<T *> Destructor;
T *const begin = this->begin();
const T *readIter = begin;
T *writeIter = begin - (e - b);
const T *const step1End = where - qMax(e - b, where - begin);
Destructor destroyer(writeIter);
// Construct new elements in array
while (writeIter != step1End) {
new (writeIter) T(*readIter);
++readIter, ++writeIter;
}
while (writeIter != begin) {
new (writeIter) T(*b);
++b, ++writeIter;
}
destroyer.commit();
this->size += begin - destroyer.end;
this->ptr -= begin - destroyer.end;
// Copy assign over existing elements
while (readIter != where) {
*writeIter = *readIter;
++readIter, ++writeIter;
}
while (writeIter != where) {
*writeIter = *b;
++b, ++writeIter;
}
}
void insert(T *where, size_t n, parameter_type t)
{ insert(GrowsForwardTag{}, where, n, t); }
void insert(GrowsForwardTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
@ -570,11 +710,58 @@ struct QGenericArrayOps
}
}
void insert(GrowsBackwardsTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(size_t(this->freeSpaceAtBegin()) >= n);
typedef typename QArrayExceptionSafetyPrimitives<T>::template Destructor<T *> Destructor;
T *const begin = this->begin();
const T *readIter = begin;
T *writeIter = begin - n;
const T *const step1End = where - qMax<size_t>(n, where - begin);
Destructor destroyer(writeIter);
// Construct new elements in array
while (writeIter != step1End) {
new (writeIter) T(*readIter);
++readIter, ++writeIter;
}
while (writeIter != begin) {
new (writeIter) T(t);
++writeIter;
}
destroyer.commit();
this->size += begin - destroyer.end;
this->ptr -= begin - destroyer.end;
// Copy assign over existing elements
while (readIter != where) {
*writeIter = *readIter;
++readIter, ++writeIter;
}
while (writeIter != where) {
*writeIter = t;
++writeIter;
}
}
template <typename ...Args>
void createInPlace(T *where, Args&&... args) { new (where) T(std::forward<Args>(args)...); }
template <typename iterator, typename ...Args>
void emplace(iterator where, Args&&... args)
{ emplace(GrowsForwardTag{}, where, std::forward<Args>(args)...); }
template <typename iterator, typename ...Args>
void emplace(GrowsForwardTag, iterator where, Args&&... args)
{
Q_ASSERT(!this->isShared());
Q_ASSERT(where >= this->begin() && where <= this->end());
@ -586,7 +773,24 @@ struct QGenericArrayOps
std::rotate(where, this->end() - 1, this->end());
}
template <typename iterator, typename ...Args>
void emplace(GrowsBackwardsTag, iterator where, Args&&... args)
{
Q_ASSERT(!this->isShared());
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(this->freeSpaceAtBegin() >= 1);
createInPlace(this->begin() - 1, std::forward<Args>(args)...);
--this->ptr;
++this->size;
std::rotate(this->begin(), this->begin() + 1, where);
}
void erase(T *b, T *e)
{ erase(GrowsForwardTag{}, b, e); }
void erase(GrowsForwardTag, T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
@ -611,6 +815,33 @@ struct QGenericArrayOps
} while (e != b);
}
void erase(GrowsBackwardsTag, T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
Q_ASSERT(b >= this->begin() && b < this->end());
Q_ASSERT(e > this->begin() && e <= this->end());
const T *const begin = this->begin();
// move (by assignment) the elements from begin to b
// onto the new begin to e
while (b != begin) {
--b, --e;
*e = *b;
}
// destroy the final elements at the begin
// here, e points to the new begin and b to the actual begin
do {
// Exceptions or not, dtor called once per instance
++this->ptr;
--this->size;
(b++)->~T();
} while (b != e);
}
void assign(T *b, T *e, parameter_type t)
{
Q_ASSERT(b <= e);
@ -645,6 +876,9 @@ struct QMovableArrayOps
typedef typename QGenericArrayOps<T>::parameter_type parameter_type;
void insert(T *where, const T *b, const T *e)
{ insert(GrowsForwardTag{}, where, b, e); }
void insert(GrowsForwardTag, T *where, const T *b, const T *e)
{
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
@ -666,7 +900,33 @@ struct QMovableArrayOps
this->size += copiedSize;
}
void insert(GrowsBackwardsTag, T *where, const T *b, const T *e)
{
Q_ASSERT(this->isMutable() || (b == e && where == this->end()));
Q_ASSERT(!this->isShared() || (b == e && where == this->end()));
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((e - b) <= this->freeSpaceAtBegin());
typedef typename QArrayExceptionSafetyPrimitives<T>::Constructor CopyConstructor;
typedef typename QArrayExceptionSafetyPrimitives<T>::Displacer ReversibleDisplace;
// Provides strong exception safety guarantee,
// provided T::~T() nothrow
ReversibleDisplace displace(this->begin(), where, -(e - b));
CopyConstructor copier(where - (e - b));
const auto copiedSize = copier.copy(b, e);
displace.commit();
this->ptr -= copiedSize;
this->size += copiedSize;
}
void insert(T *where, size_t n, parameter_type t)
{ insert(GrowsForwardTag{}, where, n, t); }
void insert(GrowsForwardTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
@ -685,10 +945,33 @@ struct QMovableArrayOps
this->size += copiedSize;
}
void insert(GrowsBackwardsTag, T *where, size_t n, parameter_type t)
{
Q_ASSERT(!this->isShared() || (n == 0 && where == this->end()));
Q_ASSERT(where >= this->begin() && where <= this->end());
Q_ASSERT(size_t(this->freeSpaceAtBegin()) >= n);
typedef typename QArrayExceptionSafetyPrimitives<T>::Constructor CopyConstructor;
typedef typename QArrayExceptionSafetyPrimitives<T>::Displacer ReversibleDisplace;
// Provides strong exception safety guarantee,
// provided T::~T() nothrow
ReversibleDisplace displace(this->begin(), where, -qsizetype(n));
CopyConstructor copier(where - n);
const auto copiedSize = copier.clone(n, t);
displace.commit();
this->ptr -= copiedSize;
this->size += copiedSize;
}
// use moving insert
using QGenericArrayOps<T>::insert;
void erase(T *b, T *e)
{ erase(GrowsForwardTag{}, b, e); }
void erase(GrowsForwardTag, T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
@ -705,6 +988,25 @@ struct QMovableArrayOps
(--e)->~T();
} while (e != b);
}
void erase(GrowsBackwardsTag, T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
Q_ASSERT(b >= this->begin() && b < this->end());
Q_ASSERT(e > this->begin() && e <= this->end());
typedef typename QArrayExceptionSafetyPrimitives<T>::Mover Mover;
Mover mover(this->ptr, b - static_cast<const T *>(this->begin()), this->size);
// destroy the elements we're erasing
do {
// Exceptions or not, dtor called once per instance
++this->ptr;
(b++)->~T();
} while (b != e);
}
};
template <class T, class = void>