MruCache.h (4644B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_MruCache_h 8 #define mozilla_MruCache_h 9 10 #include <type_traits> 11 #include <utility> 12 13 #include "mozilla/Attributes.h" 14 #include "mozilla/HashFunctions.h" 15 16 namespace mozilla { 17 18 namespace detail { 19 20 // Helper struct for checking if a value is empty. 21 // 22 // `IsNotEmpty` will return true if `Value` is not a pointer type or if the 23 // pointer value is not null. 24 template <typename Value> 25 constexpr bool IsNotEmpty(const Value& aVal) { 26 if constexpr (!std::is_pointer_v<Value>) { 27 return true; 28 } else { 29 return aVal != nullptr; 30 } 31 } 32 33 } // namespace detail 34 35 // Provides a most recently used cache that can be used as a layer on top of 36 // a larger container where lookups can be expensive. The default size is 31, 37 // which as a prime number provides a better distrubution of cached entries. 38 // 39 // Users are expected to provide a `Cache` class that defines two required 40 // methods: 41 // - A method for providing the hash of a key: 42 // 43 // static HashNumber Hash(const KeyType& aKey) 44 // 45 // - A method for matching a key to a value, for pointer types the value 46 // is guaranteed not to be null. 47 // 48 // static bool Match(const KeyType& aKey, const ValueType& aVal) 49 // 50 // For example: 51 // class MruExample : public MruCache<void*, PtrInfo*, MruExample> 52 // { 53 // static HashNumber Hash(const KeyType& aKey) 54 // { 55 // return HashGeneric(aKey); 56 // } 57 // static Match(const KeyType& aKey, const ValueType& aVal) 58 // { 59 // return aVal->mPtr == aKey; 60 // } 61 // }; 62 template <class Key, class Value, class Cache, size_t Size = 31> 63 class MruCache { 64 // Best distribution is achieved with a prime number. Ideally the closest 65 // to a power of two will be the most efficient use of memory. This 66 // assertion is pretty weak, but should catch the common inclination to 67 // use a power-of-two. 68 static_assert(Size % 2 != 0, "Use a prime number"); 69 70 // This is a stronger assertion but significantly limits the values to just 71 // those close to a power-of-two value. 72 // static_assert(Size == 7 || Size == 13 || Size == 31 || Size == 61 || 73 // Size == 127 || Size == 251 || Size == 509 || Size == 1021, 74 // "Use a prime number less than 1024"); 75 76 public: 77 using KeyType = Key; 78 using ValueType = Value; 79 80 MruCache() = default; 81 MruCache(const MruCache&) = delete; 82 MruCache(const MruCache&&) = delete; 83 84 // Inserts the given value into the cache. Potentially overwrites an 85 // existing entry. 86 template <typename U> 87 void Put(const KeyType& aKey, U&& aVal) { 88 *RawEntry(aKey) = std::forward<U>(aVal); 89 } 90 91 // Removes the given entry if it is in the cache. 92 void Remove(const KeyType& aKey) { Lookup(aKey).Remove(); } 93 94 // Clears all cached entries and resets them to a default value. 95 void Clear() { 96 for (ValueType& val : mCache) { 97 val = ValueType{}; 98 } 99 } 100 101 // Helper that holds an entry that matched a lookup key. Usage: 102 // 103 // auto p = mCache.Lookup(aKey); 104 // if (p) { 105 // return p.Data(); 106 // } 107 // 108 // auto foo = new Foo(); 109 // p.Set(foo); 110 // return foo; 111 class Entry { 112 public: 113 Entry(ValueType* aEntry, bool aMatch) : mEntry(aEntry), mMatch(aMatch) { 114 MOZ_ASSERT(mEntry); 115 } 116 117 explicit operator bool() const { return mMatch; } 118 119 ValueType& Data() const { 120 MOZ_ASSERT(mMatch); 121 return *mEntry; 122 } 123 124 template <typename U> 125 void Set(U&& aValue) { 126 mMatch = true; 127 Data() = std::forward<U>(aValue); 128 } 129 130 void Remove() { 131 if (mMatch) { 132 Data() = ValueType{}; 133 mMatch = false; 134 } 135 } 136 137 private: 138 ValueType* mEntry; // Location of the entry in the cache. 139 bool mMatch; // Whether the value matched. 140 }; 141 142 // Retrieves an entry from the cache. Can be used to test if an entry is 143 // present, update the entry to a new value, or remove the entry if one was 144 // matched. 145 Entry Lookup(const KeyType& aKey) { 146 auto entry = RawEntry(aKey); 147 bool match = detail::IsNotEmpty(*entry) && Cache::Match(aKey, *entry); 148 return Entry(entry, match); 149 } 150 151 private: 152 MOZ_ALWAYS_INLINE ValueType* RawEntry(const KeyType& aKey) { 153 return &mCache[Cache::Hash(aKey) % Size]; 154 } 155 156 ValueType mCache[Size] = {}; 157 }; 158 159 } // namespace mozilla 160 161 #endif // mozilla_mrucache_h