LCOV - code coverage report
Current view: top level - sot/source/sdstor - stgcache.cxx (source / functions) Hit Total Coverage
Test: commit e02a6cb2c3e2b23b203b422e4e0680877f232636 Lines: 0 197 0.0 %
Date: 2014-04-14 Functions: 0 29 0.0 %
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             : //#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           0 : StgPage::StgPage( short nSize, sal_Int32 nPage )
      40             :     : mnRefCount( 0 )
      41             :     , mnPage( nPage )
      42           0 :     , mpData( new sal_uInt8[ nSize ] )
      43           0 :     , mnSize( nSize )
      44             : {
      45             :     OSL_ENSURE( mnSize >= 512, "Unexpected page size is provided!" );
      46             :     // We will write this data to a permanent file later
      47             :     // best to clear if first.
      48           0 :     memset( mpData, 0, mnSize );
      49           0 : }
      50             : 
      51           0 : StgPage::~StgPage()
      52             : {
      53           0 :     delete [] mpData;
      54           0 : }
      55             : 
      56           0 : rtl::Reference< StgPage > StgPage::Create( short nData, sal_Int32 nPage )
      57             : {
      58           0 :     return rtl::Reference< StgPage >( new StgPage( nData, nPage ) );
      59             : }
      60             : 
      61           0 : void StgCache::SetToPage ( const rtl::Reference< StgPage > xPage, short nOff, sal_Int32 nVal )
      62             : {
      63           0 :     if( ( nOff < (short) ( xPage->GetSize() / sizeof( sal_Int32 ) ) ) && nOff >= 0 )
      64             :     {
      65             : #ifdef OSL_BIGENDIAN
      66             :         nVal = OSL_SWAPDWORD(nVal);
      67             : #endif
      68           0 :         ((sal_Int32*) xPage->GetData() )[ nOff ] = nVal;
      69           0 :         SetDirty( xPage );
      70             :     }
      71           0 : }
      72             : 
      73           0 : bool StgPage::IsPageGreater( const StgPage *pA, const StgPage *pB )
      74             : {
      75           0 :     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           0 : 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           0 :     return (nFileSize >= 512) ? (nFileSize - 512 + nPageSize - 1) / nPageSize : 0;
      88             : }
      89             : 
      90           0 : 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( false )
     100           0 :    , bFile( false )
     101             : {
     102           0 : }
     103             : 
     104           0 : StgCache::~StgCache()
     105             : {
     106           0 :     Clear();
     107           0 :     SetStrm( NULL, false );
     108           0 : }
     109             : 
     110           0 : void StgCache::SetPhysPageSize( short n )
     111             : {
     112             :     OSL_ENSURE( n >= 512, "Unexpecte page size is provided!" );
     113           0 :     if ( n >= 512 )
     114             :     {
     115           0 :         nPageSize = n;
     116           0 :         sal_uLong nPos = pStrm->Tell();
     117           0 :         sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
     118           0 :         nPages = lcl_GetPageCount( nFileSize, nPageSize );
     119           0 :         pStrm->Seek( nPos );
     120             :     }
     121           0 : }
     122             : 
     123             : // Create a new cache element
     124             : 
     125           0 : rtl::Reference< StgPage > StgCache::Create( sal_Int32 nPg )
     126             : {
     127           0 :     rtl::Reference< StgPage > xElem( StgPage::Create( nPageSize, nPg ) );
     128           0 :     maLRUPages[ nReplaceIdx++ % maLRUPages.size() ] = xElem;
     129           0 :     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           0 : void StgCache::Clear()
     150             : {
     151           0 :     maDirtyPages.clear();
     152           0 :     for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
     153           0 :         it->clear();
     154           0 : }
     155             : 
     156             : // Look for a cached page
     157             : 
     158           0 : rtl::Reference< StgPage > StgCache::Find( sal_Int32 nPage )
     159             : {
     160           0 :     for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
     161           0 :         if ( it->is() && (*it)->GetPage() == nPage )
     162           0 :             return *it;
     163           0 :     IndexToStgPage::iterator it2 = maDirtyPages.find( nPage );
     164           0 :     if ( it2 != maDirtyPages.end() )
     165           0 :         return it2->second;
     166           0 :     return rtl::Reference< StgPage >();
     167             : }
     168             : 
     169             : // Load a page into the cache
     170             : 
     171           0 : rtl::Reference< StgPage > StgCache::Get( sal_Int32 nPage, bool bForce )
     172             : {
     173           0 :     rtl::Reference< StgPage > p = Find( nPage );
     174           0 :     if( !p.is() )
     175             :     {
     176           0 :         p = Create( nPage );
     177           0 :         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           0 :     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           0 : rtl::Reference< StgPage > StgCache::Copy( sal_Int32 nNew, sal_Int32 nOld )
     192             : {
     193           0 :     rtl::Reference< StgPage > p = Find( nNew );
     194           0 :     if( !p.is() )
     195           0 :         p = Create( nNew );
     196           0 :     if( nOld >= 0 )
     197             :     {
     198             :         // old page: we must have this data!
     199           0 :         rtl::Reference< StgPage > q = Get( nOld, 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           0 :     SetDirty( p );
     207             : 
     208           0 :     return p;
     209             : }
     210             : 
     211             : // Historically this wrote pages in a sorted, ascending order;
     212             : // continue that tradition.
     213           0 : bool StgCache::Commit()
     214             : {
     215           0 :     if ( Good() ) // otherwise Write does nothing
     216             :     {
     217           0 :         std::vector< StgPage * > aToWrite;
     218           0 :         for ( IndexToStgPage::iterator aIt = maDirtyPages.begin();
     219           0 :               aIt != maDirtyPages.end(); ++aIt )
     220           0 :             aToWrite.push_back( aIt->second.get() );
     221             : 
     222           0 :         std::sort( aToWrite.begin(), aToWrite.end(), StgPage::IsPageGreater );
     223           0 :         for ( std::vector< StgPage * >::iterator aWr = aToWrite.begin();
     224           0 :               aWr != aToWrite.end(); ++aWr)
     225             :         {
     226           0 :             const rtl::Reference< StgPage > &pPage = *aWr;
     227           0 :             if ( !Write( pPage->GetPage(), pPage->GetData(), 1 ) )
     228           0 :                 return false;
     229           0 :         }
     230             :     }
     231             : 
     232           0 :     maDirtyPages.clear();
     233             : 
     234           0 :     pStrm->Flush();
     235           0 :     SetError( pStrm->GetError() );
     236             : 
     237           0 :     return true;
     238             : }
     239             : 
     240             : // Set a stream
     241             : 
     242           0 : void StgCache::SetStrm( SvStream* p, bool bMy )
     243             : {
     244           0 :     if( pStorageStream )
     245             :     {
     246           0 :         pStorageStream->ReleaseRef();
     247           0 :         pStorageStream = NULL;
     248             :     }
     249             : 
     250           0 :     if( bMyStream )
     251           0 :         delete pStrm;
     252           0 :     pStrm = p;
     253           0 :     bMyStream = bMy;
     254           0 : }
     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 = false;
     274           0 : }
     275             : 
     276           0 : void StgCache::SetDirty( const rtl::Reference< StgPage > &xPage )
     277             : {
     278             :     assert( IsWritable() );
     279           0 :     maDirtyPages[ xPage->GetPage() ] = xPage;
     280           0 : }
     281             : 
     282             : // Open/close the disk file
     283             : 
     284           0 : bool StgCache::Open( const OUString& 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 :     bool bAccessDenied = false;
     292           0 :     if( ( nMode & STREAM_WRITE ) && !pFileStrm->IsWritable() )
     293             :     {
     294           0 :         pFileStrm->Close();
     295           0 :         bAccessDenied = true;
     296             :     }
     297           0 :     SetStrm( pFileStrm, 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 = true;
     307           0 :     SetError( bAccessDenied ? ERRCODE_IO_ACCESSDENIED : pStrm->GetError() );
     308           0 :     return Good();
     309             : }
     310             : 
     311           0 : void StgCache::Close()
     312             : {
     313           0 :     if( bFile )
     314             :     {
     315           0 :         ((SvFileStream*) pStrm)->Close();
     316           0 :         SetError( pStrm->GetError() );
     317             :     }
     318           0 : }
     319             : 
     320             : // low level I/O
     321             : 
     322           0 : bool StgCache::Read( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
     323             : {
     324           0 :     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           0 :         if ( nPage > nPages )
     331           0 :             SetError( SVSTREAM_READ_ERROR );
     332           0 :         else if ( nPage < nPages )
     333             :         {
     334           0 :             sal_uLong nPos = Page2Pos( nPage );
     335           0 :             sal_Int32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg;
     336           0 :             sal_uLong nBytes = nPg2 * nPageSize;
     337             :             // fixed address and size for the header
     338           0 :             if( nPage == -1 )
     339             :             {
     340           0 :                 nPos = 0L, nBytes = 512;
     341           0 :                 nPg2 = nPg;
     342             :             }
     343           0 :             if( pStrm->Tell() != nPos )
     344             :             {
     345           0 :                 if( pStrm->Seek( nPos ) != nPos ) {
     346             :     #ifdef CHECK_DIRTY
     347             :                     ErrorBox( NULL, WB_OK, OUString("SO2: Seek failed") ).Execute();
     348             :     #endif
     349             :                 }
     350             :             }
     351           0 :             pStrm->Read( pBuf, nBytes );
     352           0 :             if ( nPg != nPg2 )
     353           0 :                 SetError( SVSTREAM_READ_ERROR );
     354             :             else
     355           0 :                 SetError( pStrm->GetError() );
     356             :         }
     357             :     }
     358           0 :     return Good();
     359             : }
     360             : 
     361           0 : bool StgCache::Write( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
     362             : {
     363           0 :     if( Good() )
     364             :     {
     365           0 :         sal_uLong nPos = Page2Pos( nPage );
     366           0 :         sal_uLong nBytes = 0;
     367           0 :         if ( SAL_MAX_INT32 / nPg > nPageSize )
     368           0 :             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           0 :         if( nPage == -1 )
     373           0 :             nPos = 0L, nBytes = 512;
     374           0 :         if( pStrm->Tell() != nPos )
     375             :         {
     376           0 :             if( pStrm->Seek( nPos ) != nPos ) {
     377             : #ifdef CHECK_DIRTY
     378             :                 ErrorBox( NULL, WB_OK, OUString("SO2: Seek failed") ).Execute();
     379             : #endif
     380             :             }
     381             :         }
     382           0 :         sal_uLong nRes = pStrm->Write( pBuf, nBytes );
     383           0 :         if( nRes != nBytes )
     384           0 :             SetError( SVSTREAM_WRITE_ERROR );
     385             :         else
     386           0 :             SetError( pStrm->GetError() );
     387             : #ifdef READ_AFTER_WRITE
     388             :         sal_uInt8 cBuf[ 512 ];
     389             :         pStrm->Flush();
     390             :         pStrm->Seek( nPos );
     391             :         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, OUString("SO2: Read after Write failed") ).Execute();
     397             :             pStrm->SetError( SVSTREAM_WRITE_ERROR );
     398             :         }
     399             : #endif
     400             :     }
     401           0 :     return Good();
     402             : }
     403             : 
     404             : // set the file size in pages
     405             : 
     406           0 : bool StgCache::SetSize( sal_Int32 n )
     407             : {
     408             :     // Add the file header
     409           0 :     sal_Int32 nSize = n * nPageSize + 512;
     410           0 :     pStrm->SetStreamSize( nSize );
     411           0 :     SetError( pStrm->GetError() );
     412           0 :     if( !nError )
     413           0 :         nPages = n;
     414           0 :     return Good();
     415             : }
     416             : 
     417           0 : void StgCache::SetError( sal_uLong n )
     418             : {
     419           0 :     if( n && !nError )
     420           0 :         nError = n;
     421           0 : }
     422             : 
     423           0 : void StgCache::ResetError()
     424             : {
     425           0 :     nError = SVSTREAM_OK;
     426           0 :     pStrm->ResetError();
     427           0 : }
     428             : 
     429           0 : void StgCache::MoveError( StorageBase& r )
     430             : {
     431           0 :     if( nError != SVSTREAM_OK )
     432             :     {
     433           0 :         r.SetError( nError );
     434           0 :         ResetError();
     435             :     }
     436           0 : }
     437             : 
     438             : // Utility functions
     439             : 
     440           0 : sal_Int32 StgCache::Page2Pos( sal_Int32 nPage )
     441             : {
     442           0 :     if( nPage < 0 ) nPage = 0;
     443           0 :     return( nPage * nPageSize ) + nPageSize;
     444             : }
     445             : 
     446             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10