Caching path info is generally useful. For instance, it speeds up "nix path-info -rS /run/current-system" (i.e. showing the closure sizes of all paths in the closure of the current system) from 5.6s to 0.15s. This also eliminates some APIs like Store::queryDeriver() and Store::queryReferences().
		
			
				
	
	
		
			90 lines
		
	
	
	
		
			1.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			90 lines
		
	
	
	
		
			1.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#pragma once
 | 
						|
 | 
						|
#include <map>
 | 
						|
#include <list>
 | 
						|
 | 
						|
namespace nix {
 | 
						|
 | 
						|
/* A simple least-recently used cache. Not thread-safe. */
 | 
						|
template<typename Key, typename Value>
 | 
						|
class LRUCache
 | 
						|
{
 | 
						|
private:
 | 
						|
 | 
						|
    size_t maxSize;
 | 
						|
 | 
						|
    // Stupid wrapper to get around circular dependency between Data
 | 
						|
    // and LRU.
 | 
						|
    struct LRUIterator;
 | 
						|
 | 
						|
    using Data = std::map<Key, std::pair<LRUIterator, Value>>;
 | 
						|
    using LRU = std::list<typename Data::iterator>;
 | 
						|
 | 
						|
    struct LRUIterator { typename LRU::iterator it; };
 | 
						|
 | 
						|
    Data data;
 | 
						|
    LRU lru;
 | 
						|
 | 
						|
public:
 | 
						|
 | 
						|
    LRUCache(size_t maxSize) : maxSize(maxSize) { }
 | 
						|
 | 
						|
    /* Insert or upsert an item in the cache. */
 | 
						|
    void upsert(const Key & key, const Value & value)
 | 
						|
    {
 | 
						|
        erase(key);
 | 
						|
 | 
						|
        if (data.size() >= maxSize) {
 | 
						|
            /* Retire the oldest item. */
 | 
						|
            auto oldest = lru.begin();
 | 
						|
            data.erase(*oldest);
 | 
						|
            lru.erase(oldest);
 | 
						|
        }
 | 
						|
 | 
						|
        auto res = data.emplace(key, std::make_pair(LRUIterator(), value));
 | 
						|
        assert(res.second);
 | 
						|
        auto & i(res.first);
 | 
						|
 | 
						|
        auto j = lru.insert(lru.end(), i);
 | 
						|
 | 
						|
        i->second.first.it = j;
 | 
						|
    }
 | 
						|
 | 
						|
    bool erase(const Key & key)
 | 
						|
    {
 | 
						|
        auto i = data.find(key);
 | 
						|
        if (i == data.end()) return false;
 | 
						|
        lru.erase(i->second.first.it);
 | 
						|
        data.erase(i);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Look up an item in the cache. If it exists, it becomes the most
 | 
						|
       recently used item. */
 | 
						|
    // FIXME: use boost::optional?
 | 
						|
    Value * get(const Key & key)
 | 
						|
    {
 | 
						|
        auto i = data.find(key);
 | 
						|
        if (i == data.end()) return 0;
 | 
						|
 | 
						|
        /* Move this item to the back of the LRU list. */
 | 
						|
        lru.erase(i->second.first.it);
 | 
						|
        auto j = lru.insert(lru.end(), i);
 | 
						|
        i->second.first.it = j;
 | 
						|
 | 
						|
        return &i->second.second;
 | 
						|
    }
 | 
						|
 | 
						|
    size_t size()
 | 
						|
    {
 | 
						|
        return data.size();
 | 
						|
    }
 | 
						|
 | 
						|
    void clear()
 | 
						|
    {
 | 
						|
        data.clear();
 | 
						|
        lru.clear();
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
}
 |