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 <swcache.hxx>
22 #include <o3tl/safeint.hxx>
23 #include <osl/diagnose.h>
24 #include <sal/log.hxx>
34 assert(m_pFirst
== nullptr && m_pLast
== nullptr);
39 assert(m_pLast
->GetNext() == nullptr);
40 assert(m_pRealFirst
->GetPrev() == nullptr);
42 bool bFirstFound
= false;
43 SwCacheObj
*pObj
= m_pRealFirst
;
44 SwCacheObj
*const pOldRealFirst
= m_pRealFirst
;
48 if ( pObj
== m_pFirst
)
50 SwCacheObj
* pNext
= pObj
->GetNext();
53 assert(pObj
== m_pLast
);
57 assert(pObj
== pNext
->GetPrev());
60 assert(pObj
!= pOldRealFirst
); (void) pOldRealFirst
;
63 SAL_WARN_IF( nCnt
+ m_aFreePositions
.size() != size(), "sw.core", "Lost Chain." );
65 size() == m_nCurMax
&& m_nCurMax
!= m_aFreePositions
.size() + nCnt
, "sw.core",
66 "Lost FreePositions." );
69 #define INCREMENT( nVar ) ++nVar
70 #define CHECK Check();
73 #define INCREMENT( nVar )
77 SwCache::SwCache( const sal_uInt16 nInitSize
82 m_pRealFirst( nullptr ),
85 m_nCurMax( nInitSize
)
87 , m_aName(std::move( aNm
))
96 , m_nAverageSeekCnt( 0 )
98 , m_nFlushedObjects( 0 )
100 , m_nDecreaseMax( 0 )
103 m_aCacheObjects
.reserve( nInitSize
);
111 m_aName
<< "; number of new entries: " << m_nAppend
112 << "; number of insert on free places: " << m_nInsertFree
113 << "; number of replacements: " << m_nReplace
114 << "; number of successful Gets: " << m_nGetSuccess
115 << "; number of failed Gets: " << m_nGetFail
116 << "; number or reordering (LRU): " << m_nToTop
117 << "; number of suppressions: " << m_nDelete
118 << "; number of Gets without Index: " << m_nGetSeek
119 << "; number of Seek for Get without Index: " << m_nAverageSeekCnt
120 << "; number of Flush calls: " << m_nFlushCnt
121 << "; number of flushed objects: " << m_nFlushedObjects
122 << "; number of Cache expansions: " << m_nIncreaseMax
123 << "; number of Cache reductions: " << m_nDecreaseMax
);
128 void SwCache::IncreaseMax( const sal_uInt16 nAdd
)
130 if (o3tl::checked_add(m_nCurMax
, nAdd
, m_nCurMax
))
139 void SwCache::DecreaseMax( const sal_uInt16 nSub
)
141 if ( m_nCurMax
> nSub
)
142 m_nCurMax
= m_nCurMax
- sal::static_int_cast
< sal_uInt16
>(nSub
);
148 void SwCache::Flush()
150 INCREMENT( m_nFlushCnt
);
151 SwCacheObj
*pObj
= m_pRealFirst
;
152 m_pRealFirst
= m_pFirst
= m_pLast
= nullptr;
155 assert(!pObj
->IsLocked());
156 SwCacheObj
*pTmp
= pObj
;
157 pObj
= pTmp
->GetNext();
158 m_aFreePositions
.push_back( pTmp
->GetCachePos() );
159 m_aCacheObjects
[pTmp
->GetCachePos()].reset(); // deletes pTmp
160 INCREMENT( m_nFlushedObjects
);
164 void SwCache::ToTop( SwCacheObj
*pObj
)
166 INCREMENT( m_nToTop
);
168 // cut object out of chain and insert at beginning
169 if ( m_pRealFirst
== pObj
) // pFirst was checked by caller
177 // the first will be inserted
178 assert(!m_pFirst
&& !m_pLast
);
179 m_pRealFirst
= m_pFirst
= m_pLast
= pObj
;
184 assert(m_pFirst
&& m_pLast
);
187 if ( pObj
== m_pLast
)
189 assert(pObj
->GetPrev());
190 m_pLast
= pObj
->GetPrev();
191 m_pLast
->SetNext( nullptr );
195 if ( pObj
->GetNext() )
196 pObj
->GetNext()->SetPrev( pObj
->GetPrev() );
197 if ( pObj
->GetPrev() )
198 pObj
->GetPrev()->SetNext( pObj
->GetNext() );
201 // paste at the (virtual) beginning
202 if ( m_pRealFirst
== m_pFirst
)
204 m_pRealFirst
->SetPrev( pObj
);
205 pObj
->SetNext( m_pRealFirst
);
206 pObj
->SetPrev( nullptr );
207 m_pRealFirst
= m_pFirst
= pObj
;
212 if ( m_pFirst
->GetPrev() )
214 m_pFirst
->GetPrev()->SetNext( pObj
);
215 pObj
->SetPrev( m_pFirst
->GetPrev() );
218 pObj
->SetPrev( nullptr );
219 m_pFirst
->SetPrev( pObj
);
220 pObj
->SetNext( m_pFirst
);
226 SwCacheObj
*SwCache::Get( const void *pOwner
, const sal_uInt16 nIndex
,
229 SwCacheObj
*pRet
= (nIndex
< m_aCacheObjects
.size()) ? m_aCacheObjects
[ nIndex
].get() : nullptr;
232 if ( !pRet
->IsOwner( pOwner
) )
234 else if ( bToTop
&& pRet
!= m_pFirst
)
248 SwCacheObj
*SwCache::Get( const void *pOwner
, const bool bToTop
)
250 SwCacheObj
*pRet
= m_pRealFirst
;
251 while ( pRet
&& !pRet
->IsOwner( pOwner
) )
253 INCREMENT( m_nAverageSeekCnt
);
254 pRet
= pRet
->GetNext();
257 if ( bToTop
&& pRet
&& pRet
!= m_pFirst
)
270 void SwCache::DeleteObj( SwCacheObj
*pObj
)
273 OSL_ENSURE( !pObj
->IsLocked(), "SwCache::Delete: object is locked." );
274 if ( pObj
->IsLocked() )
277 if ( m_pFirst
== pObj
)
279 if ( m_pFirst
->GetNext() )
280 m_pFirst
= m_pFirst
->GetNext();
282 m_pFirst
= m_pFirst
->GetPrev();
284 if ( m_pRealFirst
== pObj
)
285 m_pRealFirst
= m_pRealFirst
->GetNext();
286 if ( m_pLast
== pObj
)
287 m_pLast
= m_pLast
->GetPrev();
288 if ( pObj
->GetPrev() )
289 pObj
->GetPrev()->SetNext( pObj
->GetNext() );
290 if ( pObj
->GetNext() )
291 pObj
->GetNext()->SetPrev( pObj
->GetPrev() );
293 m_aFreePositions
.push_back( pObj
->GetCachePos() );
294 assert(m_aCacheObjects
[pObj
->GetCachePos()].get() == pObj
);
295 m_aCacheObjects
[pObj
->GetCachePos()] = nullptr; // deletes pObj
298 if ( m_aCacheObjects
.size() > m_nCurMax
&&
299 (m_nCurMax
<= (m_aCacheObjects
.size() - m_aFreePositions
.size())) )
301 // Shrink if possible.To do so we need enough free positions.
302 // Unpleasant side effect: positions will be moved and the owner of
303 // these might not find them afterwards
304 for ( size_t i
= 0; i
< m_aCacheObjects
.size(); ++i
)
306 SwCacheObj
*pTmpObj
= m_aCacheObjects
[i
].get();
309 m_aCacheObjects
.erase( m_aCacheObjects
.begin() + i
);
314 pTmpObj
->SetCachePos( i
);
317 m_aFreePositions
.clear();
322 void SwCache::Delete(void const*const pOwner
, sal_uInt16
const nIndex
)
324 INCREMENT( m_nDelete
);
325 if (SwCacheObj
*const pObj
= Get(pOwner
, nIndex
, false))
329 void SwCache::Delete( const void *pOwner
)
331 INCREMENT( m_nDelete
);
332 SwCacheObj
*pObj
= Get( pOwner
, false );
337 bool SwCache::Insert(SwCacheObj
*const pNew
, bool const isDuplicateOwnerAllowed
)
340 OSL_ENSURE( !pNew
->GetPrev() && !pNew
->GetNext(), "New but not new." );
341 if (!isDuplicateOwnerAllowed
)
343 for (auto const & rpObj
: m_aCacheObjects
)
344 { // check owner doesn't have a cache object yet; required for SwTextLine
345 assert(!rpObj
|| rpObj
->GetOwner() != pNew
->GetOwner());
351 if ( m_aCacheObjects
.size() < m_nCurMax
)
353 // there is still space; insert directly
354 INCREMENT( m_nAppend
);
355 nPos
= m_aCacheObjects
.size();
356 m_aCacheObjects
.emplace_back(pNew
);
358 else if ( !m_aFreePositions
.empty() )
360 // there are placeholders; use the last of those
361 INCREMENT( m_nInsertFree
);
362 const sal_uInt16 nFreePos
= m_aFreePositions
.size() - 1;
363 nPos
= m_aFreePositions
[ nFreePos
];
364 m_aCacheObjects
[nPos
].reset(pNew
);
365 m_aFreePositions
.erase( m_aFreePositions
.begin() + nFreePos
);
369 INCREMENT( m_nReplace
);
370 // the last of the LRU has to go
371 SwCacheObj
*pObj
= m_pLast
;
373 while ( pObj
&& pObj
->IsLocked() )
374 pObj
= pObj
->GetPrev();
377 SAL_WARN("sw.core", "SwCache overflow.");
378 IncreaseMax(100); // embiggen & try again
379 return Insert(pNew
, isDuplicateOwnerAllowed
);
382 nPos
= pObj
->GetCachePos();
383 if ( pObj
== m_pLast
)
385 m_pLast
= pObj
->GetPrev();
386 assert(m_pLast
); // must have capacity > 1
388 if (pObj
== m_pFirst
)
392 m_pFirst
= pObj
->GetNext();
396 m_pFirst
= pObj
->GetPrev();
398 assert(m_pFirst
); // must have capacity > 1
400 if (pObj
== m_pRealFirst
)
402 m_pRealFirst
= pObj
->GetNext();
403 assert(m_pRealFirst
); // must have capacity > 1
407 pObj
->GetPrev()->SetNext( pObj
->GetNext() );
411 pObj
->GetNext()->SetPrev( pObj
->GetPrev() );
413 m_aCacheObjects
[nPos
].reset(pNew
);
415 pNew
->SetCachePos( nPos
);
419 if ( m_pFirst
->GetPrev() )
420 { m_pFirst
->GetPrev()->SetNext( pNew
);
421 pNew
->SetPrev( m_pFirst
->GetPrev() );
423 m_pFirst
->SetPrev( pNew
);
424 pNew
->SetNext( m_pFirst
);
431 if ( m_pFirst
== m_pRealFirst
)
439 void SwCache::SetLRUOfst( const sal_uInt16 nOfst
)
441 assert(nOfst
< m_nCurMax
);
442 if ( !m_pRealFirst
|| ((m_aCacheObjects
.size() - m_aFreePositions
.size()) < nOfst
) )
446 m_pFirst
= m_pRealFirst
;
447 for ( sal_uInt16 i
= 0; i
< m_aCacheObjects
.size() && i
< nOfst
; ++i
)
449 if ( m_pFirst
->GetNext() && m_pFirst
->GetNext()->GetNext() )
450 m_pFirst
= m_pFirst
->GetNext();
457 SwCacheObj::SwCacheObj( const void *pOwn
) :
460 m_nCachePos( USHRT_MAX
),
466 SwCacheObj::~SwCacheObj()
471 void SwCacheObj::Lock()
473 assert( m_nLock
< UCHAR_MAX
&& "Too many Locks for CacheObject." );
477 void SwCacheObj::Unlock()
479 assert( m_nLock
&& "No more Locks available." );
484 SwCacheAccess::~SwCacheAccess()
490 void SwCacheAccess::Get_(bool const isDuplicateOwnerAllowed
)
492 OSL_ENSURE( !m_pObj
, "SwCacheAcces Obj already available." );
495 if (!m_rCache
.Insert(m_pObj
, isDuplicateOwnerAllowed
))
506 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */