// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QCACHE_H #define QCACHE_H #include QT_BEGIN_NAMESPACE template class QCache { struct Value { T *t = nullptr; qsizetype cost = 0; Value() noexcept = default; Value(T *tt, qsizetype c) noexcept : t(tt), cost(c) {} Value(Value &&other) noexcept : t(other.t), cost(other.cost) { other.t = nullptr; } Value &operator=(Value &&other) noexcept { qt_ptr_swap(t, other.t); std::swap(cost, other.cost); return *this; } ~Value() { delete t; } private: Q_DISABLE_COPY(Value) }; struct Chain { Chain() noexcept : prev(this), next(this) { } Chain *prev; Chain *next; }; struct Node : public Chain { using KeyType = Key; using ValueType = Value; Key key; Value value; Node(const Key &k, Value &&t) noexcept(std::is_nothrow_move_assignable_v) : Chain(), key(k), value(std::move(t)) { } Node(Key &&k, Value &&t) noexcept(std::is_nothrow_move_assignable_v) : Chain(), key(std::move(k)), value(std::move(t)) { } static void createInPlace(Node *n, const Key &k, T *o, qsizetype cost) { new (n) Node{ Key(k), Value(o, cost) }; } void emplace(T *o, qsizetype cost) { value = Value(o, cost); } Node(Node &&other) : Chain(other), key(std::move(other.key)), value(std::move(other.value)) { Q_ASSERT(this->prev); Q_ASSERT(this->next); this->prev->next = this; this->next->prev = this; } private: Q_DISABLE_COPY(Node) }; using Data = QHashPrivate::Data; mutable Chain chain; Data d; qsizetype mx = 0; qsizetype total = 0; void unlink(Node *n) noexcept(std::is_nothrow_destructible_v) { Q_ASSERT(n->prev); Q_ASSERT(n->next); n->prev->next = n->next; n->next->prev = n->prev; total -= n->value.cost; auto it = d.findBucket(n->key); d.erase(it); } T *relink(const Key &key) const noexcept { if (isEmpty()) return nullptr; Node *n = d.findNode(key); if (!n) return nullptr; if (chain.next != n) { Q_ASSERT(n->prev); Q_ASSERT(n->next); n->prev->next = n->next; n->next->prev = n->prev; n->next = chain.next; chain.next->prev = n; n->prev = &chain; chain.next = n; } return n->value.t; } void trim(qsizetype m) noexcept(std::is_nothrow_destructible_v) { while (chain.prev != &chain && total > m) { Node *n = static_cast(chain.prev); unlink(n); } } Q_DISABLE_COPY(QCache) public: inline explicit QCache(qsizetype maxCost = 100) noexcept : mx(maxCost) { } inline ~QCache() { static_assert(std::is_nothrow_destructible_v, "Types with throwing destructors are not supported in Qt containers."); static_assert(std::is_nothrow_destructible_v, "Types with throwing destructors are not supported in Qt containers."); clear(); } inline qsizetype maxCost() const noexcept { return mx; } void setMaxCost(qsizetype m) noexcept(std::is_nothrow_destructible_v) { mx = m; trim(mx); } inline qsizetype totalCost() const noexcept { return total; } inline qsizetype size() const noexcept { return qsizetype(d.size); } inline qsizetype count() const noexcept { return qsizetype(d.size); } inline bool isEmpty() const noexcept { return !d.size; } inline QList keys() const { QList k; if (size()) { k.reserve(size()); for (auto it = d.begin(); it != d.end(); ++it) k << it.node()->key; } Q_ASSERT(k.size() == size()); return k; } void clear() noexcept(std::is_nothrow_destructible_v) { d.clear(); total = 0; chain.next = &chain; chain.prev = &chain; } bool insert(const Key &key, T *object, qsizetype cost = 1) { if (cost > mx) { remove(key); delete object; return false; } trim(mx - cost); auto result = d.findOrInsert(key); Node *n = result.it.node(); if (result.initialized) { auto prevCost = n->value.cost; result.it.node()->emplace(object, cost); cost -= prevCost; relink(key); } else { Node::createInPlace(n, key, object, cost); n->prev = &chain; n->next = chain.next; chain.next->prev = n; chain.next = n; } total += cost; return true; } T *object(const Key &key) const noexcept { return relink(key); } T *operator[](const Key &key) const noexcept { return relink(key); } inline bool contains(const Key &key) const noexcept { return !isEmpty() && d.findNode(key) != nullptr; } bool remove(const Key &key) noexcept(std::is_nothrow_destructible_v) { if (isEmpty()) return false; Node *n = d.findNode(key); if (!n) { return false; } else { unlink(n); return true; } } T *take(const Key &key) noexcept(std::is_nothrow_destructible_v) { if (isEmpty()) return nullptr; Node *n = d.findNode(key); if (!n) return nullptr; T *t = n->value.t; n->value.t = nullptr; unlink(n); return t; } }; QT_END_NAMESPACE #endif // QCACHE_H