LCOV - code coverage report
Current view: top level - libreoffice/sot/source/sdstor - stgstrms.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 475 708 67.1 %
Date: 2012-12-27 Functions: 41 46 89.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10