Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,494 changes: 474 additions & 1,020 deletions include/cachemere/cache.h

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion include/cachemere/composite_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ template<typename... Keys> class CompositeKey
template<typename H, typename T> static void HashTuple(H& h, const T& key)
{
if constexpr (detail::traits::BasicString<T>) {
h = H::combine_contiguous(std::move(h), key.data(), key.size());
h = H::combine_contiguous(std::move(h), key.c_str(), key.size());
} else if constexpr (detail::traits::BasicStringView<T>) {
h = H::combine_contiguous(std::move(h), key.data(), key.size());
} else {
Expand Down
31 changes: 31 additions & 0 deletions include/cachemere/detail/traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <utility>

#include <cachemere/item.h>
#include <cachemere/detail/transparent_eq.h>

namespace cachemere::detail::traits {

Expand Down Expand Up @@ -82,4 +83,34 @@ concept HasOnEvict = requires(P<K, KH, V> policy, K key, Item<V> item) {

} // namespace event

template<typename T>
concept Key = std::movable<T> && std::move_constructible<T> && std::assignable_from<T&, T&&> && requires(T t) {
{ t == t } -> std::convertible_to<bool>;
};

template<typename T, typename V>
concept HasherFor = std::default_initializable<T> && requires(T t, V v) {
{ t(v) } -> std::convertible_to<size_t>;
};

template<typename KeyView, typename Key, typename KeyHash>
concept LookupKeyFor = HasherFor<KeyHash, KeyView> && requires(KeyHash hash, KeyView key_view, Key key) {
{ TransparentEq<Key>{}(key, key_view) } -> std::same_as<bool>;
};

template<typename T, typename V>
concept MeasureFor = std::default_initializable<T> && requires(T t, V v) {
{ t(v) } -> std::convertible_to<size_t>;
};
Comment thread
dalloriam marked this conversation as resolved.

template<typename T, typename Key, typename Value>
concept PredicateFn = requires(T t, Key key, Value value) {
{ t(key, value) } -> std::same_as<bool>;
};

template<typename T, typename Key, typename Value>
concept UnaryFn = requires(T t, Key key, Value value) {
{ t(key, value) } -> std::same_as<void>;
};

} // namespace cachemere::detail::traits
109 changes: 45 additions & 64 deletions include/cachemere/policy/constraint_count.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <cassert>

#include <cachemere/item.h>
#include <cachemere/detail/traits.h>

namespace cachemere::policy {

Expand All @@ -11,7 +12,7 @@ namespace cachemere::policy {
/// @tparam Key The type of the keys used to identify items in the cache.
/// @tparam KeyHash The type of the hasher used to hash item keys.
/// @tparam Value The type of the values stored in the cache.
template<typename Key, typename KeyHash, typename Value> class ConstraintCount
template<cachemere::detail::traits::Key Key, cachemere::detail::traits::HasherFor<Key> KeyHash, typename Value> class ConstraintCount
{
Comment thread
dalloriam marked this conversation as resolved.
using CacheItem = Item<Value>;

Expand Down Expand Up @@ -64,100 +65,80 @@ template<typename Key, typename KeyHash, typename Value> class ConstraintCount
}
};

explicit ConstraintCount(size_t maximum_count);
explicit ConstraintCount(size_t maximum_count)
{
update(maximum_count);
}

/// @brief Clears the policy.
void clear();
void clear()
{
m_count = 0;
}

[[nodiscard]] InsertionTicket prepare_insert(const Key& /* key */, const CacheItem& /* item */)
{
const size_t count_to_free = (m_count + 1 > m_maximum_count) ? ((m_count + 1) - m_maximum_count) : 0;
return InsertionTicket{count_to_free, m_maximum_count > 0};
}

[[nodiscard]] InsertionTicket prepare_insert(const Key& key, const CacheItem& item);
[[nodiscard]] ReplacementTicket prepare_replace(const Key& key, const CacheItem& old_item, const CacheItem& new_item);
[[nodiscard]] ReplacementTicket prepare_replace(const Key& /* key */, const CacheItem& /* old_item */, const CacheItem& /* new_item */)
{
return ReplacementTicket{};
}

/// @brief Returns whether the constraint is satisfied.
/// @details Used by the cache after a constraint update to compute how many items should be evicted, if any.
/// @return Whether the cache constraint is satisfied.
[[nodiscard]] bool is_satisfied();
[[nodiscard]] bool is_satisfied()
{
return m_count <= m_maximum_count;
}

/// @brief Update the cache constraint.
/// @details Sets a new maximum count.
/// @param maximum_count The new number of items in cache.
void update(size_t maximum_count);
void update(size_t maximum_count)
{
m_maximum_count = maximum_count;
}

/// @brief Insertion event handler.
/// @details Adds one to the number of items in cache.
/// @param key The key of the inserted item.
/// @param item The item that has been inserted in cache.
void on_insert(const Key& key, const CacheItem& item);
void on_insert(const Key& /* key */, const CacheItem& /* item */)
{
++m_count;
}

/// @brief Eviction event handler.
/// @details Removes one from the number of items in cache.
/// @param key The key that was evicted.
/// @param item The item that was evicted.
void on_evict(const Key& key, const CacheItem& item);
void on_evict(const Key& /* key */, const CacheItem& /* item */)
{
assert(m_count > 0);
--m_count;
}

/// @brief Get the number of items currently in the cache.
/// @return The number of items in cache.
[[nodiscard]] size_t count() const;
[[nodiscard]] size_t count() const
{
return m_count;
}

/// @brief Get the maximum number of items allowed in cache.
/// @return The maximum number of items allowed in cache.
[[nodiscard]] size_t maximum_count() const;
[[nodiscard]] size_t maximum_count() const
{
return m_maximum_count;
}

private:
size_t m_maximum_count;
size_t m_count = 0;
};

template<class K, class KH, class V> ConstraintCount<K, KH, V>::ConstraintCount(size_t maximum_count)
{
update(maximum_count);
}

template<class K, class KH, class V> void ConstraintCount<K, KH, V>::clear()
{
m_count = 0;
}

template<class K, class KH, class V> auto ConstraintCount<K, KH, V>::prepare_insert(const K& /* key */, const CacheItem& /* item */) -> InsertionTicket
{
const size_t count_to_free = (m_count + 1 > m_maximum_count) ? ((m_count + 1) - m_maximum_count) : 0;
return InsertionTicket{count_to_free, m_maximum_count > 0};
}

template<class K, class KH, class V>
auto ConstraintCount<K, KH, V>::prepare_replace(const K& /* key */, const CacheItem& /* old_item */, const CacheItem& /* new_item */) -> ReplacementTicket
{
return ReplacementTicket{};
}

template<class K, class KH, class V> bool ConstraintCount<K, KH, V>::is_satisfied()
{
return m_count <= m_maximum_count;
}

template<class K, class KH, class V> void ConstraintCount<K, KH, V>::update(size_t maximum_count)
{
m_maximum_count = maximum_count;
}

template<class K, class KH, class V> void ConstraintCount<K, KH, V>::on_insert(const K& /* key */, const CacheItem& /* item */)
{
++m_count;
}

template<class K, class KH, class V> void ConstraintCount<K, KH, V>::on_evict(const K& /* key */, const CacheItem& /* item */)
{
assert(m_count > 0);
--m_count;
}

template<class K, class KH, class V> size_t ConstraintCount<K, KH, V>::count() const
{
return m_count;
}

template<class K, class KH, class V> size_t ConstraintCount<K, KH, V>::maximum_count() const
{
return m_maximum_count;
}

} // namespace cachemere::policy
Loading
Loading