LCOV - code coverage report
Current view: top level - sot/source/sdstor - stgstrms.cxx (source / functions) Hit Total Coverage
Test: commit e02a6cb2c3e2b23b203b422e4e0680877f232636 Lines: 0 709 0.0 %
Date: 2014-04-14 Functions: 0 46 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <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           0 : StgFAT::StgFAT( StgStrm& r, bool m ) : rStrm( r )
      42             : {
      43           0 :     bPhys   = m;
      44           0 :     nPageSize = rStrm.GetIo().GetPhysPageSize();
      45           0 :     nEntries  = nPageSize >> 2;
      46           0 :     nOffset   = 0;
      47           0 :     nMaxPage  = 0;
      48           0 :     nLimit    = 0;
      49           0 : }
      50             : 
      51             : // Retrieve the physical page for a given byte offset.
      52             : 
      53           0 : rtl::Reference< StgPage > StgFAT::GetPhysPage( sal_Int32 nByteOff )
      54             : {
      55           0 :     rtl::Reference< StgPage > pPg;
      56             :     // Position within the underlying stream
      57             :     // use the Pos2Page() method of the stream
      58           0 :     if( rStrm.Pos2Page( nByteOff ) )
      59             :     {
      60           0 :         nOffset = rStrm.GetOffset();
      61           0 :         sal_Int32 nPhysPage = rStrm.GetPage();
      62             :         // get the physical page (must be present)
      63           0 :         pPg = rStrm.GetIo().Get( nPhysPage, true );
      64             :     }
      65           0 :     return pPg;
      66             : }
      67             : 
      68             : // Get the follow page for a certain FAT page.
      69             : 
      70           0 : sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg )
      71             : {
      72           0 :     if( nPg >= 0 )
      73             :     {
      74           0 :       rtl::Reference< StgPage > pPg = GetPhysPage( nPg << 2 );
      75           0 :       nPg = pPg.is() ? rStrm.GetIo().GetFromPage( pPg, nOffset >> 2 ) : STG_EOF;
      76             :     }
      77           0 :     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           0 : sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs )
      87             : {
      88           0 :     sal_Int32 nMinStart = STG_EOF, nMinLen = 0;
      89           0 :     sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
      90           0 :     sal_Int32 nTmpStart = STG_EOF, nTmpLen = 0;
      91           0 :     sal_Int32 nPages    = rStrm.GetSize() >> 2;
      92           0 :     bool bFound     = false;
      93           0 :     rtl::Reference< StgPage > pPg;
      94           0 :     short nEntry = 0;
      95           0 :     for( sal_Int32 i = 0; i < nPages; i++, nEntry++ )
      96             :     {
      97           0 :         if( !( nEntry % nEntries ) )
      98             :         {
      99             :             // load the next page for that stream
     100           0 :             nEntry = 0;
     101           0 :             pPg = GetPhysPage( i << 2 );
     102           0 :             if( !pPg.is() )
     103           0 :                 return STG_EOF;
     104             :         }
     105           0 :         sal_Int32 nCur = rStrm.GetIo().GetFromPage( pPg, nEntry );
     106           0 :         if( nCur == STG_FREE )
     107             :         {
     108             :             // count the size of this area
     109           0 :             if( nTmpLen )
     110           0 :                 nTmpLen++;
     111             :             else
     112             :                 nTmpStart = i,
     113           0 :                 nTmpLen   = 1;
     114           0 :             if( nTmpLen == nPgs
     115             :              // If we already did find a block, stop when reaching the limit
     116           0 :              || ( bFound && ( nEntry >= nLimit ) ) )
     117             :                 break;
     118             :         }
     119           0 :         else if( nTmpLen )
     120             :         {
     121           0 :             if( nTmpLen > nPgs && nTmpLen < nMaxLen )
     122             :                 // block > requested size
     123           0 :                 nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = true;
     124           0 :             else if( nTmpLen >= nMinLen )
     125             :             {
     126             :                 // block < requested size
     127           0 :                 nMinLen = nTmpLen, nMinStart = nTmpStart;
     128           0 :                 bFound = true;
     129           0 :                 if( nTmpLen == nPgs )
     130           0 :                     break;
     131             :             }
     132           0 :             nTmpStart = STG_EOF;
     133           0 :             nTmpLen   = 0;
     134             :         }
     135             :     }
     136             :     // Determine which block to use.
     137           0 :     if( nTmpLen )
     138             :     {
     139           0 :         if( nTmpLen > nPgs  && nTmpLen < nMaxLen )
     140             :             // block > requested size
     141           0 :             nMaxLen = nTmpLen, nMaxStart = nTmpStart;
     142           0 :         else if( nTmpLen >= nMinLen )
     143             :             // block < requested size
     144           0 :             nMinLen = nTmpLen, nMinStart = nTmpStart;
     145             :     }
     146           0 :     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           0 :     if( nMinStart != STG_EOF )
     155             :     {
     156           0 :         nPgs = nMinLen; return nMinStart;
     157             :     }
     158             :     else
     159             :     {
     160           0 :         return nMaxStart;
     161           0 :     }
     162             : }
     163             : 
     164             : // Set up the consecutive chain for a given block.
     165             : 
     166           0 : bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs )
     167             : {
     168           0 :     sal_Int32 nPos = nStart << 2;
     169           0 :     rtl::Reference< StgPage > pPg = GetPhysPage( nPos );
     170           0 :     if( !pPg.is() || !nPgs )
     171           0 :         return false;
     172           0 :     while( --nPgs )
     173             :     {
     174           0 :         if( nOffset >= nPageSize )
     175             :         {
     176           0 :             pPg = GetPhysPage( nPos );
     177           0 :             if( !pPg.is() )
     178           0 :                 return false;
     179             :         }
     180           0 :         rStrm.GetIo().SetToPage( pPg, nOffset >> 2, ++nStart );
     181           0 :         nOffset += 4;
     182           0 :         nPos += 4;
     183             :     }
     184           0 :     if( nOffset >= nPageSize )
     185             :     {
     186           0 :         pPg = GetPhysPage( nPos );
     187           0 :         if( !pPg.is() )
     188           0 :             return false;
     189             :     }
     190           0 :     rStrm.GetIo().SetToPage( pPg, nOffset >> 2, STG_EOF );
     191           0 :     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           0 : sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs )
     198             : {
     199           0 :     sal_Int32 nOrig = nBgn;
     200           0 :     sal_Int32 nLast = nBgn;
     201           0 :     sal_Int32 nBegin = STG_EOF;
     202             :     sal_Int32 nAlloc;
     203           0 :     sal_Int32 nPages = rStrm.GetSize() >> 2;
     204           0 :     short nPasses = 0;
     205             :     // allow for two passes
     206           0 :     while( nPasses < 2 )
     207             :     {
     208             :         // try to satisfy the request from the pool of free pages
     209           0 :         while( nPgs )
     210             :         {
     211           0 :             nAlloc = nPgs;
     212           0 :             nBegin = FindBlock( nAlloc );
     213             :             // no more blocks left in present alloc chain
     214           0 :             if( nBegin == STG_EOF )
     215           0 :                 break;
     216           0 :             if( ( nBegin + nAlloc ) > nMaxPage )
     217           0 :                 nMaxPage = nBegin + nAlloc;
     218           0 :             if( !MakeChain( nBegin, nAlloc ) )
     219           0 :                 return STG_EOF;
     220           0 :             if( nOrig == STG_EOF )
     221           0 :                 nOrig = nBegin;
     222             :             else
     223             :             {
     224             :                 // Patch the chain
     225           0 :                 rtl::Reference< StgPage > pPg = GetPhysPage( nLast << 2 );
     226           0 :                 if( !pPg.is() )
     227           0 :                     return STG_EOF;
     228           0 :                 rStrm.GetIo().SetToPage( pPg, nOffset >> 2, nBegin );
     229             :             }
     230           0 :             nLast = nBegin + nAlloc - 1;
     231           0 :             nPgs -= nAlloc;
     232             :         }
     233           0 :         if( nPgs && !nPasses )
     234             :         {
     235             :             // we need new, fresh space, so allocate and retry
     236           0 :             if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
     237           0 :                 return STG_EOF;
     238           0 :             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           0 :             nPages = rStrm.GetSize() >> 2;
     243           0 :             nPasses++;
     244             :         }
     245             :         else
     246             :             break;
     247             :     }
     248             :     // now we should have a chain for the complete block
     249           0 :     if( nBegin == STG_EOF || nPgs )
     250             :     {
     251           0 :         rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
     252           0 :         return STG_EOF; // bad structure
     253             :     }
     254           0 :     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           0 : bool StgFAT::InitNew( sal_Int32 nPage1 )
     262             : {
     263           0 :     sal_Int32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries;
     264           0 :     if ( n > 0 )
     265             :     {
     266           0 :         while( n-- )
     267             :         {
     268           0 :             rtl::Reference< StgPage > pPg;
     269             :             // Position within the underlying stream
     270             :             // use the Pos2Page() method of the stream
     271           0 :             rStrm.Pos2Page( nPage1 << 2 );
     272             :             // Initialize the page
     273           0 :             pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE );
     274           0 :             if ( !pPg.is() )
     275           0 :                 return false;
     276           0 :             for( short i = 0; i < nEntries; i++ )
     277           0 :                 rStrm.GetIo().SetToPage( pPg, i, STG_FREE );
     278           0 :             nPage1++;
     279           0 :         }
     280             :     }
     281           0 :     return true;
     282             : }
     283             : 
     284             : // Release a chain
     285             : 
     286           0 : bool StgFAT::FreePages( sal_Int32 nStart, bool bAll )
     287             : {
     288           0 :     while( nStart >= 0 )
     289             :     {
     290           0 :         rtl::Reference< StgPage > pPg = GetPhysPage( nStart << 2 );
     291           0 :         if( !pPg.is() )
     292           0 :             return false;
     293           0 :         nStart = rStrm.GetIo().GetFromPage( pPg, nOffset >> 2 );
     294             :         // The first released page is either set to EOF or FREE
     295           0 :         rStrm.GetIo().SetToPage( pPg, nOffset >> 2, bAll ? STG_FREE : STG_EOF );
     296           0 :         bAll = true;
     297           0 :     }
     298           0 :     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           0 : StgStrm::StgStrm( StgIo& r ) : rIo( r )
     308             : {
     309           0 :     pFat    = NULL;
     310           0 :     nStart  = nPage = STG_EOF;
     311           0 :     nOffset = 0;
     312           0 :     pEntry  = NULL;
     313           0 :     nPos = nSize = 0;
     314           0 :     nPageSize = rIo.GetPhysPageSize();
     315           0 : }
     316             : 
     317           0 : StgStrm::~StgStrm()
     318             : {
     319           0 :     delete pFat;
     320           0 : }
     321             : 
     322             : // Attach the stream to the given entry.
     323             : 
     324           0 : void StgStrm::SetEntry( StgDirEntry& r )
     325             : {
     326           0 :     r.aEntry.SetLeaf( STG_DATA, nStart );
     327           0 :     r.aEntry.SetSize( nSize );
     328           0 :     pEntry = &r;
     329           0 :     r.SetDirty();
     330           0 : }
     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           0 : void StgStrm::scanBuildPageChainCache(sal_Int32 *pOptionalCalcSize)
     339             : {
     340           0 :     if (nSize > 0)
     341           0 :         m_aPagesCache.reserve(nSize/nPageSize);
     342             : 
     343           0 :     bool bError = false;
     344           0 :     sal_Int32 nBgn = nStart;
     345           0 :     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           0 :     std::set< sal_Int32 > nUsedPageNumbers;
     350             : 
     351           0 :     while( nBgn >= 0 && !bError )
     352             :     {
     353           0 :         if( nBgn >= 0 )
     354           0 :             m_aPagesCache.push_back(nBgn);
     355           0 :         nBgn = pFat->GetNextPage( nBgn );
     356             : 
     357             :         //returned second is false if it already exists
     358           0 :         if (!nUsedPageNumbers.insert(nBgn).second)
     359           0 :             bError = true;
     360             : 
     361           0 :         nOptSize += nPageSize;
     362             :     }
     363           0 :     if (bError)
     364             :     {
     365           0 :         if (pOptionalCalcSize)
     366           0 :             rIo.SetError( ERRCODE_IO_WRONGFORMAT );
     367           0 :         m_aPagesCache.clear();
     368             :     }
     369           0 :     if (pOptionalCalcSize)
     370           0 :         *pOptionalCalcSize = nOptSize;
     371           0 : }
     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           0 : bool StgStrm::Pos2Page( sal_Int32 nBytePos )
     377             : {
     378           0 :     if ( !pFat )
     379           0 :         return false;
     380             : 
     381             :     // Values < 0 seek to the end
     382           0 :     if( nBytePos < 0 || nBytePos >= nSize )
     383           0 :         nBytePos = nSize;
     384             :     // Adjust the position back to offset 0
     385           0 :     nPos -= nOffset;
     386           0 :     sal_Int32 nMask = ~( nPageSize - 1 );
     387           0 :     sal_Int32 nOld = nPos & nMask;
     388           0 :     sal_Int32 nNew = nBytePos & nMask;
     389           0 :     nOffset = (short) ( nBytePos & ~nMask );
     390           0 :     nPos = nBytePos;
     391           0 :     if( nOld == nNew )
     392           0 :         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           0 :     size_t nIdx = nNew / nPageSize;
     400           0 :     if( nIdx >= m_aPagesCache.size() )
     401             :     {
     402             :         // Extend the FAT cache ! ...
     403           0 :         size_t nToAdd = nIdx + 1;
     404             : 
     405           0 :         if (m_aPagesCache.empty())
     406           0 :             m_aPagesCache.push_back( nStart );
     407             : 
     408           0 :         nToAdd -= m_aPagesCache.size();
     409             : 
     410           0 :         sal_Int32 nBgn = m_aPagesCache.back();
     411             : 
     412             :         // Start adding pages while we can
     413           0 :         while( nToAdd > 0 && nBgn >= 0 )
     414             :         {
     415           0 :             nBgn = pFat->GetNextPage( nBgn );
     416           0 :             if( nBgn >= 0 )
     417             :             {
     418           0 :                 m_aPagesCache.push_back( nBgn );
     419           0 :                 nToAdd--;
     420             :             }
     421             :         }
     422             :     }
     423             : 
     424           0 :     if ( nIdx > m_aPagesCache.size() )
     425             :     {
     426           0 :         rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
     427           0 :         nPage = STG_EOF;
     428           0 :         nOffset = nPageSize;
     429           0 :         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           0 :     if( nBytePos == nSize && !nOffset && nIdx > 0 && nIdx == m_aPagesCache.size() )
     434             :     {
     435           0 :         nIdx--;
     436           0 :         nOffset = nPageSize;
     437             :     }
     438           0 :     else if ( nIdx == m_aPagesCache.size() )
     439             :     {
     440           0 :         nPage = STG_EOF;
     441           0 :         return false;
     442             :     }
     443             : 
     444           0 :     nPage = m_aPagesCache[ nIdx ];
     445             : 
     446           0 :     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           0 : bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes )
     462             : {
     463           0 :     if ( !pFat )
     464           0 :         return false;
     465             : 
     466           0 :     m_aPagesCache.clear();
     467             : 
     468           0 :     sal_Int32 nTo = nStart;
     469           0 :     sal_Int32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize;
     470           0 :     while( nPgs-- )
     471             :     {
     472           0 :         if( nTo < 0 )
     473             :         {
     474           0 :             rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
     475           0 :             return false;
     476             :         }
     477           0 :         rIo.Copy( nTo, nFrom );
     478           0 :         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           0 :         nTo = pFat->GetNextPage( nTo );
     488             :     }
     489           0 :     return true;
     490             : }
     491             : 
     492           0 : bool StgStrm::SetSize( sal_Int32 nBytes )
     493             : {
     494           0 :     if ( nBytes < 0 || !pFat )
     495           0 :         return false;
     496             : 
     497           0 :     m_aPagesCache.clear();
     498             : 
     499             :     // round up to page size
     500           0 :     sal_Int32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize;
     501           0 :     sal_Int32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
     502           0 :     if( nNew > nOld )
     503             :     {
     504           0 :         if( !Pos2Page( nSize ) )
     505           0 :             return false;
     506           0 :         sal_Int32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize );
     507           0 :         if( nBgn == STG_EOF )
     508           0 :             return false;
     509           0 :         if( nStart == STG_EOF )
     510           0 :             nStart = nPage = nBgn;
     511             :     }
     512           0 :     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           0 :     if( pEntry )
     521             :     {
     522             :         // change the dir entry?
     523           0 :         if( !nSize || !nBytes )
     524           0 :             pEntry->aEntry.SetLeaf( STG_DATA, nStart );
     525           0 :         pEntry->aEntry.SetSize( nBytes );
     526           0 :         pEntry->SetDirty();
     527             :     }
     528           0 :     nSize = nBytes;
     529           0 :     pFat->SetLimit( GetPages() );
     530           0 :     return true;
     531             : }
     532             : 
     533             : // Return the # of allocated pages
     534             : 
     535           0 : sal_Int32 StgStrm::GetPages() const
     536             : {
     537           0 :     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           0 : StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r )
     547             : {
     548           0 :     pFat = new StgFAT( *this, true );
     549           0 :     nSize = rIo.aHdr.GetFATSize() * nPageSize;
     550           0 : }
     551             : 
     552           0 : bool StgFATStrm::Pos2Page( sal_Int32 nBytePos )
     553             : {
     554             :     // Values < 0 seek to the end
     555           0 :     if( nBytePos < 0 || nBytePos >= nSize  )
     556           0 :         nBytePos = nSize ? nSize - 1 : 0;
     557           0 :     nPage   = nBytePos / nPageSize;
     558           0 :     nOffset = (short) ( nBytePos % nPageSize );
     559           0 :     nPos    = nBytePos;
     560           0 :     nPage   = GetPage( (short) nPage, false );
     561           0 :     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           0 : sal_Int32 StgFATStrm::GetPage( short nOff, bool bMake, sal_uInt16 *pnMasterAlloc )
     577             : {
     578             :     OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
     579           0 :     if( pnMasterAlloc ) *pnMasterAlloc = 0;
     580           0 :     if( nOff < rIo.aHdr.GetFAT1Size() )
     581           0 :         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           0 : bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
     654             : {
     655             :     OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
     656           0 :     m_aPagesCache.clear();
     657             : 
     658           0 :     bool bRes = true;
     659           0 :     if( nOff < rIo.aHdr.GetFAT1Size() )
     660           0 :         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           0 :     if( bRes )
     694             :     {
     695           0 :         Pos2Page( nNewPage << 2 );
     696           0 :         rtl::Reference< StgPage > pPg = rIo.Get( nPage, true );
     697           0 :         if( pPg.is() )
     698           0 :             rIo.SetToPage( pPg, nOffset >> 2, STG_FAT );
     699             :         else
     700           0 :             bRes = false;
     701             :     }
     702           0 :     return bRes;
     703             : }
     704             : 
     705           0 : bool StgFATStrm::SetSize( sal_Int32 nBytes )
     706             : {
     707           0 :     if ( nBytes < 0 )
     708           0 :         return false;
     709             : 
     710           0 :     m_aPagesCache.clear();
     711             : 
     712             :     // Set the number of entries to a multiple of the page size
     713           0 :     short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize );
     714             :     short nNew = (short) (
     715           0 :         ( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ;
     716           0 :     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           0 :         while( nOld < nNew )
     725             :         {
     726             :             // allocate master pages
     727             :             // find a free master page slot
     728           0 :             sal_Int32 nPg = 0;
     729           0 :             sal_uInt16 nMasterAlloc = 0;
     730           0 :             nPg = GetPage( nOld, true, &nMasterAlloc );
     731           0 :             if( nPg == STG_EOF )
     732           0 :                 return false;
     733             :             // 4 Bytes have been used for Allocation of each MegaMasterPage
     734           0 :             nBytes += nMasterAlloc << 2;
     735             : 
     736             :             // find a free page using the FAT allocator
     737           0 :             sal_Int32 n = 1;
     738             :             OSL_ENSURE( pFat, "The pointer is always initializer here!" );
     739           0 :             sal_Int32 nNewPage = pFat->FindBlock( n );
     740           0 :             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           0 :                 nNewPage = nSize >> 2;
     746             :                 // if a MegaMasterPage was created avoid taking
     747             :                 // the same Page
     748           0 :                 nNewPage += nMasterAlloc;
     749             :                 // adjust the file size if necessary
     750           0 :                 if( nNewPage >= rIo.GetPhysPages() )
     751           0 :                     if( !rIo.SetSize( nNewPage + 1 ) )
     752           0 :                         return false;
     753             :             }
     754             :             // Set up the page with empty entries
     755           0 :             rtl::Reference< StgPage > pPg = rIo.Copy( nNewPage, STG_FREE );
     756           0 :             if ( !pPg.is() )
     757           0 :                 return false;
     758           0 :             for( short j = 0; j < ( nPageSize >> 2 ); j++ )
     759           0 :                 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           0 :             nSize = ( nOld + 1 ) * nPageSize;
     764           0 :             SetPage( nOld, nNewPage );
     765             : 
     766             :             // MegaMasterPages were created, mark it them as used
     767             : 
     768           0 :             sal_uInt32 nMax = rIo.aHdr.GetMasters( );
     769           0 :             sal_uInt32 nFAT = rIo.aHdr.GetFATChain();
     770           0 :             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           0 :             nOld++;
     788             :             // We have used up 4 bytes for the STG_FAT entry
     789           0 :             nBytes += 4;
     790             :             nNew = (short) (
     791           0 :                 ( nBytes + ( nPageSize - 1 ) ) / nPageSize );
     792           0 :         }
     793             :     }
     794           0 :     nSize = nNew * nPageSize;
     795           0 :     rIo.aHdr.SetFATSize( nNew );
     796           0 :     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           0 : StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
     807             : {
     808           0 :     Init( nBgn, nLen );
     809           0 : }
     810             : 
     811           0 : StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
     812             : {
     813           0 :     pEntry = &p;
     814             :     Init( p.aEntry.GetLeaf( STG_DATA ),
     815           0 :           p.aEntry.GetSize() );
     816           0 : }
     817             : 
     818           0 : void StgDataStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
     819             : {
     820           0 :     if ( rIo.pFAT )
     821           0 :         pFat = new StgFAT( *rIo.pFAT, true );
     822             : 
     823             :     OSL_ENSURE( pFat, "The pointer should not be empty!" );
     824             : 
     825           0 :     nStart = nPage = nBgn;
     826           0 :     nSize  = nLen;
     827           0 :     nIncr  = 1;
     828           0 :     nOffset = 0;
     829           0 :     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           0 :         scanBuildPageChainCache( &nSize );
     834             :     }
     835           0 : }
     836             : 
     837             : // Set the size of a physical stream.
     838             : 
     839           0 : bool StgDataStrm::SetSize( sal_Int32 nBytes )
     840             : {
     841           0 :     if ( !pFat )
     842           0 :         return false;
     843             : 
     844           0 :     nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr;
     845           0 :     sal_Int32 nOldSz = nSize;
     846           0 :     if( ( nOldSz != nBytes ) )
     847             :     {
     848           0 :         if( !StgStrm::SetSize( nBytes ) )
     849           0 :             return false;
     850           0 :         sal_Int32 nMaxPage = pFat->GetMaxPage();
     851           0 :         if( nMaxPage > rIo.GetPhysPages() )
     852           0 :             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           0 :         if( ( nSize - 1 )  / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 )
     858             :         {
     859           0 :             Pos2Page( nBytes );
     860           0 :             if( nPage >= 0 )
     861           0 :                 rIo.Copy( nPage, STG_FREE );
     862             :         }
     863             :     }
     864           0 :     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           0 : void* StgDataStrm::GetPtr( sal_Int32 Pos, bool bForce, bool bDirty )
     872             : {
     873           0 :     if( Pos2Page( Pos ) )
     874             :     {
     875           0 :         rtl::Reference< StgPage > pPg = rIo.Get( nPage, bForce );
     876           0 :         if (pPg.is() && nOffset < pPg->GetSize())
     877             :         {
     878           0 :             if( bDirty )
     879           0 :                 rIo.SetDirty( pPg );
     880           0 :             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           0 : sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n )
     891             : {
     892           0 :     if ( n < 0 )
     893           0 :         return 0;
     894             : 
     895           0 :     if( ( nPos + n ) > nSize )
     896           0 :         n = nSize - nPos;
     897           0 :     sal_Int32 nDone = 0;
     898           0 :     while( n )
     899             :     {
     900           0 :         short nBytes = nPageSize - nOffset;
     901           0 :         rtl::Reference< StgPage > pPg;
     902           0 :         if( (sal_Int32) nBytes > n )
     903           0 :             nBytes = (short) n;
     904           0 :         if( nBytes )
     905             :         {
     906             :             short nRes;
     907           0 :             void *p = (sal_uInt8 *) pBuf + nDone;
     908           0 :             if( nBytes == nPageSize )
     909             :             {
     910           0 :                 pPg = rIo.Find( nPage );
     911           0 :                 if( pPg.is() )
     912             :                 {
     913             :                     // data is present, so use the cached data
     914           0 :                     memcpy( p, pPg->GetData(), nBytes );
     915           0 :                     nRes = nBytes;
     916             :                 }
     917             :                 else
     918             :                     // do a direct (unbuffered) read
     919           0 :                     nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize;
     920             :             }
     921             :             else
     922             :             {
     923             :                 // partial block read thru the cache.
     924           0 :                 pPg = rIo.Get( nPage, false );
     925           0 :                 if( !pPg.is() )
     926           0 :                     break;
     927           0 :                 memcpy( p, (sal_uInt8*)pPg->GetData() + nOffset, nBytes );
     928           0 :                 nRes = nBytes;
     929             :             }
     930           0 :             nDone += nRes;
     931           0 :             nPos += nRes;
     932           0 :             n -= nRes;
     933           0 :             nOffset = nOffset + nRes;
     934           0 :             if( nRes != nBytes )
     935           0 :                 break;  // read error or EOF
     936             :         }
     937             :         // Switch to next page if necessary
     938           0 :         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
     939           0 :             break;
     940           0 :     }
     941           0 :     return nDone;
     942             : }
     943             : 
     944           0 : sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n )
     945             : {
     946           0 :     if ( n < 0 )
     947           0 :         return 0;
     948             : 
     949           0 :     sal_Int32 nDone = 0;
     950           0 :     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           0 :     while( n )
     958             :     {
     959           0 :         short nBytes = nPageSize - nOffset;
     960           0 :         rtl::Reference< StgPage > pPg;
     961           0 :         if( (sal_Int32) nBytes > n )
     962           0 :             nBytes = (short) n;
     963           0 :         if( nBytes )
     964             :         {
     965             :             short nRes;
     966           0 :             const void *p = (const sal_uInt8 *) pBuf + nDone;
     967           0 :             if( nBytes == nPageSize )
     968             :             {
     969           0 :                 pPg = rIo.Find( nPage );
     970           0 :                 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           0 :                     nRes = (short) rIo.Write( nPage, (void*) p, 1 ) * nPageSize;
     980             :             }
     981             :             else
     982             :             {
     983             :                 // partial block read thru the cache.
     984           0 :                 pPg = rIo.Get( nPage, false );
     985           0 :                 if( !pPg.is() )
     986           0 :                     break;
     987           0 :                 memcpy( (sal_uInt8*)pPg->GetData() + nOffset, p, nBytes );
     988           0 :                 rIo.SetDirty( pPg );
     989           0 :                 nRes = nBytes;
     990             :             }
     991           0 :             nDone += nRes;
     992           0 :             nPos += nRes;
     993           0 :             n -= nRes;
     994           0 :             nOffset = nOffset + nRes;
     995           0 :             if( nRes != nBytes )
     996           0 :                 break;  // read error
     997             :         }
     998             :         // Switch to next page if necessary
     999           0 :         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
    1000           0 :             break;
    1001           0 :     }
    1002           0 :     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           0 : StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
    1013             : {
    1014           0 :     Init( nBgn, nLen );
    1015           0 : }
    1016             : 
    1017           0 : StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
    1018             : {
    1019           0 :     pEntry = &p;
    1020             :     Init( p.aEntry.GetLeaf( STG_DATA ),
    1021           0 :           p.aEntry.GetSize() );
    1022           0 : }
    1023             : 
    1024           0 : void StgSmallStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
    1025             : {
    1026           0 :     if ( rIo.pDataFAT )
    1027           0 :         pFat = new StgFAT( *rIo.pDataFAT, false );
    1028           0 :     pData = rIo.pDataStrm;
    1029             :     OSL_ENSURE( pFat && pData, "The pointers should not be empty!" );
    1030             : 
    1031           0 :     nPageSize = rIo.GetDataPageSize();
    1032             :     nStart =
    1033           0 :     nPage  = nBgn;
    1034           0 :     nSize  = nLen;
    1035           0 : }
    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           0 : 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           0 :     if( ( nPos + n ) > nSize )
    1046           0 :         n = nSize - nPos;
    1047           0 :     short nDone = 0;
    1048           0 :     while( n )
    1049             :     {
    1050           0 :         short nBytes = nPageSize - nOffset;
    1051           0 :         if( (sal_Int32) nBytes > n )
    1052           0 :             nBytes = (short) n;
    1053           0 :         if( nBytes )
    1054             :         {
    1055           0 :             if( !pData || !pData->Pos2Page( nPage * nPageSize + nOffset ) )
    1056           0 :                 break;
    1057             :             // all reading thru the stream
    1058           0 :             short nRes = (short) pData->Read( (sal_uInt8*)pBuf + nDone, nBytes );
    1059           0 :             nDone = nDone + nRes;
    1060           0 :             nPos += nRes;
    1061           0 :             n -= nRes;
    1062           0 :             nOffset = nOffset + nRes;
    1063             :             // read problem?
    1064           0 :             if( nRes != nBytes )
    1065           0 :                 break;
    1066             :         }
    1067             :         // Switch to next page if necessary
    1068           0 :         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
    1069           0 :             break;
    1070             :     }
    1071           0 :     return nDone;
    1072             : }
    1073             : 
    1074           0 : 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           0 :     short nDone = 0;
    1079           0 :     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           0 :     while( n )
    1087             :     {
    1088           0 :         short nBytes = nPageSize - nOffset;
    1089           0 :         if( (sal_Int32) nBytes > n )
    1090           0 :             nBytes = (short) n;
    1091           0 :         if( nBytes )
    1092             :         {
    1093             :             // all writing goes thru the stream
    1094           0 :             sal_Int32 nDataPos = nPage * nPageSize + nOffset;
    1095           0 :             if ( !pData
    1096           0 :               || ( pData->GetSize() < ( nDataPos + nBytes )
    1097           0 :                 && !pData->SetSize( nDataPos + nBytes ) ) )
    1098           0 :                 break;
    1099           0 :             if( !pData->Pos2Page( nDataPos ) )
    1100           0 :                 break;
    1101           0 :             short nRes = (short) pData->Write( (sal_uInt8*)pBuf + nDone, nBytes );
    1102           0 :             nDone = nDone + nRes;
    1103           0 :             nPos += nRes;
    1104           0 :             n -= nRes;
    1105           0 :             nOffset = nOffset + nRes;
    1106             :             // write problem?
    1107           0 :             if( nRes != nBytes )
    1108           0 :                 break;
    1109             :         }
    1110             :         // Switch to next page if necessary
    1111           0 :         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
    1112           0 :             break;
    1113             :     }
    1114           0 :     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           0 : StgTmpStrm::StgTmpStrm( sal_uLong nInitSize )
    1125             :           : SvMemoryStream( nInitSize > THRESHOLD
    1126             :                               ? 16
    1127           0 :                             : ( nInitSize ? nInitSize : 16 ), 4096 )
    1128             : {
    1129           0 :     pStrm = NULL;
    1130             :     // this calls FlushData, so all members should be set by this time
    1131           0 :     SetBufferSize( 0 );
    1132           0 :     if( nInitSize > THRESHOLD )
    1133           0 :         SetSize( nInitSize );
    1134           0 : }
    1135             : 
    1136           0 : bool StgTmpStrm::Copy( StgTmpStrm& rSrc )
    1137             : {
    1138           0 :     sal_uLong n    = rSrc.GetSize();
    1139           0 :     sal_uLong nCur = rSrc.Tell();
    1140           0 :     SetSize( n );
    1141           0 :     if( GetError() == SVSTREAM_OK )
    1142             :     {
    1143           0 :         sal_uInt8* p = new sal_uInt8[ 4096 ];
    1144           0 :         rSrc.Seek( 0L );
    1145           0 :         Seek( 0L );
    1146           0 :         while( n )
    1147             :         {
    1148           0 :             sal_uLong nn = n;
    1149           0 :             if( nn > 4096 )
    1150           0 :                 nn = 4096;
    1151           0 :             if( rSrc.Read( p, nn ) != nn )
    1152           0 :                 break;
    1153           0 :             if( Write( p, nn ) != nn )
    1154           0 :                 break;
    1155           0 :             n -= nn;
    1156             :         }
    1157           0 :         delete [] p;
    1158           0 :         rSrc.Seek( nCur );
    1159           0 :         Seek( nCur );
    1160           0 :         return n == 0;
    1161             :     }
    1162             :     else
    1163           0 :         return false;
    1164             : }
    1165             : 
    1166           0 : StgTmpStrm::~StgTmpStrm()
    1167             : {
    1168           0 :     if( pStrm )
    1169             :     {
    1170           0 :         pStrm->Close();
    1171           0 :         osl::File::remove( aName );
    1172           0 :         delete pStrm;
    1173             :     }
    1174           0 : }
    1175             : 
    1176           0 : sal_uLong StgTmpStrm::GetSize() const
    1177             : {
    1178             :     sal_uLong n;
    1179           0 :     if( pStrm )
    1180             :     {
    1181           0 :         sal_uLong old = pStrm->Tell();
    1182           0 :         n = pStrm->Seek( STREAM_SEEK_TO_END );
    1183           0 :         pStrm->Seek( old );
    1184             :     }
    1185             :     else
    1186           0 :         n = nEndOfData;
    1187           0 :     return n;
    1188             : }
    1189             : 
    1190           0 : void StgTmpStrm::SetSize(sal_uInt64 n)
    1191             : {
    1192           0 :     if( pStrm )
    1193           0 :         pStrm->SetStreamSize( n );
    1194             :     else
    1195             :     {
    1196           0 :         if( n > THRESHOLD )
    1197             :         {
    1198           0 :             aName = utl::TempFile::CreateTempName();
    1199           0 :             SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE );
    1200           0 :             sal_uLong nCur = Tell();
    1201           0 :             sal_uLong i = nEndOfData;
    1202           0 :             if( i )
    1203             :             {
    1204           0 :                 sal_uInt8* p = new sal_uInt8[ 4096 ];
    1205           0 :                 Seek( 0L );
    1206           0 :                 while( i )
    1207             :                 {
    1208           0 :                     sal_uLong nb = ( i > 4096 ) ? 4096 : i;
    1209           0 :                     if( Read( p, nb ) == nb
    1210           0 :                      && s->Write( p, nb ) == nb )
    1211           0 :                         i -= nb;
    1212             :                     else
    1213           0 :                         break;
    1214             :                 }
    1215           0 :                 delete [] p;
    1216             :             }
    1217           0 :             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           0 :                 s->Seek( n - 1 );
    1223           0 :                 s->Write( &i, 1 );
    1224           0 :                 s->Flush();
    1225           0 :                 if( s->GetError() != SVSTREAM_OK )
    1226           0 :                     i = 1;
    1227             :             }
    1228           0 :             Seek( nCur );
    1229           0 :             s->Seek( nCur );
    1230           0 :             if( i )
    1231             :             {
    1232           0 :                 SetError( s->GetError() );
    1233           0 :                 delete s;
    1234           0 :                 return;
    1235             :             }
    1236           0 :             pStrm = s;
    1237             :             // Shrink the memory to 16 bytes, which seems to be the minimum
    1238           0 :             ReAllocateMemory( - ( (long) nEndOfData - 16 ) );
    1239             :         }
    1240             :         else
    1241             :         {
    1242           0 :             if( n > nEndOfData )
    1243             :             {
    1244           0 :                 SvMemoryStream::SetSize(n);
    1245             :             }
    1246             :             else
    1247           0 :                 nEndOfData = n;
    1248             :         }
    1249             :     }
    1250             : }
    1251             : 
    1252           0 : sal_uLong StgTmpStrm::GetData( void* pData, sal_uLong n )
    1253             : {
    1254           0 :     if( pStrm )
    1255             :     {
    1256           0 :         n = pStrm->Read( pData, n );
    1257           0 :         SetError( pStrm->GetError() );
    1258           0 :         return n;
    1259             :     }
    1260             :     else
    1261           0 :         return SvMemoryStream::GetData( (sal_Char *)pData, n );
    1262             : }
    1263             : 
    1264           0 : sal_uLong StgTmpStrm::PutData( const void* pData, sal_uLong n )
    1265             : {
    1266           0 :     sal_uInt32 nCur = Tell();
    1267           0 :     sal_uInt32 nNew = nCur + n;
    1268           0 :     if( nNew > THRESHOLD && !pStrm )
    1269             :     {
    1270           0 :         SetSize( nNew );
    1271           0 :         if( GetError() != SVSTREAM_OK )
    1272           0 :             return 0;
    1273             :     }
    1274           0 :     if( pStrm )
    1275             :     {
    1276           0 :         nNew = pStrm->Write( pData, n );
    1277           0 :         SetError( pStrm->GetError() );
    1278             :     }
    1279             :     else
    1280           0 :         nNew = SvMemoryStream::PutData( (sal_Char*)pData, n );
    1281           0 :     return nNew;
    1282             : }
    1283             : 
    1284           0 : 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           0 :     if( n == STREAM_SEEK_TO_END )
    1289           0 :         n = GetSize();
    1290           0 :     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           0 :     else if( pStrm )
    1299             :     {
    1300           0 :         n = pStrm->Seek( n );
    1301           0 :         SetError( pStrm->GetError() );
    1302           0 :         return n;
    1303             :     }
    1304             :     else
    1305           0 :         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