Makefile: remove spurious tab
[xcsoar.git] / src / Util / Cache.hpp
blob5c5c60fab507d84de5fa1a7aa3a3202a0f5b6883
1 /*
2 * Copyright (C) 2011 Max Kellermann <max@duempel.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the
14 * distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 #ifndef XCSOAR_CACHE_HPP
31 #define XCSOAR_CACHE_HPP
33 #include "Util/ListHead.hpp"
34 #include "Compiler.h"
36 #include <unordered_map>
37 #include <limits>
38 #include <assert.h>
40 template<typename Key, typename Data,
41 unsigned capacity,
42 typename Hash=std::hash<Key>,
43 typename KeyEqual=std::equal_to<Key>>
44 class Cache {
46 /**
47 * Wrapper that holds an uninitialised object, which be be
48 * initialised and deinitialised on demand.
50 template<typename T>
51 class Constructible {
52 char buffer[sizeof(T)];
54 #ifndef NDEBUG
55 bool constructed;
56 #endif
58 public:
59 #ifndef NDEBUG
60 Constructible():constructed(false) {}
62 ~Constructible() {
63 assert(!constructed);
65 #endif
67 const T &Get() const {
68 assert(constructed);
70 const void *p = static_cast<const void *>(buffer);
71 return *static_cast<const T *>(p);
74 T &Get() {
75 assert(constructed);
77 void *p = static_cast<void *>(buffer);
78 return *static_cast<T *>(p);
81 void Construct() {
82 assert(!constructed);
84 void *p = static_cast<void *>(buffer);
85 new (p) T();
87 #ifndef NDEBUG
88 constructed = true;
89 #endif
92 template<typename U>
93 void Construct(U &&value) {
94 assert(!constructed);
96 void *p = static_cast<void *>(buffer);
97 new (p) T(std::forward<U>(value));
99 #ifndef NDEBUG
100 constructed = true;
101 #endif
104 void Destruct() {
105 assert(constructed);
107 T &value = Get();
108 value.T::~T();
110 #ifndef NDEBUG
111 constructed = false;
112 #endif
116 class Item;
118 /* This is a multimap, even though we use at most one cache item for
119 a key. A multimap means less overhead, because insert() does not
120 need to do a full lookup, and this class */
121 typedef std::unordered_multimap<Key, class Item *, Hash, KeyEqual> KeyMap;
123 class Item : public ListHead {
125 * This iterator is stored to allow quick removal of outdated
126 * cache items. This is safe, because rehashing is disabled in
127 * #KeyMap.
129 typename KeyMap::iterator iterator;
131 Constructible<Data> data;
133 public:
134 typename KeyMap::iterator GetIterator() {
135 return iterator;
138 void SetIterator(typename KeyMap::iterator _iterator) {
139 iterator = _iterator;
142 const Data &GetData() const {
143 return data.Get();
146 template<typename U>
147 void Construct(U &&value) {
148 data.Construct(std::forward<U>(value));
151 void Destruct() {
152 data.Destruct();
155 template<typename U>
156 void Replace(U &&value) {
157 data.Get() = std::forward<U>(value);
162 * The number of cached items.
164 unsigned size;
167 * The list of unallocated items.
169 ListHead unallocated_list;
171 ListHead chronological_list;
173 KeyMap map;
175 Item buffer[capacity];
177 Item &GetOldest() {
178 assert(!chronological_list.IsEmpty());
180 return *(Item *)chronological_list.GetPrevious();
184 * Remove the oldest item from the cache (both from the #map and
185 * from #chronological_list), but do not destruct it.
187 Item &RemoveOldest() {
188 Item &item = GetOldest();
190 map.erase(item.GetIterator());
191 item.Remove();
193 #ifndef NDEBUG
194 --size;
195 #endif
197 return item;
201 * Allocate an item from #unallocated_list, but do not construct it.
203 Item &Allocate() {
204 assert(!unallocated_list.IsEmpty());
206 Item &item = *(Item *)unallocated_list.GetNext();
207 item.Remove();
208 return item;
211 template<typename U>
212 Item &Make(U &&data) {
213 if (unallocated_list.IsEmpty()) {
214 /* cache is full: delete oldest */
215 Item &item = RemoveOldest();
216 item.Replace(std::forward<U>(data));
217 return item;
218 } else {
219 /* cache is not full: allocate new item */
220 Item &item = Allocate();
221 item.Construct(std::forward<U>(data));
222 return item;
226 public:
227 Cache()
228 :size(0),
229 unallocated_list(ListHead::empty()),
230 chronological_list(ListHead::empty()) {
231 for (unsigned i = 0; i < capacity; ++i)
232 buffer[i].InsertAfter(unallocated_list);
234 /* allocate enough buckets for the whole lifetime of this
235 object */
236 map.rehash(capacity);
238 /* forcibly disable rehashing, as that would Invalidate existing
239 iterators */
240 map.max_load_factor(std::numeric_limits<decltype(map.max_load_factor())>::infinity());
243 ~Cache() {
244 Clear();
247 bool IsFull() const {
248 assert(size <= capacity);
250 return size == capacity;
253 void Clear() {
254 map.clear();
256 while (!chronological_list.IsEmpty()) {
257 #ifndef NDEBUG
258 assert(size > 0);
259 --size;
260 #endif
262 Item &item = *(Item *)chronological_list.GetNext();
263 item.Remove();
265 item.Destruct();
266 item.InsertAfter(unallocated_list);
269 assert(size == 0);
270 size = 0;
273 const Data *Get(const Key &key) {
274 typename KeyMap::iterator i = map.find(key);
275 if (i == map.end())
276 return nullptr;
278 Item &item = *i->second;
279 assert(item.GetIterator() == i);
281 /* move to the front of the chronological list */
282 item.Remove();
283 item.InsertAfter(chronological_list);
285 return &item.GetData();
288 template<typename K, typename U>
289 void Put(K &&key, U &&data) {
290 assert(map.find(key) == map.end());
292 Item &item = Make(std::forward<U>(data));
293 item.InsertAfter(chronological_list);
294 auto iterator = map.insert(std::make_pair(std::forward<K>(key), &item));
295 item.SetIterator(iterator);
297 #ifndef NDEBUG
298 ++size;
299 #endif
303 #endif