Blob Blame History Raw
/* -*- Mode: C++; tab-width: 13; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=13 sts=4 et sw=4 tw=90: */
/* 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_MAP_H_
#define MOZILLA_CACHE_MAP_H_

#include "mozilla/UniquePtr.h"
#include <map>
#include <unordered_set>
#include <vector>

namespace mozilla {

namespace detail {
class CacheMapUntypedEntry;
}

class CacheMapInvalidator {
  friend class detail::CacheMapUntypedEntry;

  mutable std::unordered_set<const detail::CacheMapUntypedEntry*> mCacheEntries;

 public:
  ~CacheMapInvalidator() { InvalidateCaches(); }

  void InvalidateCaches() const;
};

namespace detail {

class CacheMapUntypedEntry {
  template <typename, typename>
  friend class CacheMap;

 private:
  const std::vector<const CacheMapInvalidator*> mInvalidators;

 protected:
  CacheMapUntypedEntry(std::vector<const CacheMapInvalidator*>&& invalidators);
  ~CacheMapUntypedEntry();

 public:
  virtual void Invalidate() const = 0;
};

struct DerefLess final {
  template <typename T>
  bool operator()(const T* const a, const T* const b) const {
    return *a < *b;
  }
};

}  // namespace detail

template <typename KeyT, typename ValueT>
class CacheMap final {
  class Entry final : public detail::CacheMapUntypedEntry {
   public:
    CacheMap& mParent;
    const KeyT mKey;
    const ValueT mValue;

    Entry(std::vector<const CacheMapInvalidator*>&& invalidators,
          CacheMap& parent, KeyT&& key, ValueT&& value)
        : detail::CacheMapUntypedEntry(Move(invalidators)),
          mParent(parent),
          mKey(Move(key)),
          mValue(Move(value)) {}

    void Invalidate() const override {
      const auto erased = mParent.mMap.erase(&mKey);
      MOZ_ALWAYS_TRUE(erased == 1);
    }

    bool operator<(const Entry& x) const { return mKey < x.mKey; }
  };

  typedef std::map<const KeyT*, UniquePtr<const Entry>, detail::DerefLess> MapT;
  MapT mMap;

 public:
  const ValueT* Insert(KeyT&& key, ValueT&& value,
                       std::vector<const CacheMapInvalidator*>&& invalidators) {
    UniquePtr<const Entry> entry(
        new Entry(Move(invalidators), *this, Move(key), Move(value)));

    typename MapT::value_type insertable{&entry->mKey, nullptr};
    insertable.second = Move(entry);

    const auto res = mMap.insert(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 Invalidate() {
    while (mMap.size()) {
      const auto& itr = mMap.begin();
      itr->second->Invalidate();
    }
  }
};

}  // namespace mozilla

#endif  // MOZILLA_CACHE_MAP_H_