Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcontiguouscache.h
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QCONTIGUOUSCACHE_H
5#define QCONTIGUOUSCACHE_H
6
7#include <QtCore/qatomic.h>
8#include <QtCore/qassert.h>
9#include <QtCore/qtclasshelpermacros.h>
10#include <QtCore/qtcoreexports.h>
11#include <QtCore/qminmax.h>
12#include <QtCore/qttypetraits.h>
13#include <QtCore/qtypeinfo.h>
14
15#include <climits>
16#include <limits>
17#include <new>
18
20
21#undef QT_QCONTIGUOUSCACHE_DEBUG
22
23
25{
26 QBasicAtomicInt ref;
27 qsizetype alloc;
28 qsizetype count;
29 qsizetype start;
30 qsizetype offset;
31
32 static QContiguousCacheData *allocateData(qsizetype size, qsizetype alignment);
33 static void freeData(QContiguousCacheData *data);
34
35#ifdef QT_QCONTIGUOUSCACHE_DEBUG
36 void dump() const;
37#endif
38};
39
40template <typename T>
45
46template<typename T>
48 static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
49
50 typedef QContiguousCacheTypedData<T> Data;
51 Data *d;
52public:
53 // STL compatibility
54 typedef T value_type;
56 typedef const value_type* const_pointer;
61
62 explicit QContiguousCache(qsizetype capacity = 0);
63 QContiguousCache(const QContiguousCache<T> &v) : d(v.d) { d->ref.ref(); }
64
65 inline ~QContiguousCache() { if (!d) return; if (!d->ref.deref()) freeData(d); }
66
67 inline void detach() { if (d->ref.loadRelaxed() != 1) detach_helper(); }
68 inline bool isDetached() const { return d->ref.loadRelaxed() == 1; }
69
70 QContiguousCache<T> &operator=(const QContiguousCache<T> &other);
73
74#ifndef Q_QDOC
75 template <typename U = T>
77 {
78 if (other.d == d)
79 return true;
80 if (other.d->start != d->start
81 || other.d->count != d->count
82 || other.d->offset != d->offset
83 || other.d->alloc != d->alloc)
84 return false;
85 for (qsizetype i = firstIndex(); i <= lastIndex(); ++i)
86 if (!(at(i) == other.at(i)))
87 return false;
88 return true;
89 }
90 template <typename U = T>
92 { return !(*this == other); }
93#else
94 bool operator==(const QContiguousCache &other) const;
95 bool operator!=(const QContiguousCache &other) const;
96#endif // Q_QDOC
97
98 inline qsizetype capacity() const {return d->alloc; }
99 inline qsizetype count() const { return d->count; }
100 inline qsizetype size() const { return d->count; }
101
102 inline bool isEmpty() const { return d->count == 0; }
103 inline bool isFull() const { return d->count == d->alloc; }
104 inline qsizetype available() const { return d->alloc - d->count; }
105
106 void clear();
107 void setCapacity(qsizetype size);
108
109 const T &at(qsizetype pos) const;
110 T &operator[](qsizetype i);
111 const T &operator[](qsizetype i) const;
112
113 void append(T &&value);
114 void append(const T &value);
115 void prepend(T &&value);
116 void prepend(const T &value);
117 void insert(qsizetype pos, T &&value);
118 void insert(qsizetype pos, const T &value);
119
120
121 inline bool containsIndex(qsizetype pos) const { return pos >= d->offset && pos - d->offset < d->count; }
122 inline qsizetype firstIndex() const { return d->offset; }
123 inline qsizetype lastIndex() const { return d->offset + d->count - 1; }
124
125 inline const T &first() const { Q_ASSERT(!isEmpty()); return d->array[d->start]; }
126 inline const T &last() const { Q_ASSERT(!isEmpty()); return d->array[(d->start + d->count -1) % d->alloc]; }
127 inline T &first() { Q_ASSERT(!isEmpty()); detach(); return d->array[d->start]; }
128 inline T &last() { Q_ASSERT(!isEmpty()); detach(); return d->array[(d->start + d->count -1) % d->alloc]; }
129
130 void removeFirst();
131 T takeFirst();
132 void removeLast();
133 T takeLast();
134
135 // Use extra parentheses around max to avoid expanding it if it is a macro.
136 inline bool areIndexesValid() const
137 { return d->offset >= 0 && d->offset < (std::numeric_limits<qsizetype>::max)() - d->count && (d->offset % d->alloc) == d->start; }
138
139 inline void normalizeIndexes() { d->offset = d->start; }
140
141#ifdef QT_QCONTIGUOUSCACHE_DEBUG
142 void dump() const { d->dump(); }
143#endif
144private:
145 void detach_helper();
146
147 Data *allocateData(qsizetype aalloc);
148 void freeData(Data *x);
149};
150
151template <typename T>
152void QContiguousCache<T>::detach_helper()
153{
154 Data *x = allocateData(d->alloc);
155 x->ref.storeRelaxed(1);
156 x->count = d->count;
157 x->start = d->start;
158 x->offset = d->offset;
159 x->alloc = d->alloc;
160
161 T *dest = x->array + x->start;
162 T *src = d->array + d->start;
163 qsizetype oldcount = x->count;
164 while (oldcount--) {
165 new (dest) T(*src);
166 dest++;
167 if (dest == x->array + x->alloc)
168 dest = x->array;
169 src++;
170 if (src == d->array + d->alloc)
171 src = d->array;
172 }
173
174 if (!d->ref.deref())
175 freeData(d);
176 d = x;
177}
178
179template <typename T>
180void QContiguousCache<T>::setCapacity(qsizetype asize)
181{
182 Q_ASSERT(asize >= 0);
183 if (asize == d->alloc)
184 return;
185 detach();
186 Data *x = allocateData(asize);
187 x->ref.storeRelaxed(1);
188 x->alloc = asize;
189 x->count = qMin(d->count, asize);
190 x->offset = d->offset + d->count - x->count;
191 if (asize)
192 x->start = x->offset % x->alloc;
193 else
194 x->start = 0;
195
196 qsizetype oldcount = x->count;
197 if (oldcount)
198 {
199 T *dest = x->array + (x->start + x->count-1) % x->alloc;
200 T *src = d->array + (d->start + d->count-1) % d->alloc;
201 while (oldcount--) {
202 new (dest) T(*src);
203 if (dest == x->array)
204 dest = x->array + x->alloc;
205 dest--;
206 if (src == d->array)
207 src = d->array + d->alloc;
208 src--;
209 }
210 }
211 /* free old */
212 freeData(d);
213 d = x;
214}
215
216template <typename T>
217void QContiguousCache<T>::clear()
218{
219 if (d->ref.loadRelaxed() == 1) {
220 if (QTypeInfo<T>::isComplex) {
221 qsizetype oldcount = d->count;
222 T * i = d->array + d->start;
223 T * e = d->array + d->alloc;
224 while (oldcount--) {
225 i->~T();
226 i++;
227 if (i == e)
228 i = d->array;
229 }
230 }
231 d->count = d->start = d->offset = 0;
232 } else {
233 Data *x = allocateData(d->alloc);
234 x->ref.storeRelaxed(1);
235 x->alloc = d->alloc;
236 x->count = x->start = x->offset = 0;
237 if (!d->ref.deref())
238 freeData(d);
239 d = x;
240 }
241}
242
243template <typename T>
244inline typename QContiguousCache<T>::Data *QContiguousCache<T>::allocateData(qsizetype aalloc)
245{
246 return static_cast<Data *>(QContiguousCacheData::allocateData(sizeof(Data) + (aalloc - 1) * sizeof(T), alignof(Data)));
247}
248
249template <typename T>
251{
252 Q_ASSERT(cap >= 0);
253 d = allocateData(cap);
254 d->ref.storeRelaxed(1);
255 d->alloc = cap;
256 d->count = d->start = d->offset = 0;
257}
258
259template <typename T>
261{
262 other.d->ref.ref();
263 if (!d->ref.deref())
264 freeData(d);
265 d = other.d;
266 return *this;
267}
268
269template <typename T>
270void QContiguousCache<T>::freeData(Data *x)
271{
272 if (QTypeInfo<T>::isComplex) {
273 qsizetype oldcount = d->count;
274 T * i = d->array + d->start;
275 T * e = d->array + d->alloc;
276 while (oldcount--) {
277 i->~T();
278 i++;
279 if (i == e)
280 i = d->array;
281 }
282 }
283 Data::freeData(x);
284}
285template <typename T>
286void QContiguousCache<T>::append(T &&value)
287{
288 if (!d->alloc)
289 return; // zero capacity
290 detach();
291 if (d->count == d->alloc)
292 (d->array + (d->start+d->count) % d->alloc)->~T();
293 new (d->array + (d->start+d->count) % d->alloc) T(std::move(value));
294
295 if (d->count == d->alloc) {
296 d->start++;
297 d->start %= d->alloc;
298 d->offset++;
299 } else {
300 d->count++;
301 }
302}
303
304template <typename T>
305void QContiguousCache<T>::append(const T &value)
306{
307 if (!d->alloc)
308 return; // zero capacity
309 detach();
310 if (d->count == d->alloc)
311 (d->array + (d->start+d->count) % d->alloc)->~T();
312 new (d->array + (d->start+d->count) % d->alloc) T(value);
313
314 if (d->count == d->alloc) {
315 d->start++;
316 d->start %= d->alloc;
317 d->offset++;
318 } else {
319 d->count++;
320 }
321}
322
323template<typename T>
324void QContiguousCache<T>::prepend(T &&value)
325{
326 if (!d->alloc)
327 return; // zero capacity
328 detach();
329 if (d->start)
330 d->start--;
331 else
332 d->start = d->alloc-1;
333 d->offset--;
334
335 if (d->count != d->alloc)
336 d->count++;
337 else
338 (d->array + d->start)->~T();
339
340 new (d->array + d->start) T(std::move(value));
341}
342
343template<typename T>
344void QContiguousCache<T>::prepend(const T &value)
345{
346 if (!d->alloc)
347 return; // zero capacity
348 detach();
349 if (d->start)
350 d->start--;
351 else
352 d->start = d->alloc-1;
353 d->offset--;
354
355 if (d->count != d->alloc)
356 d->count++;
357 else
358 (d->array + d->start)->~T();
359
360 new (d->array + d->start) T(value);
361}
362
363template<typename T>
364void QContiguousCache<T>::insert(qsizetype pos, T &&value)
365{
366 Q_ASSERT_X(pos >= 0, "QContiguousCache<T>::insert", "index out of range");
367 if (!d->alloc)
368 return; // zero capacity
369 detach();
370 if (containsIndex(pos)) {
371 d->array[pos % d->alloc] = std::move(value);
372 } else if (pos == d->offset-1)
373 prepend(value);
374 else if (pos == d->offset+d->count)
375 append(value);
376 else {
377 // we don't leave gaps.
378 clear();
379 d->offset = pos;
380 d->start = pos % d->alloc;
381 d->count = 1;
382 new (d->array + d->start) T(std::move(value));
383 }
384}
385
386template<typename T>
387void QContiguousCache<T>::insert(qsizetype pos, const T &value)
388{
389 return insert(pos, T(value));
390}
391template <typename T>
392inline const T &QContiguousCache<T>::at(qsizetype pos) const
393{ Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache<T>::at", "index out of range"); return d->array[pos % d->alloc]; }
394template <typename T>
395inline const T &QContiguousCache<T>::operator[](qsizetype pos) const
396{ return at(pos); }
397
398template <typename T>
399inline T &QContiguousCache<T>::operator[](qsizetype pos)
400{
401 detach();
402 if (!containsIndex(pos))
403 insert(pos, T());
404 return d->array[pos % d->alloc];
405}
406
407template <typename T>
409{
410 Q_ASSERT(d->count > 0);
411 detach();
412 d->count--;
413 if (QTypeInfo<T>::isComplex)
414 (d->array + d->start)->~T();
415 d->start = (d->start + 1) % d->alloc;
416 d->offset++;
417}
418
419template <typename T>
421{
422 Q_ASSERT(d->count > 0);
423 detach();
424 d->count--;
425 if (QTypeInfo<T>::isComplex)
426 (d->array + (d->start + d->count) % d->alloc)->~T();
427}
428
429template <typename T>
431{ T t = std::move(first()); removeFirst(); return t; }
432
433template <typename T>
435{ T t = std::move(last()); removeLast(); return t; }
436
437QT_END_NAMESPACE
438
439#endif
\inmodule QtCore
T takeFirst()
Removes the first item in the cache and returns it.
value_type & reference
void setCapacity(qsizetype size)
Sets the capacity of the cache to the given size.
~QContiguousCache()
Destroys the cache.
bool areIndexesValid() const
Returns whether the indexes for items stored in the cache are valid.
const T & at(qsizetype pos) const
Returns the item at index position i in the cache.
T & last()
Returns a reference to the last item in the cache.
void removeFirst()
Removes the first item from the cache.
void insert(qsizetype pos, T &&value)
void prepend(T &&value)
void append(const T &value)
Inserts value at the end of the cache.
const T & operator[](qsizetype i) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void prepend(const T &value)
Inserts value at the start of the cache.
const value_type & const_reference
const value_type * const_pointer
const T & first() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QContiguousCache(qsizetype capacity=0)
Constructs a cache with the given capacity.
QContiguousCache< T > & operator=(const QContiguousCache< T > &other)
Assigns other to this cache and returns a reference to this cache.
qsizetype firstIndex() const
Returns the first valid index in the cache.
T & first()
Returns a reference to the first item in the cache.
void insert(qsizetype pos, const T &value)
Inserts the value at the index position i.
bool containsIndex(qsizetype pos) const
Returns true if the cache's index range includes the given index i.
QContiguousCache(const QContiguousCache< T > &v)
Constructs a copy of other.
const T & last() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
T & operator[](qsizetype i)
Returns the item at index position i as a modifiable reference.
void removeLast()
Removes the last item from the cache.
bool isDetached() const
void append(T &&value)
qsizetype lastIndex() const
Returns the last valid index in the cache.
void normalizeIndexes()
Moves the first index and last index of the cache such that they point to valid indexes.
value_type * pointer
T takeLast()
Removes the last item in the cache and returns it.
Combined button and popup list for selecting options.