LCOV - code coverage report
Current view: top level - sot/source/sdstor - stgcache.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 157 197 79.7 %
Date: 2014-11-03 Functions: 27 29 93.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       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/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      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 .
      18             :  */
      19             : 
      20             : #include <string.h>
      21             : #include <osl/endian.h>
      22             : #include <tools/solar.h>
      23             : 
      24             : #include "sot/stg.hxx"
      25             : #include "stgelem.hxx"
      26             : #include "stgcache.hxx"
      27             : #include "stgstrms.hxx"
      28             : #include "stgdir.hxx"
      29             : #include "stgio.hxx"
      30             : 
      31             : ////////////////////////////// class StgPage
      32             : // This class implements buffer functionality. The cache will always return
      33             : // a page buffer, even if a read fails. It is up to the caller to determine
      34             : // the correctness of the I/O.
      35             : 
      36       37150 : StgPage::StgPage( short nSize, sal_Int32 nPage )
      37             :     : mnPage( nPage )
      38       37150 :     , mpData( new sal_uInt8[ nSize ] )
      39       74300 :     , mnSize( nSize )
      40             : {
      41             :     OSL_ENSURE( mnSize >= 512, "Unexpected page size is provided!" );
      42             :     // We will write this data to a permanent file later
      43             :     // best to clear if first.
      44       37150 :     memset( mpData, 0, mnSize );
      45       37150 : }
      46             : 
      47      111450 : StgPage::~StgPage()
      48             : {
      49       37150 :     delete [] mpData;
      50       74300 : }
      51             : 
      52       37150 : rtl::Reference< StgPage > StgPage::Create( short nData, sal_Int32 nPage )
      53             : {
      54       37150 :     return rtl::Reference< StgPage >( new StgPage( nData, nPage ) );
      55             : }
      56             : 
      57      191210 : void StgCache::SetToPage ( const rtl::Reference< StgPage > xPage, short nOff, sal_Int32 nVal )
      58             : {
      59      191210 :     if( ( nOff < (short) ( xPage->GetSize() / sizeof( sal_Int32 ) ) ) && nOff >= 0 )
      60             :     {
      61             : #ifdef OSL_BIGENDIAN
      62             :         nVal = OSL_SWAPDWORD(nVal);
      63             : #endif
      64      191210 :         ((sal_Int32*) xPage->GetData() )[ nOff ] = nVal;
      65      191210 :         SetDirty( xPage );
      66             :     }
      67      191210 : }
      68             : 
      69       46271 : bool StgPage::IsPageGreater( const StgPage *pA, const StgPage *pB )
      70             : {
      71       46271 :     return pA->mnPage < pB->mnPage;
      72             : }
      73             : 
      74             : //////////////////////////////// class StgCache
      75             : 
      76             : // The disk cache holds the cached sectors. The sector type differ according
      77             : // to their purpose.
      78             : 
      79        1304 : static sal_Int32 lcl_GetPageCount( sal_uLong nFileSize, short nPageSize )
      80             : {
      81             : //    return (nFileSize >= 512) ? (nFileSize - 512) / nPageSize : 0;
      82             :     // #i61980# reallife: last page may be incomplete, return number of *started* pages
      83        1304 :     return (nFileSize >= 512) ? (nFileSize - 512 + nPageSize - 1) / nPageSize : 0;
      84             : }
      85             : 
      86        2082 : StgCache::StgCache()
      87             :    : nError( SVSTREAM_OK )
      88             :    , nPages( 0 )
      89             :    , nRef( 0 )
      90             :    , nReplaceIdx( 0 )
      91             :    , maLRUPages( 8 ) // entries in the LRU lookup
      92             :    , nPageSize( 512 )
      93             :    , pStorageStream( NULL )
      94             :    , pStrm( NULL )
      95             :    , bMyStream( false )
      96        2082 :    , bFile( false )
      97             : {
      98        2082 : }
      99             : 
     100        4164 : StgCache::~StgCache()
     101             : {
     102        2082 :     Clear();
     103        2082 :     SetStrm( NULL, false );
     104        2082 : }
     105             : 
     106        1270 : void StgCache::SetPhysPageSize( short n )
     107             : {
     108             :     OSL_ENSURE( n >= 512, "Unexpecte page size is provided!" );
     109        1270 :     if ( n >= 512 )
     110             :     {
     111        1270 :         nPageSize = n;
     112        1270 :         sal_uLong nPos = pStrm->Tell();
     113        1270 :         sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
     114        1270 :         nPages = lcl_GetPageCount( nFileSize, nPageSize );
     115        1270 :         pStrm->Seek( nPos );
     116             :     }
     117        1270 : }
     118             : 
     119             : // Create a new cache element
     120             : 
     121       37150 : rtl::Reference< StgPage > StgCache::Create( sal_Int32 nPg )
     122             : {
     123       37150 :     rtl::Reference< StgPage > xElem( StgPage::Create( nPageSize, nPg ) );
     124       37150 :     maLRUPages[ nReplaceIdx++ % maLRUPages.size() ] = xElem;
     125       37150 :     return xElem;
     126             : }
     127             : 
     128             : // Delete the given element
     129             : 
     130           0 : void StgCache::Erase( const rtl::Reference< StgPage > &xElem )
     131             : {
     132             :     OSL_ENSURE( xElem.is(), "The pointer should not be NULL!" );
     133           0 :     if ( xElem.is() ) {
     134           0 :         for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it ) {
     135           0 :             if ( it->is() && (*it)->GetPage() == xElem->GetPage() ) {
     136           0 :                 it->clear();
     137           0 :                 break;
     138             :             }
     139             :         }
     140             :     }
     141           0 : }
     142             : 
     143             : // remove all cache elements without flushing them
     144             : 
     145        2082 : void StgCache::Clear()
     146             : {
     147        2082 :     maDirtyPages.clear();
     148       18738 :     for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
     149       16656 :         it->clear();
     150        2082 : }
     151             : 
     152             : // Look for a cached page
     153             : 
     154     2819072 : rtl::Reference< StgPage > StgCache::Find( sal_Int32 nPage )
     155             : {
     156    14140374 :     for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
     157    13785874 :         if ( it->is() && (*it)->GetPage() == nPage )
     158     2464572 :             return *it;
     159      354500 :     IndexToStgPage::iterator it2 = maDirtyPages.find( nPage );
     160      354500 :     if ( it2 != maDirtyPages.end() )
     161      174450 :         return it2->second;
     162      180050 :     return rtl::Reference< StgPage >();
     163             : }
     164             : 
     165             : // Load a page into the cache
     166             : 
     167     2667148 : rtl::Reference< StgPage > StgCache::Get( sal_Int32 nPage, bool bForce )
     168             : {
     169     2667148 :     rtl::Reference< StgPage > p = Find( nPage );
     170     2667148 :     if( !p.is() )
     171             :     {
     172       29050 :         p = Create( nPage );
     173       29050 :         if( !Read( nPage, p->GetData(), 1 ) && bForce )
     174             :         {
     175           0 :             Erase( p );
     176           0 :             p.clear();
     177           0 :             SetError( SVSTREAM_READ_ERROR );
     178             :         }
     179             :     }
     180     2667148 :     return p;
     181             : }
     182             : 
     183             : // Copy an existing page into a new page. Use this routine
     184             : // to duplicate an existing stream or to create new entries.
     185             : // The new page is initially marked dirty. No owner is copied.
     186             : 
     187        8318 : rtl::Reference< StgPage > StgCache::Copy( sal_Int32 nNew, sal_Int32 nOld )
     188             : {
     189        8318 :     rtl::Reference< StgPage > p = Find( nNew );
     190        8318 :     if( !p.is() )
     191        8100 :         p = Create( nNew );
     192        8318 :     if( nOld >= 0 )
     193             :     {
     194             :         // old page: we must have this data!
     195           0 :         rtl::Reference< StgPage > q = Get( nOld, true );
     196           0 :         if( q.is() )
     197             :         {
     198             :             OSL_ENSURE( p->GetSize() == q->GetSize(), "Unexpected page size!" );
     199           0 :             memcpy( p->GetData(), q->GetData(), p->GetSize() );
     200           0 :         }
     201             :     }
     202        8318 :     SetDirty( p );
     203             : 
     204        8318 :     return p;
     205             : }
     206             : 
     207             : // Historically this wrote pages in a sorted, ascending order;
     208             : // continue that tradition.
     209         721 : bool StgCache::Commit()
     210             : {
     211         721 :     if ( Good() ) // otherwise Write does nothing
     212             :     {
     213         721 :         std::vector< StgPage * > aToWrite;
     214       30141 :         for ( IndexToStgPage::iterator aIt = maDirtyPages.begin();
     215       20094 :               aIt != maDirtyPages.end(); ++aIt )
     216        9326 :             aToWrite.push_back( aIt->second.get() );
     217             : 
     218         721 :         std::sort( aToWrite.begin(), aToWrite.end(), StgPage::IsPageGreater );
     219       30117 :         for ( std::vector< StgPage * >::iterator aWr = aToWrite.begin();
     220       20078 :               aWr != aToWrite.end(); ++aWr)
     221             :         {
     222        9326 :             const rtl::Reference< StgPage > &pPage = *aWr;
     223        9326 :             if ( !Write( pPage->GetPage(), pPage->GetData(), 1 ) )
     224           8 :                 return false;
     225       10031 :         }
     226             :     }
     227             : 
     228         713 :     maDirtyPages.clear();
     229             : 
     230         713 :     pStrm->Flush();
     231         713 :     SetError( pStrm->GetError() );
     232             : 
     233         713 :     return true;
     234             : }
     235             : 
     236             : // Set a stream
     237             : 
     238        4136 : void StgCache::SetStrm( SvStream* p, bool bMy )
     239             : {
     240        4136 :     if( pStorageStream )
     241             :     {
     242           0 :         pStorageStream->ReleaseRef();
     243           0 :         pStorageStream = NULL;
     244             :     }
     245             : 
     246        4136 :     if( bMyStream )
     247          34 :         delete pStrm;
     248        4136 :     pStrm = p;
     249        4136 :     bMyStream = bMy;
     250        4136 : }
     251             : 
     252           0 : void StgCache::SetStrm( UCBStorageStream* pStgStream )
     253             : {
     254           0 :     if( pStorageStream )
     255           0 :         pStorageStream->ReleaseRef();
     256           0 :     pStorageStream = pStgStream;
     257             : 
     258           0 :     if( bMyStream )
     259           0 :         delete pStrm;
     260             : 
     261           0 :     pStrm = NULL;
     262             : 
     263           0 :     if ( pStorageStream )
     264             :     {
     265           0 :         pStorageStream->AddFirstRef();
     266           0 :         pStrm = pStorageStream->GetModifySvStream();
     267             :     }
     268             : 
     269           0 :     bMyStream = false;
     270           0 : }
     271             : 
     272      252558 : void StgCache::SetDirty( const rtl::Reference< StgPage > &xPage )
     273             : {
     274             :     assert( IsWritable() );
     275      252558 :     maDirtyPages[ xPage->GetPage() ] = xPage;
     276      252558 : }
     277             : 
     278             : // Open/close the disk file
     279             : 
     280          34 : bool StgCache::Open( const OUString& rName, StreamMode nMode )
     281             : {
     282             :     // do not open in exclusive mode!
     283          34 :     if( nMode & STREAM_SHARE_DENYALL )
     284           0 :         nMode = ( ( nMode & ~STREAM_SHARE_DENYALL ) | STREAM_SHARE_DENYWRITE );
     285          34 :     SvFileStream* pFileStrm = new SvFileStream( rName, nMode );
     286             :     // SvStream "Feature" Write Open auch erfolgreich, wenns nicht klappt
     287          34 :     bool bAccessDenied = false;
     288          34 :     if( ( nMode & STREAM_WRITE ) && !pFileStrm->IsWritable() )
     289             :     {
     290           0 :         pFileStrm->Close();
     291           0 :         bAccessDenied = true;
     292             :     }
     293          34 :     SetStrm( pFileStrm, true );
     294          34 :     if( pFileStrm->IsOpen() )
     295             :     {
     296          34 :         sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
     297          34 :         nPages = lcl_GetPageCount( nFileSize, nPageSize );
     298          34 :         pStrm->Seek( 0L );
     299             :     }
     300             :     else
     301           0 :         nPages = 0;
     302          34 :     bFile = true;
     303          34 :     SetError( bAccessDenied ? ERRCODE_IO_ACCESSDENIED : pStrm->GetError() );
     304          34 :     return Good();
     305             : }
     306             : 
     307        2020 : void StgCache::Close()
     308             : {
     309        2020 :     if( bFile )
     310             :     {
     311           0 :         static_cast<SvFileStream*>(pStrm)->Close();
     312           0 :         SetError( pStrm->GetError() );
     313             :     }
     314        2020 : }
     315             : 
     316             : // low level I/O
     317             : 
     318      161578 : bool StgCache::Read( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
     319             : {
     320      161578 :     if( Good() )
     321             :     {
     322             :         /*  #i73846# real life: a storage may refer to a page one-behind the
     323             :             last valid page (see document attached to the issue). In that case
     324             :             (if nPage==nPages), just do nothing here and let the caller work on
     325             :             the empty zero-filled buffer. */
     326      161564 :         if ( nPage > nPages )
     327           0 :             SetError( SVSTREAM_READ_ERROR );
     328      161564 :         else if ( nPage < nPages )
     329             :         {
     330      161564 :             sal_uInt32 nPos = Page2Pos( nPage );
     331      161564 :             sal_Int32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg;
     332      161564 :             sal_uInt32 nBytes = nPg2 * nPageSize;
     333             :             // fixed address and size for the header
     334      161564 :             if( nPage == -1 )
     335             :             {
     336           0 :                 nPos = 0L, nBytes = 512;
     337           0 :                 nPg2 = nPg;
     338             :             }
     339      161564 :             if( pStrm->Tell() != nPos )
     340             :             {
     341       18177 :                 pStrm->Seek(nPos);
     342             :             }
     343      161564 :             pStrm->Read( pBuf, nBytes );
     344      161564 :             if ( nPg != nPg2 )
     345           0 :                 SetError( SVSTREAM_READ_ERROR );
     346             :             else
     347      161564 :                 SetError( pStrm->GetError() );
     348             :         }
     349             :     }
     350      161578 :     return Good();
     351             : }
     352             : 
     353       19698 : bool StgCache::Write( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
     354             : {
     355       19698 :     if( Good() )
     356             :     {
     357       19698 :         sal_uInt32 nPos = Page2Pos( nPage );
     358       19698 :         sal_uInt32 nBytes = 0;
     359       19698 :         if ( SAL_MAX_INT32 / nPg > nPageSize )
     360       19698 :             nBytes = nPg * nPageSize;
     361             : 
     362             :         // fixed address and size for the header
     363             :         // nPageSize must be >= 512, otherwise the header can not be written here, we check it on import
     364       19698 :         if( nPage == -1 )
     365           0 :             nPos = 0L, nBytes = 512;
     366       19698 :         if( pStrm->Tell() != nPos )
     367             :         {
     368        2097 :             pStrm->Seek(nPos);
     369             :         }
     370       19698 :         sal_uLong nRes = pStrm->Write( pBuf, nBytes );
     371       19698 :         if( nRes != nBytes )
     372           0 :             SetError( SVSTREAM_WRITE_ERROR );
     373             :         else
     374       19698 :             SetError( pStrm->GetError() );
     375             :     }
     376       19698 :     return Good();
     377             : }
     378             : 
     379             : // set the file size in pages
     380             : 
     381        7347 : bool StgCache::SetSize( sal_Int32 n )
     382             : {
     383             :     // Add the file header
     384        7347 :     sal_Int32 nSize = n * nPageSize + 512;
     385        7347 :     pStrm->SetStreamSize( nSize );
     386        7347 :     SetError( pStrm->GetError() );
     387        7347 :     if( !nError )
     388        7347 :         nPages = n;
     389        7347 :     return Good();
     390             : }
     391             : 
     392      213807 : void StgCache::SetError( sal_uLong n )
     393             : {
     394      213807 :     if( n && !nError )
     395         226 :         nError = n;
     396      213807 : }
     397             : 
     398        2724 : void StgCache::ResetError()
     399             : {
     400        2724 :     nError = SVSTREAM_OK;
     401        2724 :     pStrm->ResetError();
     402        2724 : }
     403             : 
     404     2230036 : void StgCache::MoveError( StorageBase& r )
     405             : {
     406     2230036 :     if( nError != SVSTREAM_OK )
     407             :     {
     408         226 :         r.SetError( nError );
     409         226 :         ResetError();
     410             :     }
     411     2230036 : }
     412             : 
     413             : // Utility functions
     414             : 
     415      181262 : sal_Int32 StgCache::Page2Pos( sal_Int32 nPage )
     416             : {
     417      181262 :     if( nPage < 0 ) nPage = 0;
     418      181262 :     return( nPage * nPageSize ) + nPageSize;
     419             : }
     420             : 
     421             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10