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

Generated by: LCOV version 1.10