Dependencies: Add lru-cache library

Adds a generic C++ based caching library to support material external
modules. This is a template based library which means there is nothing
to build or link.

The only modification is to the CMakeLists.txt file to enable
integration with FreeCAD,

Original source: https://github.com/goldsborough/lru-cache
This commit is contained in:
David Carter
2025-04-02 00:07:25 -04:00
committed by Chris Hennes
parent 393ab112e5
commit 09f71cb40d
49 changed files with 10596 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_CACHE_TAGS_HPP
#define LRU_CACHE_TAGS_HPP
namespace LRU {
namespace Tag {
struct BasicCache {};
struct TimedCache {};
} // namespace Tag
namespace Lowercase {
namespace tag {
using basic_cache = ::LRU::Tag::BasicCache;
using timed_cache = ::LRU::Tag::TimedCache;
} // namespace tag
} // namespace Lowercase
} // namespace LRU
#endif // LRU_CACHE_TAGS_HPP

View File

@@ -0,0 +1,207 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_CACHE_HPP
#define LRU_CACHE_HPP
#include <cassert>
#include <chrono>
#include <cstddef>
#include <functional>
#include <iterator>
#include <list>
#include <stdexcept>
#include <unordered_map>
#include <lru/cache-tags.hpp>
#include <lru/error.hpp>
#include <lru/internal/base-cache.hpp>
#include <lru/internal/information.hpp>
#include <lru/internal/last-accessed.hpp>
namespace LRU {
namespace Internal {
template <typename Key,
typename Value,
typename HashFunction,
typename KeyEqual>
using UntimedCacheBase = Internal::BaseCache<Key,
Value,
Internal::Information,
HashFunction,
KeyEqual,
Tag::BasicCache>;
} // namespace Internal
/// A basic LRU cache implementation.
///
/// An LRU cache is a fixed-size cache that remembers the order in which
/// elements were inserted into it. When the size of the cache exceeds its
/// capacity, the "least-recently-used" (LRU) element is erased. In our
/// implementation, usage is defined as insertion, but not lookup. That is,
/// looking up an element does not move it to the "front" of the cache (making
/// the operation faster). Only insertions (and erasures) can change the order
/// of elements. The capacity of the cache can be modified at any time.
///
/// \see LRU::TimedCache
template <typename Key,
typename Value,
typename HashFunction = std::hash<Key>,
typename KeyEqual = std::equal_to<Key>>
class Cache
: public Internal::UntimedCacheBase<Key, Value, HashFunction, KeyEqual> {
private:
using super = Internal::UntimedCacheBase<Key, Value, HashFunction, KeyEqual>;
using PRIVATE_BASE_CACHE_MEMBERS;
public:
using PUBLIC_BASE_CACHE_MEMBERS;
using typename super::size_t;
/// \copydoc BaseCache::BaseCache(size_t,const HashFunction&,const KeyEqual&)
/// \detailss The capacity defaults to an internal constant, currently 128.
explicit Cache(size_t capacity = Internal::DEFAULT_CAPACITY,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(capacity, hash, equal) {
}
/// \copydoc BaseCache(size_t,Iterator,Iterator,const HashFunction&,const
/// KeyEqual&)
template <typename Iterator>
Cache(size_t capacity,
Iterator begin,
Iterator end,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(capacity, begin, end, hash, equal) {
}
/// \copydoc BaseCache(Iterator,Iterator,const HashFunction&,const
/// KeyEqual&)
template <typename Iterator>
Cache(Iterator begin,
Iterator end,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(begin, end, hash, equal) {
}
/// Constructor.
///
/// \param capacity The capacity of the cache.
/// \param range A range to construct the cache with.
/// \param hash The hash function to use for the internal map.
/// \param key_equal The key equality function to use for the internal map.
template <typename Range, typename = Internal::enable_if_range<Range>>
Cache(size_t capacity,
Range&& range,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(capacity, std::forward<Range>(range), hash, equal) {
}
/// Constructor.
///
/// \param range A range to construct the cache with.
/// \param hash The hash function to use for the internal map.
/// \param key_equal The key equality function to use for the internal map.
template <typename Range, typename = Internal::enable_if_range<Range>>
explicit Cache(Range&& range,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(std::forward<Range>(range), hash, equal) {
}
/// \copydoc BaseCache(InitializerList,const HashFunction&,const
/// KeyEqual&)
Cache(InitializerList list,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual()) // NOLINT(runtime/explicit)
: super(list, hash, equal) {
}
/// \copydoc BaseCache(size_t,InitializerList,const HashFunction&,const
/// KeyEqual&)
Cache(size_t capacity,
InitializerList list,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual()) // NOLINT(runtime/explicit)
: super(capacity, list, hash, equal) {
}
/// \copydoc BaseCache::find(const Key&)
UnorderedIterator find(const Key& key) override {
auto iterator = _map.find(key);
if (iterator != _map.end()) {
_register_hit(key, iterator->second.value);
_move_to_front(iterator->second.order);
_last_accessed = iterator;
} else {
_register_miss(key);
}
return {*this, iterator};
}
/// \copydoc BaseCache::find(const Key&) const
UnorderedConstIterator find(const Key& key) const override {
auto iterator = _map.find(key);
if (iterator != _map.end()) {
_register_hit(key, iterator->second.value);
_move_to_front(iterator->second.order);
_last_accessed = iterator;
} else {
_register_miss(key);
}
return {*this, iterator};
}
/// \returns The most-recently inserted element.
const Key& front() const noexcept {
if (is_empty()) {
throw LRU::Error::EmptyCache("front");
} else {
// The queue is reversed for natural order of iteration.
return _order.back();
}
}
/// \returns The least-recently inserted element.
const Key& back() const noexcept {
if (is_empty()) {
throw LRU::Error::EmptyCache("back");
} else {
// The queue is reversed for natural order of iteration.
return _order.front();
}
}
};
namespace Lowercase {
template <typename... Ts>
using cache = Cache<Ts...>;
} // namespace Lowercase
} // namespace LRU
#endif // LRU_CACHE_HPP

View File

@@ -0,0 +1,134 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_PAIR_HPP
#define LRU_PAIR_HPP
#include <algorithm>
#include <type_traits>
#include <utility>
namespace LRU {
/// A entry of references to the key and value of an entry in a cache.
///
/// Instances of this class are usually the result of dereferencing an iterator.
///
/// \tparam Key The key type of the pair.
/// \tparam Value The value type of the pair.
template <typename Key, typename Value>
struct Entry final {
using KeyType = Key;
using ValueType = Value;
using first_type = Key;
using second_type = Value;
/// Constructor.
///
/// \param key The key of the entry.
/// \param value The value of the entry.
Entry(const Key& key, Value& value) : first(key), second(value) {
}
/// Generalized copy constructor.
///
/// Mainly for conversion from non-const values to const values.
///
/// \param other The entry to construct from.
template <typename AnyKey,
typename AnyValue,
typename =
std::enable_if_t<(std::is_convertible<AnyKey, Key>::value &&
std::is_convertible<AnyValue, Value>::value)>>
Entry(const Entry<AnyKey, AnyValue>& other)
: first(other.first), second(other.second) {
}
/// Compares two entrys for equality.
///
/// \param first The first entry to compare.
/// \param second The second entry to compare.
/// \returns True if the firest entry equals the second, else false.
template <typename Pair, typename = typename Pair::first_type>
friend bool operator==(const Entry& first, const Pair& second) noexcept {
return first.first == second.first && first.second == second.second;
}
/// Compares two entrys for equality.
///
/// \param first The first entry to compare.
/// \param second The second entry to compare.
/// \returns True if the first entry equals the second, else false.
template <typename Pair, typename = typename Pair::first_type>
friend bool operator==(const Pair& first, const Entry& second) noexcept {
return second == first;
}
/// Compares two entrys for inequality.
///
/// \param first The first entry to compare.
/// \param second The second entry to compare.
/// \returns True if the first entry does not equal the second, else false.
template <typename Pair, typename = typename Pair::first_type>
friend bool operator!=(const Entry& first, const Pair& second) noexcept {
return !(first == second);
}
/// Compares two entrys for inequality.
///
/// \param first The first entry to compare.
/// \param second The second entry to compare.fdas
/// \returns True if the first entry does not equal the second, else false.
template <typename Pair, typename = typename Pair::first_type>
friend bool operator!=(const Pair& first, const Entry& second) noexcept {
return second != first;
}
/// \returns A `std::pair` instance with the key and value of this entry.
operator std::pair<const Key&, Value&>() noexcept {
return {first, second};
}
/// \returns The key of the entry (`first`).
const Key& key() const noexcept {
return first;
}
/// \returns The value of the entry (`second`).
Value& value() noexcept {
return second;
}
/// \returns The value of the entry (`second`).
const Value& value() const noexcept {
return second;
}
/// The key of the entry.
const Key& first;
/// The value of the entry.
Value& second;
};
} // namespace LRU
#endif // LRU_PAIR_HPP

View File

@@ -0,0 +1,107 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_ERRORS_HPP
#define LRU_INTERNAL_ERRORS_HPP
#include <stdexcept>
#include <string>
namespace LRU {
namespace Error {
/// Exception thrown when the value of an invalid key was requested.
struct KeyNotFound : public std::runtime_error {
using super = std::runtime_error;
KeyNotFound() : super("Failed to find key") {
}
explicit KeyNotFound(const std::string& key)
: super("Failed to find key: " + key) {
}
};
/// Exception thrown when the value of an expired key was requested.
struct KeyExpired : public std::runtime_error {
using super = std::runtime_error;
explicit KeyExpired(const std::string& key)
: super("Key found, but expired: " + key) {
}
KeyExpired() : super("Key found, but expired") {
}
};
/// Exception thrown when requesting the front or end key of an empty cache.
struct EmptyCache : public std::runtime_error {
using super = std::runtime_error;
explicit EmptyCache(const std::string& what_was_expected)
: super("Requested " + what_was_expected + " of empty cache") {
}
};
/// Exception thrown when attempting to convert an invalid unordered iterator to
/// an ordered iterator.
struct InvalidIteratorConversion : public std::runtime_error {
using super = std::runtime_error;
InvalidIteratorConversion()
: super("Cannot convert past-the-end unordered to ordered iterator") {
}
};
/// Exception thrown when attempting to erase the past-the-end iterator.
struct InvalidIterator : public std::runtime_error {
using super = std::runtime_error;
InvalidIterator() : super("Past-the-end iterator is invalid here") {
}
};
/// Exception thrown when requesting statistics about an unmonitored key.
struct UnmonitoredKey : public std::runtime_error {
using super = std::runtime_error;
UnmonitoredKey() : super("Requested statistics for unmonitored key") {
}
};
/// Exception thrown when requesting the statistics object of a cache when none
/// was registered.
struct NotMonitoring : public std::runtime_error {
using super = std::runtime_error;
NotMonitoring() : super("Statistics monitoring not enabled for this cache") {
}
};
namespace Lowercase {
using key_not_found = KeyNotFound;
using key_expired = KeyExpired;
using empty_cache = EmptyCache;
using invalid_iterator_conversion = InvalidIteratorConversion;
using invalid_iterator = InvalidIterator;
using unmonitored_key = UnmonitoredKey;
using not_monitoring = NotMonitoring;
} // namespace Lowercase
} // namespace Error
} // namespace LRU
#endif // LRU_INTERNAL_ERRORS_HPP

View File

@@ -0,0 +1,76 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INSERTION_RESULT_HPP
#define LRU_INSERTION_RESULT_HPP
#include <algorithm>
#include <type_traits>
#include <utility>
namespace LRU {
/// The result of an insertion into a cache.
///
/// This is a semantically nicer alternative to a generic `std::pair`, as is
/// returned by `std::unordered_map` or so. It still has the same static
/// interface as the `std::pair` (with `first` and `second` members), but adds
/// nicer `was_inserted()` and `iterator()` accessors.
///
/// \tparam Iterator The class of the iterator contained in the result.
template <typename Iterator>
struct InsertionResult final {
using IteratorType = Iterator;
/// Constructor.
///
/// \param result Whether the result was successful.
/// \param iterator The iterator pointing to the inserted or updated key.
InsertionResult(bool result, Iterator iterator)
: first(result), second(iterator) {
}
/// \returns True if the key was newly inserted, false if it was only updated.
bool was_inserted() const noexcept {
return first;
}
/// \returns The iterator pointing to the inserted or updated key.
Iterator iterator() const noexcept {
return second;
}
/// \copydoc was_inserted
explicit operator bool() const noexcept {
return was_inserted();
}
/// Whether the result was successful.
bool first;
/// The iterator pointing to the inserted or updated key.
Iterator second;
};
} // namespace LRU
#endif // LRU_INSERTION_RESULT_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_BASE_ITERATOR_HPP
#define LRU_INTERNAL_BASE_ITERATOR_HPP
#include <algorithm>
#include <iterator>
#include <lru/entry.hpp>
#include <lru/internal/optional.hpp>
#define PUBLIC_BASE_ITERATOR_MEMBERS \
typename super::Entry; \
using typename super::KeyType; \
using typename super::ValueType;
#define PRIVATE_BASE_ITERATOR_MEMBERS \
super::_iterator; \
using super::_entry; \
using super::_cache;
namespace LRU {
namespace Internal {
/// The base class for all (ordered and unordered) iterators.
///
/// All iterators over our LRU caches store a reference to the cache they point
/// into, an underlying iterator they adapt (e.g. a map iterator or list
/// iterator) as well as a entry, a reference to which is returned when
/// dereferencing the iterator.
///
/// \tparam IteratorTag A standard iterator category tag.
/// \tparam Key The key type over which instances of the iterator iterate.
/// \tparam Value The value type over which instances of the iterator iterate.
/// \tparam Cache The type of the cache instances of the iterator point into.
/// \tparam UnderlyingIterator The underlying iterator class used to implement
/// the iterator.
template <typename IteratorTag,
typename Key,
typename Value,
typename Cache,
typename UnderlyingIterator>
class BaseIterator : public std::iterator<IteratorTag, LRU::Entry<Key, Value>> {
public:
using KeyType = Key;
using ValueType =
std::conditional_t<std::is_const<Cache>::value, const Value, Value>;
using Entry = LRU::Entry<KeyType, ValueType>;
/// Default constructor.
BaseIterator() noexcept : _cache(nullptr) {
}
/// Constructor.
///
/// \param cache The cache this iterator points into.
/// \param iterator The underlying iterator to adapt.
BaseIterator(Cache& cache, const UnderlyingIterator& iterator) noexcept
: _iterator(iterator), _cache(&cache) {
}
/// Copy constructor.
///
/// Differs from the default copy constructor in that it does not copy the
/// entry.
///
/// \param other The base iterator to copy.
BaseIterator(const BaseIterator& other) noexcept
: _iterator(other._iterator), _cache(other._cache) {
// Note: we do not copy the entry, as it would require a new allocation.
// Since iterators are often taken by value, this may incur a high cost.
// As such we delay the retrieval of the entry to the first call to entry().
}
/// Copy assignment operator.
///
/// Differs from the default copy assignment
/// operator in that it does not copy the entry.
///
/// \param other The base iterator to copy.
/// \return The base iterator instance.
BaseIterator& operator=(const BaseIterator& other) noexcept {
if (this != &other) {
_iterator = other._iterator;
_cache = other._cache;
_entry.reset();
}
return *this;
}
/// Move constructor.
BaseIterator(BaseIterator&& other) noexcept = default;
/// Move assignment operator.
BaseIterator& operator=(BaseIterator&& other) noexcept = default;
/// Generalized copy constructor.
///
/// Mainly necessary for non-const to const conversion.
///
/// \param other The base iterator to copy from.
template <typename AnyIteratorTag,
typename AnyKeyType,
typename AnyValueType,
typename AnyCacheType,
typename AnyUnderlyingIteratorType>
BaseIterator(const BaseIterator<AnyIteratorTag,
AnyKeyType,
AnyValueType,
AnyCacheType,
AnyUnderlyingIteratorType>& other)
: _iterator(other._iterator), _entry(other._entry), _cache(other._cache) {
}
/// Generalized move constructor.
///
/// Mainly necessary for non-const to const conversion.
///
/// \param other The base iterator to move into this one.
template <typename AnyIteratorTag,
typename AnyKeyType,
typename AnyValueType,
typename AnyCacheType,
typename AnyUnderlyingIteratorType>
BaseIterator(BaseIterator<AnyIteratorTag,
AnyKeyType,
AnyValueType,
AnyCacheType,
AnyUnderlyingIteratorType>&& other) noexcept
: _iterator(std::move(other._iterator))
, _entry(std::move(other._entry))
, _cache(std::move(other._cache)) {
}
/// Destructor.
virtual ~BaseIterator() = default;
/// Swaps this base iterator with another one.
///
/// \param other The other iterator to swap with.
void swap(BaseIterator& other) noexcept {
// Enable ADL
using std::swap;
swap(_iterator, other._iterator);
swap(_entry, other._entry);
swap(_cache, other._cache);
}
/// Swaps two base iterator.
///
/// \param first The first iterator to swap.
/// \param second The second iterator to swap.
friend void swap(BaseIterator& first, BaseIterator& second) noexcept {
first.swap(second);
}
/// \returns A reference to the current entry pointed to by the iterator.
virtual Entry& operator*() noexcept = 0;
/// \returns A pointer to the current entry pointed to by the iterator.
Entry* operator->() noexcept {
return &(**this);
}
/// \copydoc operator*()
virtual Entry& entry() = 0;
/// \returns A reference to the value of the entry currently pointed to by the
/// iterator.
virtual ValueType& value() = 0;
/// \returns A reference to the key of the entry currently pointed to by the
/// iterator.
virtual const Key& key() = 0;
protected:
template <typename, typename, typename, typename, typename>
friend class BaseIterator;
/// The underlying iterator this iterator class adapts.
UnderlyingIterator _iterator;
/// The entry optionally being stored.
Optional<Entry> _entry;
/// A pointer to the cache this iterator points into.
/// Pointer and not reference because it's cheap to copy.
/// Pointer and not `std::reference_wrapper` because the class needs to be
/// default-constructible.
Cache* _cache;
};
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_BASE_ITERATOR_HPP

View File

@@ -0,0 +1,338 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef BASE_ORDERED_ITERATOR_HPP
#define BASE_ORDERED_ITERATOR_HPP
#include <algorithm>
#include <functional>
#include <iterator>
#include <type_traits>
#include <lru/entry.hpp>
#include <lru/error.hpp>
#include <lru/internal/base-iterator.hpp>
#include <lru/internal/base-unordered-iterator.hpp>
#include <lru/internal/definitions.hpp>
#include <lru/internal/optional.hpp>
#include <lru/iterator-tags.hpp>
namespace LRU {
namespace Internal {
template <typename Key, typename Value, typename Cache>
using BaseForBaseOrderedIterator =
BaseIterator<std::bidirectional_iterator_tag,
Key,
Value,
Cache,
typename Queue<Key>::const_iterator>;
/// The base class for all const and non-const ordered iterators.
///
/// Ordered iterators are bidirectional iterators that iterate over the keys of
/// a cache in the order in which they were inserted into the cache. As they
/// only iterate over the keys, they must perform hash lookups to retrieve the
/// value the first time they are dereferenced. This makes them slightly less
/// efficient than unordered iterators. However, they also have the additional
/// property that they may be constructed from unordered iterators, and that
/// they can be decremented.
///
/// \tparam Key The key type over which instances of the iterator iterate.
/// \tparam Value The value type over which instances of the iterator iterate.
/// \tparam Cache The type of the cache instances of the iterator point into.
template <typename Key, typename Value, typename Cache>
class BaseOrderedIterator
: public BaseForBaseOrderedIterator<Key, Value, Cache> {
protected:
using super = BaseForBaseOrderedIterator<Key, Value, Cache>;
using PRIVATE_BASE_ITERATOR_MEMBERS;
using UnderlyingIterator = typename Queue<Key>::const_iterator;
public:
using Tag = LRU::Tag::OrderedIterator;
using PUBLIC_BASE_ITERATOR_MEMBERS;
/// Constructor.
BaseOrderedIterator() noexcept = default;
/// \copydoc BaseIterator::BaseIterator(Cache,UnderlyingIterator)
BaseOrderedIterator(Cache& cache, UnderlyingIterator iterator)
: super(cache, iterator) {
}
/// Generalized copy constructor.
///
/// \param other The ordered iterator to contruct from.
template <typename AnyKey, typename AnyValue, typename AnyCache>
BaseOrderedIterator(
const BaseOrderedIterator<AnyKey, AnyValue, AnyCache>& other)
: super(other) {
}
/// Generalized move constructor.
///
/// \param other The ordered iterator to move into this one.
template <typename AnyKey, typename AnyValue, typename AnyCache>
BaseOrderedIterator(BaseOrderedIterator<AnyKey, AnyValue, AnyCache>&& other)
: super(std::move(other)) {
}
/// Generalized conversion copy constructor.
///
/// \param unordered_iterator The unordered iterator to construct from.
template <
typename AnyCache,
typename UnderlyingIterator,
typename = std::enable_if_t<
std::is_same<std::decay_t<AnyCache>, std::decay_t<Cache>>::value>>
BaseOrderedIterator(const BaseUnorderedIterator<AnyCache, UnderlyingIterator>&
unordered_iterator) {
// Atomicity
_throw_if_at_invalid(unordered_iterator);
_cache = unordered_iterator._cache;
_iterator = unordered_iterator._iterator->second.order;
}
/// Generalized conversion move constructor.
///
/// \param unordered_iterator The unordered iterator to move-construct from.
template <
typename AnyCache,
typename UnderlyingIterator,
typename = std::enable_if_t<
std::is_same<std::decay_t<AnyCache>, std::decay_t<Cache>>::value>>
BaseOrderedIterator(BaseUnorderedIterator<AnyCache, UnderlyingIterator>&&
unordered_iterator) {
// Atomicity
_throw_if_at_invalid(unordered_iterator);
_cache = std::move(unordered_iterator._cache);
_entry = std::move(unordered_iterator._entry);
_iterator = std::move(unordered_iterator._iterator->second.order);
}
/// Copy constructor.
BaseOrderedIterator(const BaseOrderedIterator& other) = default;
/// Move constructor.
BaseOrderedIterator(BaseOrderedIterator&& other) = default;
/// Copy assignment operator.
BaseOrderedIterator& operator=(const BaseOrderedIterator& other) = default;
/// Move assignment operator.
BaseOrderedIterator& operator=(BaseOrderedIterator&& other) = default;
/// Destructor.
virtual ~BaseOrderedIterator() = default;
/// Checks for equality between this iterator and another ordered iterator.
///
/// \param other The other ordered iterator.
/// \returns True if both iterators point to the same entry, else false.
bool operator==(const BaseOrderedIterator& other) const noexcept {
return this->_iterator == other._iterator;
}
/// Checks for inequality between this iterator another ordered iterator.
///
/// \param other The other ordered iterator.
/// \returns True if the iterators point to different entries, else false.
bool operator!=(const BaseOrderedIterator& other) const noexcept {
return !(*this == other);
}
/// Checks for inequality between this iterator and another unordered
/// iterator.
///
/// \param other The other unordered iterator.
/// \returns True if both iterators point to the end of the same cache, else
/// the result of comparing with the unordered iterator, converted to an
/// ordered iterator.
template <typename AnyCache, typename AnyUnderlyingIterator>
bool operator==(
const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>& other) const
noexcept {
if (this->_cache != other._cache) return false;
// The past-the-end iterators of the same cache should compare equal.
// This is an exceptional guarantee we make. This is also the reason
// why we can't rely on the conversion from unordered to ordered iterators
// because construction of an ordered iterator from the past-the-end
// unordered iterator will fail (with an InvalidIteratorConversion error)
if (other == other._cache->unordered_end()) {
return *this == this->_cache->ordered_end();
}
// Will call the other overload
return *this == static_cast<BaseOrderedIterator>(other);
}
/// Checks for equality between an unordered iterator and an ordered iterator.
///
/// \param first The unordered iterator.
/// \param second The ordered iterator.
/// \returns True if both iterators point to the end of the same cache, else
/// the result of comparing with the unordered iterator, converted to an
/// ordered iterator.
template <typename AnyCache, typename AnyUnderlyingIterator>
friend bool operator==(
const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>& first,
const BaseOrderedIterator& second) noexcept {
return second == first;
}
/// Checks for inequality between an unordered
/// iterator and an ordered iterator.
///
/// \param first The ordered iterator.
/// \param second The unordered iterator.
/// \returns True if the iterators point to different entries, else false.
template <typename AnyCache, typename AnyUnderlyingIterator>
friend bool
operator!=(const BaseOrderedIterator& first,
const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>&
second) noexcept {
return !(first == second);
}
/// Checks for inequality between an unordered
/// iterator and an ordered iterator.
///
/// \param first The unordered iterator.
/// \param second The ordered iterator.
/// \returns True if the iterators point to different entries, else false.
template <typename AnyCache, typename AnyUnderlyingIterator>
friend bool operator!=(
const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>& first,
const BaseOrderedIterator& second) noexcept {
return second != first;
}
/// Increments the iterator to the next entry.
///
/// If the iterator already pointed to the end any number of increments
/// before, behavior is undefined.
///
/// \returns The resulting iterator.
BaseOrderedIterator& operator++() {
++_iterator;
_entry.reset();
return *this;
}
/// Increments the iterator and returns a copy of the previous one.
///
/// If the iterator already pointed to the end any number of increments
/// before, behavior is undefined.
///
/// \returns A copy of the previous iterator.
BaseOrderedIterator operator++(int) {
auto previous = *this;
++*this;
return previous;
}
/// Decrements the iterator to the previous entry.
///
/// \returns The resulting iterator.
BaseOrderedIterator& operator--() {
--_iterator;
_entry.reset();
return *this;
}
/// Decrements the iterator and returns a copy of the previous entry.
///
/// \returns The previous iterator.
BaseOrderedIterator operator--(int) {
auto previous = *this;
--*this;
return previous;
}
Entry& operator*() noexcept override {
return _maybe_lookup();
}
/// \returns A reference to the entry the iterator points to.
/// \details If the iterator is invalid, behavior is undefined.
Entry& entry() override {
_cache->throw_if_invalid(*this);
return _maybe_lookup();
}
/// \returns A reference to the value the iterator points to.
/// \details If the iterator is invalid, behavior is undefined.
Value& value() override {
return entry().value();
}
/// \returns A reference to the key the iterator points to.
/// \details If the iterator is invalid, behavior is undefined.
const Key& key() override {
// No lookup required
_cache->throw_if_invalid(*this);
return *_iterator;
}
protected:
template <typename, typename, typename>
friend class BaseOrderedIterator;
/// Looks up the entry for a key if this was not done already.
///
/// \returns The entry, which was possibly newly looked up.
Entry& _maybe_lookup() {
if (!_entry.has_value()) {
_lookup();
}
return *_entry;
}
/// Looks up the entry for a key and sets the internal entry member.
void _lookup() {
auto iterator = _cache->_map.find(*_iterator);
_entry.emplace(iterator->first, iterator->second.value);
}
private:
/// Throws an exception if the given unordered iterator is invalid.
///
/// \param unordered_iterator The iterator to check.
/// \throws LRU::Error::InvalidIteratorConversion if the iterator is invalid.
template <typename UnorderedIterator>
void _throw_if_at_invalid(const UnorderedIterator& unordered_iterator) {
// For atomicity of the copy assignment, we assign the cache pointer only
// after this check in the copy/move constructor and use the iterator's
// cache. If an exception is thrown, the state of the ordered iterator is
// unchanged compared to before the assignment.
if (unordered_iterator == unordered_iterator._cache->unordered_end()) {
throw LRU::Error::InvalidIteratorConversion();
}
}
};
} // namespace Internal
} // namespace LRU
#endif // BASE_ORDERED_ITERATOR_HPP

View File

@@ -0,0 +1,216 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef BASE_UNORDERED_ITERATOR_HPP
#define BASE_UNORDERED_ITERATOR_HPP
#include <algorithm>
#include <iterator>
#include <type_traits>
#include <lru/entry.hpp>
#include <lru/internal/base-iterator.hpp>
#include <lru/internal/definitions.hpp>
#include <lru/internal/optional.hpp>
#include <lru/iterator-tags.hpp>
namespace LRU {
// Forward declaration.
template <typename, typename, typename, typename, typename>
class TimedCache;
namespace Internal {
template <typename Cache, typename UnderlyingIterator>
using BaseForBaseUnorderedIterator =
BaseIterator<std::forward_iterator_tag,
decltype(UnderlyingIterator()->first),
decltype(UnderlyingIterator()->second.value),
Cache,
UnderlyingIterator>;
/// The base class for all const and non-const unordered iterators.
///
/// An unordered iterator is a wrapper around an `unordered_map` iterator with
/// ForwardIterator category. As such, it is (nearly) as fast to access the pair
/// as through the unordered iterator as through the map iterator directly.
/// However, the order of keys is unspecified. For this reason, unordered
/// iterators have the special property that they may be used to construct
/// ordered iterators, after which the order of insertion is respected.
///
/// \tparam Cache The type of the cache instances of the iterator point into.
/// \tparam UnderlyingIterator The underlying iterator class used to implement
/// the iterator.
template <typename Cache, typename UnderlyingIterator>
class BaseUnorderedIterator
: public BaseForBaseUnorderedIterator<Cache, UnderlyingIterator> {
protected:
using super = BaseForBaseUnorderedIterator<Cache, UnderlyingIterator>;
using PRIVATE_BASE_ITERATOR_MEMBERS;
// These are the key and value types the BaseIterator extracts
using Key = typename super::KeyType;
using Value = typename super::ValueType;
public:
using Tag = LRU::Tag::UnorderedIterator;
using PUBLIC_BASE_ITERATOR_MEMBERS;
/// Constructor.
BaseUnorderedIterator() noexcept = default;
/// \copydoc BaseIterator::BaseIterator(Cache,UnderlyingIterator)
explicit BaseUnorderedIterator(Cache& cache,
const UnderlyingIterator& iterator) noexcept
: super(cache, iterator) {
}
/// Generalized copy constructor.
///
/// Useful mainly for non-const to const conversion.
///
/// \param other The iterator to copy from.
template <typename AnyCache, typename AnyUnderlyingIterator>
BaseUnorderedIterator(
const BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>&
other) noexcept
: super(other) {
}
/// Copy constructor.
BaseUnorderedIterator(const BaseUnorderedIterator& other) noexcept = default;
/// Move constructor.
BaseUnorderedIterator(BaseUnorderedIterator&& other) noexcept = default;
/// Copy assignment operator.
BaseUnorderedIterator&
operator=(const BaseUnorderedIterator& other) noexcept = default;
/// Move assignment operator.
template <typename AnyCache, typename AnyUnderlyingIterator>
BaseUnorderedIterator&
operator=(BaseUnorderedIterator<AnyCache, AnyUnderlyingIterator>
unordered_iterator) noexcept {
swap(unordered_iterator);
return *this;
}
/// Destructor.
virtual ~BaseUnorderedIterator() = default;
/// Compares this iterator for equality with another unordered iterator.
///
/// \param other Another unordered iterator.
/// \returns True if both iterators point to the same entry, else false.
template <typename AnyCache, typename AnyIterator>
bool
operator==(const BaseUnorderedIterator<AnyCache, AnyIterator>& other) const
noexcept {
return this->_iterator == other._iterator;
}
/// Compares this iterator for inequality with another unordered iterator.
///
/// \param other Another unordered iterator.
/// \returns True if the iterators point to different entries, else false.
template <typename AnyCache, typename AnyIterator>
bool
operator!=(const BaseUnorderedIterator<AnyCache, AnyIterator>& other) const
noexcept {
return !(*this == other);
}
/// Increments the iterator to the next entry.
///
/// If the iterator already pointed to the end any number of increments
/// before, behavior is undefined.
///
/// \returns The resulting iterator.
BaseUnorderedIterator& operator++() {
++_iterator;
_entry.reset();
return *this;
}
/// Increments the iterator and returns a copy of the previous one.
///
/// If the iterator already pointed to the end any number of increments
/// before, behavior is undefined.
///
/// \returns A copy of the previous iterator.
BaseUnorderedIterator operator++(int) {
auto previous = *this;
++*this;
return previous;
}
/// \copydoc BaseIterator::operator*
/// \details If the iterator is invalid, behavior is undefined. No exception
/// handling is performed.
Entry& operator*() noexcept override {
if (!_entry.has_value()) {
_entry.emplace(_iterator->first, _iterator->second.value);
}
return *_entry;
}
/// \returns A reference to the entry the iterator points to.
/// \throws LRU::Error::InvalidIterator if the iterator is the end iterator.
/// \throws LRU::Error::KeyExpired if the key pointed to by the iterator has
/// expired.
Entry& entry() override {
if (!_entry.has_value()) {
_entry.emplace(_iterator->first, _iterator->second.value);
}
_cache->throw_if_invalid(*this);
return *_entry;
}
/// \returns A reference to the key the iterator points to.
/// \throws LRU::Error::InvalidIterator if the iterator is the end iterator.
/// \throws LRU::Error::KeyExpired if the key pointed to by the iterator has
/// expired.
const Key& key() override {
return entry().key();
}
/// \returns A reference to the value the iterator points to.
/// \throws LRU::Error::InvalidIterator if the iterator is the end iterator.
/// \throws LRU::Error::KeyExpired if the key pointed to by the iterator has
/// expired.
Value& value() override {
return entry().value();
}
protected:
template <typename, typename, typename>
friend class BaseOrderedIterator;
template <typename, typename, typename, typename, typename>
friend class LRU::TimedCache;
};
} // namespace Internal
} // namespace LRU
#endif // BASE_UNORDERED_ITERATOR_HPP

View File

@@ -0,0 +1,159 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_CALLBACK_MANAGER_HPP
#define LRU_INTERNAL_CALLBACK_MANAGER_HPP
#include <functional>
#include <vector>
#include <lru/entry.hpp>
#include <lru/internal/optional.hpp>
namespace LRU {
namespace Internal {
/// Manages hit, miss and access callbacks for a cache.
///
/// The callback manager implements the "publisher" of the observer pattern we
/// implement. It stores and calls three kinds of callbacks:
/// 1. Hit callbacks, taking a key and value after a cache hit.
/// 2. Miss callbacks, taking only a key, that was not found in a cache.
/// 3. Access callbacks, taking a key and a boolean indicating a hit or a miss.
///
/// Callbacks can be added, accessed and cleared.
template <typename Key, typename Value>
class CallbackManager {
public:
using HitCallback = std::function<void(const Key&, const Value&)>;
using MissCallback = std::function<void(const Key&)>;
using AccessCallback = std::function<void(const Key&, bool)>;
using HitCallbackContainer = std::vector<HitCallback>;
using MissCallbackContainer = std::vector<MissCallback>;
using AccessCallbackContainer = std::vector<AccessCallback>;
/// Calls all callbacks registered for a hit, with the given key and value.
///
/// \param key The key for which a cache hit ocurred.
/// \param value The value that was found for the key.
void hit(const Key& key, const Value& value) {
_call_each(_hit_callbacks, key, value);
_call_each(_access_callbacks, key, true);
}
/// Calls all callbacks registered for a miss, with the given key.
///
/// \param key The key for which a cache miss ocurred.
void miss(const Key& key) {
_call_each(_miss_callbacks, key);
_call_each(_access_callbacks, key, false);
}
/// Registers a new hit callback.
///
/// \param hit_callback The hit callback function to register with the
/// manager.
template <typename Callback>
void hit_callback(Callback&& hit_callback) {
_hit_callbacks.emplace_back(std::forward<Callback>(hit_callback));
}
/// Registers a new miss callback.
///
/// \param miss_callback The miss callback function to register with the
/// manager.
template <typename Callback>
void miss_callback(Callback&& miss_callback) {
_miss_callbacks.emplace_back(std::forward<Callback>(miss_callback));
}
/// Registers a new access callback.
///
/// \param access_callback The access callback function to register with the
/// manager.
template <typename Callback>
void access_callback(Callback&& access_callback) {
_access_callbacks.emplace_back(std::forward<Callback>(access_callback));
}
/// Clears all hit callbacks.
void clear_hit_callbacks() {
_hit_callbacks.clear();
}
/// Clears all miss callbacks.
void clear_miss_callbacks() {
_miss_callbacks.clear();
}
/// Clears all access callbacks.
void clear_access_callbacks() {
_access_callbacks.clear();
}
/// Clears all callbacks.
void clear() {
clear_hit_callbacks();
clear_miss_callbacks();
clear_access_callbacks();
}
/// \returns All hit callbacks.
const HitCallbackContainer& hit_callbacks() const noexcept {
return _hit_callbacks;
}
/// \returns All miss callbacks.
const MissCallbackContainer& miss_callbacks() const noexcept {
return _miss_callbacks;
}
/// \returns All access callbacks.
const AccessCallbackContainer& access_callbacks() const noexcept {
return _access_callbacks;
}
private:
/// Calls each function in the given container with the given arguments.
///
/// \param callbacks The container of callbacks to call.
/// \param args The arguments to call the callbacks with.
template <typename CallbackContainer, typename... Args>
void _call_each(const CallbackContainer& callbacks, Args&&... args) {
for (const auto& callback : callbacks) {
callback(std::forward<Args>(args)...);
}
}
/// The container of hit callbacks registered.
HitCallbackContainer _hit_callbacks;
/// The container of miss callbacks registered.
MissCallbackContainer _miss_callbacks;
/// The container of access callbacks registered.
AccessCallbackContainer _access_callbacks;
};
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_CALLBACK_MANAGER_HPP

View File

@@ -0,0 +1,88 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_DEFINITIONS_HPP
#define LRU_INTERNAL_DEFINITIONS_HPP
#include <chrono>
#include <cstddef>
#include <functional>
#include <list>
#include <tuple>
#include <unordered_map>
namespace LRU {
namespace Internal {
/// The default capacity for all caches.
const std::size_t DEFAULT_CAPACITY = 128;
/// The reference type use to store keys in the order queue.
template <typename T>
using Reference = std::reference_wrapper<T>;
/// Compares two References for equality.
///
/// This is necessary because `std::reference_wrapper` does not define any
/// operator overloads. We do need them, however (e.g. for container
/// comparison).
///
/// \param first The first reference to compare.
/// \param second The second reference to compare.
template <typename T, typename U>
bool operator==(const Reference<T>& first, const Reference<U>& second) {
return first.get() == second.get();
}
/// Compares two References for inequality.
///
/// This is necessary because `std::reference_wrapper` does not define any
/// operator overloads. We do need them, however (e.g. for container
/// comparison).
///
/// \param first The first reference to compare.
/// \param second The second reference to compare.
template <typename T, typename U>
bool operator!=(const Reference<T>& first, const Reference<U>& second) {
return !(first == second);
}
/// The default queue type used internally.
template <typename T>
using Queue = std::list<Reference<T>>;
/// The default map type used internally.
template <typename Key,
typename Information,
typename HashFunction,
typename KeyEqual>
using Map = std::unordered_map<Key, Information, HashFunction, KeyEqual>;
/// The default clock used internally.
using Clock = std::chrono::steady_clock;
/// The default timestamp (time point) used internally.
using Timestamp = Clock::time_point;
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_DEFINITIONS_HPP

View File

@@ -0,0 +1,62 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_HASH_HPP
#define LRU_INTERNAL_HASH_HPP
#include <cstddef>
#include <functional>
#include <tuple>
/// `std::hash` specialization to allow storing tuples as keys
/// in `std::unordered_map`.
///
/// Essentially hashes all tuple elements and jumbles the
/// individual hashes together.
namespace std {
template <typename... Ts>
struct hash<std::tuple<Ts...>> {
using argument_type = std::tuple<Ts...>;
using result_type = std::size_t;
result_type operator()(const argument_type& argument) const {
return hash_tuple(argument, std::make_index_sequence<sizeof...(Ts)>());
}
private:
template <std::size_t I, std::size_t... Is>
result_type
hash_tuple(const argument_type& tuple, std::index_sequence<I, Is...>) const {
auto value = std::get<I>(tuple);
auto current = std::hash<decltype(value)>{}(value);
auto seed = hash_tuple(tuple, std::index_sequence<Is...>());
// http://www.boost.org/doc/libs/1_35_0/doc/html/boost/hash_combine_id241013.html
return current + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
result_type hash_tuple(const argument_type&, std::index_sequence<>) const {
return 0;
}
};
} // namespace std
#endif // LRU_INTERNAL_HASH_HPP

View File

@@ -0,0 +1,145 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_INFORMATION_HPP
#define LRU_INTERNAL_INFORMATION_HPP
#include <cstddef>
#include <tuple>
#include <utility>
#include <lru/internal/definitions.hpp>
#include <lru/internal/utility.hpp>
namespace LRU {
namespace Internal {
/// The value type of internal maps, used to store a value and iterator.
///
/// This information object is the basis of an LRU cache, which must associated
/// a value and such an order iterator with a key, such that the iterator may be
/// moved to the front of the order when the key is updated with a new value.
///
/// \tparam Key The key type of the information.
/// \tparam Value The value type of the information.
template <typename Key, typename Value>
struct Information {
using KeyType = Key;
using ValueType = Value;
using QueueIterator = typename Internal::Queue<const Key>::const_iterator;
/// Constructor.
///
/// \param value_ The value for the information.
/// \param order_ The order iterator for the information.
explicit Information(const Value& value_,
QueueIterator order_ = QueueIterator())
: value(value_), order(order_) {
}
/// Constructor.
///
/// \param order_ The order iterator for the information.
/// \param value_arguments Any number of arguments to perfectly forward to the
/// value type's constructor.
// template <typename... ValueArguments>
// Information(QueueIterator order_, ValueArguments&&... value_arguments)
// : value(std::forward<ValueArguments>(value_arguments)...), order(order_) {
// }
/// Constructor.
///
/// \param order_ The order iterator for the information.
/// \param value_arguments A tuple of arguments to perfectly forward to the
/// value type's constructor.
///
template <typename... ValueArguments>
explicit Information(const std::tuple<ValueArguments...>& value_arguments,
QueueIterator order_ = QueueIterator())
: Information(
order_, value_arguments, Internal::tuple_indices(value_arguments)) {
}
/// Copy constructor.
Information(const Information& other) = default;
/// Move constructor.
Information(Information&& other) = default;
/// Copy assignment operator.
Information& operator=(const Information& other) = default;
/// Move assignment operator.
Information& operator=(Information&& other) = default;
/// Destructor.
virtual ~Information() = default;
/// Compares the information for equality with another information object.
///
/// \param other The other information object to compare to.
/// \returns True if key and value (not the iterator itself) of the two
/// information objects are equal, else false.
virtual bool operator==(const Information& other) const noexcept {
if (this == &other) return true;
if (this->value != other.value) return false;
// We do not compare the iterator (because otherwise two containers
// holding information would never be equal). We also do not compare
// the key stored in the iterator, because keys will always have been
// compared before this operator is called.
return true;
}
/// Compares the information for inequality with another information object.
///
/// \param other The other information object to compare for.
/// \returns True if key and value (not the iterator itself) of the two
/// information objects are unequal, else false.
virtual bool operator!=(const Information& other) const noexcept {
return !(*this == other);
}
/// The value of the information.
Value value;
/// The order iterator of the information.
QueueIterator order;
private:
/// Implementation for the constructor taking a tuple of arguments for the
/// value.
///
/// \param order_ The order iterator for the information.
/// \param value_argument The tuple of arguments to perfectly forward to the
/// value type's constructor.
/// \param _ An index sequence to access the elements of the tuple
template <typename... ValueArguments, std::size_t... Indices>
Information(const QueueIterator& order_,
const std::tuple<ValueArguments...>& value_argument,
std::index_sequence<Indices...> _)
: value(std::forward<ValueArguments>(std::get<Indices>(value_argument))...)
, order(order_) {
}
};
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_INFORMATION_HPP

View File

@@ -0,0 +1,254 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_LAST_ACCESSED_HPP
#define LRU_INTERNAL_LAST_ACCESSED_HPP
#include <algorithm>
#include <functional>
#include <iterator>
#include <lru/internal/utility.hpp>
namespace LRU {
namespace Internal {
/// Provides a simple iterator-compatible pointer object for a key and
/// information.
///
/// The easisest idea for this class, theoretically, would be to just store an s
/// iterator to the internal cache map (i.e. template the class on the iterator
/// type). However, the major trouble with that approach is that this class
/// should be 100% *mutable*, as in "always non-const", so that keys and
/// informations
/// we store for fast access can be (quickly) retrieved as either const or
/// non-const (iterators for example). This is not possible, since the
/// const-ness of `const_iterators` are not the usual idea of const in C++,
/// meaning especially it cannot be cast away with a `const_cast` as is required
/// for the mutability. As such, we *must* store the plain keys and
/// informations.
/// This, however, means that iterators cannot be stored efficiently, since a
/// new hash table lookup would be required to go from a key to its iterator.
/// However, since the main use case of this class is to avoid a second lookup
/// in the usual `if (cache.contains(key)) return cache.lookup(key)`, which is
/// not an issue for iterators since they can be compared to the `end` iterator
/// in constant time (equivalent to the call to `contains()`).
///
/// WARNING: This class stores *pointers* to keys and informations. As such
/// lifetime
/// of the pointed-to objects must be cared for by the user of this class.
///
/// \tparam Key The type of key being accessed.
/// \tparam InformationType The type of information being accessed.
/// \tparam KeyEqual The type of the key comparison function.
template <typename Key,
typename InformationType,
typename KeyEqual = std::equal_to<Key>>
class LastAccessed {
public:
/// Constructor.
///
/// \param key_equal The function to compare keys with.
explicit LastAccessed(const KeyEqual& key_equal = KeyEqual())
: _key(nullptr)
, _information(nullptr)
, _is_valid(false)
, _key_equal(key_equal) {
}
/// Constructor.
///
/// \param key The key to store a reference to.
/// \param information The information to store a reference to.
/// \param key_equal The function to compare keys with.
LastAccessed(const Key& key,
const InformationType& information,
const KeyEqual& key_equal = KeyEqual())
: _key(const_cast<Key*>(&key))
, _information(const_cast<InformationType*>(&information))
, _is_valid(true)
, _key_equal(key_equal) {
}
/// Constructor.
///
/// \param iterator An iterator pointing to a key and information to use for
/// constructing the instance.
/// \param key_equal The function to compare keys with.
template <typename Iterator>
explicit LastAccessed(Iterator iterator,
const KeyEqual& key_equal = KeyEqual())
: LastAccessed(iterator->first, iterator->second, key_equal) {
}
/// Copy assignment operator for iterators.
///
/// \param iterator An iterator pointing to a key and value to use for the
/// `LastAccessed` instance.
/// \return The resulting `LastAccessed` instance.
template <typename Iterator>
LastAccessed& operator=(Iterator iterator) {
_key = const_cast<Key*>(&(iterator->first));
_information = const_cast<InformationType*>(&(iterator->second));
_is_valid = true;
return *this;
}
/// Compares a `LastAccessed` object for equality with a key.
///
/// \param last_accessed The `LastAccessed` instance to compare.
/// \param key The key instance to compare.
/// \returns True if the key of the `LastAccessed` object's key equals the
/// given key, else false.
friend bool
operator==(const LastAccessed& last_accessed, const Key& key) noexcept {
if (!last_accessed._is_valid) return false;
return last_accessed._key_equal(key, last_accessed.key());
}
/// \copydoc operator==(const LastAccessed&,const Key&)
friend bool
operator==(const Key& key, const LastAccessed& last_accessed) noexcept {
return last_accessed == key;
}
/// Compares a `LastAccessed` object for equality with an iterator.
///
/// \param last_accessed The `LastAccessed` instance to compare.
/// \param iterator The iterator to compare with.
/// \returns True if the `LastAccessed` object's key equals that of the
/// iterator, else false.
template <typename Iterator, typename = enable_if_iterator<Iterator>>
friend bool
operator==(const LastAccessed& last_accessed, Iterator iterator) noexcept {
/// Fast comparisons to an iterator (not relying on implicit conversion)
return last_accessed == iterator->first;
}
/// \copydoc operator==(const LastAccessed&,Iterator)
template <typename Iterator, typename = enable_if_iterator<Iterator>>
friend bool
operator==(Iterator iterator, const LastAccessed& last_accessed) noexcept {
return last_accessed == iterator;
}
/// Compares a `LastAccessed` object for inequality with something.
///
/// \param last_accessed The `LastAccessed` instance to compare.
/// \param other Something else to compare to.
/// \returns True if the key of the `LastAccessed` object's key does not equal
/// the given other object's key, else false.
template <typename T>
friend bool
operator!=(const LastAccessed& last_accessed, const T& other) noexcept {
return !(last_accessed == other);
}
/// \copydoc operator!=(const LastAccessed&,const T&)
template <typename T>
friend bool
operator!=(const T& other, const LastAccessed& last_accessed) noexcept {
return !(other == last_accessed);
}
/// \returns The last accessed key.
Key& key() noexcept {
assert(is_valid());
return *_key;
}
/// \returns The last accessed key.
const Key& key() const noexcept {
assert(is_valid());
return *_key;
}
/// \returns The last accessed information.
InformationType& information() noexcept {
assert(is_valid());
return *_information;
}
/// \returns The last accessed information.
const InformationType& information() const noexcept {
assert(is_valid());
return *_information;
}
/// \returns The last accessed information.
auto& iterator() noexcept {
assert(is_valid());
return _information->order;
}
/// \returns The last accessed value.
auto& value() noexcept {
assert(is_valid());
return _information->value;
}
/// \returns The last accessed value.
const auto& value() const noexcept {
assert(is_valid());
return _information->value;
}
/// \returns True if the key and information of the instance may be accessed,
/// else false.
bool is_valid() const noexcept {
return _is_valid;
}
/// \copydoc is_valid()
explicit operator bool() const noexcept {
return is_valid();
}
/// Invalidates the instance.
void invalidate() noexcept {
_is_valid = false;
_key = nullptr;
_information = nullptr;
}
/// \returns The key comparison function used.
const KeyEqual& key_equal() const noexcept {
return _key_equal;
}
private:
/// A pointer to the key that was last accessed (if any).
Key* _key;
/// A pointer to the information that was last accessed (if any).
InformationType* _information;
/// True if the key and information pointers are valid, else false.
bool _is_valid;
/// The function used to compare keys.
KeyEqual _key_equal;
};
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_LAST_ACCESSED_HPP

View File

@@ -0,0 +1,207 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_OPTIONAL_HPP
#define LRU_INTERNAL_OPTIONAL_HPP
#ifndef __has_include
#define USE_LRU_OPTIONAL
#elif __has_include(<optional>)
#include <optional>
namespace LRU {
namespace Internal {
template <typename T>
using Optional = std::optional<T>;
} // namespace Internal
} // namespace LRU
#else
#define USE_LRU_OPTIONAL
#endif
#ifdef USE_LRU_OPTIONAL
#include <memory>
#include <stdexcept>
namespace LRU {
namespace Internal {
// A roll-your-own replacement of `std::optional`.
//
// This class is only to be used if `std::optional` is unavailable. It
// implements an optional type simply on top of a `unique_ptr`. It is
// API-compatible with `std::optional`, as required for our purposes.
template <typename T>
class Optional {
public:
/// Constructor.
Optional() = default;
/// Copy constructor.
///
/// \param other The other optional object to copy from.
Optional(const Optional& other) {
if (other) emplace(*other);
}
/// Generalized copy constructor.
///
/// \param other The other optional object to copy from.
template <typename U,
typename = std::enable_if_t<std::is_convertible<T, U>::value>>
Optional(const Optional<U>& other) {
if (other) emplace(*other);
}
/// Move constructor.
///
/// \param other The other optional object to move into this one.
Optional(Optional&& other) noexcept {
swap(other);
}
/// Generalized move constructor.
///
/// \param other The other optional object to move into this one.
template <typename U,
typename = std::enable_if_t<std::is_convertible<T, U>::value>>
Optional(Optional<U>&& other) noexcept {
if (other) {
_value = std::make_unique<T>(std::move(*other));
}
}
/// Assignment operator.
///
/// \param other The other object to assign from.
/// \returns The resulting optional instance.
Optional& operator=(Optional other) noexcept {
swap(other);
return *this;
}
/// Swaps the contents of this optional with another one.
///
/// \param other The other optional to swap with.
void swap(Optional& other) {
_value.swap(other._value);
}
/// Swaps the contents of two optionals.
///
/// \param first The first optional to swap.
/// \param second The second optional to swap.
friend void swap(Optional& first, Optional& second) /* NOLINT */ {
first.swap(second);
}
/// \returns True if the `Optional` has a value, else false.
bool has_value() const noexcept {
return static_cast<bool>(_value);
}
/// \copydoc has_value()
explicit operator bool() const noexcept {
return has_value();
}
/// \returns A pointer to the current value. Behavior is undefined if the
/// optional has no value.
T* operator->() {
return _value.get();
}
/// \returns A const pointer to the current value. Behavior is undefined if
/// the `Optional` has no value.
const T* operator->() const {
return _value.get();
}
/// \returns A const reference to the current value. Behavior is undefined if
/// the `Optional` has no value.
const T& operator*() const {
return *_value;
}
/// \returns A reference to the current value. Behavior is undefined if
/// the `Optional` has no value.
T& operator*() {
return *_value;
}
/// \returns A reference to the current value.
/// \throws std::runtime_error If the `Optional` currently has no value.
T& value() {
if (!has_value()) {
// Actually std::bad_optional_access
throw std::runtime_error("optional has no value");
}
return *_value;
}
/// \returns A const reference to the current value.
/// \throws std::runtime_error If the `Optional` currently has no value.
const T& value() const {
if (!has_value()) {
// Actually std::bad_optional_access
throw std::runtime_error("optional has no value");
}
return *_value;
}
/// \returns The current value, or the given argument if there is no value.
/// \param default_value The value to return if this `Optional` currently has
/// no value.
template <class U>
T value_or(U&& default_value) const {
return *this ? **this : static_cast<T>(std::forward<U>(default_value));
}
/// Resets the `Optional` to have no value.
void reset() noexcept {
_value.reset();
}
/// Constructs the `Optional`'s value with the given arguments.
///
/// \param args Arguments to perfeclty forward to the value's constructor.
template <typename... Args>
void emplace(Args&&... args) {
_value = std::make_unique<T>(std::forward<Args>(args)...);
}
private:
template <typename>
friend class Optional;
/// The value, as we implement it.
std::unique_ptr<T> _value;
};
} // namespace Internal
} // namespace LRU
#endif
#endif // LRU_INTERNAL_OPTIONAL_HPP

View File

@@ -0,0 +1,150 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_STATISTICS_MUTATOR_HPP
#define LRU_STATISTICS_MUTATOR_HPP
#include <cassert>
#include <cstddef>
#include <memory>
#include <utility>
#include <lru/internal/optional.hpp>
#include <lru/statistics.hpp>
namespace LRU {
namespace Internal {
/// A mutable proxy interface to a statistics object.
///
/// The `StatisticsMutator` allows modification of the members of a statistics
/// object via a narrow interface, available only to internal classes. The point
/// of this is that while we don't want the user to be able to modify the hit or
/// miss count on a statistics object (it is "getter-only" in that sense), it's
/// also not ideal, from an encapsulation standpoint, to make the cache classes
/// (which do need to access and modify the hit and miss counts) friends of the
/// statistics. This is especially true since the caches should only need to
/// register hits or misses and not have to increment the count of total
/// accesses. As such, we really require a "package-level" interface that is not
/// visible to the end user, while at the same time providing an interface to
/// internal classes. The `StatisticsMutator` is a proxy/adapter class that
/// serves exactly this purpose. It is friends with the `Statistics` and can
/// thus access its members. At the same time the interface it defines is narrow
/// and provides only the necessary interface for the cache classes to register
/// hits and misses.
template <typename Key>
class StatisticsMutator {
public:
using StatisticsPointer = std::shared_ptr<Statistics<Key>>;
/// Constructor.
StatisticsMutator() noexcept = default;
/// Constructor.
///
/// \param stats A shared pointer lvalue reference.
StatisticsMutator(const StatisticsPointer& stats) // NOLINT(runtime/explicit)
: _stats(stats) {
}
/// Constructor.
///
/// \param stats A shared pointer rvalue reference to move into the
/// mutator.
StatisticsMutator(StatisticsPointer&& stats) // NOLINT(runtime/explicit)
: _stats(std::move(stats)) {
}
/// Registers a hit for the given key with the internal statistics.
///
/// \param key The key to register a hit for.
void register_hit(const Key& key) {
assert(has_stats());
_stats->_total_accesses += 1;
_stats->_total_hits += 1;
auto iterator = _stats->_key_map.find(key);
if (iterator != _stats->_key_map.end()) {
iterator->second.hits += 1;
}
}
/// Registers a miss for the given key with the internal statistics.
///
/// \param key The key to register a miss for.
void register_miss(const Key& key) {
assert(has_stats());
_stats->_total_accesses += 1;
auto iterator = _stats->_key_map.find(key);
if (iterator != _stats->_key_map.end()) {
iterator->second.misses += 1;
}
}
/// \returns A reference to the statistics object.
Statistics<Key>& get() noexcept {
assert(has_stats());
return *_stats;
}
/// \returns A const reference to the statistics object.
const Statistics<Key>& get() const noexcept {
assert(has_stats());
return *_stats;
}
/// \returns A `shared_ptr` to the statistics object.
StatisticsPointer& shared() noexcept {
return _stats;
}
/// \returns A const `shared_ptr` to the statistics object.
const StatisticsPointer& shared() const noexcept {
return _stats;
}
/// \returns True if the mutator has a statistics object, else false.
bool has_stats() const noexcept {
return _stats != nullptr;
}
/// \copydoc has_stats()
explicit operator bool() const noexcept {
return has_stats();
}
/// Resets the internal statistics pointer.
void reset() {
_stats.reset();
}
private:
/// A shared pointer to a statistics object.
std::shared_ptr<Statistics<Key>> _stats;
};
} // namespace Internal
} // namespace LRU
#endif // LRU_STATISTICS_MUTATOR_HPP

View File

@@ -0,0 +1,116 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_INTERNAL_TIMED_INFORMATION_HPP
#define LRU_INTERNAL_TIMED_INFORMATION_HPP
#include <cstddef>
#include <tuple>
#include <utility>
#include <lru/internal/definitions.hpp>
#include <lru/internal/information.hpp>
#include <lru/internal/utility.hpp>
namespace LRU {
namespace Internal {
/// The information object for timed caches.
///
/// TimedInformation differs from plain information only in that it stores the
/// creation time, to know when a key has expired.
///
/// \tparam Key The key type of the information.
/// \tparam Value The value type of the information.
template <typename Key, typename Value>
struct TimedInformation : public Information<Key, Value> {
using super = Information<Key, Value>;
using typename super::QueueIterator;
using Timestamp = Internal::Timestamp;
/// Constructor.
///
/// \param value_ The value for the information.
/// \param insertion_time_ The insertion timestamp of the key.
/// \param order_ The order iterator for the information.
TimedInformation(const Value& value_,
const Timestamp& insertion_time_,
QueueIterator order_ = QueueIterator())
: super(value_, order_), insertion_time(insertion_time_) {
}
/// Constructor.
///
/// Uses the current time as the insertion timestamp.
///
/// \param value_ The value for the information.
/// \param order_ The order iterator for the information.
explicit TimedInformation(const Value& value_,
QueueIterator order_ = QueueIterator())
: TimedInformation(value_, Internal::Clock::now(), order_) {
}
/// \copydoc Information::Information(QueueIterator,ValueArguments&&)
template <typename... ValueArguments>
TimedInformation(QueueIterator order_, ValueArguments&&... value_argument)
: super(std::forward<ValueArguments>(value_argument)..., order_)
, insertion_time(Internal::Clock::now()) {
}
/// \copydoc Information::Information(QueueIterator,const
/// std::tuple<ValueArguments...>&)
template <typename... ValueArguments>
explicit TimedInformation(
const std::tuple<ValueArguments...>& value_arguments,
QueueIterator order_ = QueueIterator())
: super(value_arguments, order_), insertion_time(Internal::Clock::now()) {
}
/// Compares this timed information for equality with another one.
///
/// Additionally to key and value equality, the timed information requires
/// that the insertion timestamps be equal.
///
/// \param other The other timed information.
/// \returns True if this information equals the other one, else false.
bool operator==(const TimedInformation& other) const noexcept {
if (super::operator!=(other)) return false;
return this->insertion_time == other.insertion_time;
}
/// Compares this timed information for inequality with another one.
///
/// \param other The other timed information.
/// \returns True if this information does not equal the other one, else
/// false.
/// \see operator==()
bool operator!=(const TimedInformation& other) const noexcept {
return !(*this == other);
}
/// The time at which the key of the information was insterted into a cache.
const Timestamp insertion_time;
};
} // namespace Internal
} // namespace LRU
#endif // LRU_INTERNAL_TIMED_INFORMATION_HPP

View File

@@ -0,0 +1,178 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_UTILITY_HPP
#define LRU_UTILITY_HPP
#include <cstddef>
#include <iterator>
#include <tuple>
#include <utility>
namespace LRU {
namespace Internal {
/// Generates an index sequence for a tuple.
///
/// \tparam Ts The types of the tuple (to deduce the size).
template <typename... Ts>
constexpr auto tuple_indices(const std::tuple<Ts...>&) {
return std::make_index_sequence<sizeof...(Ts)>();
}
/// Applies (in the functional sense) a tuple to the constructor of a class.
///
/// \tparam T The type to construct.
/// \tparam Indices The indices into the tuple (generated from an index
/// sequence).
/// \param args The tuple of arguments to construct the object with.
template <typename T, typename... Args, std::size_t... Indices>
constexpr T construct_from_tuple(const std::tuple<Args...>& arguments,
std::index_sequence<Indices...>) {
return T(std::forward<Args>(std::get<Indices>(arguments))...);
}
/// Applies (in the functional sense) a tuple to the constructor of a class.
///
/// \tparam T The type to construct.
/// \param args The tuple of arguments to construct the object with.
template <typename T, typename... Args>
constexpr T construct_from_tuple(const std::tuple<Args...>& args) {
return construct_from_tuple<T>(args, tuple_indices(args));
}
/// Applies (in the functional sense) a tuple to the constructor of a class.
///
/// \tparam T The type to construct.
/// \param args The tuple of arguments to construct the object with.
template <typename T, typename... Args>
constexpr T construct_from_tuple(std::tuple<Args...>&& args) {
return construct_from_tuple<T>(std::move(args), tuple_indices(args));
}
/// A type trait that disables a template overload if a type is not an iterator.
///
/// \tparam T the type to check.
template <typename T>
using enable_if_iterator = typename std::iterator_traits<T>::value_type;
/// A type trait that disables a template overload if a type is not a range.
///
/// \tparam T the type to check.
template <typename T>
using enable_if_range = std::pair<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>;
/// A type trait that disables a template overload if a type is not an iterator
/// over a pair.
///
/// \tparam T the type to check.
template <typename T>
using enable_if_iterator_over_pair =
std::pair<typename std::iterator_traits<T>::value_type::first_type,
typename std::iterator_traits<T>::value_type::first_type>;
/// A type trait that disables a template overload if a type is not convertible
/// to a target type.
///
/// \tparam Target The type one wants to check against.
/// \tparam T The type to check.
template <typename Target, typename T>
using enable_if_same = std::enable_if_t<std::is_convertible<T, Target>::value>;
/// Base case for `static_all_of` (the neutral element of AND is true).
constexpr bool static_all_of() noexcept {
return true;
}
/// Checks if all the given parameters evaluate to true.
///
/// \param head The first expression to check.
/// \param tail The remaining expression to check.
template <typename Head, typename... Tail>
constexpr bool static_all_of(Head&& head, Tail&&... tail) {
// Replace with (ts && ...) when the time is right
return std::forward<Head>(head) && static_all_of(std::forward<Tail>(tail)...);
}
/// Base case for `static_any_of` (the neutral element of OR is false).
constexpr bool static_any_of() noexcept {
return false;
}
/// Checks if any the given parameters evaluate to true.
///
/// \param head The first expression to check.
/// \param tail The remaining expression to check.
/// \returns True if any of the given parameters evaluate to true.
template <typename Head, typename... Tail>
constexpr bool static_any_of(Head&& head, Tail&&... tail) {
// Replace with (ts || ...) when the time is right
return std::forward<Head>(head) || static_any_of(std::forward<Tail>(tail)...);
}
/// Checks if none the given parameters evaluate to true.
///
/// \param ts The expressions to check.
/// \returns True if any of the given parameters evaluate to true.
template <typename... Ts>
constexpr bool static_none_of(Ts&&... ts) {
// Replace with (!ts && ...) when the time is right
return !static_any_of(std::forward<Ts>(ts)...);
}
/// Checks if all the given types are convertible to the first type.
///
/// \tparam T the first type.
/// \tparam Ts The types to check against the first.
template <typename T, typename... Ts>
constexpr bool
all_of_type = static_all_of(std::is_convertible<Ts, T>::value...);
/// Checks if none of the given types are convertible to the first type.
///
/// \tparam T the first type.
/// \tparam Ts The types to check against the first.
template <typename T, typename... Ts>
constexpr bool
none_of_type = static_none_of(std::is_convertible<Ts, T>::value...);
/// Base case for `for_each`.
template <typename Function>
void for_each(Function) noexcept {
}
/// Calls a function for each of the given variadic arguments.
///
/// \param function The function to call for each argument.
/// \param head The first value to call the function with.
/// \param tail The remaining values to call the function with.
template <typename Function, typename Head, typename... Tail>
void for_each(Function function, Head&& head, Tail&&... tail) {
function(std::forward<Head>(head));
for_each(function, std::forward<Tail>(tail)...);
}
} // namespace Internal
} // namespace LRU
#endif // LRU_UTILITY_HPP

View File

@@ -0,0 +1,40 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_ITERATOR_TAGS_HPP
#define LRU_ITERATOR_TAGS_HPP
namespace LRU {
namespace Tag {
struct OrderedIterator {};
struct UnorderedIterator {};
} // namespace Tag
namespace Lowercase {
namespace tag {
using ordered_iterator = ::LRU::Tag::OrderedIterator;
using unordered_iterator = ::LRU::Tag::UnorderedIterator;
} // namespace tag
} // namespace Lowercase
} // namespace LRU
#endif // LRU_ITERATOR_TAGS_HPP

View File

@@ -0,0 +1,67 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_KEY_STATISTICS_HPP
#define LRU_KEY_STATISTICS_HPP
#include <cstddef>
namespace LRU {
/// Stores statistics for a single key.
///
/// The statistics stored are the total number of hits and the total number of
/// misses. The total number of acccesses (the sum of hits and misses) may be
/// accessed as well.
struct KeyStatistics {
using size_t = std::size_t;
/// Constructor.
///
/// \param hits_ The initial number of hits for the key.
/// \param misses_ The initial number of misses for the key.
explicit KeyStatistics(size_t hits_ = 0, size_t misses_ = 0)
: hits(hits_), misses(misses_) {
}
/// \returns The total number of accesses made for the key.
/// \details This is the sum of the hits and misses.
size_t accesses() const noexcept {
return hits + misses;
}
/// Resets the statistics for a key (sets them to zero).
void reset() {
hits = 0;
misses = 0;
}
/// The number of hits for the key.
size_t hits;
/// The number of misses for the key.
size_t misses;
};
} // namespace LRU
#endif // LRU_KEY_STATISTICS_HPP

View File

@@ -0,0 +1,34 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_LOWERCASE_HPP
#define LRU_LOWERCASE_HPP
#include <lru/lru.hpp>
namespace LRU {
using namespace Lowercase; // NOLINT(build/namespaces)
} // namespace LRU
namespace lru = LRU;
#endif // LRU_LOWERCASE_HPP

View File

@@ -0,0 +1,33 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_HPP
#define LRU_HPP
#include <lru/cache-tags.hpp>
#include <lru/cache.hpp>
#include <lru/error.hpp>
#include <lru/iterator-tags.hpp>
#include <lru/statistics.hpp>
#include <lru/timed-cache.hpp>
#include <lru/wrap.hpp>
#endif // LRU_HPP

View File

@@ -0,0 +1,256 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_STATISTICS_HPP
#define LRU_STATISTICS_HPP
#include <cstddef>
#include <initializer_list>
#include <iterator>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <lru/error.hpp>
#include <lru/internal/utility.hpp>
#include <lru/key-statistics.hpp>
namespace LRU {
namespace Internal {
template <typename>
class StatisticsMutator;
}
/// Stores statistics about LRU cache utilization and efficiency.
///
/// The statistics object stores the number of misses and hits were recorded for
/// a cache in total. Furthemore, it is possibly to register a number of keys
/// for *monitoring*. For each of these keys, an additional hit and miss count
/// is maintained, that can keep insight into the utiliization of a particular
/// cache. Note that accesses only mean lookups -- insertions or erasures will
/// never signify an "access".
///
/// \tparam Key The type of the keys being monitored.
template <typename Key>
class Statistics {
public:
using size_t = std::size_t;
using InitializerList = std::initializer_list<Key>;
/// Constructor.
Statistics() noexcept : _total_accesses(0), _total_hits(0) {
}
/// Constructor.
///
/// \param keys Any number of keys to monitor.
template <typename... Keys,
typename = std::enable_if_t<Internal::all_of_type<Key, Keys...>>>
explicit Statistics(Keys&&... keys) : Statistics() {
// clang-format off
Internal::for_each([this](auto&& key) {
this->monitor(std::forward<decltype(key)>(key));
}, std::forward<Keys>(keys)...);
// clang-format on
}
/// Constructor.
///
/// \param range A range of keys to monitor.
template <typename Range, typename = Internal::enable_if_range<Range>>
explicit Statistics(const Range& range)
: Statistics(std::begin(range), std::end(range)) {
}
/// Constructor.
///
/// \param begin The start iterator of a range of keys to monitor.
/// \param end The end iterator of a range of keys to monitor.
template <typename Iterator,
typename = Internal::enable_if_iterator<Iterator>>
Statistics(Iterator begin, Iterator end) : Statistics() {
for (; begin != end; ++begin) {
monitor(*begin);
}
}
/// Constructor.
///
/// \param list A list of keys to monitor.
Statistics(InitializerList list) // NOLINT(runtime/explicit)
: Statistics(list.begin(), list.end()) {
}
/// \returns The total number of accesses (hits + misses) made to the cache.
size_t total_accesses() const noexcept {
return _total_accesses;
}
/// \returns The total number of hits made to the cache.
size_t total_hits() const noexcept {
return _total_hits;
}
/// \returns The total number of misses made to the cache.
size_t total_misses() const noexcept {
return total_accesses() - total_hits();
}
/// \returns The ratio of hits ($\in [0, 1]$) relative to all accesses.
double hit_rate() const noexcept {
return static_cast<double>(total_hits()) / total_accesses();
}
/// \returns The ratio of misses ($\in [0, 1]$) relative to all accesses.
double miss_rate() const noexcept {
return 1 - hit_rate();
}
/// \returns The number of hits for the given key.
/// \param key The key to retrieve the hits for.
/// \throws LRU::UnmonitoredKey if the key was not registered for monitoring.
size_t hits_for(const Key& key) const {
return stats_for(key).hits;
}
/// \returns The number of misses for the given key.
/// \param key The key to retrieve the misses for.
/// \throws LRU::UnmonitoredKey if the key was not registered for monitoring.
size_t misses_for(const Key& key) const {
return stats_for(key).misses;
}
/// \returns The number of accesses (hits + misses) for the given key.
/// \param key The key to retrieve the accesses for.
/// \throws LRU::UnmonitoredKey if the key was not registered for monitoring.
size_t accesses_for(const Key& key) const {
return stats_for(key).accesses();
}
/// \returns A `KeyStatistics` object for the given key.
/// \param key The key to retrieve the stats for.
/// \throws LRU::UnmonitoredKey if the key was not registered for monitoring.
const KeyStatistics& stats_for(const Key& key) const {
auto iterator = _key_map.find(key);
if (iterator == _key_map.end()) {
throw LRU::Error::UnmonitoredKey();
}
return iterator->second;
}
/// \copydoc stats_for()
const KeyStatistics& operator[](const Key& key) const {
return stats_for(key);
}
/// Registers the key for monitoring.
///
/// If the key was already registered, this is a no-op (most importantly, the
/// old statistics are __not__ wiped).
///
/// \param key The key to register.
void monitor(const Key& key) {
// emplace does nothing if the key is already present
_key_map.emplace(key, KeyStatistics());
}
/// Unregisters the given key from monitoring.
///
/// \param key The key to unregister.
/// \throws LRU::Error::UnmonitoredKey if the key was never registered for
/// monitoring.
void unmonitor(const Key& key) {
auto iterator = _key_map.find(key);
if (iterator == _key_map.end()) {
throw LRU::Error::UnmonitoredKey();
} else {
_key_map.erase(iterator);
}
}
/// Unregisters all keys from monitoring.
void unmonitor_all() {
_key_map.clear();
}
/// Clears all statistics for the given key, but keeps on monitoring it.
///
/// \param key The key to reset.
void reset_key(const Key& key) {
auto iterator = _key_map.find(key);
if (iterator == _key_map.end()) {
throw LRU::Error::UnmonitoredKey();
} else {
iterator->second.reset();
}
}
/// Clears the statistics of all keys, but keeps on monitoring it them.
void reset_all() {
for (auto& pair : _key_map) {
_key_map.second.reset();
}
}
/// \returns True if the given key is currently registered for monitoring,
/// else false.
/// \param key The key to check for.
bool is_monitoring(const Key& key) const noexcept {
return _key_map.count(key);
}
/// \returns The number of keys currnetly being monitored.
size_t number_of_monitored_keys() const noexcept {
return _key_map.size();
}
/// \returns True if currently any keys at all are being monitored, else
/// false.
bool is_monitoring_keys() const noexcept {
return !_key_map.empty();
}
private:
template <typename>
friend class Internal::StatisticsMutator;
using HitMap = std::unordered_map<Key, KeyStatistics>;
/// The total number of accesses made for any key.
size_t _total_accesses;
/// The total number of htis made for any key.
size_t _total_hits;
/// The map to keep track of statistics for monitored keys.
HitMap _key_map;
};
namespace Lowercase {
template <typename... Ts>
using statistics = Statistics<Ts...>;
} // namespace Lowercase
} // namespace LRU
#endif // LRU_STATISTICS_HPP

View File

@@ -0,0 +1,391 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_TIMED_CACHE_HPP
#define LRU_TIMED_CACHE_HPP
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <functional>
#include <iterator>
#include <list>
#include <stdexcept>
#include <unordered_map>
#include <utility>
#include <lru/error.hpp>
#include <lru/internal/base-cache.hpp>
#include <lru/internal/last-accessed.hpp>
#include <lru/internal/timed-information.hpp>
namespace LRU {
namespace Internal {
template <typename Key,
typename Value,
typename HashFunction,
typename KeyEqual>
using TimedCacheBase = BaseCache<Key,
Value,
Internal::TimedInformation,
HashFunction,
KeyEqual,
Tag::TimedCache>;
} // namespace Internal
/// A timed LRU cache.
///
/// A timed LRU cache behaves like a regular LRU cache, but adds the concept of
/// "expiration". The cache now not only remembers the order of insertion, but
/// also the point in time at which each element was inserted into the cache.
/// The cache then has an additional "time to live" property, which designates
/// the time after which a key in the cache is said to be "expired". Once a key
/// has expired, the cache will behave as if the key were not present in the
/// cache at all and, for example, return false on calls to `contains()` or
/// throw on calls to `lookup()`.
///
/// \see LRU::Cache
template <typename Key,
typename Value,
typename Duration = std::chrono::duration<double, std::milli>,
typename HashFunction = std::hash<Key>,
typename KeyEqual = std::equal_to<Key>>
class TimedCache
: public Internal::TimedCacheBase<Key, Value, HashFunction, KeyEqual> {
private:
using super = Internal::TimedCacheBase<Key, Value, HashFunction, KeyEqual>;
using PRIVATE_BASE_CACHE_MEMBERS;
public:
using Tag = LRU::Tag::TimedCache;
using PUBLIC_BASE_CACHE_MEMBERS;
using super::ordered_end;
using super::unordered_end;
using typename super::size_t;
/// \param time_to_live The time to live for keys in the cache.
/// \copydoc BaseCache::BaseCache(size_t,const HashFunction&,const KeyEqual&)
template <typename AnyDurationType = Duration>
explicit TimedCache(const AnyDurationType& time_to_live,
size_t capacity = Internal::DEFAULT_CAPACITY,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(capacity, hash, equal)
, _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) {
}
/// \param time_to_live The time to live for keys in the cache.
/// \copydoc BaseCache::BaseCache(size_t,Iterator,Iterator,const
/// HashFunction&,const
/// KeyEqual&)
template <typename Iterator, typename AnyDurationType = Duration>
TimedCache(const AnyDurationType& time_to_live,
size_t capacity,
Iterator begin,
Iterator end,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(capacity, begin, end, hash, equal)
, _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) {
}
/// \param time_to_live The time to live for keys in the cache.
/// \copydoc BaseCache::BaseCache(Iterator,Iterator,const HashFunction&,const
/// KeyEqual&)
template <typename Iterator, typename AnyDurationType = Duration>
TimedCache(const AnyDurationType& time_to_live,
Iterator begin,
Iterator end,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(begin, end, hash, equal)
, _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) {
}
/// \param time_to_live The time to live for keys in the cache.
/// \copydoc BaseCache::BaseCache(Range,size_t,const HashFunction&,const
/// KeyEqual&)
template <typename Range,
typename AnyDurationType = Duration,
typename = Internal::enable_if_range<Range>>
TimedCache(const AnyDurationType& time_to_live,
size_t capacity,
Range&& range,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(capacity, std::forward<Range>(range), hash, equal)
, _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) {
}
/// \param time_to_live The time to live for keys in the cache.
/// \copydoc BaseCache::BaseCache(Range,const HashFunction&,const
/// KeyEqual&)
template <typename Range,
typename AnyDurationType = Duration,
typename = Internal::enable_if_range<Range>>
explicit TimedCache(const AnyDurationType& time_to_live,
Range&& range,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual())
: super(std::forward<Range>(range), hash, equal)
, _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) {
}
/// \param time_to_live The time to live for keys in the cache.
/// \copydoc BaseCache::BaseCache(InitializerList,const HashFunction&,const
/// KeyEqual&)
template <typename AnyDurationType = Duration>
TimedCache(const AnyDurationType& time_to_live,
InitializerList list,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual()) // NOLINT(runtime/explicit)
: super(list, hash, equal),
_time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) {
}
/// \param time_to_live The time to live for keys in the cache.
/// \copydoc BaseCache::BaseCache(InitializerList,size_t,const
/// HashFunction&,const
/// KeyEqual&)
template <typename AnyDurationType = Duration>
TimedCache(const AnyDurationType& time_to_live,
size_t capacity,
InitializerList list,
const HashFunction& hash = HashFunction(),
const KeyEqual& equal = KeyEqual()) // NOLINT(runtime/explicit)
: super(capacity, list, hash, equal),
_time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) {
}
/// \copydoc BaseCache::swap
void swap(TimedCache& other) noexcept {
using std::swap;
super::swap(other);
swap(_time_to_live, other._time_to_live);
}
/// Swaps the contents of one cache with another cache.
///
/// \param first The first cache to swap.
/// \param second The second cache to swap.
friend void swap(TimedCache& first, TimedCache& second) noexcept {
first.swap(second);
}
/// \copydoc BaseCache::find(const Key&)
UnorderedIterator find(const Key& key) override {
auto iterator = _map.find(key);
if (iterator != _map.end()) {
if (!_has_expired(iterator->second)) {
_register_hit(key, iterator->second.value);
_move_to_front(iterator->second.order);
_last_accessed = iterator;
return {*this, iterator};
}
}
_register_miss(key);
return end();
}
/// \copydoc BaseCache::find(const Key&) const
UnorderedConstIterator find(const Key& key) const override {
auto iterator = _map.find(key);
if (iterator != _map.end()) {
if (!_has_expired(iterator->second)) {
_register_hit(key, iterator->second.value);
_move_to_front(iterator->second.order);
_last_accessed = iterator;
return {*this, iterator};
}
}
_register_miss(key);
return cend();
}
// no front() because we may have to erase the
// entire cache if everything happens to be expired
/// \returns True if all keys in the cache have expired, else false.
bool all_expired() const {
// By the laws of predicate logic, any statement about any empty set is true
if (is_empty()) return true;
/// If the most-recently inserted key has expired, all others must have too.
auto latest = _map.find(_order.back());
return _has_expired(latest->second);
}
/// Erases all expired elements from the cache.
///
/// \complexity O(N)
/// \returns The number of elements erased.
size_t clear_expired() {
// We have to do a linear search here because linked lists do not
// support O(log N) binary searches given their node-based nature.
// Either way, in the worst case the entire cache has expired and
// we would have to do O(N) erasures.
if (is_empty()) return 0;
auto iterator = _order.begin();
size_t number_of_erasures = 0;
while (iterator != _order.end()) {
auto map_iterator = _map.find(*iterator);
// If the current element hasn't expired, also all elements inserted
// after will not have, so we can stop.
if (!_has_expired(map_iterator->second)) break;
_erase(map_iterator);
iterator = _order.begin();
number_of_erasures += 1;
}
return number_of_erasures;
}
/// \returns True if the given key is contained in the cache and has expired.
/// \param key The key to test expiration for.
bool has_expired(const Key& key) const noexcept {
auto iterator = _map.find(key);
return iterator != _map.end() && _has_expired(iterator->second);
}
/// \returns True if the key pointed to by the iterator has expired.
/// \param ordered_iterator The ordered iterator to check.
/// \details If this is the end iterator, this method returns false.
bool has_expired(OrderedConstIterator ordered_iterator) const noexcept {
if (ordered_iterator == ordered_end()) return false;
auto iterator = _map.find(ordered_iterator->key());
assert(iterator != _map.end());
return _has_expired(iterator->second);
}
/// \returns True if the key pointed to by the iterator has expired.
/// \param unordered_iterator The unordered iterator to check.
/// \details If this is the end iterator, this method returns false.
bool has_expired(UnorderedConstIterator unordered_iterator) const noexcept {
if (unordered_iterator == unordered_end()) return false;
assert(unordered_iterator._iterator != _map.end());
return _has_expired(unordered_iterator._iterator->second);
}
/// \copydoc BaseCache::is_valid(UnorderedConstIterator)
bool is_valid(UnorderedConstIterator unordered_iterator) const
noexcept override {
if (!super::is_valid(unordered_iterator)) return false;
if (has_expired(unordered_iterator)) return false;
return true;
}
/// \copydoc BaseCache::is_valid(OrderedConstIterator)
bool is_valid(OrderedConstIterator ordered_iterator) const noexcept override {
if (!super::is_valid(ordered_iterator)) return false;
if (has_expired(ordered_iterator)) return false;
return true;
}
/// \copydoc BaseCache::is_valid(UnorderedConstIterator)
/// \throws LRU::Error::KeyExpired if the key pointed to by the iterator has
/// expired.
void
throw_if_invalid(UnorderedConstIterator unordered_iterator) const override {
super::throw_if_invalid(unordered_iterator);
if (has_expired(unordered_iterator)) {
throw LRU::Error::KeyExpired();
}
}
/// \copydoc BaseCache::is_valid(OrderedConstIterator)
/// \throws LRU::Error::KeyExpired if the key pointed to by the iterator has
/// expired.
void throw_if_invalid(OrderedConstIterator ordered_iterator) const override {
super::throw_if_invalid(ordered_iterator);
if (has_expired(ordered_iterator)) {
throw LRU::Error::KeyExpired();
}
}
private:
using Clock = Internal::Clock;
/// \returns True if the last accessed object is valid.
/// \details Next to performing the base cache's action, this method also
/// checks for expiration of the last accessed key.
bool _last_accessed_is_ok(const Key& key) const noexcept override {
if (!super::_last_accessed_is_ok(key)) return false;
return !_has_expired(_last_accessed.information());
}
/// \copydoc _value_for_last_accessed() const
Value& _value_for_last_accessed() override {
auto& information = _last_accessed.information();
if (_has_expired(information)) {
throw LRU::Error::KeyExpired();
} else {
return information.value;
}
}
/// Attempts to access the last accessed key's value.
/// \throws LRU::Error::KeyExpired if the key has expired.
/// \returns The value of the last accessed key.
const Value& _value_for_last_accessed() const override {
const auto& information = _last_accessed.information();
if (_has_expired(information)) {
throw LRU::Error::KeyExpired();
} else {
return information.value;
}
}
/// Checks if a key has expired, given its information.
///
/// \param information The information to check expiration with.
/// \returns True if the key has expired, else false.
bool _has_expired(const Information& information) const noexcept {
auto elapsed = Clock::now() - information.insertion_time;
return std::chrono::duration_cast<Duration>(elapsed) > _time_to_live;
}
/// The duration after which a key is said to be expired.
Duration _time_to_live;
};
namespace Lowercase {
template <typename... Ts>
using timed_cache = TimedCache<Ts...>;
} // namespace Lowercase
} // namespace LRU
#endif // LRU_TIMED_CACHE_HPP

View File

@@ -0,0 +1,99 @@
/// The MIT License (MIT)
/// Copyright (c) 2016 Peter Goldsborough
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to
/// deal in the Software without restriction, including without limitation the
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
/// sell copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
#ifndef LRU_WRAP_HPP
#define LRU_WRAP_HPP
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
#include <lru/cache.hpp>
#include <lru/internal/hash.hpp>
#include <lru/internal/utility.hpp>
namespace LRU {
/// Wraps a function with a "shallow" LRU cache.
///
/// Given a function, this function will return a new function, where
/// "top-level" calls are cached. With "top-level" or "shallow", we mean
/// that recursive calls to the same function are not cached, since those
/// will call the original function symbol, not the wrapped one.
///
/// \tparam CacheType The cache template class to use.
/// \param original_function The function to wrap.
/// \param args Any arguments to forward to the cache.
/// \returns A new function with a shallow LRU cache.
template <typename Function,
template <typename...> class CacheType = Cache,
typename... Args>
auto wrap(Function original_function, Args&&... args) {
return [
original_function,
cache_args = std::forward_as_tuple(std::forward<Args>(args)...)
](auto&&... arguments) mutable {
using Arguments = std::tuple<std::decay_t<decltype(arguments)>...>;
using ReturnType = decltype(
original_function(std::forward<decltype(arguments)>(arguments)...));
static_assert(!std::is_void<ReturnType>::value,
"Return type of wrapped function must not be void");
static auto cache =
Internal::construct_from_tuple<CacheType<Arguments, ReturnType>>(
cache_args);
auto key = std::make_tuple(arguments...);
auto iterator = cache.find(key);
if (iterator != cache.end()) {
return iterator->second;
}
auto value =
original_function(std::forward<decltype(arguments)>(arguments)...);
cache.emplace(key, value);
return value;
};
}
/// Wraps a function with a "shallow" LRU timed cache.
///
/// Given a function, this function will return a new function, where
/// "top-level" calls are cached. With "top-level" or "shallow", we mean
/// that recursive calls to the same function are not cached, since those
/// will call the original function symbol, not the wrapped one.
///
/// \param original_function The function to wrap.
/// \param args Any arguments to forward to the cache.
/// \returns A new function with a shallow LRU cache.
template <typename Function, typename Duration, typename... Args>
auto timed_wrap(Function original_function, Duration duration, Args&&... args) {
return wrap<Function, TimedCache>(
original_function, duration, std::forward<Args>(args)...);
}
} // namespace LRU
#endif // LRU_WRAP_HPP