Warning, file /include/QtCore/qcache.h was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004 #ifndef QCACHE_H
0005 #define QCACHE_H
0006
0007 #include <QtCore/qhash.h>
0008
0009 QT_BEGIN_NAMESPACE
0010
0011
0012 template <class Key, class T>
0013 class QCache
0014 {
0015 struct Value
0016 {
0017 T *t = nullptr;
0018 qsizetype cost = 0;
0019 Value() noexcept = default;
0020 Value(T *tt, qsizetype c) noexcept
0021 : t(tt), cost(c)
0022 {}
0023 Value(Value &&other) noexcept
0024 : t(other.t),
0025 cost(other.cost)
0026 {
0027 other.t = nullptr;
0028 }
0029 Value &operator=(Value &&other) noexcept
0030 {
0031 qt_ptr_swap(t, other.t);
0032 std::swap(cost, other.cost);
0033 return *this;
0034 }
0035 ~Value() { delete t; }
0036
0037 private:
0038 Q_DISABLE_COPY(Value)
0039 };
0040
0041 struct Chain
0042 {
0043 Chain() noexcept : prev(this), next(this) { }
0044 Chain *prev;
0045 Chain *next;
0046 };
0047
0048 struct Node : public Chain
0049 {
0050 using KeyType = Key;
0051 using ValueType = Value;
0052
0053 Key key;
0054 Value value;
0055
0056 Node(const Key &k, Value &&t) noexcept(std::is_nothrow_move_assignable_v<Key>)
0057 : Chain(),
0058 key(k),
0059 value(std::move(t))
0060 {
0061 }
0062 Node(Key &&k, Value &&t) noexcept(std::is_nothrow_move_assignable_v<Key>)
0063 : Chain(),
0064 key(std::move(k)),
0065 value(std::move(t))
0066 {
0067 }
0068 static void createInPlace(Node *n, const Key &k, T *o, qsizetype cost)
0069 {
0070 new (n) Node{ Key(k), Value(o, cost) };
0071 }
0072 void emplace(T *o, qsizetype cost)
0073 {
0074 value = Value(o, cost);
0075 }
0076
0077 Node(Node &&other)
0078 : Chain(other),
0079 key(std::move(other.key)),
0080 value(std::move(other.value))
0081 {
0082 Q_ASSERT(this->prev);
0083 Q_ASSERT(this->next);
0084 this->prev->next = this;
0085 this->next->prev = this;
0086 }
0087 private:
0088 Q_DISABLE_COPY(Node)
0089 };
0090
0091 using Data = QHashPrivate::Data<Node>;
0092
0093 mutable Chain chain;
0094 Data d;
0095 qsizetype mx = 0;
0096 qsizetype total = 0;
0097
0098 void unlink(Node *n) noexcept(std::is_nothrow_destructible_v<Node>)
0099 {
0100 Q_ASSERT(n->prev);
0101 Q_ASSERT(n->next);
0102 n->prev->next = n->next;
0103 n->next->prev = n->prev;
0104 total -= n->value.cost;
0105 auto it = d.findBucket(n->key);
0106 d.erase(it);
0107 }
0108 T *relink(const Key &key) const noexcept
0109 {
0110 if (isEmpty())
0111 return nullptr;
0112 Node *n = d.findNode(key);
0113 if (!n)
0114 return nullptr;
0115
0116 if (chain.next != n) {
0117 Q_ASSERT(n->prev);
0118 Q_ASSERT(n->next);
0119 n->prev->next = n->next;
0120 n->next->prev = n->prev;
0121 n->next = chain.next;
0122 chain.next->prev = n;
0123 n->prev = &chain;
0124 chain.next = n;
0125 }
0126 return n->value.t;
0127 }
0128
0129 void trim(qsizetype m) noexcept(std::is_nothrow_destructible_v<Node>)
0130 {
0131 while (chain.prev != &chain && total > m) {
0132 Node *n = static_cast<Node *>(chain.prev);
0133 unlink(n);
0134 }
0135 }
0136
0137
0138 Q_DISABLE_COPY(QCache)
0139
0140 public:
0141 inline explicit QCache(qsizetype maxCost = 100) noexcept
0142 : mx(maxCost)
0143 {
0144 }
0145 inline ~QCache()
0146 {
0147 static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
0148 static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
0149
0150 clear();
0151 }
0152
0153 inline qsizetype maxCost() const noexcept { return mx; }
0154 void setMaxCost(qsizetype m) noexcept(std::is_nothrow_destructible_v<Node>)
0155 {
0156 mx = m;
0157 trim(mx);
0158 }
0159 inline qsizetype totalCost() const noexcept { return total; }
0160
0161 inline qsizetype size() const noexcept { return qsizetype(d.size); }
0162 inline qsizetype count() const noexcept { return qsizetype(d.size); }
0163 inline bool isEmpty() const noexcept { return !d.size; }
0164 inline QList<Key> keys() const
0165 {
0166 QList<Key> k;
0167 if (size()) {
0168 k.reserve(size());
0169 for (auto it = d.begin(); it != d.end(); ++it)
0170 k << it.node()->key;
0171 }
0172 Q_ASSERT(k.size() == size());
0173 return k;
0174 }
0175
0176 void clear() noexcept(std::is_nothrow_destructible_v<Node>)
0177 {
0178 d.clear();
0179 total = 0;
0180 chain.next = &chain;
0181 chain.prev = &chain;
0182 }
0183
0184 bool insert(const Key &key, T *object, qsizetype cost = 1)
0185 {
0186 if (cost > mx) {
0187 remove(key);
0188 delete object;
0189 return false;
0190 }
0191 trim(mx - cost);
0192 auto result = d.findOrInsert(key);
0193 Node *n = result.it.node();
0194 if (result.initialized) {
0195 auto prevCost = n->value.cost;
0196 result.it.node()->emplace(object, cost);
0197 cost -= prevCost;
0198 relink(key);
0199 } else {
0200 Node::createInPlace(n, key, object, cost);
0201 n->prev = &chain;
0202 n->next = chain.next;
0203 chain.next->prev = n;
0204 chain.next = n;
0205 }
0206 total += cost;
0207 return true;
0208 }
0209 T *object(const Key &key) const noexcept
0210 {
0211 return relink(key);
0212 }
0213 T *operator[](const Key &key) const noexcept
0214 {
0215 return relink(key);
0216 }
0217 inline bool contains(const Key &key) const noexcept
0218 {
0219 return !isEmpty() && d.findNode(key) != nullptr;
0220 }
0221
0222 bool remove(const Key &key) noexcept(std::is_nothrow_destructible_v<Node>)
0223 {
0224 if (isEmpty())
0225 return false;
0226 Node *n = d.findNode(key);
0227 if (!n) {
0228 return false;
0229 } else {
0230 unlink(n);
0231 return true;
0232 }
0233 }
0234
0235 T *take(const Key &key) noexcept(std::is_nothrow_destructible_v<Key>)
0236 {
0237 if (isEmpty())
0238 return nullptr;
0239 Node *n = d.findNode(key);
0240 if (!n)
0241 return nullptr;
0242
0243 T *t = n->value.t;
0244 n->value.t = nullptr;
0245 unlink(n);
0246 return t;
0247 }
0248
0249 };
0250
0251 QT_END_NAMESPACE
0252
0253 #endif