diff options
Diffstat (limited to 'dom/canvas/CacheInvalidator.h')
-rw-r--r-- | dom/canvas/CacheInvalidator.h | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/dom/canvas/CacheInvalidator.h b/dom/canvas/CacheInvalidator.h new file mode 100644 index 0000000000..e63f1e4cc2 --- /dev/null +++ b/dom/canvas/CacheInvalidator.h @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 13; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=13 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_CACHE_INVALIDATOR_H_ +#define MOZILLA_CACHE_INVALIDATOR_H_ + +#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" +#include <unordered_map> +#include <unordered_set> +#include <vector> + +// - + +namespace mozilla { + +class AbstractCache; + +// - + +class CacheInvalidator { + friend class AbstractCache; + + private: + mutable std::unordered_set<AbstractCache*> mCaches; + + public: + virtual ~CacheInvalidator() { + // It's actually generally unsafe to wait until now to invalidate caches, + // because when used as a mixin, this dtor is called after the dtor for the + // derived class. This means that if the derived class holds a cache (or is + // a cache!), OnInvalidate() will be called on a destroyed object. + // MOZ_ASSERT(!mCaches); + InvalidateCaches(); + } + + void InvalidateCaches() const; +}; + +// - + +class AbstractCache { + using InvalidatorListT = std::vector<const CacheInvalidator*>; + + private: + InvalidatorListT mInvalidators; + + public: + AbstractCache() = default; + + explicit AbstractCache(InvalidatorListT&& invalidators) { + ResetInvalidators(std::move(invalidators)); + } + + virtual ~AbstractCache() { ResetInvalidators({}); } + + public: + virtual void OnInvalidate() = 0; + + void ResetInvalidators(InvalidatorListT&&); + void AddInvalidator(const CacheInvalidator&); +}; + +// - + +template <typename T> +class CacheMaybe : public AbstractCache { + Maybe<T> mVal; + + public: + template <typename U> + CacheMaybe& operator=(Maybe<U>&& rhs) { + mVal.reset(); + if (rhs) { + mVal.emplace(std::move(rhs.ref())); + } + return *this; + } + + CacheMaybe& operator=(Nothing) { return *this = Maybe<T>(); } + + void OnInvalidate() override { + *this = Nothing(); + ResetInvalidators({}); + } + + explicit operator bool() const { return bool(mVal); } + T* get() const { return mVal.ptrOr(nullptr); } + T* operator->() const { return get(); } +}; + +// - + +template <typename KeyT, typename ValueT> +class CacheWeakMap final { + class Entry final : public AbstractCache { + public: + CacheWeakMap& mParent; + const KeyT mKey; + const ValueT mValue; + + Entry(CacheWeakMap& parent, const KeyT& key, ValueT&& value) + : mParent(parent), mKey(key), mValue(std::move(value)) {} + + void OnInvalidate() override { + const auto erased = mParent.mMap.erase(&mKey); + MOZ_ALWAYS_TRUE(erased == 1); + } + }; + + struct DerefHash final { + size_t operator()(const KeyT* const a) const { + return std::hash<KeyT>()(*a); + } + }; + struct DerefEqual final { + bool operator()(const KeyT* const a, const KeyT* const b) const { + return *a == *b; + } + }; + + using MapT = + std::unordered_map<const KeyT*, UniquePtr<Entry>, DerefHash, DerefEqual>; + MapT mMap; + + public: + UniquePtr<Entry> MakeEntry(const KeyT& key, ValueT&& value) { + return UniquePtr<Entry>(new Entry(*this, key, std::move(value))); + } + UniquePtr<Entry> MakeEntry(const KeyT& key, const ValueT& value) { + return MakeEntry(key, ValueT(value)); + } + + const ValueT* Insert(UniquePtr<Entry>&& entry) { + auto insertable = typename MapT::value_type{&entry->mKey, std::move(entry)}; + + const auto res = mMap.insert(std::move(insertable)); + const auto& didInsert = res.second; + MOZ_ALWAYS_TRUE(didInsert); + + const auto& itr = res.first; + return &itr->second->mValue; + } + + const ValueT* Find(const KeyT& key) const { + const auto itr = mMap.find(&key); + if (itr == mMap.end()) return nullptr; + + return &itr->second->mValue; + } + + void Clear() const { + while (true) { + const auto itr = mMap.begin(); + if (itr == mMap.end()) return; + itr->second->OnInvalidate(); + } + } + + ~CacheWeakMap() { Clear(); } +}; + +} // namespace mozilla + +#endif // MOZILLA_CACHE_INVALIDATOR_H_ |