File indexing completed on 2024-12-08 10:25:49

0001 /*
0002    SPDX-FileCopyrightText: 2020 Milian Wolff <mail@milianw.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <algorithm>
0010 #include <vector>
0011 
0012 template<typename Key, typename Value>
0013 class LRUCache
0014 {
0015 public:
0016     struct Entry {
0017         Key key;
0018         Value value;
0019         bool operator==(const Key &rhs) const
0020         {
0021             return key == rhs;
0022         }
0023     };
0024     using Entries = std::vector<Entry>;
0025     using value_type = typename Entries::value_type;
0026     using size_type = typename Entries::size_type;
0027     using difference_type = typename Entries::difference_type;
0028     // only const access
0029     using reference = typename Entries::const_reference;
0030     using const_reference = typename Entries::const_reference;
0031     using pointer = typename Entries::const_pointer;
0032     using iterator = typename Entries::const_iterator;
0033     using const_iterator = typename Entries::const_iterator;
0034 
0035     void setMaxEntries(int maxEntries)
0036     {
0037         mMaxEntries = maxEntries;
0038     }
0039 
0040     std::size_t size() const
0041     {
0042         return mEntries.size();
0043     }
0044 
0045     const_iterator begin() const
0046     {
0047         return mEntries.begin();
0048     }
0049 
0050     const_iterator end() const
0051     {
0052         return std::next(mEntries.begin(), size());
0053     }
0054 
0055     const_iterator find(const Key &key)
0056     {
0057         // using non-const iterators here since we will re-insert when we find
0058         const auto begin = mEntries.begin();
0059         const auto end = std::next(mEntries.begin(), size());
0060         auto it = std::find(begin, end, key);
0061         if (it == begin || it == end) { // not found or already the last recently used one
0062             return it;
0063         }
0064 
0065         // rotate to mark entry as last recently used one
0066         std::rotate(begin, it, it + 1);
0067         return mEntries.cbegin();
0068     }
0069 
0070     void insert(Key key, Value value)
0071     {
0072         auto entriesSize = size();
0073         if (mMaxEntries == -1 || (entriesSize < static_cast<size_t>(mMaxEntries))) {
0074             // open up a new slot
0075             ++entriesSize;
0076             mEntries.resize(entriesSize);
0077         }
0078 
0079         // right shift to make space at the front
0080         std::rotate(mEntries.begin(), std::next(mEntries.begin(), entriesSize - 1), std::next(mEntries.begin(), entriesSize));
0081 
0082         // insert up front
0083         mEntries.front() = {std::move(key), std::move(value)};
0084     }
0085 
0086     bool remove(const Key &key)
0087     {
0088         const auto begin = mEntries.begin();
0089         const auto end = std::next(mEntries.begin(), size());
0090         auto it = std::find(begin, end, key);
0091         if (it == end) { // not found or already the last recently used one
0092             return false;
0093         }
0094 
0095         std::move(std::next(it), end, it);
0096         mEntries.resize(size() - 1);
0097         return true;
0098     }
0099 
0100     void clear()
0101     {
0102         mEntries.clear();
0103     }
0104 
0105 private:
0106     Entries mEntries;
0107     int mMaxEntries = -1;
0108 };