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_);
+}