// Version: $Id: 006f7d582ab9fcd9c6d369d295f0fff6451b2c90 $ // // // Commentary: // // // Change Log: // // // Code: #pragma once #include #include #include // /////////////////////////////////////////////////////////////////// // Private practices // /////////////////////////////////////////////////////////////////// template inline void dtkArray::copyData(Data *other_d) { if (other_d->ref.ref()) { d = other_d; } else { if (other_d->capacityReserved) { if (other_d->alloc > PreallocSize) { d = Data::allocate(other_d->alloc); d->capacityReserved = true; } else { d = Data::fromRawData(this->preallocData(), other_d->size, dtkArrayData::Unsharable); d->capacityReserved = bool(PreallocSize); d->alloc = PreallocSize; } } else { d = Data::allocate(other_d->size); } if (d->alloc) { copyConstruct(other_d->begin(), other_d->end(), d->begin()); d->size = other_d->size; } } } template inline void dtkArray::reallocData(const qlonglong asize, const qlonglong aalloc, dtkArrayData::AllocationOptions options) { Q_ASSERT(asize >= 0 && asize <= aalloc); Data *x = d; const bool isShared = d->ref.isShared(); if (aalloc != 0) { if (aalloc != qlonglong(d->alloc) || isShared) { QT_TRY { if (aalloc > PreallocSize) { // allocate memory x = Data::allocate(aalloc, options); x->size = asize; x->capacityReserved = d->capacityReserved; } else { x = Data::fromRawData(this->preallocData(), asize, dtkArrayData::Unsharable); x->capacityReserved = bool(PreallocSize); x->alloc = PreallocSize; } Q_CHECK_PTR(x); // aalloc is bigger then 0 so it is not [un]sharedEmpty #if QT_SUPPORTS(UNSHARABLE_CONTAINERS) Q_ASSERT(x->ref.isSharable() || options.testFlag(dtkArrayData::Unsharable) || x->alloc == PreallocSize); #endif Q_ASSERT(!x->ref.isStatic()); T *srcBegin = d->begin(); T *srcEnd = asize > d->size ? d->end() : d->begin() + asize; T *dst = x->begin(); if (QTypeInfo::isStatic || (isShared && QTypeInfo::isComplex)) { // we can not move the data, we need to copy construct it while (srcBegin != srcEnd) { new (dst++) T(*srcBegin++); } } else { ::memcpy(static_cast(dst), static_cast(srcBegin), (srcEnd - srcBegin) * sizeof(T)); dst += srcEnd - srcBegin; // destruct unused / not moved data if (asize < d->size) destruct(d->begin() + asize, d->end()); } if (asize > d->size) { // construct all new objects when growing QT_TRY { defaultConstruct(dst, x->end()); } QT_CATCH (...) { // destruct already copied objects destruct(x->begin(), dst); QT_RETHROW; } } } QT_CATCH (...) { Data::deallocate(x); QT_RETHROW; } } else { Q_ASSERT(qlonglong(d->alloc) == aalloc); // resize, without changing allocation size Q_ASSERT(isDetached()); // can be done only on detached d Q_ASSERT(x == d); // in this case we do not need to allocate anything if (asize <= d->size) { destruct(x->begin() + asize, x->end()); // from future end to current end } else { defaultConstruct(x->end(), x->begin() + asize); // from current end to future end } x->size = asize; } } else { x = Data::fromRawData(this->preallocData(), 0, dtkArrayData::Unsharable); x->capacityReserved = bool(PreallocSize); x->alloc = PreallocSize; } if (d != x) { if (!d->ref.deref()) { if (QTypeInfo::isStatic || !aalloc || (isShared && QTypeInfo::isComplex)) { // data was copy constructed, we need to call destructors // or if !alloc we did nothing to the old 'd'. freeData(d); } else { Data::deallocate(d); } } d = x; } Q_ASSERT(d->data()); Q_ASSERT(quintptr(d->size) <= d->alloc); #if QT_SUPPORTS(UNSHARABLE_CONTAINERS) Q_ASSERT(d != Data::unsharableEmpty()); #endif //Q_ASSERT(aalloc ? d != Data::sharedNull() : d == Data::sharedNull()); Q_ASSERT(d->alloc >= quintptr(aalloc)); Q_ASSERT(d->size == asize); } template inline void dtkArray::reallocData(const qlonglong size) { return this->reallocData(size, d->alloc); } template inline void dtkArray::freeData(Data *x) { if (!isRawData) destruct(x->begin(), x->end()); Data::deallocate(x); } template inline void dtkArray::defaultConstruct(T *from, T *to) { if (QTypeInfo::isComplex) { while (from != to) { new (from++) T(); } } else { ::memset(static_cast(from), 0, (to - from) * sizeof(T)); } } template inline void dtkArray::copyConstruct(const T *srcFrom, const T *srcTo, T *dstFrom) { if (QTypeInfo::isComplex) { while (srcFrom != srcTo) new (dstFrom++) T(*srcFrom++); } else { ::memcpy(static_cast(dstFrom), static_cast(srcFrom), (srcTo - srcFrom) * sizeof(T)); } } template inline void dtkArray::destruct(T *from, T *to) { if (QTypeInfo::isComplex) { while (from != to) { from++->~T(); } } } template inline bool dtkArray::isValidIterator(const iterator& i) const { return (i <= d->end()) && (d->begin() <= i); } // /////////////////////////////////////////////////////////////////// // Public API // /////////////////////////////////////////////////////////////////// template inline dtkArray::dtkArray(void) : isRawData(false) { Q_STATIC_ASSERT_X(PreallocSize >= 0, "dtkArray PreallocSize must be greater than 0."); if (PreallocSize) { d = Data::fromRawData(this->preallocData(), 0, dtkArrayData::Unsharable); d->capacityReserved = bool(PreallocSize); d->alloc = PreallocSize; } else { d = Data::fromRawData(this->preallocData(), 0); } } template inline dtkArray::dtkArray(qlonglong size) : isRawData(false) { Q_STATIC_ASSERT_X(PreallocSize >= 0, "dtkArray PreallocSize must be greater or equal than 0."); Q_ASSERT_X(size >= 0, "dtkArray::dtkArray", "Size must be greater than or equal to 0."); if (size > PreallocSize) { d = Data::allocate(size); d->size = size; } else { d = Data::fromRawData(this->preallocData(), size, dtkArrayData::Unsharable); d->capacityReserved = bool(PreallocSize); d->alloc = PreallocSize; } defaultConstruct(d->begin(), d->end()); } template inline dtkArray::dtkArray(qlonglong size, const T& value) : isRawData(false) { Q_STATIC_ASSERT_X(PreallocSize >= 0, "dtkArray PreallocSize must be greater than 0."); Q_ASSERT_X(size >= 0, "dtkArray::dtkArray", "Size must be greater than or equal to 0."); if (size > PreallocSize) { d = Data::allocate(size); d->size = size; } else { d = Data::fromRawData(this->preallocData(), size, dtkArrayData::Unsharable); d->capacityReserved = bool(PreallocSize); d->alloc = PreallocSize; } T *i = d->end(); while (i != d->begin()) new (--i) T(value); } template inline dtkArray::dtkArray(const T *values, qlonglong size) : isRawData(false) { Q_STATIC_ASSERT_X(PreallocSize >= 0, "dtkArray PreallocSize must be greater than 0."); Q_ASSERT_X(size >= 0, "dtkArray::dtkArray", "Size must be greater than or equal to 0."); if (size > PreallocSize) { d = Data::allocate(size); d->size = size; } else { d = Data::fromRawData(this->preallocData(), size, dtkArrayData::Unsharable); d->capacityReserved = bool(PreallocSize); d->alloc = PreallocSize; } T *i = d->end(); const T *value = values + size; while (i != d->begin()) new (--i) T(*(--value)); } template inline dtkArray::dtkArray(std::initializer_list args) : isRawData(false) { Q_STATIC_ASSERT_X(PreallocSize >= 0, "dtkArray PreallocSize must be greater than 0."); Q_ASSERT_X(args.size() >= 0, "dtkArray::dtkArray", "Size must be greater than or equal to 0."); if (args.size() > PreallocSize) { d = Data::allocate(args.size()); d->size = int(args.size()); } else { d = Data::fromRawData(this->preallocData(), args.size(), dtkArrayData::Unsharable); d->capacityReserved = bool(PreallocSize); d->alloc = PreallocSize; } copyConstruct(args.begin(), args.end(), d->begin()); } template inline dtkArray::dtkArray(const dtkArray& other) : isRawData(false) { this->copyData(other.d); } #ifdef Q_COMPILER_RVALUE_REFS template inline dtkArray::dtkArray(dtkArray&& other) : isRawData(false) { this->copyData(other.d); other.d = Data::sharedNull(); } #endif template inline dtkArray::~dtkArray(void) { if (!d->ref.deref()) freeData(d); } template inline dtkArray& dtkArray::operator = (const dtkArray& other) { if (other.d != d) { if (!d->ref.deref()) freeData(d); this->copyData(other.d); } return *this; } #ifdef Q_COMPILER_RVALUE_REFS template inline dtkArray& dtkArray::operator = (dtkArray&& other) { this->swap(other); return *this; } #endif template inline bool dtkArray::operator == (const dtkArray& other) const { if (d->size != other.d->size) return false; if (d == other.d) return true; T *b = d->begin(); T *i = b + d->size; T *j = other.d->end(); while (i != b) if (!(*--i == *--j)) return false; return true; } template inline bool dtkArray::operator != (const dtkArray& other) const { return !(*this == other); } template template inline dtkArray::dtkArray(const dtkArray& other) { this->copyData(other.d); } template template inline dtkArray& dtkArray::operator = (const dtkArray& other) { if (other.d != d) { if (!d->ref.deref()) freeData(d); this->copyData(other.d); } return *this; } template template inline bool dtkArray::operator == (const dtkArray& other) const { if (d->size != other.d->size) return false; if (d == other.d) return true; T *b = d->begin(); T *i = b + d->size; T *j = other.d->end(); while (i != b) if (!(*--i == *--j)) return false; return true; } template template inline bool dtkArray::operator != (const dtkArray& other) const { return !(*this == other); } template template inline bool dtkArray::isSharedWith(const dtkArray& other) const { return d == other.d; } template inline void dtkArray::swap(dtkArray& other) { if (other.d->ref.isSharable() && d->ref.isSharable()) { qSwap(d, other.d); } else { dtkArray tmp(other); other = *this; *this = tmp; } } template inline bool dtkArray::isDetached(void) const { return !d->ref.isShared(); } template inline void dtkArray::detach(void) { if (!isDetached()) { #if QT_SUPPORTS(UNSHARABLE_CONTAINERS) if (!d->alloc) d = Data::unsharableEmpty(); else #endif dtkDebug() << Q_FUNC_INFO << "detaching array of size " << d->size; reallocData(d->size, qlonglong(d->alloc)); } Q_ASSERT(isDetached()); } template inline bool dtkArray::isSharedWith(const dtkArray& other) const { return d == other.d; } #if QT_SUPPORTS(UNSHARABLE_CONTAINERS) template inline void dtkArray::setSharable(bool sharable) { if (sharable == d->ref.isSharable()) return; if (!sharable) detach(); if (d == Data::unsharableEmpty()) { if (sharable) d = Data::sharedNull(); } else { d->ref.setSharable(sharable); } Q_ASSERT(d->ref.isSharable() == sharable); } #endif template inline bool dtkArray::empty(void) const { return (d->size == 0); } template inline bool dtkArray::isEmpty(void) const { return (d->size == 0); } template inline bool dtkArray::usePreallocation(void) const { return this->dtkArrayPrealloc::isPreallocated(d->data()); } template inline qlonglong dtkArray::size(void) const { return d->size; } template inline qlonglong dtkArray::count(void) const { return d->size; } template inline qlonglong dtkArray::length(void) const { return d->size; } template inline qlonglong dtkArray::capacity(void) const { return qlonglong(d->alloc); } template inline qlonglong dtkArray::preallocatedCapacity(void) const { return PreallocSize; } template inline void dtkArray::clear(void) { *this = dtkArray(); } template inline void dtkArray::resize(qlonglong asize) { if (asize < 0) asize = 0; qlonglong newAlloc; const qlonglong oldAlloc = qlonglong(d->alloc); dtkArrayData::AllocationOptions opt; if (asize > oldAlloc) { // there is not enough space newAlloc = asize; opt = dtkArrayData::Grow; } else if (!d->capacityReserved && asize < d->size && asize < (oldAlloc >> 1)) { // we want to shrink newAlloc = asize; opt = dtkArrayData::Grow; } else { newAlloc = oldAlloc; } reallocData(asize, newAlloc, opt); } template inline void dtkArray::reserve(qlonglong asize) { if (asize > qlonglong(d->alloc)) reallocData(d->size, asize); if (isDetached()) d->capacityReserved = 1; Q_ASSERT(capacity() >= asize); } template inline void dtkArray::squeeze(void) { reallocData(d->size, d->size); if (d->capacityReserved && d->size > PreallocSize) { // capacity reserved in a read only memory would be useless // this checks avoid writing to such memory. d->capacityReserved = 0; } } template inline void dtkArray::append(const T& value) { const bool isTooSmall = quintptr(d->size + 1) > d->alloc; if (!isDetached() || isTooSmall) { dtkArrayData::AllocationOptions opt(isTooSmall ? dtkArrayData::Grow : dtkArrayData::Default); reallocData(d->size, isTooSmall ? d->size + 1 : d->alloc, opt); } if (QTypeInfo::isComplex) { new (d->end()) T(value); } else { *d->end() = T(value); } ++d->size; } template inline void dtkArray::append(const T& v1, const T& v2) { quintptr newSize = d->size + 2; const bool isTooSmall = newSize > d->alloc; if (!isDetached() || isTooSmall) { dtkArrayData::AllocationOptions opt(isTooSmall ? dtkArrayData::Grow : dtkArrayData::Default); reallocData(d->size, isTooSmall ? newSize : d->alloc, opt); } if (d->alloc) { T *w = d->begin() + newSize; if (QTypeInfo::isComplex) { new (--w) T(v2); new (--w) T(v1); } else { *--w = T(v2); *--w = T(v1); } d->size = newSize; } } template inline void dtkArray::append(const T& v1, const T& v2, const T& v3) { quintptr newSize = d->size + 3; const bool isTooSmall = newSize > d->alloc; if (!isDetached() || isTooSmall) { dtkArrayData::AllocationOptions opt(isTooSmall ? dtkArrayData::Grow : dtkArrayData::Default); reallocData(d->size, isTooSmall ? newSize : d->alloc, opt); } if (d->alloc) { T *w = d->begin() + newSize; if (QTypeInfo::isComplex) { new (--w) T(v3); new (--w) T(v2); new (--w) T(v1); } else { *--w = T(v3); *--w = T(v2); *--w = T(v1); } d->size = newSize; } } template inline void dtkArray::append(const T& v1, const T& v2, const T& v3, const T& v4) { quintptr newSize = d->size + 4; const bool isTooSmall = newSize > d->alloc; if (!isDetached() || isTooSmall) { dtkArrayData::AllocationOptions opt(isTooSmall ? dtkArrayData::Grow : dtkArrayData::Default); reallocData(d->size, isTooSmall ? newSize : d->alloc, opt); } if (d->alloc) { T *w = d->begin() + newSize; if (QTypeInfo::isComplex) { new (--w) T(v4); new (--w) T(v3); new (--w) T(v2); new (--w) T(v1); } else { *--w = T(v4); *--w = T(v3); *--w = T(v2); *--w = T(v1); } d->size = newSize; } } template inline void dtkArray::append(const T *values, qlonglong size) { quintptr newSize = d->size + size; const bool isTooSmall = newSize > d->alloc; if (!isDetached() || isTooSmall) { dtkArrayData::AllocationOptions opt(isTooSmall ? dtkArrayData::Grow : dtkArrayData::Default); reallocData(d->size, isTooSmall ? newSize : d->alloc, opt); } if (d->alloc) { T *w = d->begin() + newSize; const T *i = values + size; const T *b = values; while (i != b) { if (QTypeInfo::isComplex) new (--w) T(*--i); else *--w = *--i; } d->size = newSize; } } template inline void dtkArray::append(std::initializer_list args) { qlonglong size = args.size(); if (size != 0) { quintptr newSize = d->size + size; const bool isTooSmall = newSize > d->alloc; if (!isDetached() || isTooSmall) { dtkArrayData::AllocationOptions opt(isTooSmall ? dtkArrayData::Grow : dtkArrayData::Default); reallocData(d->size, isTooSmall ? newSize : d->alloc, opt); } if (d->alloc) { copyConstruct(args.begin(), args.end(), d->begin() + d->size); d->size = newSize; } } } template inline void dtkArray::append(const dtkArray& other) { quintptr newSize = d->size + other.d->size; const bool isTooSmall = newSize > d->alloc; if (!isDetached() || isTooSmall) { dtkArrayData::AllocationOptions opt(isTooSmall ? dtkArrayData::Grow : dtkArrayData::Default); reallocData(d->size, isTooSmall ? newSize : d->alloc, opt); } if (d->alloc) { T *w = d->begin() + newSize; T *i = other.d->end(); T *b = other.d->begin(); while (i != b) { if (QTypeInfo::isComplex) new (--w) T(*--i); else *--w = *--i; } d->size = newSize; } } template inline void dtkArray::prepend(const T& value) { insert(begin(), 1, value); } template inline void dtkArray::insert(qlonglong index, const T& value) { Q_ASSERT_X(index >= 0 && index <= d->size, "dtkArray::insert", "index out of range"); insert(begin() + index, 1, value); } template inline void dtkArray::insert(qlonglong index, qlonglong length, const T& value) { Q_ASSERT_X(index >= 0 && index <= d->size, "dtkArray::insert", "index out of range"); insert(begin() + index, length, value); } template inline typename dtkArray::iterator dtkArray::insert(iterator before, const T& value) { return insert(before, 1, value); } template inline typename dtkArray::iterator dtkArray::insert(iterator before, qlonglong n, const T& t) { Q_ASSERT_X(isValidIterator(before), "dtkArray::insert", "The specified iterator argument 'before' is invalid"); qlonglong offset = std::distance(d->begin(), before); if (n > 0) { const T copy(t); if (!isDetached() || d->size + n > qlonglong(d->alloc)) reallocData(d->size, d->size + n, dtkArrayData::Grow); if (QTypeInfo::isStatic) { T *b = d->end(); T *i = d->end() + n; while (i != b) new (--i) T; i = d->end(); T *j = i + n; b = d->begin() + offset; while (i != b) *--j = *--i; i = b + n; while (i != b) *--i = copy; } else { T *b = d->begin() + offset; T *i = b + n; ::memmove(i, b, (d->size - offset) * sizeof(T)); while (i != b) new (--i) T(copy); } d->size += n; } return d->begin() + offset; } template inline void dtkArray::setAt(qlonglong index, const T& value) { Q_ASSERT_X(index >= 0 && index < d->size, "dtkArray::setAt", "index out of range"); data()[index] = value; } template inline void dtkArray::setAt(qlonglong index, const T *values, qlonglong length) { Q_ASSERT_X(index >= 0 && length >= 0, "dtkArray::setAt", "index out of range or negative length"); if (length != 0) { quintptr newSize = qMax((quintptr)d->size, (quintptr)(index + length)); const bool isTooSmall = newSize > d->alloc; if (!isDetached() || isTooSmall) { dtkArrayData::AllocationOptions opt(isTooSmall ? dtkArrayData::Grow : dtkArrayData::Default); reallocData(d->size, isTooSmall ? newSize : d->alloc, opt); } if (QTypeInfo::isStatic) { if (index >= d->size) { T *b = d->end(); T *i = d->begin() + index; while (i != b) new (--i) T; } T *w = d->begin() + index + length; const T *b = values; const T *i = b + length; while (i != b) new (--w) T(*--i); } else { if (index >= d->size) { T *b = d->end(); T *i = d->begin() + index; while (i != b) { new (--i) T(); } } T *src = const_cast(values); T *dst = d->begin() + index; ::memmove(dst, src, length * sizeof(T)); } d->size = newSize; } } template inline void dtkArray::replace(qlonglong index, const T& value) { this->setAt(index, value); } template inline void dtkArray::replace(qlonglong index, const T *values, qlonglong length) { this->setAt(index, values, length); } template inline dtkArray& dtkArray::fill(const T& from, qlonglong asize) { resize(asize < 0 ? d->size : asize); if (d->size) { T *i = d->end(); T *b = d->begin(); while (i != b) *--i = from; } return *this; } template inline void dtkArray::setRawData(const T *raw_data, qlonglong size) { if (!d->ref.deref()) freeData(d); if (!raw_data || ! size) { d = Data::fromRawData(this->preallocData(), 0); } else { d = Data::fromRawData(raw_data, size); d->ref.atomic.store(2); d->alloc = size; isRawData = true; } } template inline void dtkArray::setWritableRawData(T *raw_data, qlonglong size) { if (!d->ref.deref()) freeData(d); if (!raw_data || ! size) { d = Data::fromRawData(this->preallocData(), 0); } else { d = Data::fromRawData(raw_data, size); d->alloc = size; isRawData = true; } } template inline dtkArray dtkArray::fromRawData(const T *raw_data, qlonglong size) { dtkArray result; result.d = Data::fromRawData(raw_data, size); result.d->ref.atomic.store(2); result.d->alloc = size; result.isRawData = true; return result; } template inline dtkArray dtkArray::fromWritableRawData(T *raw_data, qlonglong size) { dtkArray result; result.d = Data::fromRawData(raw_data, size); result.d->alloc = size; result.isRawData = true; return result; } template inline typename dtkArray::iterator dtkArray::erase(iterator abegin, iterator aend) { Q_ASSERT_X(isValidIterator(abegin), "dtkArray::erase", "The specified iterator argument 'begin' is invalid"); Q_ASSERT_X(isValidIterator(aend), "dtkArray::erase", "The specified iterator argument 'end' is invalid"); const int itemsToErase = aend - abegin; if (!itemsToErase) return abegin; Q_ASSERT(abegin >= d->begin()); Q_ASSERT(aend <= d->end()); Q_ASSERT(abegin <= aend); const int itemsUntouched = abegin - d->begin(); // FIXME we could do a proper realloc, which copy constructs only needed data. // FIXME we ara about to delete data maybe it is good time to shrink? // FIXME the shrink is also an issue in removeLast, that is just a copy + reduce of this. if (d->alloc) { detach(); abegin = d->begin() + itemsUntouched; aend = abegin + itemsToErase; if (QTypeInfo::isStatic) { iterator moveBegin = abegin + itemsToErase; iterator moveEnd = d->end(); while (moveBegin != moveEnd) { if (QTypeInfo::isComplex) static_cast(abegin)->~T(); new (abegin++) T(*moveBegin++); } if (abegin < d->end()) { // destroy rest of instances destruct(abegin, d->end()); } } else { destruct(abegin, aend); memmove(abegin, aend, (d->size - itemsToErase - itemsUntouched) * sizeof(T)); } d->size -= itemsToErase; } return d->begin() + itemsUntouched; } template inline typename dtkArray::iterator dtkArray::erase(iterator pos) { return erase(pos, pos + 1); } template inline void dtkArray::remove(qlonglong index) { Q_ASSERT_X(index >= 0 && index <= d->size, "dtkArray::remove", "index out of range"); erase(d->begin() + index, d->begin() + index + 1); } template inline void dtkArray::remove(qlonglong index, qlonglong length) { Q_ASSERT_X(index >= 0 && length >= 0 && index + length <= d->size, "dtkArray::remove", "index out of range"); erase(d->begin() + index, d->begin() + index + length); } template inline void dtkArray::removeFirst(void) { Q_ASSERT(!isEmpty()); erase(d->begin()); } template inline void dtkArray::removeLast(void) { Q_ASSERT(!isEmpty()); Q_ASSERT(d->alloc); if (!d->ref.isShared()) { --d->size; if (QTypeInfo::isComplex) (d->data() + d->size)->~T(); } else { reallocData(d->size - 1); } } template inline void dtkArray::removeAt(qlonglong index) { Q_ASSERT_X(index >= 0 && index <= d->size, "dtkArray::removeAt", "index out of range"); erase(d->begin() + index, d->begin() + index + 1); } template inline T dtkArray::takeFirst(void) { Q_ASSERT(!isEmpty()); T r = first(); removeFirst(); return r; } template inline T dtkArray::takeLast(void) { Q_ASSERT(!isEmpty()); T r = last(); removeLast(); return r; } template inline T dtkArray::takeAt(qlonglong index) { T t = at(index); remove(index); return t; } template Q_OUTOFLINE_TEMPLATE void dtkArray::reverse(void) { detach(); T *src = begin(); T *dst = end() - 1; while (src < dst) qSwap(*(dst--), *(src++)); } template Q_OUTOFLINE_TEMPLATE dtkArray dtkArray::reversed(void) const { dtkArray result(size()); const T *src = begin(); T *dst = result.end() - 1; if (!QTypeInfo::isComplex) { while (src != end()) *(dst--) = *(src++); } else { while (src != end()) new (dst--) T(*src++); } return result; } template inline dtkArray& dtkArray::operator += (const T& value) { this->append(value); return *this; } template inline dtkArray& dtkArray::operator += (const dtkArray& other) { this->append(other); return *this; } template inline dtkArray& dtkArray::operator << (const T& value) { this->append(value); return *this; } template inline dtkArray& dtkArray::operator << (const dtkArray& other) { *this += other; return *this; } template template inline dtkArray& dtkArray::operator += (const dtkArray& other) { quintptr newSize = d->size + other.d->size; const bool isTooSmall = newSize > d->alloc; if (!isDetached() || isTooSmall) { dtkArrayData::AllocationOptions opt(isTooSmall ? dtkArrayData::Grow : dtkArrayData::Default); reallocData(d->size, isTooSmall ? newSize : d->alloc, opt); } if (d->alloc) { T *w = d->begin() + newSize; T *i = other.d->end(); T *b = other.d->begin(); while (i != b) { if (QTypeInfo::isComplex) new (--w) T(*--i); else *--w = *--i; } d->size = newSize; } return *this; } template template inline dtkArray& dtkArray::operator << (const dtkArray& other) { *this += other; return *this; } template inline const T& dtkArray::at(qlonglong i) const { Q_ASSERT_X(i >= 0 && i < d->size, "dtkArray::at", "index out of range"); return d->begin()[i]; } template inline const T& dtkArray::operator[](qlonglong i) const { Q_ASSERT_X(i >= 0 && i < d->size, "dtkArray::operator[]", "index out of range"); return d->begin()[i]; } template inline T& dtkArray::operator[](qlonglong i) { Q_ASSERT_X(i >= 0 && i < d->size, "dtkArray::operator[]", "index out of range"); return data()[i]; } template inline T& dtkArray::first(void) { Q_ASSERT(!isEmpty()); return *begin(); } template inline const T& dtkArray::first(void) const { Q_ASSERT(!isEmpty()); return *begin(); } template inline T& dtkArray::last(void) { Q_ASSERT(!isEmpty()); return *(end() - 1); } template inline const T& dtkArray::last(void) const { Q_ASSERT(!isEmpty()); return *(end() - 1); } template Q_OUTOFLINE_TEMPLATE T dtkArray::value(qlonglong index) const { return quintptr(index) >= quintptr(d->size) ? T() : d->begin()[index]; } template Q_OUTOFLINE_TEMPLATE T dtkArray::value(qlonglong index, const T& defaultValue) const { return quintptr(index) >= quintptr(d->size) ? defaultValue : d->begin()[index]; } template inline bool dtkArray::startsWith(const T& t) const { return !isEmpty() && first() == t; } template inline bool dtkArray::endsWith(const T& t) const { return !isEmpty() && last() == t; } template inline bool dtkArray::contains(const T& t) const { T *b = d->begin(); T *i = d->end(); while (i != b) if (*--i == t) return true; return false; } template inline qlonglong dtkArray::indexOf(const T& t, qlonglong from) const { if (from < 0) from = qMax(from + d->size, qlonglong(0)); if (from < d->size) { T *n = d->begin() + from - 1; T *e = d->end(); while (++n != e) if (*n == t) return n - d->begin(); } return -1; } template inline qlonglong dtkArray::lastIndexOf(const T& t, qlonglong from) const { if (from < 0) from += d->size; else if (from >= d->size) from = d->size - 1; if (from >= 0) { T *b = d->begin(); T *n = d->begin() + from + 1; while (n != b) { if (*--n == t) return n - b; } } return -1; } template inline qlonglong dtkArray::count(const T& t) const { qlonglong c = 0; T *b = d->begin(); T *i = d->end(); while (i != b) if (*--i == t) ++c; return c; } template inline dtkArray dtkArray::mid(qlonglong pos, qlonglong len) const { if (len < 0) len = size() - pos; if (pos == 0 && len == size()) return *this; if (pos + len > size()) len = size() - pos; dtkArray copy; copy.append(constData() + pos, len); return copy; } template inline dtkArray dtkArray::left(qlonglong length) const { return mid(0, length); } template inline dtkArray dtkArray::right(qlonglong length) const { if (length < 0 || length >= size()) length = size(); return mid(size() - length, length); } // /////////////////////////////////////////////////////////////////// template inline QDebug& operator << (QDebug debug, const dtkArray& array) { const bool oldSetting = debug.autoInsertSpaces(); debug.nospace() << "dtkArray"; debug.nospace() << '('; for (typename dtkArray::size_type i = 0; i < array.size(); ++i) { if (i) debug << ", "; debug << array.at(i); } debug << ')'; debug.setAutoInsertSpaces(oldSetting); return debug.maybeSpace(); } // /////////////////////////////////////////////////////////////////// template inline QDataStream& operator << (QDataStream& s, const dtkArray& array) { s << quint64(array.size()); for (const T& t : array) s << t; return s; } template inline QDataStream& operator >> (QDataStream& s, dtkArray& array) { array.clear(); quint64 size; s >> size; array.resize(size); for (T& t : array) { s >> t; } return s; } template inline QDataStream& operator << (QDataStream& s, const dtkArray& array) { s << quint64(array.size()); for (T *t : array) { s << dtkMetaType::variantFromValue(t); } return s; } template inline QDataStream& operator >> (QDataStream& s, dtkArray& array) { array.clear(); quint64 size; s >> size; array.resize(size); for (T *&t : array) { QVariant var; s >> var; t = var.value(); } return s; } // /////////////////////////////////////////////////////////////////// // Registering dtkArray to QMetaType system // /////////////////////////////////////////////////////////////////// template struct QMetaTypeId< dtkArray > { enum { Defined = QMetaTypeId2::Defined }; static int qt_metatype_id() { static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); if (const int id = metatype_id.load()) return id; const char *array_type = QMetaType::typeName(qMetaTypeId()); Q_ASSERT(array_type); int type_length = int(qstrlen(array_type)); QByteArray array_name("dtkArray", 8); QByteArray array_size = QByteArray::number(PreallocSize); QByteArray typeName; typeName.reserve(array_name.size() + 1 + type_length + 1 + array_size.size() + 1); typeName.append(array_name).append('<').append(array_type).append(',').append(array_size).append('>'); const int newId = qRegisterNormalizedMetaType< dtkArray >(typeName, reinterpret_cast*>(quintptr(-1))); metatype_id.storeRelease(newId); qRegisterMetaTypeStreamOperators(array_type); qRegisterMetaTypeStreamOperators< dtkArray >(typeName.constData()); return newId; } }; namespace QtPrivate { template struct IsSequentialContainer< dtkArray > { enum { Value = true }; }; // Pointer version } template struct QMetaTypeId< dtkArray *> { enum { Defined = QMetaTypeId2::Defined }; static int qt_metatype_id() { static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); if (const int id = metatype_id.load()) return id; const char *array_type = QMetaType::typeName(qMetaTypeId()); Q_ASSERT(array_type); int type_length = int(qstrlen(array_type)); QByteArray array_name("dtkArray", 8); QByteArray array_size = QByteArray::number(PreallocSize); QByteArray typeName; typeName.reserve(array_name.size() + 1 + type_length + 1 + array_size.size() + 1 + 1); typeName.append(array_name).append('<').append(array_type).append(',').append(array_size).append('>').append('*'); const int newId = qRegisterNormalizedMetaType *>(typeName, reinterpret_cast* *>(quintptr(-1))); metatype_id.storeRelease(newId); if (newId > 0) { QMetaTypeId< dtkArray >::qt_metatype_id(); dtkMetaType::registerContainerPointerConverter *>(newId); qRegisterMetaTypeStreamOperators(array_type); qRegisterMetaTypeStreamOperators< dtkArray *>(typeName.constData()); } return newId; } }; template struct dtkMetaTypeIsSequentialContainerPointer< dtkArray *> : std::true_type {}; // /////////////////////////////////////////////////////////////////// // Specialization of typetraits // /////////////////////////////////////////////////////////////////// #include template< typename T, qlonglong PreallocSize > struct dtkMetaContainerIsReservable< dtkArray > : std::true_type {}; template< typename T, qlonglong PreallocSize > struct dtkMetaContainerIsResizable< dtkArray > : std::true_type {}; // // dtkArray.tpp ends here