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

Generated by: LCOV version 1.10