LCOV - code coverage report
Current view: top level - sot/source/sdstor - stgstrms.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 529 707 74.8 %
Date: 2014-11-03 Functions: 41 45 91.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 <algorithm>
      21             : 
      22             : #include <string.h>
      23             : #include <sal/log.hxx>
      24             : #include <osl/file.hxx>
      25             : #include <unotools/tempfile.hxx>
      26             : #include <set>
      27             : 
      28             : #include "sot/stg.hxx"
      29             : #include "stgelem.hxx"
      30             : #include "stgcache.hxx"
      31             : #include "stgstrms.hxx"
      32             : #include "stgdir.hxx"
      33             : #include "stgio.hxx"
      34             : #include <boost/scoped_array.hpp>
      35             : 
      36             : ///////////////////////////// class StgFAT
      37             : 
      38             : // The FAT class performs FAT operations on an underlying storage stream.
      39             : // This stream is either the master FAT stream (m == true ) or a normal
      40             : // storage stream, which then holds the FAT for small data allocations.
      41             : 
      42       19996 : StgFAT::StgFAT( StgStrm& r, bool m ) : rStrm( r )
      43             : {
      44       19996 :     bPhys   = m;
      45       19996 :     nPageSize = rStrm.GetIo().GetPhysPageSize();
      46       19996 :     nEntries  = nPageSize >> 2;
      47       19996 :     nOffset   = 0;
      48       19996 :     nMaxPage  = 0;
      49       19996 :     nLimit    = 0;
      50       19996 : }
      51             : 
      52             : // Retrieve the physical page for a given byte offset.
      53             : 
      54      372560 : rtl::Reference< StgPage > StgFAT::GetPhysPage( sal_Int32 nByteOff )
      55             : {
      56      372560 :     rtl::Reference< StgPage > pPg;
      57             :     // Position within the underlying stream
      58             :     // use the Pos2Page() method of the stream
      59      372560 :     if( rStrm.Pos2Page( nByteOff ) )
      60             :     {
      61      372560 :         nOffset = rStrm.GetOffset();
      62      372560 :         sal_Int32 nPhysPage = rStrm.GetPage();
      63             :         // get the physical page (must be present)
      64      372560 :         pPg = rStrm.GetIo().Get( nPhysPage, true );
      65             :     }
      66      372560 :     return pPg;
      67             : }
      68             : 
      69             : // Get the follow page for a certain FAT page.
      70             : 
      71      341836 : sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg )
      72             : {
      73      341836 :     if( nPg >= 0 )
      74             :     {
      75      341836 :       rtl::Reference< StgPage > pPg = GetPhysPage( nPg << 2 );
      76      341836 :       nPg = pPg.is() ? rStrm.GetIo().GetFromPage( pPg, nOffset >> 2 ) : STG_EOF;
      77             :     }
      78      341836 :     return nPg;
      79             : }
      80             : 
      81             : // Find the best fit block for the given size. Return
      82             : // the starting block and its size or STG_EOF and 0.
      83             : // nLastPage is a stopper which tells the current
      84             : // underlying stream size. It is treated as a recommendation
      85             : // to abort the search to inhibit excessive file growth.
      86             : 
      87       12575 : sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs )
      88             : {
      89       12575 :     sal_Int32 nMinStart = STG_EOF, nMinLen = 0;
      90       12575 :     sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
      91       12575 :     sal_Int32 nTmpStart = STG_EOF, nTmpLen = 0;
      92       12575 :     sal_Int32 nPages    = rStrm.GetSize() >> 2;
      93       12575 :     bool bFound     = false;
      94       12575 :     rtl::Reference< StgPage > pPg;
      95       12575 :     short nEntry = 0;
      96      623027 :     for( sal_Int32 i = 0; i < nPages; i++, nEntry++ )
      97             :     {
      98      621501 :         if( !( nEntry % nEntries ) )
      99             :         {
     100             :             // load the next page for that stream
     101       13077 :             nEntry = 0;
     102       13077 :             pPg = GetPhysPage( i << 2 );
     103       13077 :             if( !pPg.is() )
     104           0 :                 return STG_EOF;
     105             :         }
     106      621501 :         sal_Int32 nCur = rStrm.GetIo().GetFromPage( pPg, nEntry );
     107      621501 :         if( nCur == STG_FREE )
     108             :         {
     109             :             // count the size of this area
     110       65039 :             if( nTmpLen )
     111       53764 :                 nTmpLen++;
     112             :             else
     113             :                 nTmpStart = i,
     114       11275 :                 nTmpLen   = 1;
     115       65039 :             if( nTmpLen == nPgs
     116             :              // If we already did find a block, stop when reaching the limit
     117       53998 :              || ( bFound && ( nEntry >= nLimit ) ) )
     118             :                 break;
     119             :         }
     120      556462 :         else if( nTmpLen )
     121             :         {
     122           8 :             if( nTmpLen > nPgs && nTmpLen < nMaxLen )
     123             :                 // block > requested size
     124           0 :                 nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = true;
     125           8 :             else if( nTmpLen >= nMinLen )
     126             :             {
     127             :                 // block < requested size
     128           8 :                 nMinLen = nTmpLen, nMinStart = nTmpStart;
     129           8 :                 bFound = true;
     130           8 :                 if( nTmpLen == nPgs )
     131           0 :                     break;
     132             :             }
     133           8 :             nTmpStart = STG_EOF;
     134           8 :             nTmpLen   = 0;
     135             :         }
     136             :     }
     137             :     // Determine which block to use.
     138       12575 :     if( nTmpLen )
     139             :     {
     140       11267 :         if( nTmpLen > nPgs  && nTmpLen < nMaxLen )
     141             :             // block > requested size
     142           0 :             nMaxLen = nTmpLen, nMaxStart = nTmpStart;
     143       11267 :         else if( nTmpLen >= nMinLen )
     144             :             // block < requested size
     145       11267 :             nMinLen = nTmpLen, nMinStart = nTmpStart;
     146             :     }
     147       12575 :     if( nMinStart != STG_EOF && nMaxStart != STG_EOF )
     148             :     {
     149             :         // two areas found; return the best fit area
     150           0 :         sal_Int32 nMinDiff = nPgs - nMinLen;
     151           0 :         sal_Int32 nMaxDiff = nMaxLen - nPgs;
     152           0 :         if( nMinDiff > nMaxDiff )
     153           0 :             nMinStart = STG_EOF;
     154             :     }
     155       12575 :     if( nMinStart != STG_EOF )
     156             :     {
     157       11267 :         nPgs = nMinLen; return nMinStart;
     158             :     }
     159             :     else
     160             :     {
     161        1308 :         return nMaxStart;
     162       12575 :     }
     163             : }
     164             : 
     165             : // Set up the consecutive chain for a given block.
     166             : 
     167       11267 : bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs )
     168             : {
     169       11267 :     sal_Int32 nPos = nStart << 2;
     170       11267 :     rtl::Reference< StgPage > pPg = GetPhysPage( nPos );
     171       11267 :     if( !pPg.is() || !nPgs )
     172           0 :         return false;
     173       76298 :     while( --nPgs )
     174             :     {
     175       53764 :         if( nOffset >= nPageSize )
     176             :         {
     177           0 :             pPg = GetPhysPage( nPos );
     178           0 :             if( !pPg.is() )
     179           0 :                 return false;
     180             :         }
     181       53764 :         rStrm.GetIo().SetToPage( pPg, nOffset >> 2, ++nStart );
     182       53764 :         nOffset += 4;
     183       53764 :         nPos += 4;
     184             :     }
     185       11267 :     if( nOffset >= nPageSize )
     186             :     {
     187           0 :         pPg = GetPhysPage( nPos );
     188           0 :         if( !pPg.is() )
     189           0 :             return false;
     190             :     }
     191       11267 :     rStrm.GetIo().SetToPage( pPg, nOffset >> 2, STG_EOF );
     192       11267 :     return true;
     193             : }
     194             : 
     195             : // Allocate a block of data from the given page number on.
     196             : // It the page number is != STG_EOF, chain the block.
     197             : 
     198       11041 : sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs )
     199             : {
     200       11041 :     sal_Int32 nOrig = nBgn;
     201       11041 :     sal_Int32 nLast = nBgn;
     202       11041 :     sal_Int32 nBegin = STG_EOF;
     203             :     sal_Int32 nAlloc;
     204       11041 :     sal_Int32 nPages = rStrm.GetSize() >> 2;
     205       11041 :     short nPasses = 0;
     206             :     // allow for two passes
     207       23015 :     while( nPasses < 2 )
     208             :     {
     209             :         // try to satisfy the request from the pool of free pages
     210       35215 :         while( nPgs )
     211             :         {
     212       12200 :             nAlloc = nPgs;
     213       12200 :             nBegin = FindBlock( nAlloc );
     214             :             // no more blocks left in present alloc chain
     215       12200 :             if( nBegin == STG_EOF )
     216         933 :                 break;
     217       11267 :             if( ( nBegin + nAlloc ) > nMaxPage )
     218       11259 :                 nMaxPage = nBegin + nAlloc;
     219       11267 :             if( !MakeChain( nBegin, nAlloc ) )
     220           0 :                 return STG_EOF;
     221       11267 :             if( nOrig == STG_EOF )
     222        5265 :                 nOrig = nBegin;
     223             :             else
     224             :             {
     225             :                 // Patch the chain
     226        6002 :                 rtl::Reference< StgPage > pPg = GetPhysPage( nLast << 2 );
     227        6002 :                 if( !pPg.is() )
     228           0 :                     return STG_EOF;
     229        6002 :                 rStrm.GetIo().SetToPage( pPg, nOffset >> 2, nBegin );
     230             :             }
     231       11267 :             nLast = nBegin + nAlloc - 1;
     232       11267 :             nPgs -= nAlloc;
     233             :         }
     234       11974 :         if( nPgs && !nPasses )
     235             :         {
     236             :             // we need new, fresh space, so allocate and retry
     237         933 :             if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
     238           0 :                 return STG_EOF;
     239         933 :             if( !bPhys && !InitNew( nPages ) )
     240           0 :                 return 0;
     241             :                     // FIXME: this was originally "FALSE", whether or not that
     242             :                     // makes sense (or should be STG_EOF instead, say?)
     243         933 :             nPages = rStrm.GetSize() >> 2;
     244         933 :             nPasses++;
     245             :         }
     246             :         else
     247             :             break;
     248             :     }
     249             :     // now we should have a chain for the complete block
     250       11041 :     if( nBegin == STG_EOF || nPgs )
     251             :     {
     252           0 :         rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
     253           0 :         return STG_EOF; // bad structure
     254             :     }
     255       11041 :     return nOrig;
     256             : }
     257             : 
     258             : // Initialize newly allocated pages for a standard FAT stream
     259             : // It can be assumed that the stream size is always on
     260             : // a page boundary
     261             : 
     262         558 : bool StgFAT::InitNew( sal_Int32 nPage1 )
     263             : {
     264         558 :     sal_Int32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries;
     265         558 :     if ( n > 0 )
     266             :     {
     267        1674 :         while( n-- )
     268             :         {
     269         558 :             rtl::Reference< StgPage > pPg;
     270             :             // Position within the underlying stream
     271             :             // use the Pos2Page() method of the stream
     272         558 :             rStrm.Pos2Page( nPage1 << 2 );
     273             :             // Initialize the page
     274         558 :             pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE );
     275         558 :             if ( !pPg.is() )
     276           0 :                 return false;
     277       71982 :             for( short i = 0; i < nEntries; i++ )
     278       71424 :                 rStrm.GetIo().SetToPage( pPg, i, STG_FREE );
     279         558 :             nPage1++;
     280         558 :         }
     281             :     }
     282         558 :     return true;
     283             : }
     284             : 
     285             : // Release a chain
     286             : 
     287         721 : bool StgFAT::FreePages( sal_Int32 nStart, bool bAll )
     288             : {
     289        1820 :     while( nStart >= 0 )
     290             :     {
     291         378 :         rtl::Reference< StgPage > pPg = GetPhysPage( nStart << 2 );
     292         378 :         if( !pPg.is() )
     293           0 :             return false;
     294         378 :         nStart = rStrm.GetIo().GetFromPage( pPg, nOffset >> 2 );
     295             :         // The first released page is either set to EOF or FREE
     296         378 :         rStrm.GetIo().SetToPage( pPg, nOffset >> 2, bAll ? STG_FREE : STG_EOF );
     297         378 :         bAll = true;
     298         378 :     }
     299         721 :     return true;
     300             : }
     301             : 
     302             : ///////////////////////////// class StgStrm
     303             : 
     304             : // The base stream class provides basic functionality for seeking
     305             : // and accessing the data on a physical basis. It uses the built-in
     306             : // FAT class for the page allocations.
     307             : 
     308       19996 : StgStrm::StgStrm( StgIo& r ) : rIo( r )
     309             : {
     310       19996 :     pFat    = NULL;
     311       19996 :     nStart  = nPage = STG_EOF;
     312       19996 :     nOffset = 0;
     313       19996 :     pEntry  = NULL;
     314       19996 :     nPos = nSize = 0;
     315       19996 :     nPageSize = rIo.GetPhysPageSize();
     316       19996 : }
     317             : 
     318       39992 : StgStrm::~StgStrm()
     319             : {
     320       19996 :     delete pFat;
     321       19996 : }
     322             : 
     323             : // Attach the stream to the given entry.
     324             : 
     325        5188 : void StgStrm::SetEntry( StgDirEntry& r )
     326             : {
     327        5188 :     r.aEntry.SetLeaf( STG_DATA, nStart );
     328        5188 :     r.aEntry.SetSize( nSize );
     329        5188 :     pEntry = &r;
     330        5188 :     r.SetDirty();
     331        5188 : }
     332             : 
     333             : /*
     334             :  * The page chain, is basically a singly linked list of slots each
     335             :  * point to the next page. Instead of traversing the file structure
     336             :  * for this each time build a simple flat in-memory vector list
     337             :  * of pages.
     338             :  */
     339        2532 : void StgStrm::scanBuildPageChainCache(sal_Int32 *pOptionalCalcSize)
     340             : {
     341        2532 :     if (nSize > 0)
     342           0 :         m_aPagesCache.reserve(nSize/nPageSize);
     343             : 
     344        2532 :     bool bError = false;
     345        2532 :     sal_Int32 nBgn = nStart;
     346        2532 :     sal_Int32 nOptSize = 0;
     347             : 
     348             :     // Track already scanned PageNumbers here and use them to
     349             :     // see if an  already counted page is re-visited
     350        2532 :     std::set< sal_Int32 > nUsedPageNumbers;
     351             : 
     352        9077 :     while( nBgn >= 0 && !bError )
     353             :     {
     354        4013 :         if( nBgn >= 0 )
     355        4013 :             m_aPagesCache.push_back(nBgn);
     356        4013 :         nBgn = pFat->GetNextPage( nBgn );
     357             : 
     358             :         //returned second is false if it already exists
     359        4013 :         if (!nUsedPageNumbers.insert(nBgn).second)
     360           2 :             bError = true;
     361             : 
     362        4013 :         nOptSize += nPageSize;
     363             :     }
     364        2532 :     if (bError)
     365             :     {
     366           2 :         if (pOptionalCalcSize)
     367           2 :             rIo.SetError( ERRCODE_IO_WRONGFORMAT );
     368           2 :         m_aPagesCache.clear();
     369             :     }
     370        2532 :     if (pOptionalCalcSize)
     371        2532 :         *pOptionalCalcSize = nOptSize;
     372        2532 : }
     373             : 
     374             : // Compute page number and offset for the given byte position.
     375             : // If the position is behind the size, set the stream right
     376             : // behind the EOF.
     377     4329310 : bool StgStrm::Pos2Page( sal_Int32 nBytePos )
     378             : {
     379     4329310 :     if ( !pFat )
     380           0 :         return false;
     381             : 
     382             :     // Values < 0 seek to the end
     383     4329310 :     if( nBytePos < 0 || nBytePos >= nSize )
     384       52643 :         nBytePos = nSize;
     385             :     // Adjust the position back to offset 0
     386     4329310 :     nPos -= nOffset;
     387     4329310 :     sal_Int32 nMask = ~( nPageSize - 1 );
     388     4329310 :     sal_Int32 nOld = nPos & nMask;
     389     4329310 :     sal_Int32 nNew = nBytePos & nMask;
     390     4329310 :     nOffset = (short) ( nBytePos & ~nMask );
     391     4329310 :     nPos = nBytePos;
     392     4329310 :     if( nOld == nNew )
     393     3991546 :         return true;
     394             : 
     395             :     // See fdo#47644 for a .doc with a vast amount of pages where seeking around the
     396             :     // document takes a colossal amount of time
     397             : 
     398             :     // Please Note: we build the pagescache incrementally as we go if necessary,
     399             :     // so that a corrupted FAT doesn't poison the stream state for earlier reads
     400      337764 :     size_t nIdx = nNew / nPageSize;
     401      337764 :     if( nIdx >= m_aPagesCache.size() )
     402             :     {
     403             :         // Extend the FAT cache ! ...
     404      155232 :         size_t nToAdd = nIdx + 1;
     405             : 
     406      155232 :         if (m_aPagesCache.empty())
     407       22074 :             m_aPagesCache.push_back( nStart );
     408             : 
     409      155232 :         nToAdd -= m_aPagesCache.size();
     410             : 
     411      155232 :         sal_Int32 nBgn = m_aPagesCache.back();
     412             : 
     413             :         // Start adding pages while we can
     414      646678 :         while( nToAdd > 0 && nBgn >= 0 )
     415             :         {
     416      336214 :             nBgn = pFat->GetNextPage( nBgn );
     417      336214 :             if( nBgn >= 0 )
     418             :             {
     419      324374 :                 m_aPagesCache.push_back( nBgn );
     420      324374 :                 nToAdd--;
     421             :             }
     422             :         }
     423             :     }
     424             : 
     425      337764 :     if ( nIdx > m_aPagesCache.size() )
     426             :     {
     427          12 :         rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
     428          12 :         nPage = STG_EOF;
     429          12 :         nOffset = nPageSize;
     430          12 :         return false;
     431             :     }
     432             :     // special case: seek to 1st byte of new, unallocated page
     433             :     // (in case the file size is a multiple of the page size)
     434      337752 :     if( nBytePos == nSize && !nOffset && nIdx > 0 && nIdx == m_aPagesCache.size() )
     435             :     {
     436       11828 :         nIdx--;
     437       11828 :         nOffset = nPageSize;
     438             :     }
     439      325924 :     else if ( nIdx == m_aPagesCache.size() )
     440             :     {
     441           0 :         nPage = STG_EOF;
     442           0 :         return false;
     443             :     }
     444             : 
     445      337752 :     nPage = m_aPagesCache[ nIdx ];
     446             : 
     447      337752 :     return nPage >= 0;
     448             : }
     449             : 
     450             : // Retrieve the physical page for a given byte offset.
     451             : 
     452           0 : rtl::Reference< StgPage > StgStrm::GetPhysPage( sal_Int32 nBytePos, bool bForce )
     453             : {
     454           0 :     if( !Pos2Page( nBytePos ) )
     455           0 :         return NULL;
     456           0 :     return rIo.Get( nPage, bForce );
     457             : }
     458             : 
     459             : // Copy an entire stream. Both streams are allocated in the FAT.
     460             : // The target stream is this stream.
     461             : 
     462         721 : bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes )
     463             : {
     464         721 :     if ( !pFat )
     465           0 :         return false;
     466             : 
     467         721 :     m_aPagesCache.clear();
     468             : 
     469         721 :     sal_Int32 nTo = nStart;
     470         721 :     sal_Int32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize;
     471        3051 :     while( nPgs-- )
     472             :     {
     473        1609 :         if( nTo < 0 )
     474             :         {
     475           0 :             rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
     476           0 :             return false;
     477             :         }
     478        1609 :         rIo.Copy( nTo, nFrom );
     479        1609 :         if( nFrom >= 0 )
     480             :         {
     481           0 :             nFrom = pFat->GetNextPage( nFrom );
     482           0 :             if( nFrom < 0 )
     483             :             {
     484           0 :                 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
     485           0 :                 return false;
     486             :             }
     487             :         }
     488        1609 :         nTo = pFat->GetNextPage( nTo );
     489             :     }
     490         721 :     return true;
     491             : }
     492             : 
     493       55265 : bool StgStrm::SetSize( sal_Int32 nBytes )
     494             : {
     495       55265 :     if ( nBytes < 0 || !pFat )
     496           0 :         return false;
     497             : 
     498       55265 :     m_aPagesCache.clear();
     499             : 
     500             :     // round up to page size
     501       55265 :     sal_Int32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize;
     502       55265 :     sal_Int32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
     503       55265 :     if( nNew > nOld )
     504             :     {
     505       11041 :         if( !Pos2Page( nSize ) )
     506           0 :             return false;
     507       11041 :         sal_Int32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize );
     508       11041 :         if( nBgn == STG_EOF )
     509           0 :             return false;
     510       11041 :         if( nStart == STG_EOF )
     511        5265 :             nStart = nPage = nBgn;
     512             :     }
     513       44224 :     else if( nNew < nOld )
     514             :     {
     515           0 :         bool bAll = ( nBytes == 0 );
     516           0 :         if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) )
     517           0 :             return false;
     518           0 :         if( bAll )
     519           0 :             nStart = nPage = STG_EOF;
     520             :     }
     521       55265 :     if( pEntry )
     522             :     {
     523             :         // change the dir entry?
     524       50060 :         if( !nSize || !nBytes )
     525        4322 :             pEntry->aEntry.SetLeaf( STG_DATA, nStart );
     526       50060 :         pEntry->aEntry.SetSize( nBytes );
     527       50060 :         pEntry->SetDirty();
     528             :     }
     529       55265 :     nSize = nBytes;
     530       55265 :     pFat->SetLimit( GetPages() );
     531       55265 :     return true;
     532             : }
     533             : 
     534             : // Return the # of allocated pages
     535             : 
     536             : 
     537             : //////////////////////////// class StgFATStrm
     538             : 
     539             : // The FAT stream class provides physical access to the master FAT.
     540             : // Since this access is implemented as a StgStrm, we can use the
     541             : // FAT allocator.
     542             : 
     543        1270 : StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r )
     544             : {
     545        1270 :     pFat = new StgFAT( *this, true );
     546        1270 :     nSize = rIo.aHdr.GetFATSize() * nPageSize;
     547        1270 : }
     548             : 
     549      256275 : bool StgFATStrm::Pos2Page( sal_Int32 nBytePos )
     550             : {
     551             :     // Values < 0 seek to the end
     552      256275 :     if( nBytePos < 0 || nBytePos >= nSize  )
     553          10 :         nBytePos = nSize ? nSize - 1 : 0;
     554      256275 :     nPage   = nBytePos / nPageSize;
     555      256275 :     nOffset = (short) ( nBytePos % nPageSize );
     556      256275 :     nPos    = nBytePos;
     557      256275 :     nPage   = GetPage( (short) nPage, false );
     558      256275 :     return nPage >= 0;
     559             : }
     560             : 
     561             : // Retrieve the physical page for a given byte offset.
     562             : // Since Pos2Page() already has computed the physical offset,
     563             : // use the byte offset directly.
     564             : 
     565           0 : rtl::Reference< StgPage > StgFATStrm::GetPhysPage( sal_Int32 nBytePos, bool bForce )
     566             : {
     567             :     OSL_ENSURE( nBytePos >= 0, "The value may not be negative!" );
     568           0 :     return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce );
     569             : }
     570             : 
     571             : // Get the page number entry for the given page offset.
     572             : 
     573      256650 : sal_Int32 StgFATStrm::GetPage( short nOff, bool bMake, sal_uInt16 *pnMasterAlloc )
     574             : {
     575             :     OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
     576      256650 :     if( pnMasterAlloc ) *pnMasterAlloc = 0;
     577      256650 :     if( nOff < rIo.aHdr.GetFAT1Size() )
     578      256650 :         return rIo.aHdr.GetFATPage( nOff );
     579           0 :     sal_Int32 nMaxPage = nSize >> 2;
     580           0 :     nOff = nOff - rIo.aHdr.GetFAT1Size();
     581             :     // Anzahl der Masterpages, durch die wir iterieren muessen
     582           0 :     sal_uInt16 nMasterCount =  ( nPageSize >> 2 ) - 1;
     583           0 :     sal_uInt16 nBlocks = nOff / nMasterCount;
     584             :     // Offset in letzter Masterpage
     585           0 :     nOff = nOff % nMasterCount;
     586             : 
     587           0 :     rtl::Reference< StgPage > pOldPage;
     588           0 :     rtl::Reference< StgPage > pMaster;
     589           0 :     sal_Int32 nFAT = rIo.aHdr.GetFATChain();
     590           0 :     for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
     591             :     {
     592           0 :         if( nFAT == STG_EOF || nFAT == STG_FREE )
     593             :         {
     594           0 :             if( bMake )
     595             :             {
     596           0 :                 m_aPagesCache.clear();
     597             : 
     598             :                 // create a new master page
     599           0 :                 nFAT = nMaxPage++;
     600           0 :                 pMaster = rIo.Copy( nFAT, STG_FREE );
     601           0 :                 if ( pMaster.is() )
     602             :                 {
     603           0 :                     for( short k = 0; k < ( nPageSize >> 2 ); k++ )
     604           0 :                         rIo.SetToPage( pMaster, k, STG_FREE );
     605             :                     // Verkettung herstellen
     606           0 :                     if( !pOldPage.is() )
     607           0 :                         rIo.aHdr.SetFATChain( nFAT );
     608             :                     else
     609           0 :                         rIo.SetToPage( pOldPage, nMasterCount, nFAT );
     610           0 :                     if( nMaxPage >= rIo.GetPhysPages() )
     611           0 :                         if( !rIo.SetSize( nMaxPage ) )
     612           0 :                             return STG_EOF;
     613             :                     // mark the page as used
     614             :                     // Platz fuer Masterpage schaffen
     615           0 :                     if( !pnMasterAlloc ) // Selbst Platz schaffen
     616             :                     {
     617           0 :                         if( !Pos2Page( nFAT << 2 ) )
     618           0 :                             return STG_EOF;
     619           0 :                         rtl::Reference< StgPage > pPg = rIo.Get( nPage, true );
     620           0 :                         if( !pPg.is() )
     621           0 :                             return STG_EOF;
     622           0 :                         rIo.SetToPage( pPg, nOffset >> 2, STG_MASTER );
     623             :                     }
     624             :                     else
     625           0 :                         (*pnMasterAlloc)++;
     626           0 :                     rIo.aHdr.SetMasters( nCount + 1 );
     627           0 :                     pOldPage = pMaster;
     628             :                 }
     629           0 :             }
     630             :         }
     631             :         else
     632             :         {
     633           0 :             pMaster = rIo.Get( nFAT, true );
     634           0 :             if ( pMaster.is() )
     635             :             {
     636           0 :                 nFAT = rIo.GetFromPage( pMaster, nMasterCount );
     637           0 :                 pOldPage = pMaster;
     638             :             }
     639             :         }
     640             :     }
     641           0 :     if( pMaster.is() )
     642           0 :         return rIo.GetFromPage( pMaster, nOff );
     643           0 :     rIo.SetError( SVSTREAM_GENERALERROR );
     644           0 :     return STG_EOF;
     645             : }
     646             : 
     647             : 
     648             : // Set the page number entry for the given page offset.
     649             : 
     650         375 : bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
     651             : {
     652             :     OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
     653         375 :     m_aPagesCache.clear();
     654             : 
     655         375 :     bool bRes = true;
     656         375 :     if( nOff < rIo.aHdr.GetFAT1Size() )
     657         375 :         rIo.aHdr.SetFATPage( nOff, nNewPage );
     658             :     else
     659             :     {
     660           0 :         nOff = nOff - rIo.aHdr.GetFAT1Size();
     661             :         // Anzahl der Masterpages, durch die wir iterieren muessen
     662           0 :         sal_uInt16 nMasterCount =  ( nPageSize >> 2 ) - 1;
     663           0 :         sal_uInt16 nBlocks = nOff / nMasterCount;
     664             :         // Offset in letzter Masterpage
     665           0 :         nOff = nOff % nMasterCount;
     666             : 
     667           0 :         rtl::Reference< StgPage > pMaster;
     668           0 :         sal_Int32 nFAT = rIo.aHdr.GetFATChain();
     669           0 :         for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
     670             :         {
     671           0 :             if( nFAT == STG_EOF || nFAT == STG_FREE )
     672             :             {
     673           0 :                 pMaster = 0;
     674           0 :                 break;
     675             :             }
     676           0 :             pMaster = rIo.Get( nFAT, true );
     677           0 :             if ( pMaster.is() )
     678           0 :                 nFAT = rIo.GetFromPage( pMaster, nMasterCount );
     679             :         }
     680           0 :         if( pMaster.is() )
     681           0 :             rIo.SetToPage( pMaster, nOff, nNewPage );
     682             :         else
     683             :         {
     684           0 :             rIo.SetError( SVSTREAM_GENERALERROR );
     685           0 :             bRes = false;
     686           0 :         }
     687             :     }
     688             : 
     689             :     // lock the page against access
     690         375 :     if( bRes )
     691             :     {
     692         375 :         Pos2Page( nNewPage << 2 );
     693         375 :         rtl::Reference< StgPage > pPg = rIo.Get( nPage, true );
     694         375 :         if( pPg.is() )
     695         375 :             rIo.SetToPage( pPg, nOffset >> 2, STG_FAT );
     696             :         else
     697           0 :             bRes = false;
     698             :     }
     699         375 :     return bRes;
     700             : }
     701             : 
     702         375 : bool StgFATStrm::SetSize( sal_Int32 nBytes )
     703             : {
     704         375 :     if ( nBytes < 0 )
     705           0 :         return false;
     706             : 
     707         375 :     m_aPagesCache.clear();
     708             : 
     709             :     // Set the number of entries to a multiple of the page size
     710         375 :     short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize );
     711             :     short nNew = (short) (
     712         375 :         ( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ;
     713         375 :     if( nNew < nOld )
     714             :     {
     715             :         // release master pages
     716           0 :         for( short i = nNew; i < nOld; i++ )
     717           0 :             SetPage( i, STG_FREE );
     718             :     }
     719             :     else
     720             :     {
     721        1125 :         while( nOld < nNew )
     722             :         {
     723             :             // allocate master pages
     724             :             // find a free master page slot
     725         375 :             sal_Int32 nPg = 0;
     726         375 :             sal_uInt16 nMasterAlloc = 0;
     727         375 :             nPg = GetPage( nOld, true, &nMasterAlloc );
     728         375 :             if( nPg == STG_EOF )
     729           0 :                 return false;
     730             :             // 4 Bytes have been used for Allocation of each MegaMasterPage
     731         375 :             nBytes += nMasterAlloc << 2;
     732             : 
     733             :             // find a free page using the FAT allocator
     734         375 :             sal_Int32 n = 1;
     735             :             OSL_ENSURE( pFat, "The pointer is always initializer here!" );
     736         375 :             sal_Int32 nNewPage = pFat->FindBlock( n );
     737         375 :             if( nNewPage == STG_EOF )
     738             :             {
     739             :                 // no free pages found; create a new page
     740             :                 // Since all pages are allocated, extend
     741             :                 // the file size for the next page!
     742         375 :                 nNewPage = nSize >> 2;
     743             :                 // if a MegaMasterPage was created avoid taking
     744             :                 // the same Page
     745         375 :                 nNewPage += nMasterAlloc;
     746             :                 // adjust the file size if necessary
     747         375 :                 if( nNewPage >= rIo.GetPhysPages() )
     748         375 :                     if( !rIo.SetSize( nNewPage + 1 ) )
     749           0 :                         return false;
     750             :             }
     751             :             // Set up the page with empty entries
     752         375 :             rtl::Reference< StgPage > pPg = rIo.Copy( nNewPage, STG_FREE );
     753         375 :             if ( !pPg.is() )
     754           0 :                 return false;
     755       48375 :             for( short j = 0; j < ( nPageSize >> 2 ); j++ )
     756       48000 :                 rIo.SetToPage( pPg, j, STG_FREE );
     757             : 
     758             :             // store the page number into the master FAT
     759             :             // Set the size before so the correct FAT can be found
     760         375 :             nSize = ( nOld + 1 ) * nPageSize;
     761         375 :             SetPage( nOld, nNewPage );
     762             : 
     763             :             // MegaMasterPages were created, mark it them as used
     764             : 
     765         375 :             sal_uInt32 nMax = rIo.aHdr.GetMasters( );
     766         375 :             sal_uInt32 nFAT = rIo.aHdr.GetFATChain();
     767         375 :             if( nMasterAlloc )
     768           0 :                 for( sal_uInt16 nCount = 0; nCount < nMax; nCount++ )
     769             :                 {
     770           0 :                     if( !Pos2Page( nFAT << 2 ) )
     771           0 :                         return false;
     772           0 :                     if( nMax - nCount <= nMasterAlloc )
     773             :                     {
     774           0 :                         rtl::Reference< StgPage > piPg = rIo.Get( nPage, true );
     775           0 :                         if( !piPg.is() )
     776           0 :                             return false;
     777           0 :                         rIo.SetToPage( piPg, nOffset >> 2, STG_MASTER );
     778             :                     }
     779           0 :                     rtl::Reference< StgPage > pPage = rIo.Get( nFAT, true );
     780           0 :                     if( !pPage.is() ) return false;
     781           0 :                     nFAT = rIo.GetFromPage( pPage, (nPageSize >> 2 ) - 1 );
     782           0 :                 }
     783             : 
     784         375 :             nOld++;
     785             :             // We have used up 4 bytes for the STG_FAT entry
     786         375 :             nBytes += 4;
     787             :             nNew = (short) (
     788         375 :                 ( nBytes + ( nPageSize - 1 ) ) / nPageSize );
     789         375 :         }
     790             :     }
     791         375 :     nSize = nNew * nPageSize;
     792         375 :     rIo.aHdr.SetFATSize( nNew );
     793         375 :     return true;
     794             : }
     795             : 
     796             : /////////////////////////// class StgDataStrm
     797             : 
     798             : // This class is a normal physical stream which can be initialized
     799             : // either with an existing dir entry or an existing FAT chain.
     800             : // The stream has a size increment which normally is 1, but which can be
     801             : // set to any value is you want the size to be incremented by certain values.
     802             : 
     803        3048 : StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
     804             : {
     805        3048 :     Init( nBgn, nLen );
     806        3048 : }
     807             : 
     808        2870 : StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
     809             : {
     810        2870 :     pEntry = &p;
     811             :     Init( p.aEntry.GetLeaf( STG_DATA ),
     812        2870 :           p.aEntry.GetSize() );
     813        2870 : }
     814             : 
     815        5918 : void StgDataStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
     816             : {
     817        5918 :     if ( rIo.pFAT )
     818        5918 :         pFat = new StgFAT( *rIo.pFAT, true );
     819             : 
     820             :     OSL_ENSURE( pFat, "The pointer should not be empty!" );
     821             : 
     822        5918 :     nStart = nPage = nBgn;
     823        5918 :     nSize  = nLen;
     824        5918 :     nIncr  = 1;
     825        5918 :     nOffset = 0;
     826        5918 :     if( nLen < 0 && pFat )
     827             :     {
     828             :         // determine the actual size of the stream by scanning
     829             :         // the FAT chain and counting the # of pages allocated
     830        2532 :         scanBuildPageChainCache( &nSize );
     831             :     }
     832        5918 : }
     833             : 
     834             : // Set the size of a physical stream.
     835             : 
     836       47152 : bool StgDataStrm::SetSize( sal_Int32 nBytes )
     837             : {
     838       47152 :     if ( !pFat )
     839           0 :         return false;
     840             : 
     841       47152 :     nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr;
     842       47152 :     sal_Int32 nOldSz = nSize;
     843       47152 :     if( ( nOldSz != nBytes ) )
     844             :     {
     845       47152 :         if( !StgStrm::SetSize( nBytes ) )
     846           0 :             return false;
     847       47152 :         sal_Int32 nMaxPage = pFat->GetMaxPage();
     848       47152 :         if( nMaxPage > rIo.GetPhysPages() )
     849        6972 :             if( !rIo.SetSize( nMaxPage ) )
     850           0 :                 return false;
     851             :         // If we only allocated one page or less, create this
     852             :         // page in the cache for faster throughput. The current
     853             :         // position is the former EOF point.
     854       47152 :         if( ( nSize - 1 )  / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 )
     855             :         {
     856        5776 :             Pos2Page( nBytes );
     857        5776 :             if( nPage >= 0 )
     858        5776 :                 rIo.Copy( nPage, STG_FREE );
     859             :         }
     860             :     }
     861       47152 :     return true;
     862             : }
     863             : 
     864             : // Get the address of the data byte at a specified offset.
     865             : // If bForce = true, a read of non-existent data causes
     866             : // a read fault.
     867             : 
     868       16601 : void* StgDataStrm::GetPtr( sal_Int32 Pos, bool bForce, bool bDirty )
     869             : {
     870       16601 :     if( Pos2Page( Pos ) )
     871             :     {
     872       16601 :         rtl::Reference< StgPage > pPg = rIo.Get( nPage, bForce );
     873       16601 :         if (pPg.is() && nOffset < pPg->GetSize())
     874             :         {
     875       16601 :             if( bDirty )
     876        6436 :                 rIo.SetDirty( pPg );
     877       16601 :             return ((sal_uInt8 *)pPg->GetData()) + nOffset;
     878           0 :         }
     879             :     }
     880           0 :     return NULL;
     881             : }
     882             : 
     883             : // This could easily be adapted to a better algorithm by determining
     884             : // the amount of consecutable blocks before doing a read. The result
     885             : // is the number of bytes read. No error is generated on EOF.
     886             : 
     887     2245824 : sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n )
     888             : {
     889     2245824 :     if ( n < 0 )
     890           0 :         return 0;
     891             : 
     892     2245824 :     if( ( nPos + n ) > nSize )
     893       19590 :         n = nSize - nPos;
     894     2245824 :     sal_Int32 nDone = 0;
     895     6855890 :     while( n )
     896             :     {
     897     2364252 :         short nBytes = nPageSize - nOffset;
     898     2364252 :         rtl::Reference< StgPage > pPg;
     899     2364252 :         if( (sal_Int32) nBytes > n )
     900     2213910 :             nBytes = (short) n;
     901     2364252 :         if( nBytes )
     902             :         {
     903             :             short nRes;
     904     2364252 :             void *p = (sal_uInt8 *) pBuf + nDone;
     905     2364252 :             if( nBytes == nPageSize )
     906             :             {
     907      133234 :                 pPg = rIo.Find( nPage );
     908      133234 :                 if( pPg.is() )
     909             :                 {
     910             :                     // data is present, so use the cached data
     911         706 :                     memcpy( p, pPg->GetData(), nBytes );
     912         706 :                     nRes = nBytes;
     913             :                 }
     914             :                 else
     915             :                     // do a direct (unbuffered) read
     916      132528 :                     nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize;
     917             :             }
     918             :             else
     919             :             {
     920             :                 // partial block read thru the cache.
     921     2231018 :                 pPg = rIo.Get( nPage, false );
     922     2231018 :                 if( !pPg.is() )
     923           0 :                     break;
     924     2231018 :                 memcpy( p, (sal_uInt8*)pPg->GetData() + nOffset, nBytes );
     925     2231018 :                 nRes = nBytes;
     926             :             }
     927     2364252 :             nDone += nRes;
     928     2364252 :             nPos += nRes;
     929     2364252 :             n -= nRes;
     930     2364252 :             nOffset = nOffset + nRes;
     931     2364252 :             if( nRes != nBytes )
     932          10 :                 break;  // read error or EOF
     933             :         }
     934             :         // Switch to next page if necessary
     935     2364242 :         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
     936           0 :             break;
     937     2364242 :     }
     938     2245824 :     return nDone;
     939             : }
     940             : 
     941       47714 : sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n )
     942             : {
     943       47714 :     if ( n < 0 )
     944           0 :         return 0;
     945             : 
     946       47714 :     sal_Int32 nDone = 0;
     947       47714 :     if( ( nPos + n ) > nSize )
     948             :     {
     949           0 :         sal_Int32 nOld = nPos;
     950           0 :         if( !SetSize( nPos + n ) )
     951           0 :             return 0;
     952           0 :         Pos2Page( nOld );
     953             :     }
     954      152394 :     while( n )
     955             :     {
     956       56966 :         short nBytes = nPageSize - nOffset;
     957       56966 :         rtl::Reference< StgPage > pPg;
     958       56966 :         if( (sal_Int32) nBytes > n )
     959       41298 :             nBytes = (short) n;
     960       56966 :         if( nBytes )
     961             :         {
     962             :             short nRes;
     963       56966 :             const void *p = (const sal_uInt8 *) pBuf + nDone;
     964       56966 :             if( nBytes == nPageSize )
     965             :             {
     966       10372 :                 pPg = rIo.Find( nPage );
     967       10372 :                 if( pPg.is() )
     968             :                 {
     969             :                     // data is present, so use the cached data
     970           0 :                     memcpy( pPg->GetData(), p, nBytes );
     971           0 :                     rIo.SetDirty( pPg );
     972           0 :                     nRes = nBytes;
     973             :                 }
     974             :                 else
     975             :                     // do a direct (unbuffered) write
     976       10372 :                     nRes = (short) rIo.Write( nPage, (void*) p, 1 ) * nPageSize;
     977             :             }
     978             :             else
     979             :             {
     980             :                 // partial block read thru the cache.
     981       46594 :                 pPg = rIo.Get( nPage, false );
     982       46594 :                 if( !pPg.is() )
     983           0 :                     break;
     984       46594 :                 memcpy( (sal_uInt8*)pPg->GetData() + nOffset, p, nBytes );
     985       46594 :                 rIo.SetDirty( pPg );
     986       46594 :                 nRes = nBytes;
     987             :             }
     988       56966 :             nDone += nRes;
     989       56966 :             nPos += nRes;
     990       56966 :             n -= nRes;
     991       56966 :             nOffset = nOffset + nRes;
     992       56966 :             if( nRes != nBytes )
     993           0 :                 break;  // read error
     994             :         }
     995             :         // Switch to next page if necessary
     996       56966 :         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
     997           0 :             break;
     998       56966 :     }
     999       47714 :     return nDone;
    1000             : }
    1001             : 
    1002             : //////////////////////////// class StgSmallStream
    1003             : 
    1004             : // The small stream class provides access to streams with a size < 4096 bytes.
    1005             : // This stream is a StgStream containing small pages. The FAT for this stream
    1006             : // is also a StgStream. The start of the FAT is in the header at DataRootPage,
    1007             : // the stream itself is pointed to by the root entry (it holds start & size).
    1008             : 
    1009        3410 : StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
    1010             : {
    1011        3410 :     Init( nBgn, nLen );
    1012        3410 : }
    1013             : 
    1014        9398 : StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
    1015             : {
    1016        9398 :     pEntry = &p;
    1017             :     Init( p.aEntry.GetLeaf( STG_DATA ),
    1018        9398 :           p.aEntry.GetSize() );
    1019        9398 : }
    1020             : 
    1021       12808 : void StgSmallStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
    1022             : {
    1023       12808 :     if ( rIo.pDataFAT )
    1024       12808 :         pFat = new StgFAT( *rIo.pDataFAT, false );
    1025       12808 :     pData = rIo.pDataStrm;
    1026             :     OSL_ENSURE( pFat && pData, "The pointers should not be empty!" );
    1027             : 
    1028       12808 :     nPageSize = rIo.GetDataPageSize();
    1029             :     nStart =
    1030       12808 :     nPage  = nBgn;
    1031       12808 :     nSize  = nLen;
    1032       12808 : }
    1033             : 
    1034             : // This could easily be adapted to a better algorithm by determining
    1035             : // the amount of consecutable blocks before doing a read. The result
    1036             : // is the number of bytes read. No error is generated on EOF.
    1037             : 
    1038       31732 : sal_Int32 StgSmallStrm::Read( void* pBuf, sal_Int32 n )
    1039             : {
    1040             :     // We can safely assume that reads are not huge, since the
    1041             :     // small stream is likely to be < 64 KBytes.
    1042       31732 :     if( ( nPos + n ) > nSize )
    1043        5886 :         n = nSize - nPos;
    1044       31732 :     short nDone = 0;
    1045      158018 :     while( n )
    1046             :     {
    1047       94556 :         short nBytes = nPageSize - nOffset;
    1048       94556 :         if( (sal_Int32) nBytes > n )
    1049       28498 :             nBytes = (short) n;
    1050       94556 :         if( nBytes )
    1051             :         {
    1052       94556 :             if( !pData || !pData->Pos2Page( nPage * nPageSize + nOffset ) )
    1053           0 :                 break;
    1054             :             // all reading thru the stream
    1055       94556 :             short nRes = (short) pData->Read( (sal_uInt8*)pBuf + nDone, nBytes );
    1056       94556 :             nDone = nDone + nRes;
    1057       94556 :             nPos += nRes;
    1058       94556 :             n -= nRes;
    1059       94556 :             nOffset = nOffset + nRes;
    1060             :             // read problem?
    1061       94556 :             if( nRes != nBytes )
    1062           2 :                 break;
    1063             :         }
    1064             :         // Switch to next page if necessary
    1065       94554 :         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
    1066           0 :             break;
    1067             :     }
    1068       31732 :     return nDone;
    1069             : }
    1070             : 
    1071        3348 : sal_Int32 StgSmallStrm::Write( const void* pBuf, sal_Int32 n )
    1072             : {
    1073             :     // you can safely assume that reads are not huge, since the
    1074             :     // small stream is likely to be < 64 KBytes.
    1075        3348 :     short nDone = 0;
    1076        3348 :     if( ( nPos + n ) > nSize )
    1077             :     {
    1078           0 :         sal_Int32 nOld = nPos;
    1079           0 :         if( !SetSize( nPos + n ) )
    1080           0 :             return 0;
    1081           0 :         Pos2Page( nOld );
    1082             :     }
    1083       52774 :     while( n )
    1084             :     {
    1085       46078 :         short nBytes = nPageSize - nOffset;
    1086       46078 :         if( (sal_Int32) nBytes > n )
    1087        3270 :             nBytes = (short) n;
    1088       46078 :         if( nBytes )
    1089             :         {
    1090             :             // all writing goes thru the stream
    1091       46078 :             sal_Int32 nDataPos = nPage * nPageSize + nOffset;
    1092       92156 :             if ( !pData
    1093       46078 :               || ( pData->GetSize() < ( nDataPos + nBytes )
    1094       46078 :                 && !pData->SetSize( nDataPos + nBytes ) ) )
    1095           0 :                 break;
    1096       46078 :             if( !pData->Pos2Page( nDataPos ) )
    1097           0 :                 break;
    1098       46078 :             short nRes = (short) pData->Write( (sal_uInt8*)pBuf + nDone, nBytes );
    1099       46078 :             nDone = nDone + nRes;
    1100       46078 :             nPos += nRes;
    1101       46078 :             n -= nRes;
    1102       46078 :             nOffset = nOffset + nRes;
    1103             :             // write problem?
    1104       46078 :             if( nRes != nBytes )
    1105           0 :                 break;
    1106             :         }
    1107             :         // Switch to next page if necessary
    1108       46078 :         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
    1109           0 :             break;
    1110             :     }
    1111        3348 :     return nDone;
    1112             : }
    1113             : 
    1114             : /////////////////////////// class StgTmpStrm
    1115             : 
    1116             : // The temporary stream uses a memory stream if < 32K, otherwise a
    1117             : // temporary file.
    1118             : 
    1119             : #define THRESHOLD 32768L
    1120             : 
    1121        3962 : StgTmpStrm::StgTmpStrm( sal_uLong nInitSize )
    1122             :           : SvMemoryStream( nInitSize > THRESHOLD
    1123             :                               ? 16
    1124        3962 :                             : ( nInitSize ? nInitSize : 16 ), 4096 )
    1125             : {
    1126        3962 :     pStrm = NULL;
    1127             :     // this calls FlushData, so all members should be set by this time
    1128        3962 :     SetBufferSize( 0 );
    1129        3962 :     if( nInitSize > THRESHOLD )
    1130           0 :         SetSize( nInitSize );
    1131        3962 : }
    1132             : 
    1133          36 : bool StgTmpStrm::Copy( StgTmpStrm& rSrc )
    1134             : {
    1135          36 :     sal_uLong n    = rSrc.GetSize();
    1136          36 :     sal_uLong nCur = rSrc.Tell();
    1137          36 :     SetSize( n );
    1138          36 :     if( GetError() == SVSTREAM_OK )
    1139             :     {
    1140          36 :         boost::scoped_array<sal_uInt8> p(new sal_uInt8[ 4096 ]);
    1141          36 :         rSrc.Seek( 0L );
    1142          36 :         Seek( 0L );
    1143         108 :         while( n )
    1144             :         {
    1145          36 :             sal_uLong nn = n;
    1146          36 :             if( nn > 4096 )
    1147           0 :                 nn = 4096;
    1148          36 :             if( rSrc.Read( p.get(), nn ) != nn )
    1149           0 :                 break;
    1150          36 :             if( Write( p.get(), nn ) != nn )
    1151           0 :                 break;
    1152          36 :             n -= nn;
    1153             :         }
    1154          36 :         p.reset();
    1155          36 :         rSrc.Seek( nCur );
    1156          36 :         Seek( nCur );
    1157          36 :         return n == 0;
    1158             :     }
    1159             :     else
    1160           0 :         return false;
    1161             : }
    1162             : 
    1163       11886 : StgTmpStrm::~StgTmpStrm()
    1164             : {
    1165        3962 :     if( pStrm )
    1166             :     {
    1167          20 :         pStrm->Close();
    1168          20 :         osl::File::remove( aName );
    1169          20 :         delete pStrm;
    1170             :     }
    1171        7924 : }
    1172             : 
    1173       18790 : sal_uLong StgTmpStrm::GetSize() const
    1174             : {
    1175             :     sal_uLong n;
    1176       18790 :     if( pStrm )
    1177             :     {
    1178         754 :         sal_uLong old = pStrm->Tell();
    1179         754 :         n = pStrm->Seek( STREAM_SEEK_TO_END );
    1180         754 :         pStrm->Seek( old );
    1181             :     }
    1182             :     else
    1183       18036 :         n = nEndOfData;
    1184       18790 :     return n;
    1185             : }
    1186             : 
    1187        2112 : void StgTmpStrm::SetSize(sal_uInt64 n)
    1188             : {
    1189        2112 :     if( pStrm )
    1190           0 :         pStrm->SetStreamSize( n );
    1191             :     else
    1192             :     {
    1193        2112 :         if( n > THRESHOLD )
    1194             :         {
    1195          20 :             aName = utl::TempFile(0, false).GetURL();
    1196          20 :             SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE );
    1197          20 :             sal_uLong nCur = Tell();
    1198          20 :             sal_uLong i = nEndOfData;
    1199          20 :             if( i )
    1200             :             {
    1201          10 :                 boost::scoped_array<sal_uInt8> p(new sal_uInt8[ 4096 ]);
    1202          10 :                 Seek( 0L );
    1203         100 :                 while( i )
    1204             :                 {
    1205          80 :                     sal_uLong nb = ( i > 4096 ) ? 4096 : i;
    1206         160 :                     if( Read( p.get(), nb ) == nb
    1207          80 :                         && s->Write( p.get(), nb ) == nb )
    1208          80 :                         i -= nb;
    1209             :                     else
    1210           0 :                         break;
    1211          10 :                 }
    1212             :             }
    1213          20 :             if( !i && n > nEndOfData )
    1214             :             {
    1215             :                 // We have to write one byte at the end of the file
    1216             :                 // if the file is bigger than the memstream to see
    1217             :                 // if it fits on disk
    1218          20 :                 s->Seek( n - 1 );
    1219          20 :                 s->Write( &i, 1 );
    1220          20 :                 s->Flush();
    1221          20 :                 if( s->GetError() != SVSTREAM_OK )
    1222           0 :                     i = 1;
    1223             :             }
    1224          20 :             Seek( nCur );
    1225          20 :             s->Seek( nCur );
    1226          20 :             if( i )
    1227             :             {
    1228           0 :                 SetError( s->GetError() );
    1229           0 :                 delete s;
    1230        2112 :                 return;
    1231             :             }
    1232          20 :             pStrm = s;
    1233             :             // Shrink the memory to 16 bytes, which seems to be the minimum
    1234          20 :             ReAllocateMemory( - ( (long) nEndOfData - 16 ) );
    1235             :         }
    1236             :         else
    1237             :         {
    1238        2092 :             if( n > nEndOfData )
    1239             :             {
    1240        1880 :                 SvMemoryStream::SetSize(n);
    1241             :             }
    1242             :             else
    1243         212 :                 nEndOfData = n;
    1244             :         }
    1245             :     }
    1246             : }
    1247             : 
    1248        5100 : sal_uLong StgTmpStrm::GetData( void* pData, sal_uLong n )
    1249             : {
    1250        5100 :     if( pStrm )
    1251             :     {
    1252         256 :         n = pStrm->Read( pData, n );
    1253         256 :         SetError( pStrm->GetError() );
    1254         256 :         return n;
    1255             :     }
    1256             :     else
    1257        4844 :         return SvMemoryStream::GetData( (sal_Char *)pData, n );
    1258             : }
    1259             : 
    1260       21482 : sal_uLong StgTmpStrm::PutData( const void* pData, sal_uLong n )
    1261             : {
    1262       21482 :     sal_uInt32 nCur = Tell();
    1263       21482 :     sal_uInt32 nNew = nCur + n;
    1264       21482 :     if( nNew > THRESHOLD && !pStrm )
    1265             :     {
    1266          10 :         SetSize( nNew );
    1267          10 :         if( GetError() != SVSTREAM_OK )
    1268           0 :             return 0;
    1269             :     }
    1270       21482 :     if( pStrm )
    1271             :     {
    1272         508 :         nNew = pStrm->Write( pData, n );
    1273         508 :         SetError( pStrm->GetError() );
    1274             :     }
    1275             :     else
    1276       20974 :         nNew = SvMemoryStream::PutData( (sal_Char*)pData, n );
    1277       21482 :     return nNew;
    1278             : }
    1279             : 
    1280       54778 : sal_uInt64 StgTmpStrm::SeekPos(sal_uInt64 n)
    1281             : {
    1282             :     // check if a truncated STREAM_SEEK_TO_END was passed
    1283             :     assert(n != SAL_MAX_UINT32);
    1284       54778 :     if( n == STREAM_SEEK_TO_END )
    1285           0 :         n = GetSize();
    1286       54778 :     if( n && n > THRESHOLD && !pStrm )
    1287             :     {
    1288           0 :         SetSize( n );
    1289           0 :         if( GetError() != SVSTREAM_OK )
    1290           0 :             return Tell();
    1291             :         else
    1292           0 :             return n;
    1293             :     }
    1294       54778 :     else if( pStrm )
    1295             :     {
    1296        1646 :         n = pStrm->Seek( n );
    1297        1646 :         SetError( pStrm->GetError() );
    1298        1646 :         return n;
    1299             :     }
    1300             :     else
    1301       53132 :         return SvMemoryStream::SeekPos( n );
    1302             : }
    1303             : 
    1304           0 : void StgTmpStrm::FlushData()
    1305             : {
    1306           0 :     if( pStrm )
    1307             :     {
    1308           0 :         pStrm->Flush();
    1309           0 :         SetError( pStrm->GetError() );
    1310             :     }
    1311             :     else
    1312           0 :         SvMemoryStream::FlushData();
    1313           0 : }
    1314             : 
    1315             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10