/// maximum desired entry caching duration (a.k.a. TTL), in seconds
using Ttl = int;
+ /// the keeper of cache entry Key, Value, and caching-related entry metadata
+ class Entry
+ {
+ public:
+ Entry(const Key &, const Value &, const Ttl);
+
+ /// whether the entry is stale
+ bool expired() const { return expires < squid_curtime; }
+
+ public:
+ Key key; ///< the entry search key; see ClpMap::get()
+ Value value; ///< cached value provided by the map user
+ time_t expires = 0; ///< get() stops returning the entry after this time
+ uint64_t memCounted = 0; ///< memory accounted for this entry in our ClpMap
+ };
+
+ /// Entries in LRU order
+ using Entries = std::list<Entry, PoolingAllocator<Entry> >;
+ using EntriesIterator = typename Entries::iterator;
+ using ConstEntriesIterator = typename Entries::const_iterator;
+
explicit ClpMap(const uint64_t capacity) { setMemLimit(capacity); }
ClpMap(uint64_t capacity, Ttl defaultTtl);
~ClpMap() = default;
/// The number of currently stored entries, including expired ones
size_t entries() const { return entries_.size(); }
-private:
- /// the keeper of cache entry Key, Value, and caching-related entry metadata
- class Entry
- {
- public:
- Entry(const Key &, const Value &, const Ttl);
-
- /// whether the entry is stale
- bool expired() const { return expires < squid_curtime; }
-
- public:
- Key key; ///< the entry search key; see ClpMap::get()
- Value value; ///< cached value provided by the map user
- time_t expires = 0; ///< get() stops returning the entry after this time
- uint64_t memCounted = 0; ///< memory accounted for this entry in our ClpMap
- };
-
- /// Entries in LRU order
- using Entries = std::list<Entry, PoolingAllocator<Entry> >;
- using EntriesIterator = typename Entries::iterator;
+ /// Read-only traversal of all cached entries in LRU order, least recently
+ /// used entry first. Stored expired entries (if any) are included. Any map
+ /// modification may invalidate these iterators and their derivatives.
+ ConstEntriesIterator cbegin() const { return entries_.cbegin(); }
+ ConstEntriesIterator cend() const { return entries_.cend(); }
+ /// range-based `for` loop support; \sa cbegin()
+ ConstEntriesIterator begin() const { return cbegin(); }
+ ConstEntriesIterator end() const { return cend(); }
+private:
using IndexItem = std::pair<const Key, EntriesIterator>;
/// key:entry_position mapping for fast entry lookups by key
using Index = std::unordered_map<Key, EntriesIterator, std::hash<Key>, std::equal_to<Key>, PoolingAllocator<IndexItem> >;
CPPUNIT_TEST( testZeroTtl );
CPPUNIT_TEST( testNegativeTtl );
CPPUNIT_TEST( testPurgeIsLru );
+ CPPUNIT_TEST( testClassicLoopTraversal );
+ CPPUNIT_TEST( testRangeLoopTraversal );
CPPUNIT_TEST_SUITE_END();
public:
void testZeroTtl();
void testNegativeTtl();
void testPurgeIsLru();
+ void testClassicLoopTraversal();
+ void testRangeLoopTraversal();
/// Generate and insert the given number of entries into the given map. Each
/// entry is guaranteed to be inserted, but that insertion may purge other
fillMapWithEntries(m);
CPPUNIT_ASSERT(!m.get("0")); // removable when not recently used
}
+
+void
+TestClpMap::testClassicLoopTraversal()
+{
+ Map m(2048);
+ const size_t expectedEntryCount = 10;
+ addSequenceOfEntriesToMap(m, expectedEntryCount, 0, 50);
+ size_t iterations = 0;
+ for (auto i = m.cbegin(); i != m.cend(); ++i) {
+ ++iterations;
+ const auto expectedValue = static_cast<Map::mapped_type>(expectedEntryCount - iterations);
+ CPPUNIT_ASSERT_EQUAL(expectedValue, i->value);
+ }
+ CPPUNIT_ASSERT_EQUAL(expectedEntryCount, iterations);
+}
+
+void
+TestClpMap::testRangeLoopTraversal()
+{
+ Map m(2048);
+ const size_t expectedEntryCount = 10;
+ addSequenceOfEntriesToMap(m, expectedEntryCount, 0, 50);
+ size_t iterations = 0;
+ for (const auto &entry: m) {
+ ++iterations;
+ const auto expectedValue = static_cast<Map::mapped_type>(expectedEntryCount - iterations);
+ CPPUNIT_ASSERT_EQUAL(expectedValue, entry.value);
+ }
+ CPPUNIT_ASSERT_EQUAL(expectedEntryCount, iterations);
+}
+