File indexing completed on 2026-08-27 09:33:02
0001
0002
0003
0004
0005 #ifndef QCONTIGUOUSCACHE_H
0006 #define QCONTIGUOUSCACHE_H
0007
0008 #include <QtCore/qatomic.h>
0009 #include <QtCore/qassert.h>
0010 #include <QtCore/qtclasshelpermacros.h>
0011 #include <QtCore/qtcoreexports.h>
0012 #include <QtCore/qminmax.h>
0013 #include <QtCore/qttypetraits.h>
0014 #include <QtCore/qtypeinfo.h>
0015
0016 #include <climits>
0017 #include <limits>
0018 #include <new>
0019
0020 QT_BEGIN_NAMESPACE
0021
0022 #undef QT_QCONTIGUOUSCACHE_DEBUG
0023
0024
0025 struct Q_CORE_EXPORT QContiguousCacheData
0026 {
0027 QBasicAtomicInt ref;
0028 qsizetype alloc;
0029 qsizetype count;
0030 qsizetype start;
0031 qsizetype offset;
0032
0033 static QContiguousCacheData *allocateData(qsizetype size, qsizetype alignment);
0034 static void freeData(QContiguousCacheData *data);
0035
0036 #ifdef QT_QCONTIGUOUSCACHE_DEBUG
0037 void dump() const;
0038 #endif
0039 };
0040
0041 template <typename T>
0042 struct QContiguousCacheTypedData : public QContiguousCacheData
0043 {
0044 T array[1];
0045 };
0046
0047 template<typename T>
0048 class QContiguousCache {
0049 static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
0050
0051 typedef QContiguousCacheTypedData<T> Data;
0052 Data *d;
0053 public:
0054
0055 typedef T value_type;
0056 typedef value_type* pointer;
0057 typedef const value_type* const_pointer;
0058 typedef value_type& reference;
0059 typedef const value_type& const_reference;
0060 typedef qptrdiff difference_type;
0061 typedef qsizetype size_type;
0062
0063 explicit QContiguousCache(qsizetype capacity = 0);
0064 QContiguousCache(const QContiguousCache<T> &v) : d(v.d) { d->ref.ref(); }
0065
0066 inline ~QContiguousCache() { if (!d) return; if (!d->ref.deref()) freeData(d); }
0067
0068 inline void detach() { if (d->ref.loadRelaxed() != 1) detach_helper(); }
0069 inline bool isDetached() const { return d->ref.loadRelaxed() == 1; }
0070
0071 QContiguousCache<T> &operator=(const QContiguousCache<T> &other);
0072 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QContiguousCache)
0073 void swap(QContiguousCache &other) noexcept { qt_ptr_swap(d, other.d); }
0074
0075 #ifndef Q_QDOC
0076 template <typename U = T>
0077 QTypeTraits::compare_eq_result<U> operator==(const QContiguousCache<T> &other) const
0078 {
0079 if (other.d == d)
0080 return true;
0081 if (other.d->start != d->start
0082 || other.d->count != d->count
0083 || other.d->offset != d->offset
0084 || other.d->alloc != d->alloc)
0085 return false;
0086 for (qsizetype i = firstIndex(); i <= lastIndex(); ++i)
0087 if (!(at(i) == other.at(i)))
0088 return false;
0089 return true;
0090 }
0091 template <typename U = T>
0092 QTypeTraits::compare_eq_result<U> operator!=(const QContiguousCache<T> &other) const
0093 { return !(*this == other); }
0094 #else
0095 bool operator==(const QContiguousCache &other) const;
0096 bool operator!=(const QContiguousCache &other) const;
0097 #endif
0098
0099 inline qsizetype capacity() const {return d->alloc; }
0100 inline qsizetype count() const { return d->count; }
0101 inline qsizetype size() const { return d->count; }
0102
0103 inline bool isEmpty() const { return d->count == 0; }
0104 inline bool isFull() const { return d->count == d->alloc; }
0105 inline qsizetype available() const { return d->alloc - d->count; }
0106
0107 void clear();
0108 void setCapacity(qsizetype size);
0109
0110 const T &at(qsizetype pos) const;
0111 T &operator[](qsizetype i);
0112 const T &operator[](qsizetype i) const;
0113
0114 void append(T &&value);
0115 void append(const T &value);
0116 void prepend(T &&value);
0117 void prepend(const T &value);
0118 void insert(qsizetype pos, T &&value);
0119 void insert(qsizetype pos, const T &value);
0120
0121
0122 inline bool containsIndex(qsizetype pos) const { return pos >= d->offset && pos - d->offset < d->count; }
0123 inline qsizetype firstIndex() const { return d->offset; }
0124 inline qsizetype lastIndex() const { return d->offset + d->count - 1; }
0125
0126 inline const T &first() const { Q_ASSERT(!isEmpty()); return d->array[d->start]; }
0127 inline const T &last() const { Q_ASSERT(!isEmpty()); return d->array[(d->start + d->count -1) % d->alloc]; }
0128 inline T &first() { Q_ASSERT(!isEmpty()); detach(); return d->array[d->start]; }
0129 inline T &last() { Q_ASSERT(!isEmpty()); detach(); return d->array[(d->start + d->count -1) % d->alloc]; }
0130
0131 void removeFirst();
0132 T takeFirst();
0133 void removeLast();
0134 T takeLast();
0135
0136
0137 inline bool areIndexesValid() const
0138 { return d->offset >= 0 && d->offset < (std::numeric_limits<qsizetype>::max)() - d->count && (d->offset % d->alloc) == d->start; }
0139
0140 inline void normalizeIndexes() { d->offset = d->start; }
0141
0142 #ifdef QT_QCONTIGUOUSCACHE_DEBUG
0143 void dump() const { d->dump(); }
0144 #endif
0145 private:
0146 void detach_helper();
0147
0148 Data *allocateData(qsizetype aalloc);
0149 void freeData(Data *x);
0150 };
0151
0152 template <typename T>
0153 void QContiguousCache<T>::detach_helper()
0154 {
0155 Data *x = allocateData(d->alloc);
0156 x->count = d->count;
0157 x->start = d->start;
0158 x->offset = d->offset;
0159 x->alloc = d->alloc;
0160
0161 T *dest = x->array + x->start;
0162 T *src = d->array + d->start;
0163 qsizetype oldcount = x->count;
0164 while (oldcount--) {
0165 new (dest) T(*src);
0166 dest++;
0167 if (dest == x->array + x->alloc)
0168 dest = x->array;
0169 src++;
0170 if (src == d->array + d->alloc)
0171 src = d->array;
0172 }
0173
0174 if (!d->ref.deref())
0175 freeData(d);
0176 d = x;
0177 }
0178
0179 template <typename T>
0180 void QContiguousCache<T>::setCapacity(qsizetype asize)
0181 {
0182 Q_ASSERT(asize >= 0);
0183 if (asize == d->alloc)
0184 return;
0185 detach();
0186 Data *x = allocateData(asize);
0187 x->alloc = asize;
0188 x->count = qMin(d->count, asize);
0189 x->offset = d->offset + d->count - x->count;
0190 if (asize)
0191 x->start = x->offset % x->alloc;
0192 else
0193 x->start = 0;
0194
0195 qsizetype oldcount = x->count;
0196 if (oldcount)
0197 {
0198 T *dest = x->array + (x->start + x->count-1) % x->alloc;
0199 T *src = d->array + (d->start + d->count-1) % d->alloc;
0200 while (oldcount--) {
0201 new (dest) T(*src);
0202 if (dest == x->array)
0203 dest = x->array + x->alloc;
0204 dest--;
0205 if (src == d->array)
0206 src = d->array + d->alloc;
0207 src--;
0208 }
0209 }
0210
0211 freeData(d);
0212 d = x;
0213 }
0214
0215 template <typename T>
0216 void QContiguousCache<T>::clear()
0217 {
0218 if (d->ref.loadRelaxed() == 1) {
0219 if (QTypeInfo<T>::isComplex) {
0220 qsizetype oldcount = d->count;
0221 T * i = d->array + d->start;
0222 T * e = d->array + d->alloc;
0223 while (oldcount--) {
0224 i->~T();
0225 i++;
0226 if (i == e)
0227 i = d->array;
0228 }
0229 }
0230 d->count = d->start = d->offset = 0;
0231 } else {
0232 Data *x = allocateData(d->alloc);
0233 x->alloc = d->alloc;
0234 x->count = x->start = x->offset = 0;
0235 if (!d->ref.deref())
0236 freeData(d);
0237 d = x;
0238 }
0239 }
0240
0241 template <typename T>
0242 inline typename QContiguousCache<T>::Data *QContiguousCache<T>::allocateData(qsizetype aalloc)
0243 {
0244 return static_cast<Data *>(QContiguousCacheData::allocateData(sizeof(Data) + (aalloc - 1) * sizeof(T), alignof(Data)));
0245 }
0246
0247 template <typename T>
0248 QContiguousCache<T>::QContiguousCache(qsizetype cap)
0249 {
0250 Q_ASSERT(cap >= 0);
0251 d = allocateData(cap);
0252 d->alloc = cap;
0253 d->count = d->start = d->offset = 0;
0254 }
0255
0256 template <typename T>
0257 QContiguousCache<T> &QContiguousCache<T>::operator=(const QContiguousCache<T> &other)
0258 {
0259 other.d->ref.ref();
0260 if (!d->ref.deref())
0261 freeData(d);
0262 d = other.d;
0263 return *this;
0264 }
0265
0266 template <typename T>
0267 void QContiguousCache<T>::freeData(Data *x)
0268 {
0269 if (QTypeInfo<T>::isComplex) {
0270 qsizetype oldcount = d->count;
0271 T * i = d->array + d->start;
0272 T * e = d->array + d->alloc;
0273 while (oldcount--) {
0274 i->~T();
0275 i++;
0276 if (i == e)
0277 i = d->array;
0278 }
0279 }
0280 Data::freeData(x);
0281 }
0282 template <typename T>
0283 void QContiguousCache<T>::append(T &&value)
0284 {
0285 if (!d->alloc)
0286 return;
0287 detach();
0288 if (d->count == d->alloc)
0289 (d->array + (d->start+d->count) % d->alloc)->~T();
0290 new (d->array + (d->start+d->count) % d->alloc) T(std::move(value));
0291
0292 if (d->count == d->alloc) {
0293 d->start++;
0294 d->start %= d->alloc;
0295 d->offset++;
0296 } else {
0297 d->count++;
0298 }
0299 }
0300
0301 template <typename T>
0302 void QContiguousCache<T>::append(const T &value)
0303 {
0304 if (!d->alloc)
0305 return;
0306 detach();
0307 if (d->count == d->alloc)
0308 (d->array + (d->start+d->count) % d->alloc)->~T();
0309 new (d->array + (d->start+d->count) % d->alloc) T(value);
0310
0311 if (d->count == d->alloc) {
0312 d->start++;
0313 d->start %= d->alloc;
0314 d->offset++;
0315 } else {
0316 d->count++;
0317 }
0318 }
0319
0320 template<typename T>
0321 void QContiguousCache<T>::prepend(T &&value)
0322 {
0323 if (!d->alloc)
0324 return;
0325 detach();
0326 if (d->start)
0327 d->start--;
0328 else
0329 d->start = d->alloc-1;
0330 d->offset--;
0331
0332 if (d->count != d->alloc)
0333 d->count++;
0334 else
0335 (d->array + d->start)->~T();
0336
0337 new (d->array + d->start) T(std::move(value));
0338 }
0339
0340 template<typename T>
0341 void QContiguousCache<T>::prepend(const T &value)
0342 {
0343 if (!d->alloc)
0344 return;
0345 detach();
0346 if (d->start)
0347 d->start--;
0348 else
0349 d->start = d->alloc-1;
0350 d->offset--;
0351
0352 if (d->count != d->alloc)
0353 d->count++;
0354 else
0355 (d->array + d->start)->~T();
0356
0357 new (d->array + d->start) T(value);
0358 }
0359
0360 template<typename T>
0361 void QContiguousCache<T>::insert(qsizetype pos, T &&value)
0362 {
0363 Q_ASSERT_X(pos >= 0, "QContiguousCache<T>::insert", "index out of range");
0364 if (!d->alloc)
0365 return;
0366 detach();
0367 if (containsIndex(pos)) {
0368 d->array[pos % d->alloc] = std::move(value);
0369 } else if (pos == d->offset-1)
0370 prepend(value);
0371 else if (pos == d->offset+d->count)
0372 append(value);
0373 else {
0374
0375 clear();
0376 d->offset = pos;
0377 d->start = pos % d->alloc;
0378 d->count = 1;
0379 new (d->array + d->start) T(std::move(value));
0380 }
0381 }
0382
0383 template<typename T>
0384 void QContiguousCache<T>::insert(qsizetype pos, const T &value)
0385 {
0386 return insert(pos, T(value));
0387 }
0388 template <typename T>
0389 inline const T &QContiguousCache<T>::at(qsizetype pos) const
0390 { Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache<T>::at", "index out of range"); return d->array[pos % d->alloc]; }
0391 template <typename T>
0392 inline const T &QContiguousCache<T>::operator[](qsizetype pos) const
0393 { return at(pos); }
0394
0395 template <typename T>
0396 inline T &QContiguousCache<T>::operator[](qsizetype pos)
0397 {
0398 detach();
0399 if (!containsIndex(pos))
0400 insert(pos, T());
0401 return d->array[pos % d->alloc];
0402 }
0403
0404 template <typename T>
0405 inline void QContiguousCache<T>::removeFirst()
0406 {
0407 Q_ASSERT(d->count > 0);
0408 detach();
0409 d->count--;
0410 if (QTypeInfo<T>::isComplex)
0411 (d->array + d->start)->~T();
0412 d->start = (d->start + 1) % d->alloc;
0413 d->offset++;
0414 }
0415
0416 template <typename T>
0417 inline void QContiguousCache<T>::removeLast()
0418 {
0419 Q_ASSERT(d->count > 0);
0420 detach();
0421 d->count--;
0422 if (QTypeInfo<T>::isComplex)
0423 (d->array + (d->start + d->count) % d->alloc)->~T();
0424 }
0425
0426 template <typename T>
0427 inline T QContiguousCache<T>::takeFirst()
0428 { T t = std::move(first()); removeFirst(); return t; }
0429
0430 template <typename T>
0431 inline T QContiguousCache<T>::takeLast()
0432 { T t = std::move(last()); removeLast(); return t; }
0433
0434 QT_END_NAMESPACE
0435
0436 #endif