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
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
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"
36 #include <unordered_map>
40 template<typename Key
, typename Data
,
42 typename Hash
=std::hash
<Key
>,
43 typename KeyEqual
=std::equal_to
<Key
>>
47 * Wrapper that holds an uninitialised object, which be be
48 * initialised and deinitialised on demand.
52 char buffer
[sizeof(T
)];
60 Constructible():constructed(false) {}
67 const T
&Get() const {
70 const void *p
= static_cast<const void *>(buffer
);
71 return *static_cast<const T
*>(p
);
77 void *p
= static_cast<void *>(buffer
);
78 return *static_cast<T
*>(p
);
84 void *p
= static_cast<void *>(buffer
);
93 void Construct(U
&&value
) {
96 void *p
= static_cast<void *>(buffer
);
97 new (p
) T(std::forward
<U
>(value
));
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
129 typename
KeyMap::iterator iterator
;
131 Constructible
<Data
> data
;
134 typename
KeyMap::iterator
GetIterator() {
138 void SetIterator(typename
KeyMap::iterator _iterator
) {
139 iterator
= _iterator
;
142 const Data
&GetData() const {
147 void Construct(U
&&value
) {
148 data
.Construct(std::forward
<U
>(value
));
156 void Replace(U
&&value
) {
157 data
.Get() = std::forward
<U
>(value
);
162 * The number of cached items.
167 * The list of unallocated items.
169 ListHead unallocated_list
;
171 ListHead chronological_list
;
175 Item buffer
[capacity
];
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());
201 * Allocate an item from #unallocated_list, but do not construct it.
204 assert(!unallocated_list
.IsEmpty());
206 Item
&item
= *(Item
*)unallocated_list
.GetNext();
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
));
219 /* cache is not full: allocate new item */
220 Item
&item
= Allocate();
221 item
.Construct(std::forward
<U
>(data
));
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
236 map
.rehash(capacity
);
238 /* forcibly disable rehashing, as that would Invalidate existing
240 map
.max_load_factor(std::numeric_limits
<decltype(map
.max_load_factor())>::infinity());
247 bool IsFull() const {
248 assert(size
<= capacity
);
250 return size
== capacity
;
256 while (!chronological_list
.IsEmpty()) {
262 Item
&item
= *(Item
*)chronological_list
.GetNext();
266 item
.InsertAfter(unallocated_list
);
273 const Data
*Get(const Key
&key
) {
274 typename
KeyMap::iterator i
= map
.find(key
);
278 Item
&item
= *i
->second
;
279 assert(item
.GetIterator() == i
);
281 /* move to the front of the chronological list */
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
);