1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <cache/SlsPageCacheManager.hxx>
22 #include "SlsBitmapCache.hxx"
27 #include <unordered_map>
31 /** Collection of data that is stored for all active preview caches.
36 ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument
;
40 ::sd::slidesorter::cache::PageCacheManager::DocumentKey
const & pDocument
,
41 const Size
& rPreviewSize
)
42 :mpDocument(pDocument
),maPreviewSize(rPreviewSize
)
44 /// Test for equality with respect to all members.
45 class Equal
{public: bool operator() (
46 const CacheDescriptor
& rDescriptor1
, const CacheDescriptor
& rDescriptor2
) const {
47 return rDescriptor1
.mpDocument
==rDescriptor2
.mpDocument
48 && rDescriptor1
.maPreviewSize
==rDescriptor2
.maPreviewSize
;
50 /// Hash function that takes all members into account.
51 class Hash
{public: size_t operator() (const CacheDescriptor
& rDescriptor
) const {
52 return reinterpret_cast<size_t>(rDescriptor
.mpDocument
.get()) + rDescriptor
.maPreviewSize
.Width();
56 /** Collection of data that is stored for the inactive, recently used
59 class RecentlyUsedCacheDescriptor
63 std::shared_ptr
< ::sd::slidesorter::cache::BitmapCache
> mpCache
;
65 RecentlyUsedCacheDescriptor(
66 const Size
& rPreviewSize
,
67 const std::shared_ptr
< ::sd::slidesorter::cache::BitmapCache
>& rpCache
)
68 :maPreviewSize(rPreviewSize
),mpCache(rpCache
)
72 /** The list of recently used caches is organized as queue. When elements
73 are added the list is shortened to the maximally allowed number of
74 elements by removing the least recently used elements.
76 typedef ::std::deque
<RecentlyUsedCacheDescriptor
> RecentlyUsedQueue
;
78 /** Compare the caches by preview size. Those that match the given size
79 come first, then, regardless of the given size, the largest ones before
82 class BestFittingCacheComparer
85 explicit BestFittingCacheComparer (const Size
& rPreferredSize
)
86 : maPreferredSize(rPreferredSize
)
88 bool operator()(const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type
& rElement1
,
89 const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type
& rElement2
)
91 if (rElement2
.first
== maPreferredSize
)
93 else if (rElement1
.first
== maPreferredSize
)
96 return (rElement1
.first
.Width()*rElement1
.first
.Height()
97 > rElement2
.first
.Width()*rElement2
.first
.Height());
101 Size maPreferredSize
;
104 } // end of anonymous namespace
106 namespace sd::slidesorter::cache
{
108 /** Container for the active caches.
110 class PageCacheManager::PageCacheContainer
111 : public std::unordered_map
<CacheDescriptor
,
112 std::shared_ptr
<BitmapCache
>,
113 CacheDescriptor::Hash
,
114 CacheDescriptor::Equal
>
117 PageCacheContainer() {}
119 /** Compare entries in the cache container with respect to the cache
122 class CompareWithCache
{ public:
123 explicit CompareWithCache(const std::shared_ptr
<BitmapCache
>& rpCache
)
124 : mpCache(rpCache
) {}
125 bool operator () (const PageCacheContainer::value_type
& rValue
) const
126 { return rValue
.second
== mpCache
; }
128 std::shared_ptr
<BitmapCache
> mpCache
;
132 /** The recently used caches are stored in one queue for each document.
134 class PageCacheManager::RecentlyUsedPageCaches
137 typedef DocumentKey key_type
;
138 typedef RecentlyUsedQueue mapped_type
;
139 typedef std::map
<key_type
,mapped_type
>::iterator iterator
;
141 std::map
<key_type
,mapped_type
> maMap
;
143 RecentlyUsedPageCaches () {};
145 iterator
end() { return maMap
.end(); }
146 void clear() { maMap
.clear(); }
147 iterator
find(const key_type
& key
) { return maMap
.find(key
); }
148 template<class... Args
>
149 std::pair
<iterator
,bool> emplace(Args
&&... args
) { return maMap
.emplace(std::forward
<Args
>(args
)...); }
152 class PageCacheManager::Deleter
155 void operator() (PageCacheManager
* pObject
) { delete pObject
; }
158 //===== PageCacheManager ====================================================
160 std::weak_ptr
<PageCacheManager
> PageCacheManager::mpInstance
;
162 std::shared_ptr
<PageCacheManager
> PageCacheManager::Instance()
164 std::shared_ptr
<PageCacheManager
> pInstance
;
166 ::osl::MutexGuard
aGuard (::osl::Mutex::getGlobalMutex());
168 pInstance
= mpInstance
.lock();
169 if (pInstance
== nullptr)
171 pInstance
= std::shared_ptr
<PageCacheManager
>(
172 new PageCacheManager(),
173 PageCacheManager::Deleter());
174 mpInstance
= pInstance
;
180 PageCacheManager::PageCacheManager()
181 : mpPageCaches(new PageCacheContainer()),
182 mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches())
186 PageCacheManager::~PageCacheManager()
190 std::shared_ptr
<BitmapCache
> PageCacheManager::GetCache (
191 const DocumentKey
& pDocument
,
192 const Size
& rPreviewSize
)
194 std::shared_ptr
<BitmapCache
> pResult
;
196 // Look for the cache in the list of active caches.
197 CacheDescriptor
aKey (pDocument
, rPreviewSize
);
198 PageCacheContainer::iterator
iCache (mpPageCaches
->find(aKey
));
199 if (iCache
!= mpPageCaches
->end())
200 pResult
= iCache
->second
;
202 // Look for the cache in the list of recently used caches.
203 if (pResult
== nullptr)
204 pResult
= GetRecentlyUsedCache(pDocument
, rPreviewSize
);
206 // Create the cache when no suitable one does exist.
207 if (pResult
== nullptr)
208 pResult
= std::make_shared
<BitmapCache
>();
210 // The cache may be newly created and thus empty or is old and may
211 // contain previews that are not up-to-date. Recycle previews from
212 // other caches to fill in the holes.
213 Recycle(pResult
, pDocument
,rPreviewSize
);
215 // Put the new (or old) cache into the container.
216 mpPageCaches
->emplace(aKey
, pResult
);
221 void PageCacheManager::Recycle (
222 const std::shared_ptr
<BitmapCache
>& rpCache
,
223 const DocumentKey
& pDocument
,
224 const Size
& rPreviewSize
)
226 BestFittingPageCaches aCaches
;
228 // Add bitmap caches from active caches.
229 for (auto& rActiveCache
: *mpPageCaches
)
231 if (rActiveCache
.first
.mpDocument
== pDocument
)
232 aCaches
.emplace_back(
233 rActiveCache
.first
.maPreviewSize
, rActiveCache
.second
);
236 // Add bitmap caches from recently used caches.
237 RecentlyUsedPageCaches::iterator
iQueue (mpRecentlyUsedPageCaches
->find(pDocument
));
238 if (iQueue
!= mpRecentlyUsedPageCaches
->end())
240 for (const auto& rRecentCache
: iQueue
->second
)
241 aCaches
.emplace_back(
242 rRecentCache
.maPreviewSize
, rRecentCache
.mpCache
);
245 ::std::sort(aCaches
.begin(), aCaches
.end(), BestFittingCacheComparer(rPreviewSize
));
247 for (const auto& rBestCache
: aCaches
)
249 rpCache
->Recycle(*rBestCache
.second
);
253 void PageCacheManager::ReleaseCache (const std::shared_ptr
<BitmapCache
>& rpCache
)
255 PageCacheContainer::iterator
iCache (::std::find_if(
256 mpPageCaches
->begin(),
258 PageCacheContainer::CompareWithCache(rpCache
)));
260 if (iCache
!= mpPageCaches
->end())
262 assert(iCache
->second
== rpCache
);
264 PutRecentlyUsedCache(iCache
->first
.mpDocument
,iCache
->first
.maPreviewSize
,rpCache
);
266 mpPageCaches
->erase(iCache
);
270 std::shared_ptr
<BitmapCache
> PageCacheManager::ChangeSize (
271 const std::shared_ptr
<BitmapCache
>& rpCache
,
273 const Size
& rNewPreviewSize
)
275 std::shared_ptr
<BitmapCache
> pResult
;
277 if (rpCache
!= nullptr)
279 // Look up the given cache in the list of active caches.
280 PageCacheContainer::iterator
iCacheToChange (::std::find_if(
281 mpPageCaches
->begin(),
283 PageCacheContainer::CompareWithCache(rpCache
)));
284 if (iCacheToChange
!= mpPageCaches
->end())
286 assert(iCacheToChange
->second
== rpCache
);
288 // Now, we can change the preview size of the existing one by
289 // removing the cache from the list and re-insert it with the
291 const ::sd::slidesorter::cache::PageCacheManager::DocumentKey
aKey (
292 iCacheToChange
->first
.mpDocument
);
293 mpPageCaches
->erase(iCacheToChange
);
294 mpPageCaches
->emplace(
295 CacheDescriptor(aKey
,rNewPreviewSize
),
302 assert(iCacheToChange
!= mpPageCaches
->end());
309 bool PageCacheManager::InvalidatePreviewBitmap (
310 const DocumentKey
& pDocument
,
313 bool bHasChanged (false);
315 if (pDocument
!=nullptr)
317 // Iterate over all caches that are currently in use and invalidate
318 // the previews in those that belong to the document.
319 for (auto& rCache
: *mpPageCaches
)
320 if (rCache
.first
.mpDocument
== pDocument
)
321 bHasChanged
|= rCache
.second
->InvalidateBitmap(pKey
);
323 // Invalidate the previews in the recently used caches belonging to
324 // the given document.
325 RecentlyUsedPageCaches::iterator
iQueue (mpRecentlyUsedPageCaches
->find(pDocument
));
326 if (iQueue
!= mpRecentlyUsedPageCaches
->end())
328 for (const auto& rCache2
: iQueue
->second
)
329 bHasChanged
|= rCache2
.mpCache
->InvalidateBitmap(pKey
);
336 void PageCacheManager::InvalidateAllPreviewBitmaps (const DocumentKey
& pDocument
)
338 if (pDocument
== nullptr)
341 // Iterate over all caches that are currently in use and invalidate the
342 // previews in those that belong to the document.
343 for (auto& rCache
: *mpPageCaches
)
344 if (rCache
.first
.mpDocument
== pDocument
)
345 rCache
.second
->InvalidateCache();
347 // Invalidate the previews in the recently used caches belonging to the
349 RecentlyUsedPageCaches::iterator
iQueue (mpRecentlyUsedPageCaches
->find(pDocument
));
350 if (iQueue
!= mpRecentlyUsedPageCaches
->end())
352 for (const auto& rCache2
: iQueue
->second
)
353 rCache2
.mpCache
->InvalidateCache();
357 void PageCacheManager::InvalidateAllCaches()
359 // Iterate over all caches that are currently in use and invalidate
361 for (auto& rCache
: *mpPageCaches
)
362 rCache
.second
->InvalidateCache();
364 // Remove all recently used caches, there is not much sense in storing
365 // invalidated and unused caches.
366 mpRecentlyUsedPageCaches
->clear();
369 void PageCacheManager::ReleasePreviewBitmap (const SdrPage
* pPage
)
371 for (auto& rCache
: *mpPageCaches
)
372 rCache
.second
->ReleaseBitmap(pPage
);
375 std::shared_ptr
<BitmapCache
> PageCacheManager::GetRecentlyUsedCache (
376 const DocumentKey
& pDocument
,
377 const Size
& rPreviewSize
)
379 std::shared_ptr
<BitmapCache
> pCache
;
381 // Look for the cache in the list of recently used caches.
382 RecentlyUsedPageCaches::iterator
iQueue (mpRecentlyUsedPageCaches
->find(pDocument
));
383 if (iQueue
!= mpRecentlyUsedPageCaches
->end())
385 RecentlyUsedQueue::iterator iCache
= std::find_if(iQueue
->second
.begin(), iQueue
->second
.end(),
386 [&rPreviewSize
](const RecentlyUsedCacheDescriptor
& rCache
) { return rCache
.maPreviewSize
== rPreviewSize
; });
387 if (iCache
!= iQueue
->second
.end())
389 pCache
= iCache
->mpCache
;
390 iQueue
->second
.erase(iCache
);
397 void PageCacheManager::PutRecentlyUsedCache(
398 DocumentKey
const & pDocument
,
399 const Size
& rPreviewSize
,
400 const std::shared_ptr
<BitmapCache
>& rpCache
)
402 // Look up the list of recently used caches for the given document.
403 RecentlyUsedPageCaches::iterator
iQueue (mpRecentlyUsedPageCaches
->find(pDocument
));
404 if (iQueue
== mpRecentlyUsedPageCaches
->end())
405 iQueue
= mpRecentlyUsedPageCaches
->emplace(
406 pDocument
, RecentlyUsedQueue()
409 if (iQueue
!= mpRecentlyUsedPageCaches
->end())
411 iQueue
->second
.push_front(RecentlyUsedCacheDescriptor(rPreviewSize
,rpCache
));
412 // Shorten the list of recently used caches to the allowed maximal length.
413 while (iQueue
->second
.size() > mnMaximalRecentlyCacheCount
)
414 iQueue
->second
.pop_back();
418 } // end of namespace ::sd::slidesorter::cache
420 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */