blob: 56c4698daee2bb8990622c92f06d35cd142b51c4 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sot.hxx"
#include <string.h> // memcpy()
#include <osl/file.hxx>
#include <tools/tempfile.hxx>
#include <tools/debug.hxx>
#include <set>
#include "sot/stg.hxx"
#include "stgelem.hxx"
#include "stgcache.hxx"
#include "stgstrms.hxx"
#include "stgdir.hxx"
#include "stgio.hxx"
#define __HUGE
///////////////////////////// class StgFAT ///////////////////////////////
// The FAT class performs FAT operations on an underlying storage stream.
// This stream is either the master FAT stream (m == sal_True ) or a normal
// storage stream, which then holds the FAT for small data allocations.
StgFAT::StgFAT( StgStrm& r, sal_Bool m ) : rStrm( r )
{
bPhys = m;
nPageSize = rStrm.GetIo().GetPhysPageSize();
nEntries = nPageSize >> 2;
nOffset = 0;
nMaxPage = 0;
nLimit = 0;
}
// Retrieve the physical page for a given byte offset.
StgPage* StgFAT::GetPhysPage( sal_Int32 nByteOff )
{
StgPage* pPg = NULL;
// Position within the underlying stream
// use the Pos2Page() method of the stream
if( rStrm.Pos2Page( nByteOff ) )
{
nOffset = rStrm.GetOffset();
sal_Int32 nPhysPage = rStrm.GetPage();
// get the physical page (must be present)
pPg = rStrm.GetIo().Get( nPhysPage, sal_True );
}
return pPg;
}
// Get the follow page for a certain FAT page.
sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg )
{
if( nPg >= 0 )
{
StgPage* pPg = GetPhysPage( nPg << 2 );
nPg = pPg ? pPg->GetPage( nOffset >> 2 ) : STG_EOF;
}
return nPg;
}
// Find the best fit block for the given size. Return
// the starting block and its size or STG_EOF and 0.
// nLastPage is a stopper which tells the current
// underlying stream size. It is treated as a recommendation
// to abort the search to inhibit excessive file growth.
sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs )
{
sal_Int32 nMinStart = STG_EOF, nMinLen = 0;
sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
sal_Int32 nTmpStart = STG_EOF, nTmpLen = 0;
sal_Int32 nPages = rStrm.GetSize() >> 2;
sal_Bool bFound = sal_False;
StgPage* pPg = NULL;
short nEntry = 0;
for( sal_Int32 i = 0; i < nPages; i++, nEntry++ )
{
if( !( nEntry % nEntries ) )
{
// load the next page for that stream
nEntry = 0;
pPg = GetPhysPage( i << 2 );
if( !pPg )
return STG_EOF;
}
sal_Int32 nCur = pPg->GetPage( nEntry );
if( nCur == STG_FREE )
{
// count the size of this area
if( nTmpLen )
nTmpLen++;
else
nTmpStart = i,
nTmpLen = 1;
if( nTmpLen == nPgs
// If we already did find a block, stop when reaching the limit
|| ( bFound && ( nEntry >= nLimit ) ) )
break;
}
else if( nTmpLen )
{
if( nTmpLen > nPgs && nTmpLen < nMaxLen )
// block > requested size
nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = sal_True;
else if( nTmpLen >= nMinLen )
{
// block < requested size
nMinLen = nTmpLen, nMinStart = nTmpStart;
bFound = sal_True;
if( nTmpLen == nPgs )
break;
}
nTmpStart = STG_EOF;
nTmpLen = 0;
}
}
// Determine which block to use.
if( nTmpLen )
{
if( nTmpLen > nPgs && nTmpLen < nMaxLen )
// block > requested size
nMaxLen = nTmpLen, nMaxStart = nTmpStart;
else if( nTmpLen >= nMinLen )
// block < requested size
nMinLen = nTmpLen, nMinStart = nTmpStart;
}
if( nMinStart != STG_EOF && nMaxStart != STG_EOF )
{
// two areas found; return the best fit area
sal_Int32 nMinDiff = nPgs - nMinLen;
sal_Int32 nMaxDiff = nMaxLen - nPgs;
if( nMinDiff > nMaxDiff )
nMinStart = STG_EOF;
}
if( nMinStart != STG_EOF )
{
nPgs = nMinLen; return nMinStart;
}
else
{
return nMaxStart;
}
}
// Set up the consecutive chain for a given block.
sal_Bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs )
{
sal_Int32 nPos = nStart << 2;
StgPage* pPg = GetPhysPage( nPos );
if( !pPg || !nPgs )
return sal_False;
while( --nPgs )
{
if( nOffset >= nPageSize )
{
pPg = GetPhysPage( nPos );
if( !pPg )
return sal_False;
}
pPg->SetPage( nOffset >> 2, ++nStart );
nOffset += 4;
nPos += 4;
}
if( nOffset >= nPageSize )
{
pPg = GetPhysPage( nPos );
if( !pPg )
return sal_False;
}
pPg->SetPage( nOffset >> 2, STG_EOF );
return sal_True;
}
// Allocate a block of data from the given page number on.
// It the page number is != STG_EOF, chain the block.
sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs )
{
sal_Int32 nOrig = nBgn;
sal_Int32 nLast = nBgn;
sal_Int32 nBegin = STG_EOF;
sal_Int32 nAlloc;
sal_Int32 nPages = rStrm.GetSize() >> 2;
short nPasses = 0;
// allow for two passes
while( nPasses < 2 )
{
// try to satisfy the request from the pool of free pages
while( nPgs )
{
nAlloc = nPgs;
nBegin = FindBlock( nAlloc );
// no more blocks left in present alloc chain
if( nBegin == STG_EOF )
break;
if( ( nBegin + nAlloc ) > nMaxPage )
nMaxPage = nBegin + nAlloc;
if( !MakeChain( nBegin, nAlloc ) )
return STG_EOF;
if( nOrig == STG_EOF )
nOrig = nBegin;
else
{
// Patch the chain
StgPage* pPg = GetPhysPage( nLast << 2 );
if( !pPg )
return STG_EOF;
pPg->SetPage( nOffset >> 2, nBegin );
}
nLast = nBegin + nAlloc - 1;
nPgs -= nAlloc;
}
if( nPgs && !nPasses )
{
// we need new, fresh space, so allocate and retry
if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
return STG_EOF;
if( !bPhys && !InitNew( nPages ) )
return sal_False;
nPages = rStrm.GetSize() >> 2;
nPasses++;
}
else
break;
}
// now we should have a chain for the complete block
if( nBegin == STG_EOF || nPgs )
{
rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
return STG_EOF; // bad structure
}
return nOrig;
}
// Initialize newly allocated pages for a standard FAT stream
// It can be assumed that the stream size is always on
// a page boundary
sal_Bool StgFAT::InitNew( sal_Int32 nPage1 )
{
sal_Int32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries;
if ( n > 0 )
{
while( n-- )
{
StgPage* pPg = NULL;
// Position within the underlying stream
// use the Pos2Page() method of the stream
rStrm.Pos2Page( nPage1 << 2 );
// Initialize the page
pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE );
if ( !pPg )
return sal_False;
for( short i = 0; i < nEntries; i++ )
pPg->SetPage( i, STG_FREE );
nPage1++;
}
}
return sal_True;
}
// Release a chain
sal_Bool StgFAT::FreePages( sal_Int32 nStart, sal_Bool bAll )
{
while( nStart >= 0 )
{
StgPage* pPg = GetPhysPage( nStart << 2 );
if( !pPg )
return sal_False;
nStart = pPg->GetPage( nOffset >> 2 );
// The first released page is either set to EOF or FREE
pPg->SetPage( nOffset >> 2, bAll ? STG_FREE : STG_EOF );
bAll = sal_True;
}
return sal_True;
}
///////////////////////////// class StgStrm ////////////////////////////////
// The base stream class provides basic functionality for seeking
// and accessing the data on a physical basis. It uses the built-in
// FAT class for the page allocations.
StgStrm::StgStrm( StgIo& r ) : rIo( r )
{
pFat = NULL;
nStart = nPage = STG_EOF;
nOffset = 0;
pEntry = NULL;
nPos = nSize = 0;
nPageSize = rIo.GetPhysPageSize();
}
StgStrm::~StgStrm()
{
delete pFat;
}
// Attach the stream to the given entry.
void StgStrm::SetEntry( StgDirEntry& r )
{
r.aEntry.SetLeaf( STG_DATA, nStart );
r.aEntry.SetSize( nSize );
pEntry = &r;
r.SetDirty();
}
// Compute page number and offset for the given byte position.
// If the position is behind the size, set the stream right
// behind the EOF.
sal_Bool StgStrm::Pos2Page( sal_Int32 nBytePos )
{
if ( !pFat )
return sal_False;
sal_Int32 nRel, nBgn;
// Values < 0 seek to the end
if( nBytePos < 0 || nBytePos >= nSize )
nBytePos = nSize;
// Adjust the position back to offset 0
nPos -= nOffset;
sal_Int32 nMask = ~( nPageSize - 1 );
sal_Int32 nOld = nPos & nMask;
sal_Int32 nNew = nBytePos & nMask;
nOffset = (short) ( nBytePos & ~nMask );
nPos = nBytePos;
if( nOld == nNew )
return sal_True;
if( nNew > nOld )
{
// the new position is behind the current, so an incremental
// positioning is OK. Set the page relative position
nRel = nNew - nOld;
nBgn = nPage;
}
else
{
// the new position is before the current, so we have to scan
// the entire chain.
nRel = nNew;
nBgn = nStart;
}
// now, traverse the FAT chain.
nRel /= nPageSize;
sal_Int32 nLast = STG_EOF;
while( nRel && nBgn >= 0 )
{
nLast = nBgn;
nBgn = pFat->GetNextPage( nBgn );
nRel--;
}
// special case: seek to 1st byte of new, unallocated page
// (in case the file size is a multiple of the page size)
if( nBytePos == nSize && nBgn == STG_EOF && !nRel && !nOffset )
nBgn = nLast, nOffset = nPageSize;
if( nBgn < 0 && nBgn != STG_EOF )
{
rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
nBgn = STG_EOF;
nOffset = nPageSize;
}
nPage = nBgn;
return sal_Bool( nRel == 0 && nPage >= 0 );
}
// Retrieve the physical page for a given byte offset.
StgPage* StgStrm::GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce )
{
if( !Pos2Page( nBytePos ) )
return NULL;
return rIo.Get( nPage, bForce );
}
// Copy an entire stream. Both streams are allocated in the FAT.
// The target stream is this stream.
sal_Bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes )
{
if ( !pFat )
return sal_False;
sal_Int32 nTo = nStart;
sal_Int32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize;
while( nPgs-- )
{
if( nTo < 0 )
{
rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
return sal_False;
}
rIo.Copy( nTo, nFrom );
if( nFrom >= 0 )
{
nFrom = pFat->GetNextPage( nFrom );
if( nFrom < 0 )
{
rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
return sal_False;
}
}
nTo = pFat->GetNextPage( nTo );
}
return sal_True;
}
sal_Bool StgStrm::SetSize( sal_Int32 nBytes )
{
if ( nBytes < 0 || !pFat )
return sal_False;
// round up to page size
sal_Int32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize;
sal_Int32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
if( nNew > nOld )
{
if( !Pos2Page( nSize ) )
return sal_False;
sal_Int32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize );
if( nBgn == STG_EOF )
return sal_False;
if( nStart == STG_EOF )
nStart = nPage = nBgn;
}
else if( nNew < nOld )
{
sal_Bool bAll = sal_Bool( nBytes == 0 );
if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) )
return sal_False;
if( bAll )
nStart = nPage = STG_EOF;
}
if( pEntry )
{
// change the dir entry?
if( !nSize || !nBytes )
pEntry->aEntry.SetLeaf( STG_DATA, nStart );
pEntry->aEntry.SetSize( nBytes );
pEntry->SetDirty();
}
nSize = nBytes;
pFat->SetLimit( GetPages() );
return sal_True;
}
// Return the # of allocated pages
sal_Int32 StgStrm::GetPages()
{
return ( nSize + nPageSize - 1 ) / nPageSize;
}
//////////////////////////// class StgFATStrm //////////////////////////////
// The FAT stream class provides physical access to the master FAT.
// Since this access is implemented as a StgStrm, we can use the
// FAT allocator.
StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r )
{
pFat = new StgFAT( *this, sal_True );
nSize = rIo.aHdr.GetFATSize() * nPageSize;
}
sal_Bool StgFATStrm::Pos2Page( sal_Int32 nBytePos )
{
// Values < 0 seek to the end
if( nBytePos < 0 || nBytePos >= nSize )
nBytePos = nSize ? nSize - 1 : 0;
nPage = nBytePos / nPageSize;
nOffset = (short) ( nBytePos % nPageSize );
nPos = nBytePos;
nPage = GetPage( (short) nPage, sal_False );
return sal_Bool( nPage >= 0 );
}
// Retrieve the physical page for a given byte offset.
// Since Pos2Page() already has computed the physical offset,
// use the byte offset directly.
StgPage* StgFATStrm::GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce )
{
OSL_ENSURE( nBytePos >= 0, "The value may not be negative!" );
return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce );
}
// Get the page number entry for the given page offset.
sal_Int32 StgFATStrm::GetPage( short nOff, sal_Bool bMake, sal_uInt16 *pnMasterAlloc )
{
OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
if( pnMasterAlloc ) *pnMasterAlloc = 0;
if( nOff < rIo.aHdr.GetFAT1Size() )
return rIo.aHdr.GetFATPage( nOff );
sal_Int32 nMaxPage = nSize >> 2;
nOff = nOff - rIo.aHdr.GetFAT1Size();
// Anzahl der Masterpages, durch die wir iterieren muessen
sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1;
sal_uInt16 nBlocks = nOff / nMasterCount;
// Offset in letzter Masterpage
nOff = nOff % nMasterCount;
StgPage* pOldPage = 0;
StgPage* pMaster = 0;
sal_Int32 nFAT = rIo.aHdr.GetFATChain();
for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
{
if( nFAT == STG_EOF || nFAT == STG_FREE )
{
if( bMake )
{
// create a new master page
nFAT = nMaxPage++;
pMaster = rIo.Copy( nFAT, STG_FREE );
if ( pMaster )
{
for( short k = 0; k < ( nPageSize >> 2 ); k++ )
pMaster->SetPage( k, STG_FREE );
// Verkettung herstellen
if( !pOldPage )
rIo.aHdr.SetFATChain( nFAT );
else
pOldPage->SetPage( nMasterCount, nFAT );
if( nMaxPage >= rIo.GetPhysPages() )
if( !rIo.SetSize( nMaxPage ) )
return STG_EOF;
// mark the page as used
// Platz fuer Masterpage schaffen
if( !pnMasterAlloc ) // Selbst Platz schaffen
{
if( !Pos2Page( nFAT << 2 ) )
return STG_EOF;
StgPage* pPg = rIo.Get( nPage, sal_True );
if( !pPg )
return STG_EOF;
pPg->SetPage( nOffset >> 2, STG_MASTER );
}
else
(*pnMasterAlloc)++;
rIo.aHdr.SetMasters( nCount + 1 );
pOldPage = pMaster;
}
}
}
else
{
pMaster = rIo.Get( nFAT, sal_True );
if ( pMaster )
{
nFAT = pMaster->GetPage( nMasterCount );
pOldPage = pMaster;
}
}
}
if( pMaster )
return pMaster->GetPage( nOff );
rIo.SetError( SVSTREAM_GENERALERROR );
return STG_EOF;
}
// Set the page number entry for the given page offset.
sal_Bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
{
OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
sal_Bool bRes = sal_True;
if( nOff < rIo.aHdr.GetFAT1Size() )
rIo.aHdr.SetFATPage( nOff, nNewPage );
else
{
nOff = nOff - rIo.aHdr.GetFAT1Size();
// Anzahl der Masterpages, durch die wir iterieren muessen
sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1;
sal_uInt16 nBlocks = nOff / nMasterCount;
// Offset in letzter Masterpage
nOff = nOff % nMasterCount;
StgPage* pMaster = 0;
sal_Int32 nFAT = rIo.aHdr.GetFATChain();
for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
{
if( nFAT == STG_EOF || nFAT == STG_FREE )
{
pMaster = 0;
break;
}
pMaster = rIo.Get( nFAT, sal_True );
if ( pMaster )
nFAT = pMaster->GetPage( nMasterCount );
}
if( pMaster )
pMaster->SetPage( nOff, nNewPage );
else
{
rIo.SetError( SVSTREAM_GENERALERROR );
bRes = sal_False;
}
}
// lock the page against access
if( bRes )
{
Pos2Page( nNewPage << 2 );
StgPage* pPg = rIo.Get( nPage, sal_True );
if( pPg )
pPg->SetPage( nOffset >> 2, STG_FAT );
else
bRes = sal_False;
}
return bRes;
}
sal_Bool StgFATStrm::SetSize( sal_Int32 nBytes )
{
if ( nBytes < 0 )
return sal_False;
// Set the number of entries to a multiple of the page size
short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize );
short nNew = (short) (
( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ;
if( nNew < nOld )
{
// release master pages
for( short i = nNew; i < nOld; i++ )
SetPage( i, STG_FREE );
}
else
{
while( nOld < nNew )
{
// allocate master pages
// find a free master page slot
sal_Int32 nPg = 0;
sal_uInt16 nMasterAlloc = 0;
nPg = GetPage( nOld, sal_True, &nMasterAlloc );
if( nPg == STG_EOF )
return sal_False;
// 4 Bytes have been used for Allocation of each MegaMasterPage
nBytes += nMasterAlloc << 2;
// find a free page using the FAT allocator
sal_Int32 n = 1;
OSL_ENSURE( pFat, "The pointer is always initializer here!" );
sal_Int32 nNewPage = pFat->FindBlock( n );
if( nNewPage == STG_EOF )
{
// no free pages found; create a new page
// Since all pages are allocated, extend
// the file size for the next page!
nNewPage = nSize >> 2;
// if a MegaMasterPage was created avoid taking
// the same Page
nNewPage += nMasterAlloc;
// adjust the file size if necessary
if( nNewPage >= rIo.GetPhysPages() )
if( !rIo.SetSize( nNewPage + 1 ) )
return sal_False;
}
// Set up the page with empty entries
StgPage* pPg = rIo.Copy( nNewPage, STG_FREE );
if ( !pPg )
return sal_False;
for( short j = 0; j < ( nPageSize >> 2 ); j++ )
pPg->SetPage( j, STG_FREE );
// store the page number into the master FAT
// Set the size before so the correct FAT can be found
nSize = ( nOld + 1 ) * nPageSize;
SetPage( nOld, nNewPage );
// MegaMasterPages were created, mark it them as used
sal_uInt32 nMax = rIo.aHdr.GetMasters( );
sal_uInt32 nFAT = rIo.aHdr.GetFATChain();
if( nMasterAlloc )
for( sal_uInt16 nCount = 0; nCount < nMax; nCount++ )
{
if( !Pos2Page( nFAT << 2 ) )
return sal_False;
if( nMax - nCount <= nMasterAlloc )
{
StgPage* piPg = rIo.Get( nPage, sal_True );
if( !piPg )
return sal_False;
piPg->SetPage( nOffset >> 2, STG_MASTER );
}
StgPage* pPage = rIo.Get( nFAT, sal_True );
if( !pPage ) return sal_False;
nFAT = pPage->GetPage( (nPageSize >> 2 ) - 1 );
}
nOld++;
// We have used up 4 bytes for the STG_FAT entry
nBytes += 4;
nNew = (short) (
( nBytes + ( nPageSize - 1 ) ) / nPageSize );
}
}
nSize = nNew * nPageSize;
rIo.aHdr.SetFATSize( nNew );
return sal_True;
}
/////////////////////////// class StgDataStrm //////////////////////////////
// This class is a normal physical stream which can be initialized
// either with an existing dir entry or an existing FAT chain.
// The stream has a size increment which normally is 1, but which can be
// set to any value is you want the size to be incremented by certain values.
StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
{
Init( nBgn, nLen );
}
StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
{
pEntry = &p;
Init( p.aEntry.GetLeaf( STG_DATA ),
p.aEntry.GetSize() );
}
void StgDataStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
{
if ( rIo.pFAT )
pFat = new StgFAT( *rIo.pFAT, sal_True );
OSL_ENSURE( pFat, "The pointer should not be empty!" );
nStart = nPage = nBgn;
nSize = nLen;
nIncr = 1;
nOffset = 0;
if( nLen < 0 && pFat )
{
// determine the actual size of the stream by scanning
// the FAT chain and counting the # of pages allocated
nSize = 0;
// there may be files with double page numbers or loops of page
// references. This is not allowed. To be able to track this and
// to exit with an error, track already scanned PageNumbers here
// and use them to see if an already counted page is re-visited
std::set< sal_Int32 > nUsedPageNumbers;
while(nBgn >= 0)
{
if(nUsedPageNumbers.find(nBgn) == nUsedPageNumbers.end())
{
nUsedPageNumbers.insert(nBgn);
nSize += nPageSize;
nBgn = pFat->GetNextPage(nBgn);
}
else
{
rIo.SetError(ERRCODE_IO_WRONGFORMAT);
nBgn = -1;
}
}
}
}
// Set the size of a physical stream.
sal_Bool StgDataStrm::SetSize( sal_Int32 nBytes )
{
if ( !pFat )
return sal_False;
nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr;
sal_Int32 nOldSz = nSize;
if( ( nOldSz != nBytes ) )
{
if( !StgStrm::SetSize( nBytes ) )
return sal_False;
sal_Int32 nMaxPage = pFat->GetMaxPage();
if( nMaxPage > rIo.GetPhysPages() )
if( !rIo.SetSize( nMaxPage ) )
return sal_False;
// If we only allocated one page or less, create this
// page in the cache for faster throughput. The current
// position is the former EOF point.
if( ( nSize - 1 ) / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 )
{
Pos2Page( nBytes );
if( nPage >= 0 )
rIo.Copy( nPage, STG_FREE );
}
}
return sal_True;
}
// Get the address of the data byte at a specified offset.
// If bForce = sal_True, a read of non-existent data causes
// a read fault.
void* StgDataStrm::GetPtr( sal_Int32 Pos, sal_Bool bForce, sal_Bool bDirty )
{
if( Pos2Page( Pos ) )
{
StgPage* pPg = rIo.Get( nPage, bForce );
if( pPg )
{
pPg->SetOwner( pEntry );
if( bDirty )
pPg->SetDirty();
return ((sal_uInt8 *)pPg->GetData()) + nOffset;
}
}
return NULL;
}
// This could easily be adapted to a better algorithm by determining
// the amount of consecutable blocks before doing a read. The result
// is the number of bytes read. No error is generated on EOF.
sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n )
{
if ( n < 0 )
return 0;
if( ( nPos + n ) > nSize )
n = nSize - nPos;
sal_Int32 nDone = 0;
while( n )
{
short nBytes = nPageSize - nOffset;
short nRes;
StgPage* pPg;
if( (sal_Int32) nBytes > n )
nBytes = (short) n;
if( nBytes )
{
void *p = (sal_uInt8 *) pBuf + nDone;
if( nBytes == nPageSize )
{
pPg = rIo.Find( nPage );
if( pPg )
{
// data is present, so use the cached data
pPg->SetOwner( pEntry );
memcpy( p, pPg->GetData(), nBytes );
nRes = nBytes;
}
else
// do a direct (unbuffered) read
nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize;
}
else
{
// partial block read thru the cache.
pPg = rIo.Get( nPage, sal_False );
if( !pPg )
break;
pPg->SetOwner( pEntry );
memcpy( p, (sal_uInt8*)pPg->GetData() + nOffset, nBytes );
nRes = nBytes;
}
nDone += nRes;
nPos += nRes;
n -= nRes;
nOffset = nOffset + nRes;
if( nRes != nBytes )
break; // read error or EOF
}
// Switch to next page if necessary
if( nOffset >= nPageSize && !Pos2Page( nPos ) )
break;
}
return nDone;
}
sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n )
{
if ( n < 0 )
return 0;
sal_Int32 nDone = 0;
if( ( nPos + n ) > nSize )
{
sal_Int32 nOld = nPos;
if( !SetSize( nPos + n ) )
return 0;
Pos2Page( nOld );
}
while( n )
{
short nBytes = nPageSize - nOffset;
short nRes;
StgPage* pPg;
if( (sal_Int32) nBytes > n )
nBytes = (short) n;
if( nBytes )
{
const void *p = (const sal_uInt8 *) pBuf + nDone;
if( nBytes == nPageSize )
{
pPg = rIo.Find( nPage );
if( pPg )
{
// data is present, so use the cached data
pPg->SetOwner( pEntry );
memcpy( pPg->GetData(), p, nBytes );
pPg->SetDirty();
nRes = nBytes;
}
else
// do a direct (unbuffered) write
nRes = (short) rIo.Write( nPage, (void*) p, 1 ) * nPageSize;
}
else
{
// partial block read thru the cache.
pPg = rIo.Get( nPage, sal_False );
if( !pPg )
break;
pPg->SetOwner( pEntry );
memcpy( (sal_uInt8*)pPg->GetData() + nOffset, p, nBytes );
pPg->SetDirty();
nRes = nBytes;
}
nDone += nRes;
nPos += nRes;
n -= nRes;
nOffset = nOffset + nRes;
if( nRes != nBytes )
break; // read error
}
// Switch to next page if necessary
if( nOffset >= nPageSize && !Pos2Page( nPos ) )
break;
}
return nDone;
}
//////////////////////////// class StgSmallStream ///////////////////////////
// The small stream class provides access to streams with a size < 4096 bytes.
// This stream is a StgStream containing small pages. The FAT for this stream
// is also a StgStream. The start of the FAT is in the header at DataRootPage,
// the stream itself is pointed to by the root entry (it holds start & size).
StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
{
Init( nBgn, nLen );
}
StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
{
pEntry = &p;
Init( p.aEntry.GetLeaf( STG_DATA ),
p.aEntry.GetSize() );
}
void StgSmallStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
{
if ( rIo.pDataFAT )
pFat = new StgFAT( *rIo.pDataFAT, sal_False );
pData = rIo.pDataStrm;
OSL_ENSURE( pFat && pData, "The pointers should not be empty!" );
nPageSize = rIo.GetDataPageSize();
nStart =
nPage = nBgn;
nSize = nLen;
}
// This could easily be adapted to a better algorithm by determining
// the amount of consecutable blocks before doing a read. The result
// is the number of bytes read. No error is generated on EOF.
sal_Int32 StgSmallStrm::Read( void* pBuf, sal_Int32 n )
{
// We can safely assume that reads are not huge, since the
// small stream is likely to be < 64 KBytes.
if( ( nPos + n ) > nSize )
n = nSize - nPos;
short nDone = 0;
while( n )
{
short nBytes = nPageSize - nOffset;
if( (sal_Int32) nBytes > n )
nBytes = (short) n;
if( nBytes )
{
if( !pData || !pData->Pos2Page( nPage * nPageSize + nOffset ) )
break;
// all reading thru the stream
short nRes = (short) pData->Read( (sal_uInt8*)pBuf + nDone, nBytes );
nDone = nDone + nRes;
nPos += nRes;
n -= nRes;
nOffset = nOffset + nRes;
// read problem?
if( nRes != nBytes )
break;
}
// Switch to next page if necessary
if( nOffset >= nPageSize && !Pos2Page( nPos ) )
break;
}
return nDone;
}
sal_Int32 StgSmallStrm::Write( const void* pBuf, sal_Int32 n )
{
// you can safely assume that reads are not huge, since the
// small stream is likely to be < 64 KBytes.
short nDone = 0;
if( ( nPos + n ) > nSize )
{
sal_Int32 nOld = nPos;
if( !SetSize( nPos + n ) )
return sal_False;
Pos2Page( nOld );
}
while( n )
{
short nBytes = nPageSize - nOffset;
if( (sal_Int32) nBytes > n )
nBytes = (short) n;
if( nBytes )
{
// all writing goes thru the stream
sal_Int32 nDataPos = nPage * nPageSize + nOffset;
if ( !pData
|| ( pData->GetSize() < ( nDataPos + nBytes )
&& !pData->SetSize( nDataPos + nBytes ) ) )
break;
if( !pData->Pos2Page( nDataPos ) )
break;
short nRes = (short) pData->Write( (sal_uInt8*)pBuf + nDone, nBytes );
nDone = nDone + nRes;
nPos += nRes;
n -= nRes;
nOffset = nOffset + nRes;
// write problem?
if( nRes != nBytes )
break;
}
// Switch to next page if necessary
if( nOffset >= nPageSize && !Pos2Page( nPos ) )
break;
}
return nDone;
}
/////////////////////////// class StgTmpStrm /////////////////////////////
// The temporary stream uses a memory stream if < 32K, otherwise a
// temporary file.
#define THRESHOLD 32768L
StgTmpStrm::StgTmpStrm( sal_uLong nInitSize )
: SvMemoryStream( nInitSize > THRESHOLD
? 16
: ( nInitSize ? nInitSize : 16 ), 4096 )
{
pStrm = NULL;
// this calls FlushData, so all members should be set by this time
SetBufferSize( 0 );
if( nInitSize > THRESHOLD )
SetSize( nInitSize );
}
sal_Bool StgTmpStrm::Copy( StgTmpStrm& rSrc )
{
sal_uLong n = rSrc.GetSize();
sal_uLong nCur = rSrc.Tell();
SetSize( n );
if( GetError() == SVSTREAM_OK )
{
sal_uInt8* p = new sal_uInt8[ 4096 ];
rSrc.Seek( 0L );
Seek( 0L );
while( n )
{
sal_uLong nn = n;
if( nn > 4096 )
nn = 4096;
if( rSrc.Read( p, nn ) != nn )
break;
if( Write( p, nn ) != nn )
break;
n -= nn;
}
delete [] p;
rSrc.Seek( nCur );
Seek( nCur );
return sal_Bool( n == 0 );
}
else
return sal_False;
}
StgTmpStrm::~StgTmpStrm()
{
if( pStrm )
{
pStrm->Close();
osl::File::remove( aName );
delete pStrm;
}
}
sal_uLong StgTmpStrm::GetSize() const
{
sal_uLong n;
if( pStrm )
{
sal_uLong old = pStrm->Tell();
n = pStrm->Seek( STREAM_SEEK_TO_END );
pStrm->Seek( old );
}
else
n = nEndOfData;
return n;
}
void StgTmpStrm::SetSize( sal_uLong n )
{
if( pStrm )
pStrm->SetStreamSize( n );
else
{
if( n > THRESHOLD )
{
aName = TempFile::CreateTempName();
SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE );
sal_uLong nCur = Tell();
sal_uLong i = nEndOfData;
if( i )
{
sal_uInt8* p = new sal_uInt8[ 4096 ];
Seek( 0L );
while( i )
{
sal_uLong nb = ( i > 4096 ) ? 4096 : i;
if( Read( p, nb ) == nb
&& s->Write( p, nb ) == nb )
i -= nb;
else
break;
}
delete [] p;
}
if( !i && n > nEndOfData )
{
// We have to write one byte at the end of the file
// if the file is bigger than the memstream to see
// if it fits on disk
s->Seek( n - 1 );
s->Write( &i, 1 );
s->Flush();
if( s->GetError() != SVSTREAM_OK )
i = 1;
}
Seek( nCur );
s->Seek( nCur );
if( i )
{
SetError( s->GetError() );
delete s;
return;
}
pStrm = s;
// Shrink the memory to 16 bytes, which seems to be the minimum
ReAllocateMemory( - ( (long) nEndOfData - 16 ) );
}
else
{
if( n > nEndOfData )
{
sal_uLong nCur = Tell();
Seek( nEndOfData - 1 );
*this << (sal_uInt8) 0;
Seek( nCur );
}
else
nEndOfData = n;
}
}
}
sal_uLong StgTmpStrm::GetData( void* pData, sal_uLong n )
{
if( pStrm )
{
n = pStrm->Read( pData, n );
SetError( pStrm->GetError() );
return n;
}
else
return SvMemoryStream::GetData( (sal_Char *)pData, n );
}
sal_uLong StgTmpStrm::PutData( const void* pData, sal_uLong n )
{
sal_uInt32 nCur = Tell();
sal_uInt32 nNew = nCur + n;
if( nNew > THRESHOLD && !pStrm )
{
SetSize( nNew );
if( GetError() != SVSTREAM_OK )
return 0;
}
if( pStrm )
{
nNew = pStrm->Write( pData, n );
SetError( pStrm->GetError() );
}
else
nNew = SvMemoryStream::PutData( (sal_Char*)pData, n );
return nNew;
}
sal_uLong StgTmpStrm::SeekPos( sal_uLong n )
{
if( n == STREAM_SEEK_TO_END )
n = GetSize();
if( n && n > THRESHOLD && !pStrm )
{
SetSize( n );
if( GetError() != SVSTREAM_OK )
return Tell();
else
return n;
}
else if( pStrm )
{
n = pStrm->Seek( n );
SetError( pStrm->GetError() );
return n;
}
else
return SvMemoryStream::SeekPos( n );
}
void StgTmpStrm::FlushData()
{
if( pStrm )
{
pStrm->Flush();
SetError( pStrm->GetError() );
}
else
SvMemoryStream::FlushData();
}