Mercurial > hg > orthanc
diff UnitTests/MemoryCache.cpp @ 283:c9977db00e1d
caching
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 12 Dec 2012 15:32:15 +0100 |
parents | 915ed24547ea |
children | 06aa7b7b6723 |
line wrap: on
line diff
--- a/UnitTests/MemoryCache.cpp Wed Dec 12 13:17:42 2012 +0100 +++ b/UnitTests/MemoryCache.cpp Wed Dec 12 15:32:15 2012 +0100 @@ -1,6 +1,9 @@ #include "gtest/gtest.h" +#include <glog/logging.h> #include <memory> +#include <boost/thread.hpp> +#include <boost/lexical_cast.hpp> #include "../Core/IDynamicObject.h" #include "../Core/MultiThreading/CacheIndex.h" @@ -51,6 +54,10 @@ ASSERT_FALSE(r.Contains("b")); int p; + ASSERT_TRUE(r.Contains("a", p)); ASSERT_EQ(420, p); + ASSERT_TRUE(r.Contains("c", p)); ASSERT_EQ(422, p); + ASSERT_TRUE(r.Contains("d", p)); ASSERT_EQ(423, p); + ASSERT_EQ("a", r.RemoveOldest(p)); ASSERT_EQ(420, p); ASSERT_EQ("d", r.RemoveOldest(p)); ASSERT_EQ(423, p); ASSERT_EQ("c", r.RemoveOldest(p)); ASSERT_EQ(422, p); @@ -61,21 +68,174 @@ namespace Orthanc { + + class ICacheProvider + { + public: + virtual ~ICacheProvider() + { + } + + virtual IDynamicObject* Provide(const std::string& id) = 0; + }; + class MemoryCache { private: - struct CacheElement + struct Page { std::string id_; - std::auto_ptr<IDynamicObject> object_; + std::auto_ptr<IDynamicObject> content_; + }; + + ICacheProvider& provider_; + size_t cacheSize_; + CacheIndex<std::string, Page*> index_; + + Page& Load(const std::string& id) + { + // Reuse the cache entry if it already exists + Page* p = NULL; + if (index_.Contains(id, p)) + { + assert(p != NULL); + index_.TagAsMostRecent(id); + return *p; + } + + // The id is not in the cache yet. Make some room if the cache + // is full. + if (index_.GetSize() == cacheSize_) + { + index_.RemoveOldest(p); + delete p; + } + + // Create a new cache page + std::auto_ptr<Page> result(new Page); + result->id_ = id; + result->content_.reset(provider_.Provide(id)); + + // Add the newly create page to the cache + p = result.release(); + index_.Add(id, p); + return *p; + } + + public: + class Accessor + { + friend class MemoryCache; + + private: + Page& element_; + + Accessor(Page& element) : + element_(element) + { + } + + public: + const std::string GetId() const + { + return element_.id_; + } + + IDynamicObject& GetContent() + { + return *element_.content_; + } + + const IDynamicObject& GetContent() const + { + return *element_.content_; + } }; - //typedef std::map<CacheElement + MemoryCache(ICacheProvider& provider, + size_t cacheSize) : + provider_(provider), + cacheSize_(cacheSize) + { + } + + ~MemoryCache() + { + while (!index_.IsEmpty()) + { + Page* element = NULL; + index_.RemoveOldest(element); + assert(element != NULL); + delete element; + } + } - size_t places_; - CacheIndex<const char*> index_; + Accessor* Access(const std::string& id) + { + Page& element = Load(id); + return new Accessor(element); + } + }; +} + + + +namespace +{ + class Integer : public Orthanc::IDynamicObject + { + private: + std::string& log_; + int value_; public: - + Integer(std::string& log, int v) : log_(log), value_(v) + { + } + + virtual ~Integer() + { + LOG(INFO) << "Removing cache entry for " << value_; + log_ += boost::lexical_cast<std::string>(value_) + " "; + } + + int GetValue() const + { + return value_; + } + }; + + class IntegerProvider : public Orthanc::ICacheProvider + { + public: + std::string log_; + + Orthanc::IDynamicObject* Provide(const std::string& s) + { + LOG(INFO) << "Providing " << s; + return new Integer(log_, boost::lexical_cast<int>(s)); + } }; } + + +TEST(MemoryCache, Basic) +{ + IntegerProvider provider; + + { + Orthanc::MemoryCache cache(provider, 3); + std::auto_ptr<Orthanc::MemoryCache::Accessor> a; + a.reset(cache.Access("42")); // 42 -> exit + a.reset(cache.Access("43")); // 43, 42 -> exit + a.reset(cache.Access("45")); // 45, 43, 42 -> exit + a.reset(cache.Access("42")); // 42, 45, 43 -> exit + a.reset(cache.Access("43")); // 43, 42, 45 -> exit + a.reset(cache.Access("47")); // 45 is removed; 47, 43, 42 -> exit + a.reset(cache.Access("44")); // 42 is removed; 44, 47, 43 -> exit + a.reset(cache.Access("42")); // 43 is removed; 42, 44, 47 -> exit + // Closing the cache: 47, 44, 42 are successively removed + } + + ASSERT_EQ("45 42 43 47 44 42 ", provider.log_); +}