blob: 6037b79139d31d7bfbf6c0711409559bebe6965b [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"
#include <tools/debug.hxx>
#include <errhdl.hxx>
#include <swcache.hxx>
SV_IMPL_PTRARR(SwCacheObjArr,SwCacheObj*);
#ifndef DBG_UTIL
#define INCREMENT( nVar )
#else
#define INCREMENT( nVar ) ++nVar
#endif
/*************************************************************************
|*
|* SwCache::Check()
|*
|* Ersterstellung MA 23. Mar. 94
|* Letzte Aenderung MA 23. Mar. 94
|*
|*************************************************************************/
#ifdef DBG_UTIL
void SwCache::Check()
{
if ( !pRealFirst )
return;
//Konsistenspruefung.
ASSERT( !pLast->GetNext(), "Last but not last." );
ASSERT( !pRealFirst->GetPrev(), "First but not first." );
sal_uInt16 nCnt = 0;
sal_Bool bFirstFound = sal_False;
SwCacheObj *pObj = pRealFirst;
SwCacheObj *pRekursive = pObj;
while ( pObj )
{
//Das Objekt muss auch auf dem Rueckwaertsweg gefunden werden.
SwCacheObj *pTmp = pLast;
while ( pTmp && pTmp != pObj )
pTmp = pTmp->GetPrev();
ASSERT( pTmp, "Objekt not found." );
++nCnt;
if ( pObj == pFirst )
bFirstFound = sal_True;
if ( !pObj->GetNext() )
ASSERT( pObj == pLast, "Last not Found." );
pObj = pObj->GetNext();
ASSERT( pObj != pRekursive, "Recursion in SwCache." );
}
ASSERT( bFirstFound, "First not Found." );
ASSERT( (nCnt + aFreePositions.Count()) == Count(), "Lost Chain." );
if ( Count() == nCurMax )
ASSERT( (nCurMax - nCnt) == aFreePositions.Count(), "Lost FreePositions." );
}
#endif
#if defined(DBG_UTIL) && defined(MADEBUG)
#define CHECK Check();
#else
#define CHECK
#endif
/*************************************************************************
|*
|* SwCache::SwCache(), ~SwCache()
|*
|* Ersterstellung MA 15. Mar. 94
|* Letzte Aenderung MA 15. Mar. 94
|*
|*************************************************************************/
SwCache::SwCache( const sal_uInt16 nInitSize, const sal_uInt16 nGrowSize
#ifdef DBG_UTIL
, const ByteString &rNm
#endif
) :
SwCacheObjArr( (sal_uInt8)nInitSize, (sal_uInt8)nGrowSize ),
aFreePositions( 5, 5 ),
pRealFirst( 0 ),
pFirst( 0 ),
pLast( 0 ),
nMax( nInitSize ),
nCurMax( nInitSize )
#ifdef DBG_UTIL
, aName( rNm ),
nAppend( 0 ),
nInsertFree( 0 ),
nReplace( 0 ),
nGetSuccess( 0 ),
nGetFail( 0 ),
nToTop( 0 ),
nDelete( 0 ),
nGetSeek( 0 ),
nAverageSeekCnt( 0 ),
nFlushCnt( 0 ),
nFlushedObjects( 0 ),
nIncreaseMax( 0 ),
nDecreaseMax( 0 )
#endif
{
}
#ifdef DBG_UTIL
SwCache::~SwCache()
{
#if OSL_DEBUG_LEVEL > 1
{
ByteString sOut( aName ); sOut += '\n';
(( sOut += "Anzahl neuer Eintraege: " )
+= ByteString::CreateFromInt32( nAppend ))+= '\n';
(( sOut += "Anzahl Insert auf freie Plaetze: " )
+= ByteString::CreateFromInt32( nInsertFree ))+= '\n';
(( sOut += "Anzahl Ersetzungen: " )
+= ByteString::CreateFromInt32( nReplace ))+= '\n';
(( sOut += "Anzahl Erfolgreicher Get's: " )
+= ByteString::CreateFromInt32( nGetSuccess ))+= '\n';
(( sOut += "Anzahl Fehlgeschlagener Get's: " )
+= ByteString::CreateFromInt32( nGetFail ))+= '\n';
(( sOut += "Anzahl Umsortierungen (LRU): " )
+= ByteString::CreateFromInt32( nToTop ))+= '\n';
(( sOut += "Anzahl Loeschungen: " )
+= ByteString::CreateFromInt32( nDelete ))+= '\n';
(( sOut += "Anzahl Get's ohne Index: " )
+= ByteString::CreateFromInt32( nGetSeek ))+= '\n';
(( sOut += "Anzahl Seek fuer Get ohne Index: " )
+= ByteString::CreateFromInt32( nAverageSeekCnt ))+= '\n';
(( sOut += "Anzahl Flush-Aufrufe: " )
+= ByteString::CreateFromInt32( nFlushCnt ))+= '\n';
(( sOut += "Anzahl geflush'ter Objekte: " )
+= ByteString::CreateFromInt32( nFlushedObjects ))+= '\n';
(( sOut += "Anzahl Cache-Erweiterungen: " )
+= ByteString::CreateFromInt32( nIncreaseMax ))+= '\n';
(( sOut += "Anzahl Cache-Verkleinerungen: " )
+= ByteString::CreateFromInt32( nDecreaseMax ))+= '\n';
DBG_ERROR( sOut.GetBuffer() );
}
Check();
#endif
}
#endif
/*************************************************************************
|*
|* SwCache::Flush()
|*
|* Ersterstellung MA 15. Mar. 94
|* Letzte Aenderung MA 15. Mar. 94
|*
|*************************************************************************/
void SwCache::Flush( const sal_uInt8 )
{
INCREMENT( nFlushCnt );
SwCacheObj *pObj = pRealFirst;
pRealFirst = pFirst = pLast = 0;
SwCacheObj *pTmp;
while ( pObj )
{
#ifdef DBG_UTIL
if ( pObj->IsLocked() )
{
ASSERT( sal_True, "Flushing locked objects." );
if ( !pRealFirst )
{
pRealFirst = pFirst = pLast = pObj;
pTmp = pObj->GetNext();
pObj->SetNext( 0 ); pObj->SetPrev( 0 );
pObj = pTmp;
}
else
{ pLast->SetNext( pObj );
pObj->SetPrev( pLast );
pLast = pObj;
pTmp = pObj->GetNext();
pObj->SetNext( 0 );
pObj = pTmp;
}
}
else
#endif
{
pTmp = (SwCacheObj*)pObj;
pObj = pTmp->GetNext();
aFreePositions.Insert( pTmp->GetCachePos(), aFreePositions.Count() );
*(pData + pTmp->GetCachePos()) = (void*)0;
delete pTmp;
INCREMENT( nFlushedObjects );
}
}
}
/*************************************************************************
|*
|* SwCache::ToTop()
|*
|* Ersterstellung MA 15. Mar. 94
|* Letzte Aenderung MA 24. Apr. 95
|*
|*************************************************************************/
void SwCache::ToTop( SwCacheObj *pObj )
{
INCREMENT( nToTop );
//Objekt aus der LRU-Kette ausschneiden und am Anfang einfuegen.
if ( pRealFirst == pObj ) //pFirst wurde vom Aufrufer geprueft!
{ CHECK;
return;
}
if ( !pRealFirst )
{ //Der erste wird eingetragen.
ASSERT( !pFirst && !pLast, "First not first." );
pRealFirst = pFirst = pLast = pObj;
CHECK;
return;
}
//Ausschneiden.
if ( pObj == pLast )
{
ASSERT( pObj->GetPrev(), "Last but no Prev." );
pLast = pObj->GetPrev();
pLast->SetNext( 0 );
}
else
{
if ( pObj->GetNext() )
pObj->GetNext()->SetPrev( pObj->GetPrev() );
if ( pObj->GetPrev() )
pObj->GetPrev()->SetNext( pObj->GetNext() );
}
//Am (virtuellen) Anfang einfuegen.
if ( pRealFirst == pFirst )
{
pRealFirst->SetPrev( pObj );
pObj->SetNext( pRealFirst );
pObj->SetPrev( 0 );
pRealFirst = pFirst = pObj;
CHECK;
}
else
{
ASSERT( pFirst, "ToTop, First ist not RealFirst an Empty." );
if ( pFirst->GetPrev() )
{
pFirst->GetPrev()->SetNext( pObj );
pObj->SetPrev( pFirst->GetPrev() );
}
else
pObj->SetPrev( 0 );
pFirst->SetPrev( pObj );
pObj->SetNext( pFirst );
pFirst = pObj;
CHECK;
}
}
/*************************************************************************
|*
|* SwCache::Get()
|*
|* Ersterstellung MA 15. Mar. 94
|* Letzte Aenderung MA 22. Aug. 94
|*
|*************************************************************************/
SwCacheObj *SwCache::Get( const void *pOwner, const sal_uInt16 nIndex,
const sal_Bool bToTop )
{
SwCacheObj *pRet;
if ( 0 != (pRet = nIndex < Count() ? operator[]( nIndex ) : 0) )
{
if ( !pRet->IsOwner( pOwner ) )
pRet = 0;
else if ( bToTop && pRet != pFirst )
ToTop( pRet );
}
#ifdef DBG_UTIL
if ( pRet )
++nGetSuccess;
else
++nGetFail;
#endif
return pRet;
}
SwCacheObj *SwCache::Get( const void *pOwner, const sal_Bool bToTop )
{
SwCacheObj *pRet = pRealFirst;
while ( pRet && !pRet->IsOwner( pOwner ) )
{
INCREMENT( nAverageSeekCnt );
pRet = pRet->GetNext();
}
if ( bToTop && pRet && pRet != pFirst )
ToTop( pRet );
#ifdef DBG_UTIL
if ( pRet )
++nGetSuccess;
else
++nGetFail;
++nGetSeek;
#endif
return pRet;
}
/*************************************************************************
|*
|* SwCache::Delete()
|*
|* Ersterstellung MA 15. Mar. 94
|* Letzte Aenderung MA 15. Mar. 94
|*
|*************************************************************************/
void SwCache::DeleteObj( SwCacheObj *pObj )
{
CHECK;
ASSERT( !pObj->IsLocked(), "SwCache::Delete: Object ist Locked." );
if ( pObj->IsLocked() )
return;
if ( pFirst == pObj )
{
if ( pFirst->GetNext() )
pFirst = pFirst->GetNext();
else
pFirst = pFirst->GetPrev();
}
if ( pRealFirst == pObj )
pRealFirst = pRealFirst->GetNext();
if ( pLast == pObj )
pLast = pLast->GetPrev();
if ( pObj->GetPrev() )
pObj->GetPrev()->SetNext( pObj->GetNext() );
if ( pObj->GetNext() )
pObj->GetNext()->SetPrev( pObj->GetPrev() );
aFreePositions.Insert( pObj->GetCachePos(), aFreePositions.Count() );
*(pData + pObj->GetCachePos()) = (void*)0;
delete pObj;
CHECK;
if ( Count() > nCurMax &&
(nCurMax <= (Count() - aFreePositions.Count())) )
{
//Falls moeglich wieder verkleinern, dazu muessen allerdings ausreichend
//Freie Positionen bereitstehen.
//Unangenehmer Nebeneffekt ist, das die Positionen verschoben werden
//muessen, und die Eigentuemer der Objekte diese wahrscheinlich nicht
//wiederfinden werden.
for ( sal_uInt16 i = 0; i < Count(); ++i )
{
SwCacheObj *pTmpObj = operator[](i);
if ( !pTmpObj )
{ SwCacheObjArr::Remove( i, 1 );
--i;
}
else
pTmpObj->SetCachePos( i );
}
aFreePositions.Remove( 0, aFreePositions.Count() );
}
CHECK;
}
/*
void SwCache::Delete( const void *pOwner, const sal_uInt16 nIndex )
{
INCREMENT( nDelete );
SwCacheObj *pObj;
if ( 0 != (pObj = Get( pOwner, nIndex, sal_False )) )
DeleteObj( pObj );
}
*/
void SwCache::Delete( const void *pOwner )
{
INCREMENT( nDelete );
SwCacheObj *pObj;
if ( 0 != (pObj = Get( pOwner, sal_Bool(sal_False) )) )
DeleteObj( pObj );
}
/*************************************************************************
|*
|* SwCache::Insert()
|*
|* Ersterstellung MA 15. Mar. 94
|* Letzte Aenderung MA 20. Sep. 94
|*
|*************************************************************************/
sal_Bool SwCache::Insert( SwCacheObj *pNew )
{
CHECK;
ASSERT( !pNew->GetPrev() && !pNew->GetNext(), "New but not new." );
sal_uInt16 nPos;//Wird hinter den if's zum setzen am Obj benutzt.
if ( Count() < nCurMax )
{
//Es ist noch Platz frei, also einfach einfuegen.
INCREMENT( nAppend );
nPos = Count();
SwCacheObjArr::C40_INSERT( SwCacheObj, pNew, nPos );
}
else if ( aFreePositions.Count() )
{
//Es exitieren Platzhalter, also den letzten benutzen.
INCREMENT( nInsertFree );
const sal_uInt16 nFreePos = aFreePositions.Count() - 1;
nPos = aFreePositions[ nFreePos ];
*(pData + nPos) = pNew;
aFreePositions.Remove( nFreePos );
}
else
{
INCREMENT( nReplace );
//Der letzte des LRU fliegt raus.
SwCacheObj *pObj = pLast;
while ( pObj && pObj->IsLocked() )
pObj = pObj->GetPrev();
if ( !pObj )
{
ASSERT( sal_False, "Cache overflow." );
return sal_False;
}
nPos = pObj->GetCachePos();
if ( pObj == pLast )
{ ASSERT( pObj->GetPrev(), "Last but no Prev" );
pLast = pObj->GetPrev();
pLast->SetNext( 0 );
}
else
{
if ( pObj->GetPrev() )
pObj->GetPrev()->SetNext( pObj->GetNext() );
if ( pObj->GetNext() )
pObj->GetNext()->SetPrev( pObj->GetPrev() );
}
delete pObj;
*(pData + nPos) = pNew;
}
pNew->SetCachePos( nPos );
//Anstelle von ToTop, einfach als pFirst einfuegen.
// ToTop( nPos );
if ( pFirst )
{
if ( pFirst->GetPrev() )
{ pFirst->GetPrev()->SetNext( pNew );
pNew->SetPrev( pFirst->GetPrev() );
}
pFirst->SetPrev( pNew );
pNew->SetNext( pFirst );
}
else
{ ASSERT( !pLast, "Last but no First." );
pLast = pNew;
}
if ( pFirst == pRealFirst )
pRealFirst = pNew;
pFirst = pNew;
CHECK;
return sal_True;
}
/*************************************************************************
|*
|* SwCache::SetLRUOfst()
|*
|* Ersterstellung MA 15. Mar. 94
|* Letzte Aenderung MA 15. Mar. 94
|*
|*************************************************************************/
void SwCache::SetLRUOfst( const sal_uInt16 nOfst )
{
if ( !pRealFirst || ((Count() - aFreePositions.Count()) < nOfst) )
return;
CHECK;
pFirst = pRealFirst;
for ( sal_uInt16 i = 0; i < Count() && i < nOfst; ++i )
{
if ( pFirst->GetNext() && pFirst->GetNext()->GetNext() )
pFirst = pFirst->GetNext();
else
break;
}
CHECK;
}
/*************************************************************************
|*
|* SwCacheObj::SwCacheObj()
|*
|* Ersterstellung MA 15. Mar. 94
|* Letzte Aenderung MA 24. Nov. 95
|*
|*************************************************************************/
SwCacheObj::SwCacheObj( const void *pOwn ) :
pNext( 0 ),
pPrev( 0 ),
nCachePos( USHRT_MAX ),
nLock( 0 ),
pOwner( pOwn )
{
}
SwCacheObj::~SwCacheObj()
{
}
/*************************************************************************
|*
|* SwCacheObj::SetLock(), Unlock()
|*
|* Ersterstellung MA 15. Mar. 94
|* Letzte Aenderung MA 15. Mar. 94
|*
|*************************************************************************/
#ifdef DBG_UTIL
void SwCacheObj::Lock()
{
ASSERT( nLock < UCHAR_MAX, "To many Locks for CacheObject." );
++nLock;
}
void SwCacheObj::Unlock()
{
ASSERT( nLock, "No more Locks available." );
--nLock;
}
#endif
SwCacheAccess::~SwCacheAccess()
{
if ( pObj )
pObj->Unlock();
}
/*************************************************************************
|*
|* SwCacheAccess::Get()
|*
|* Ersterstellung MA 15. Mar. 94
|* Letzte Aenderung MA 04. Apr. 95
|*
|*************************************************************************/
void SwCacheAccess::_Get()
{
ASSERT( !pObj, "SwCacheAcces Obj already available." );
pObj = NewObj();
if ( !rCache.Insert( pObj ) )
{
delete pObj;
pObj = 0;
}
else
pObj->Lock();
}
/*************************************************************************
|*
|* SwCacheAccess::IsAvailable()
|*
|* Ersterstellung MA 23. Mar. 94
|* Letzte Aenderung MA 23. Mar. 94
|*
|*************************************************************************/
sal_Bool SwCacheAccess::IsAvailable() const
{
return pObj != 0;
}