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// Qt-Security score:significant reason:default
4
5#ifndef QCONTIGUOUSCACHE_H
6#define QCONTIGUOUSCACHE_H
7
8#include <QtCore/qatomic.h>
9#include <QtCore/qassert.h>
10#include <QtCore/qtclasshelpermacros.h>
11#include <QtCore/qtcoreexports.h>
12#include <QtCore/qminmax.h>
13#include <QtCore/qttypetraits.h>
14#include <QtCore/qtypeinfo.h>
15
16#include <climits>
17#include <limits>
18#include <new>
19
21
22#undef QT_QCONTIGUOUSCACHE_DEBUG
23
24
26{
27 QBasicAtomicInt ref;
28 qsizetype alloc;
29 qsizetype count;
30 qsizetype start;
31 qsizetype offset;
32
33 static QContiguousCacheData *allocateData(qsizetype size, qsizetype alignment);
34 static void freeData(QContiguousCacheData *data);
35
36#ifdef QT_QCONTIGUOUSCACHE_DEBUG
37 void dump() const;
38#endif
39};
40
41template <typename T>
46
47template<typename T>
49 static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
50
51 typedef QContiguousCacheTypedData<T> Data;
52 Data *d;
53public:
54 // STL compatibility
55 typedef T value_type;
57 typedef const value_type* const_pointer;
62
63 explicit QContiguousCache(qsizetype capacity = 0);
64 QContiguousCache(const QContiguousCache<T> &v) : d(v.d) { d->ref.ref(); }
65
66 inline ~QContiguousCache() { if (!d) return; if (!d->ref.deref()) freeData(d); }
67
68 inline void detach() { if (d->ref.loadRelaxed() != 1) detach_helper(); }
69 inline bool isDetached() const { return d->ref.loadRelaxed() == 1; }
70
71 QContiguousCache<T> &operator=(const QContiguousCache<T> &other);
74
75#ifndef Q_QDOC
76 template <typename U = T>
78 {
79 if (other.d == d)
80 return true;
81 if (other.d->start != d->start
82 || other.d->count != d->count
83 || other.d->offset != d->offset
84 || other.d->alloc != d->alloc)
85 return false;
86 for (qsizetype i = firstIndex(); i <= lastIndex(); ++i)
87 if (!(at(i) == other.at(i)))
88 return false;
89 return true;
90 }
91 template <typename U = T>
93 { return !(*this == other); }
94#else
95 bool operator==(const QContiguousCache &other) const;
96 bool operator!=(const QContiguousCache &other) const;
97#endif // Q_QDOC
98
99 inline qsizetype capacity() const {return d->alloc; }
100 inline qsizetype count() const { return d->count; }
101 inline qsizetype size() const { return d->count; }
102
103 inline bool isEmpty() const { return d->count == 0; }
104 inline bool isFull() const { return d->count == d->alloc; }
105 inline qsizetype available() const { return d->alloc - d->count; }
106
107 void clear();
108 void setCapacity(qsizetype size);
109
110 const T &at(qsizetype pos) const;
111 T &operator[](qsizetype i);
112 const T &operator[](qsizetype i) const;
113
114 void append(T &&value);
115 void append(const T &value);
116 void prepend(T &&value);
117 void prepend(const T &value);
118 void insert(qsizetype pos, T &&value);
119 void insert(qsizetype pos, const T &value);
120
121
122 inline bool containsIndex(qsizetype pos) const { return pos >= d->offset && pos - d->offset < d->count; }
123 inline qsizetype firstIndex() const { return d->offset; }
124 inline qsizetype lastIndex() const { return d->offset + d->count - 1; }
125
126 inline const T &first() const { Q_ASSERT(!isEmpty()); return d->array[d->start]; }
127 inline const T &last() const { Q_ASSERT(!isEmpty()); return d->array[(d->start + d->count -1) % d->alloc]; }
128 inline T &first() { Q_ASSERT(!isEmpty()); detach(); return d->array[d->start]; }
129 inline T &last() { Q_ASSERT(!isEmpty()); detach(); return d->array[(d->start + d->count -1) % d->alloc]; }
130
131 void removeFirst();
132 T takeFirst();
133 void removeLast();
134 T takeLast();
135
136 // Use extra parentheses around max to avoid expanding it if it is a macro.
137 inline bool areIndexesValid() const
138 { return d->offset >= 0 && d->offset < (std::numeric_limits<qsizetype>::max)() - d->count && (d->offset % d->alloc) == d->start; }
139
140 inline void normalizeIndexes() { d->offset = d->start; }
141
142#ifdef QT_QCONTIGUOUSCACHE_DEBUG
143 void dump() const { d->dump(); }
144#endif
145private:
146 void detach_helper();
147
148 Data *allocateData(qsizetype aalloc);
149 void freeData(Data *x);
150};
151
152template <typename T>
153void QContiguousCache<T>::detach_helper()
154{
155 Data *x = allocateData(d->alloc);
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->alloc = asize;
188 x->count = qMin(d->count, asize);
189 x->offset = d->offset + d->count - x->count;
190 if (asize)
191 x->start = x->offset % x->alloc;
192 else
193 x->start = 0;
194
195 qsizetype oldcount = x->count;
196 if (oldcount)
197 {
198 T *dest = x->array + (x->start + x->count-1) % x->alloc;
199 T *src = d->array + (d->start + d->count-1) % d->alloc;
200 while (oldcount--) {
201 new (dest) T(*src);
202 if (dest == x->array)
203 dest = x->array + x->alloc;
204 dest--;
205 if (src == d->array)
206 src = d->array + d->alloc;
207 src--;
208 }
209 }
210 /* free old */
211 freeData(d);
212 d = x;
213}
214
215template <typename T>
216void QContiguousCache<T>::clear()
217{
218 if (d->ref.loadRelaxed() == 1) {
219 if (QTypeInfo<T>::isComplex) {
220 qsizetype oldcount = d->count;
221 T * i = d->array + d->start;
222 T * e = d->array + d->alloc;
223 while (oldcount--) {
224 i->~T();
225 i++;
226 if (i == e)
227 i = d->array;
228 }
229 }
230 d->count = d->start = d->offset = 0;
231 } else {
232 Data *x = allocateData(d->alloc);
233 x->alloc = d->alloc;
234 x->count = x->start = x->offset = 0;
235 if (!d->ref.deref())
236 freeData(d);
237 d = x;
238 }
239}
240
241template <typename T>
242inline typename QContiguousCache<T>::Data *QContiguousCache<T>::allocateData(qsizetype aalloc)
243{
244 return static_cast<Data *>(QContiguousCacheData::allocateData(sizeof(Data) + (aalloc - 1) * sizeof(T), alignof(Data)));
245}
246
247template <typename T>
249{
250 Q_ASSERT(cap >= 0);
251 d = allocateData(cap);
252 d->alloc = cap;
253 d->count = d->start = d->offset = 0;
254}
255
256template <typename T>
258{
259 other.d->ref.ref();
260 if (!d->ref.deref())
261 freeData(d);
262 d = other.d;
263 return *this;
264}
265
266template <typename T>
267void QContiguousCache<T>::freeData(Data *x)
268{
269 if (QTypeInfo<T>::isComplex) {
270 qsizetype oldcount = d->count;
271 T * i = d->array + d->start;
272 T * e = d->array + d->alloc;
273 while (oldcount--) {
274 i->~T();
275 i++;
276 if (i == e)
277 i = d->array;
278 }
279 }
280 Data::freeData(x);
281}
282template <typename T>
283void QContiguousCache<T>::append(T &&value)
284{
285 if (!d->alloc)
286 return; // zero capacity
287 detach();
288 if (d->count == d->alloc)
289 (d->array + (d->start+d->count) % d->alloc)->~T();
290 new (d->array + (d->start+d->count) % d->alloc) T(std::move(value));
291
292 if (d->count == d->alloc) {
293 d->start++;
294 d->start %= d->alloc;
295 d->offset++;
296 } else {
297 d->count++;
298 }
299}
300
301template <typename T>
302void QContiguousCache<T>::append(const T &value)
303{
304 if (!d->alloc)
305 return; // zero capacity
306 detach();
307 if (d->count == d->alloc)
308 (d->array + (d->start+d->count) % d->alloc)->~T();
309 new (d->array + (d->start+d->count) % d->alloc) T(value);
310
311 if (d->count == d->alloc) {
312 d->start++;
313 d->start %= d->alloc;
314 d->offset++;
315 } else {
316 d->count++;
317 }
318}
319
320template<typename T>
321void QContiguousCache<T>::prepend(T &&value)
322{
323 if (!d->alloc)
324 return; // zero capacity
325 detach();
326 if (d->start)
327 d->start--;
328 else
329 d->start = d->alloc-1;
330 d->offset--;
331
332 if (d->count != d->alloc)
333 d->count++;
334 else
335 (d->array + d->start)->~T();
336
337 new (d->array + d->start) T(std::move(value));
338}
339
340template<typename T>
341void QContiguousCache<T>::prepend(const T &value)
342{
343 if (!d->alloc)
344 return; // zero capacity
345 detach();
346 if (d->start)
347 d->start--;
348 else
349 d->start = d->alloc-1;
350 d->offset--;
351
352 if (d->count != d->alloc)
353 d->count++;
354 else
355 (d->array + d->start)->~T();
356
357 new (d->array + d->start) T(value);
358}
359
360template<typename T>
361void QContiguousCache<T>::insert(qsizetype pos, T &&value)
362{
363 Q_ASSERT_X(pos >= 0, "QContiguousCache<T>::insert", "index out of range");
364 if (!d->alloc)
365 return; // zero capacity
366 detach();
367 if (containsIndex(pos)) {
368 d->array[pos % d->alloc] = std::move(value);
369 } else if (pos == d->offset-1)
370 prepend(value);
371 else if (pos == d->offset+d->count)
372 append(value);
373 else {
374 // we don't leave gaps.
375 clear();
376 d->offset = pos;
377 d->start = pos % d->alloc;
378 d->count = 1;
379 new (d->array + d->start) T(std::move(value));
380 }
381}
382
383template<typename T>
384void QContiguousCache<T>::insert(qsizetype pos, const T &value)
385{
386 return insert(pos, T(value));
387}
388template <typename T>
389inline const T &QContiguousCache<T>::at(qsizetype pos) const
390{ Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache<T>::at", "index out of range"); return d->array[pos % d->alloc]; }
391template <typename T>
392inline const T &QContiguousCache<T>::operator[](qsizetype pos) const
393{ return at(pos); }
394
395template <typename T>
396inline T &QContiguousCache<T>::operator[](qsizetype pos)
397{
398 detach();
399 if (!containsIndex(pos))
400 insert(pos, T());
401 return d->array[pos % d->alloc];
402}
403
404template <typename T>
406{
407 Q_ASSERT(d->count > 0);
408 detach();
409 d->count--;
410 if (QTypeInfo<T>::isComplex)
411 (d->array + d->start)->~T();
412 d->start = (d->start + 1) % d->alloc;
413 d->offset++;
414}
415
416template <typename T>
418{
419 Q_ASSERT(d->count > 0);
420 detach();
421 d->count--;
422 if (QTypeInfo<T>::isComplex)
423 (d->array + (d->start + d->count) % d->alloc)->~T();
424}
425
426template <typename T>
428{ T t = std::move(first()); removeFirst(); return t; }
429
430template <typename T>
432{ T t = std::move(last()); removeLast(); return t; }
433
434QT_END_NAMESPACE
435
436#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.