comparison 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
comparison
equal deleted inserted replaced
282:915ed24547ea 283:c9977db00e1d
1 #include "gtest/gtest.h" 1 #include "gtest/gtest.h"
2 2
3 #include <glog/logging.h>
3 #include <memory> 4 #include <memory>
5 #include <boost/thread.hpp>
6 #include <boost/lexical_cast.hpp>
4 #include "../Core/IDynamicObject.h" 7 #include "../Core/IDynamicObject.h"
5 #include "../Core/MultiThreading/CacheIndex.h" 8 #include "../Core/MultiThreading/CacheIndex.h"
6 9
7 10
8 TEST(CacheIndex, Basic) 11 TEST(CacheIndex, Basic)
49 ASSERT_TRUE(r.Contains("b")); 52 ASSERT_TRUE(r.Contains("b"));
50 ASSERT_EQ(421, r.Invalidate("b")); 53 ASSERT_EQ(421, r.Invalidate("b"));
51 ASSERT_FALSE(r.Contains("b")); 54 ASSERT_FALSE(r.Contains("b"));
52 55
53 int p; 56 int p;
57 ASSERT_TRUE(r.Contains("a", p)); ASSERT_EQ(420, p);
58 ASSERT_TRUE(r.Contains("c", p)); ASSERT_EQ(422, p);
59 ASSERT_TRUE(r.Contains("d", p)); ASSERT_EQ(423, p);
60
54 ASSERT_EQ("a", r.RemoveOldest(p)); ASSERT_EQ(420, p); 61 ASSERT_EQ("a", r.RemoveOldest(p)); ASSERT_EQ(420, p);
55 ASSERT_EQ("d", r.RemoveOldest(p)); ASSERT_EQ(423, p); 62 ASSERT_EQ("d", r.RemoveOldest(p)); ASSERT_EQ(423, p);
56 ASSERT_EQ("c", r.RemoveOldest(p)); ASSERT_EQ(422, p); 63 ASSERT_EQ("c", r.RemoveOldest(p)); ASSERT_EQ(422, p);
57 64
58 ASSERT_TRUE(r.IsEmpty()); 65 ASSERT_TRUE(r.IsEmpty());
59 } 66 }
60 67
61 68
62 namespace Orthanc 69 namespace Orthanc
63 { 70 {
71
72 class ICacheProvider
73 {
74 public:
75 virtual ~ICacheProvider()
76 {
77 }
78
79 virtual IDynamicObject* Provide(const std::string& id) = 0;
80 };
81
64 class MemoryCache 82 class MemoryCache
65 { 83 {
66 private: 84 private:
67 struct CacheElement 85 struct Page
68 { 86 {
69 std::string id_; 87 std::string id_;
70 std::auto_ptr<IDynamicObject> object_; 88 std::auto_ptr<IDynamicObject> content_;
71 }; 89 };
72 90
73 //typedef std::map<CacheElement 91 ICacheProvider& provider_;
74 92 size_t cacheSize_;
75 size_t places_; 93 CacheIndex<std::string, Page*> index_;
76 CacheIndex<const char*> index_; 94
77 95 Page& Load(const std::string& id)
78 public: 96 {
79 97 // Reuse the cache entry if it already exists
80 }; 98 Page* p = NULL;
81 } 99 if (index_.Contains(id, p))
100 {
101 assert(p != NULL);
102 index_.TagAsMostRecent(id);
103 return *p;
104 }
105
106 // The id is not in the cache yet. Make some room if the cache
107 // is full.
108 if (index_.GetSize() == cacheSize_)
109 {
110 index_.RemoveOldest(p);
111 delete p;
112 }
113
114 // Create a new cache page
115 std::auto_ptr<Page> result(new Page);
116 result->id_ = id;
117 result->content_.reset(provider_.Provide(id));
118
119 // Add the newly create page to the cache
120 p = result.release();
121 index_.Add(id, p);
122 return *p;
123 }
124
125 public:
126 class Accessor
127 {
128 friend class MemoryCache;
129
130 private:
131 Page& element_;
132
133 Accessor(Page& element) :
134 element_(element)
135 {
136 }
137
138 public:
139 const std::string GetId() const
140 {
141 return element_.id_;
142 }
143
144 IDynamicObject& GetContent()
145 {
146 return *element_.content_;
147 }
148
149 const IDynamicObject& GetContent() const
150 {
151 return *element_.content_;
152 }
153 };
154
155 MemoryCache(ICacheProvider& provider,
156 size_t cacheSize) :
157 provider_(provider),
158 cacheSize_(cacheSize)
159 {
160 }
161
162 ~MemoryCache()
163 {
164 while (!index_.IsEmpty())
165 {
166 Page* element = NULL;
167 index_.RemoveOldest(element);
168 assert(element != NULL);
169 delete element;
170 }
171 }
172
173 Accessor* Access(const std::string& id)
174 {
175 Page& element = Load(id);
176 return new Accessor(element);
177 }
178 };
179 }
180
181
182
183 namespace
184 {
185 class Integer : public Orthanc::IDynamicObject
186 {
187 private:
188 std::string& log_;
189 int value_;
190
191 public:
192 Integer(std::string& log, int v) : log_(log), value_(v)
193 {
194 }
195
196 virtual ~Integer()
197 {
198 LOG(INFO) << "Removing cache entry for " << value_;
199 log_ += boost::lexical_cast<std::string>(value_) + " ";
200 }
201
202 int GetValue() const
203 {
204 return value_;
205 }
206 };
207
208 class IntegerProvider : public Orthanc::ICacheProvider
209 {
210 public:
211 std::string log_;
212
213 Orthanc::IDynamicObject* Provide(const std::string& s)
214 {
215 LOG(INFO) << "Providing " << s;
216 return new Integer(log_, boost::lexical_cast<int>(s));
217 }
218 };
219 }
220
221
222 TEST(MemoryCache, Basic)
223 {
224 IntegerProvider provider;
225
226 {
227 Orthanc::MemoryCache cache(provider, 3);
228 std::auto_ptr<Orthanc::MemoryCache::Accessor> a;
229 a.reset(cache.Access("42")); // 42 -> exit
230 a.reset(cache.Access("43")); // 43, 42 -> exit
231 a.reset(cache.Access("45")); // 45, 43, 42 -> exit
232 a.reset(cache.Access("42")); // 42, 45, 43 -> exit
233 a.reset(cache.Access("43")); // 43, 42, 45 -> exit
234 a.reset(cache.Access("47")); // 45 is removed; 47, 43, 42 -> exit
235 a.reset(cache.Access("44")); // 42 is removed; 44, 47, 43 -> exit
236 a.reset(cache.Access("42")); // 43 is removed; 42, 44, 47 -> exit
237 // Closing the cache: 47, 44, 42 are successively removed
238 }
239
240 ASSERT_EQ("45 42 43 47 44 42 ", provider.log_);
241 }