#ifndef RIPPLE_KEYCACHE_H #define RIPPLE_KEYCACHE_H /** Maintains a cache of keys with no associated data Timer must have this interface: static int Timer::getElapsedSeconds (); */ template class KeyCache { public: typedef c_Key key_type; typedef boost::unordered_map map_type; typedef typename map_type::iterator map_iterator; protected: const std::string mName; boost::mutex mNCLock; map_type mCache; unsigned int mTargetSize, mTargetAge; public: KeyCache(const std::string& name, int size = 0, int age = 120) : mName(name), mTargetSize(size), mTargetAge(age) { assert((size >= 0) && (age > 2)); } int getSize() { boost::mutex::scoped_lock sl(mNCLock); return mCache.size(); } int getTargetSize() { boost::mutex::scoped_lock sl(mNCLock); return mTargetSize; } int getTargetAge() { boost::mutex::scoped_lock sl(mNCLock); return mTargetAge; } void setTargets(int size, int age) { boost::mutex::scoped_lock sl(mNCLock); mTargetSize = size; mTargetAge = age; assert((mTargetSize >= 0) && (mTargetAge > 2)); } const std::string& getName() { return mName; } bool isPresent(const key_type& key, bool refresh = true) { // Check if an entry is cached, refresh it if so boost::mutex::scoped_lock sl(mNCLock); map_iterator it = mCache.find(key); if (it == mCache.end()) return false; if (refresh) it->second = Timer::getElapsedSeconds (); return true; } bool del(const key_type& key) { // Remove an entry from the cache, return false if not-present boost::mutex::scoped_lock sl(mNCLock); map_iterator it = mCache.find(key); if (it == mCache.end()) return false; mCache.erase(it); return true; } bool add(const key_type& key) { // Add an entry to the cache, return true if it is new boost::mutex::scoped_lock sl(mNCLock); map_iterator it = mCache.find(key); if (it != mCache.end()) { it->second = Timer::getElapsedSeconds (); return false; } mCache.insert(std::make_pair(key, Timer::getElapsedSeconds ())); return true; } void sweep() { // Remove stale entries from the cache int now = Timer::getElapsedSeconds (); boost::mutex::scoped_lock sl(mNCLock); int target; if ((mTargetSize == 0) || (mCache.size() <= mTargetSize)) target = now - mTargetAge; else { target = now - (mTargetAge * mTargetSize / mCache.size()); if (target > (now - 2)) target = now - 2; } map_iterator it = mCache.begin(); while (it != mCache.end()) { if (it->second > now) { it->second = now; ++it; } else if (it->second < target) it = mCache.erase(it); else ++it; } } }; #endif