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

Generated by: LCOV version 1.11