diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index d62c411511..e7c66a456b 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -88,6 +88,19 @@ struct QPodArrayOps // As this is to be called only from destructor, it doesn't need to be // exception safe; size not updated. } + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(this->ref == 1); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + ::memmove(where + (e - b), where, (this->end() - where) * sizeof(T)); + ::memcpy(where, b, (e - b) * sizeof(T)); + this->size += (e - b); + } }; template @@ -133,6 +146,71 @@ struct QGenericArrayOps while (i != b) (--i)->~T(); } + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(this->ref == 1); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + // Array may be truncated at where in case of exceptions + + T *const end = this->end(); + const T *readIter = end; + T *writeIter = end + (e - b); + + const T *const step1End = where + qMax(e - b, end - where); + + struct Destructor + { + Destructor(T *&iter) + : iter(&iter) + , end(iter) + { + } + + void commit() + { + iter = &end; + } + + ~Destructor() + { + for (; *iter != end; --*iter) + (*iter)->~T(); + } + + T **iter; + T *end; + } destroyer(writeIter); + + // Construct new elements in array + do { + --readIter, --writeIter; + new (writeIter) T(*readIter); + } while (writeIter != step1End); + + while (writeIter != end) { + --e, --writeIter; + new (writeIter) T(*e); + } + + destroyer.commit(); + this->size += destroyer.end - end; + + // Copy assign over existing elements + while (readIter != where) { + --readIter, --writeIter; + *writeIter = *readIter; + } + + while (writeIter != where) { + --e, --writeIter; + *writeIter = *e; + } + } }; template @@ -141,6 +219,70 @@ struct QMovableArrayOps { // using QGenericArrayOps::copyAppend; // using QGenericArrayOps::destroyAll; + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(this->ref == 1); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + // Provides strong exception safety guarantee, + // provided T::~T() nothrow + + struct ReversibleDisplace + { + ReversibleDisplace(T *begin, T *end, size_t displace) + : begin(begin) + , end(end) + , displace(displace) + { + ::memmove(begin + displace, begin, (end - begin) * sizeof(T)); + } + + void commit() { displace = 0; } + + ~ReversibleDisplace() + { + if (displace) + ::memmove(begin, begin + displace, (end - begin) * sizeof(T)); + } + + T *const begin; + T *const end; + size_t displace; + + } displace(where, this->end(), size_t(e - b)); + + struct CopyConstructor + { + CopyConstructor(T *where) : where(where) {} + + void copy(const T *src, const T *const srcEnd) + { + n = 0; + for (; src != srcEnd; ++src) { + new (where + n) T(*src); + ++n; + } + n = 0; + } + + ~CopyConstructor() + { + while (n) + where[--n].~T(); + } + + T *const where; + size_t n; + } copier(where); + + copier.copy(b, e); + displace.commit(); + this->size += (e - b); + } }; template diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h index 36c9b7bc50..4aa3ab09c8 100644 --- a/tests/auto/corelib/tools/qarraydata/simplevector.h +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -135,6 +135,113 @@ public: return *(end() - 1); } + void reserve(size_t n) + { + if (n > capacity() + || (n + && !d->capacityReserved + && (d->ref != 1 || (d->capacityReserved = 1, false)))) { + SimpleVector detached(Data::allocate(n, true)); + static_cast(detached.d)->copyAppend(constBegin(), constEnd()); + detached.swap(*this); + } + } + + void prepend(const_iterator first, const_iterator last) + { + if (!d->size) { + append(first, last); + return; + } + + if (first == last) + return; + + T *const begin = d->begin(); + if (d->ref != 1 + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), d->capacityReserved)); + + static_cast(detached.d)->copyAppend(first, last); + static_cast(detached.d)->copyAppend(begin, begin + d->size); + detached.swap(*this); + + return; + } + + static_cast(d)->insert(begin, first, last); + } + + void append(const_iterator first, const_iterator last) + { + if (first == last) + return; + + if (d->ref != 1 + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), d->capacityReserved)); + + if (d->size) { + const T *const begin = constBegin(); + static_cast(detached.d)->copyAppend(begin, begin + d->size); + } + static_cast(detached.d)->copyAppend(first, last); + detached.swap(*this); + + return; + } + + static_cast(d)->copyAppend(first, last); + } + + void insert(int position, const_iterator first, const_iterator last) + { + if (position < 0) + position += d->size + 1; + + if (position <= 0) { + prepend(first, last); + return; + } + + if (size_t(position) >= size()) { + append(first, last); + return; + } + + if (first == last) + return; + + T *const begin = d->begin(); + T *const where = begin + position; + const T *const end = begin + d->size; + if (d->ref != 1 + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), d->capacityReserved)); + + if (position) + static_cast(detached.d)->copyAppend(begin, where); + static_cast(detached.d)->copyAppend(first, last); + static_cast(detached.d)->copyAppend(where, end); + detached.swap(*this); + + return; + } + + // Temporarily copy overlapping data, if needed + if ((first >= where && first < end) + || (last > where && last <= end)) { + SimpleVector tmp(first, last); + static_cast(d)->insert(where, tmp.constBegin(), tmp.constEnd()); + return; + } + + static_cast(d)->insert(where, first, last); + } + void swap(SimpleVector &other) { qSwap(d, other.d); diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index 2972f0a678..808aa73c98 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -293,6 +293,67 @@ void tst_QArrayData::simpleVector() QVERIFY(v5.isSharedWith(v6)); QVERIFY(!v1.isSharedWith(v5)); } + + v1.prepend(array, array + sizeof(array)/sizeof(array[0])); + QCOMPARE(v1.size(), size_t(10)); + QVERIFY(v1 == v8); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.prepend(array, array + sizeof(array)/sizeof(array[0])); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(20)); + QCOMPARE(v6.size(), size_t(10)); + + for (int i = 0; i < 20; ++i) + QCOMPARE(v1[i], v6[i % 10]); + + v1.clear(); + + v1.append(array, array + sizeof(array)/sizeof(array[0])); + QCOMPARE(v1.size(), size_t(10)); + QVERIFY(v1 == v8); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.append(array, array + sizeof(array)/sizeof(array[0])); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(20)); + QCOMPARE(v6.size(), size_t(10)); + + for (int i = 0; i < 20; ++i) + QCOMPARE(v1[i], v6[i % 10]); + + v1.insert(0, v6.constBegin(), v6.constEnd()); + QCOMPARE(v1.size(), size_t(30)); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.insert(10, v6.constBegin(), v6.constEnd()); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(60)); + QCOMPARE(v6.size(), size_t(30)); + + for (int i = 0; i < 30; ++i) + QCOMPARE(v6[i], v8[i % 10]); + + v1.insert(v1.size(), v6.constBegin(), v6.constEnd()); + QCOMPARE(v1.size(), size_t(90)); + + v1.insert(-1, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(100)); + + v1.insert(-11, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(110)); + + v1.insert(-200, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(120)); + + for (int i = 0; i < 120; ++i) + QCOMPARE(v1[i], v8[i % 10]); } struct Deallocator @@ -669,12 +730,82 @@ void tst_QArrayData::arrayOps() vs = SimpleVector(5, referenceString); vo = SimpleVector(5, referenceObject); + QCOMPARE(vi.size(), size_t(5)); + QCOMPARE(vs.size(), size_t(5)); + QCOMPARE(vo.size(), size_t(5)); + QCOMPARE(CountedObject::liveCount, size_t(11)); for (int i = 0; i < 5; ++i) { QCOMPARE(vi[i], referenceInt); QVERIFY(vs[i].isSharedWith(referenceString)); QCOMPARE(vo[i].id, referenceObject.id); } + + //////////////////////////////////////////////////////////////////////////// + // insert + vi.reserve(30); + vs.reserve(30); + vo.reserve(30); + + QCOMPARE(vi.size(), size_t(5)); + QCOMPARE(vs.size(), size_t(5)); + QCOMPARE(vo.size(), size_t(5)); + + QVERIFY(vi.capacity() >= 30); + QVERIFY(vs.capacity() >= 30); + QVERIFY(vo.capacity() >= 30); + + // Displace as many elements as array is extended by + vi.insert(0, intArray, intArray + 5); + vs.insert(0, stringArray, stringArray + 5); + vo.insert(0, objArray, objArray + 5); + + QCOMPARE(vi.size(), size_t(10)); + QCOMPARE(vs.size(), size_t(10)); + QCOMPARE(vo.size(), size_t(10)); + + // Displace more elements than array is extended by + vi.insert(0, intArray, intArray + 5); + vs.insert(0, stringArray, stringArray + 5); + vo.insert(0, objArray, objArray + 5); + + QCOMPARE(vi.size(), size_t(15)); + QCOMPARE(vs.size(), size_t(15)); + QCOMPARE(vo.size(), size_t(15)); + + // Displace less elements than array is extended by + vi.insert(5, vi.constBegin(), vi.constEnd()); + vs.insert(5, vs.constBegin(), vs.constEnd()); + vo.insert(5, vo.constBegin(), vo.constEnd()); + + QCOMPARE(vi.size(), size_t(30)); + QCOMPARE(vs.size(), size_t(30)); + QCOMPARE(vo.size(), size_t(30)); + + QCOMPARE(CountedObject::liveCount, size_t(36)); + for (int i = 0; i < 15; ++i) { + QCOMPARE(vi[i], intArray[i % 5]); + QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); + QCOMPARE(vo[i].id, objArray[i % 5].id); + } + + for (int i = 15; i < 20; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + QCOMPARE(vo[i].id, referenceObject.id); + } + + for (int i = 20; i < 25; ++i) { + QCOMPARE(vi[i], intArray[i % 5]); + QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); + QCOMPARE(vo[i].id, objArray[i % 5].id); + } + + for (int i = 25; i < 30; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + QCOMPARE(vo[i].id, referenceObject.id); + } } QTEST_APPLESS_MAIN(tst_QArrayData)