/**************************************************************
 * 
 * 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 "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;
		sal_Int32 nOldBgn = -1;
        while( nBgn >= 0 && nBgn != nOldBgn )
        {
			nOldBgn = nBgn;
            nBgn = pFat->GetNextPage( nBgn );
			if( nBgn == nOldBgn )
				rIo.SetError( ERRCODE_IO_WRONGFORMAT );
            nSize += nPageSize;
        }
    }
}

// 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();
}