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

Generated by: LCOV version 1.11