From e1e573cee8f0197a1549929dc9e818f8004fb1cb Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 23 Jan 2020 14:59:35 +0100 Subject: new QCache implementation Make use of the new features available in QHash and do a more performant implementation than the old one. Change-Id: Ie74b3cdcc9871cd241aca205672093dc395d04a7 Reviewed-by: Thiago Macieira --- src/corelib/tools/qcache.h | 325 ++++++++++++++++++++++++++++----------------- 1 file changed, 204 insertions(+), 121 deletions(-) (limited to 'src/corelib/tools/qcache.h') diff --git a/src/corelib/tools/qcache.h b/src/corelib/tools/qcache.h index 4fcde46fbc..f043ea9cff 100644 --- a/src/corelib/tools/qcache.h +++ b/src/corelib/tools/qcache.h @@ -48,151 +48,234 @@ QT_BEGIN_NAMESPACE template class QCache { - struct Node { - inline Node() : keyPtr(0) {} - inline Node(T *data, int cost) - : keyPtr(nullptr), t(data), c(cost), p(nullptr), n(nullptr) {} - const Key *keyPtr; T *t; int c; Node *p,*n; + struct Value { + T *t = nullptr; + int cost = 0; + Value() noexcept = default; + Value(T *tt, int c) noexcept + : t(tt), cost(c) + {} + Value(Value &&other) noexcept + : t(other.t), + cost(other.cost) + { + other.t = nullptr; + } + Value &operator=(Value &&other) noexcept + { + qSwap(t, other.t); + qSwap(cost, other.cost); + return *this; + } + ~Value() { delete t; } + private: + Q_DISABLE_COPY(Value) }; - Node *f, *l; - QHash hash; - int mx, total; - - inline void unlink(Node &n) { - if (n.p) n.p->n = n.n; - if (n.n) n.n->p = n.p; - if (l == &n) l = n.p; - if (f == &n) f = n.n; - total -= n.c; - T *obj = n.t; - hash.remove(*n.keyPtr); - delete obj; - } - inline T *relink(const Key &key) { - typename QHash::iterator i = hash.find(key); - if (typename QHash::const_iterator(i) == hash.constEnd()) - return nullptr; - Node &n = *i; - if (f != &n) { - if (n.p) n.p->n = n.n; - if (n.n) n.n->p = n.p; - if (l == &n) l = n.p; - n.p = nullptr; - n.n = f; - f->p = &n; - f = &n; + 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)) + { } - return n.t; - } + Node(Key &&k, Value &&t) noexcept(std::is_nothrow_move_assignable_v) + : Chain(), + key(std::move(k)), + value(std::move(t)) + { + } + static Node create(Key &&k, Value &&t) noexcept(std::is_nothrow_move_assignable_v && std::is_nothrow_move_assignable_v) + { + return Node(std::move(k), std::move(t)); + } + static Node create(const Key &k, Value &&t) noexcept(std::is_nothrow_move_assignable_v && std::is_nothrow_move_assignable_v) + { + return Node(k, std::move(t)); + } + void replace(const Value &t) noexcept(std::is_nothrow_assignable_v) + { + value = t; + } + void replace(Value &&t) noexcept(std::is_nothrow_move_assignable_v) + { + value = std::move(t); + } + Value takeValue() noexcept(std::is_nothrow_move_constructible_v) + { + return std::move(value); + } + bool valuesEqual(const Node *other) const { return value == other->value; } - Q_DISABLE_COPY(QCache) + 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) + }; -public: - inline explicit QCache(int maxCost = 100) noexcept; - inline ~QCache() { clear(); } + using Data = QHashPrivate::Data; - inline int maxCost() const { return mx; } - void setMaxCost(int m); - inline int totalCost() const { return total; } + mutable Chain chain; + Data d; + int mx = 0; + int total = 0; - inline int size() const { return hash.size(); } - inline int count() const { return hash.size(); } - inline bool isEmpty() const { return hash.isEmpty(); } - inline QList keys() const { return hash.keys(); } + 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.find(n->key); + d.erase(it); + } + T *relink(const Key &key) const noexcept + { + Node *n = d.findNode(key); + if (!n) + return nullptr; - void clear(); + 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; + } - bool insert(const Key &key, T *object, int cost = 1); - T *object(const Key &key) const; - inline bool contains(const Key &key) const { return hash.contains(key); } - T *operator[](const Key &key) const; + void trim(int m) noexcept(std::is_nothrow_destructible_v) + { + Chain *n = chain.prev; + while (n != &chain && total > m) { + Node *u = static_cast(n); + n = n->prev; + unlink(u); + } + } - bool remove(const Key &key); - T *take(const Key &key); -private: - void trim(int m); -}; + Q_DISABLE_COPY(QCache) -template -inline QCache::QCache(int amaxCost) noexcept - : f(nullptr), l(nullptr), mx(amaxCost), total(0) {} +public: + inline explicit QCache(int maxCost = 100) noexcept + : mx(maxCost) + {} + inline ~QCache() { clear(); } -template -inline void QCache::clear() -{ while (f) { delete f->t; f = f->n; } - hash.clear(); l = nullptr; total = 0; } + inline int maxCost() const noexcept { return mx; } + void setMaxCost(int m) noexcept(std::is_nothrow_destructible_v) + { + mx = m; + trim(mx); + } + inline int totalCost() const noexcept { return total; } -template -inline void QCache::setMaxCost(int m) -{ mx = m; trim(mx); } + inline int size() const noexcept { return d.size; } + inline int count() const noexcept { return d.size; } + inline bool isEmpty() const noexcept { return !d.size; } + inline QVector keys() const + { + QVector k; + if (d.size) { + k.reserve(d.size); + for (auto it = d.begin(); it != d.end(); ++it) + k << it.node()->key; + } + Q_ASSERT(k.size() == qsizetype(d.size)); + return k; + } -template -inline T *QCache::object(const Key &key) const -{ return const_cast*>(this)->relink(key); } + void clear() noexcept(std::is_nothrow_destructible_v) + { + d.clear(); + total = 0; + chain.next = &chain; + chain.prev = &chain; + } -template -inline T *QCache::operator[](const Key &key) const -{ return object(key); } + bool insert(const Key &key, T *object, int cost = 1) + { + remove(key); -template -inline bool QCache::remove(const Key &key) -{ - typename QHash::iterator i = hash.find(key); - if (typename QHash::const_iterator(i) == hash.constEnd()) { - return false; - } else { - unlink(*i); + if (cost > mx) { + delete object; + return false; + } + trim(mx - cost); + auto it = d.insert(Node::create(Key(key), Value(object, cost))); + total += cost; + Node *n = it.node(); + n->prev = &chain; + n->next = chain.next; + chain.next->prev = n; + chain.next = n; 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 d.findNode(key) != nullptr; + } -template -inline T *QCache::take(const Key &key) -{ - typename QHash::iterator i = hash.find(key); - if (i == hash.end()) - return nullptr; + bool remove(const Key &key) noexcept(std::is_nothrow_destructible_v) + { + Node *n = d.findNode(key); + if (!n) { + return false; + } else { + unlink(n); + return true; + } + } - Node &n = *i; - T *t = n.t; - n.t = nullptr; - unlink(n); - return t; -} + T *take(const Key &key) noexcept(std::is_nothrow_destructible_v) + { + Node *n = d.findNode(key); + if (!n) + return nullptr; -template -bool QCache::insert(const Key &akey, T *aobject, int acost) -{ - remove(akey); - if (acost > mx) { - delete aobject; - return false; + T *t = n->value.t; + n->value.t = nullptr; + unlink(n); + return t; } - trim(mx - acost); - Node sn(aobject, acost); - typename QHash::iterator i = hash.insert(akey, sn); - total += acost; - Node *n = &i.value(); - n->keyPtr = &i.key(); - if (f) f->p = n; - n->n = f; - f = n; - if (!l) l = f; - return true; -} -template -void QCache::trim(int m) -{ - Node *n = l; - while (n && total > m) { - Node *u = n; - n = n->p; - unlink(*u); - } -} +}; QT_END_NAMESPACE -- cgit v1.2.3