tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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