diff options
Diffstat (limited to 'sot/source/sdstor/stgstrms.cxx')
-rw-r--r-- | sot/source/sdstor/stgstrms.cxx | 1268 |
1 files changed, 1268 insertions, 0 deletions
diff --git a/sot/source/sdstor/stgstrms.cxx b/sot/source/sdstor/stgstrms.cxx new file mode 100644 index 000000000000..e295edc92ac6 --- /dev/null +++ b/sot/source/sdstor/stgstrms.cxx @@ -0,0 +1,1268 @@ +/************************************************************************* + * + * $RCSfile: stgstrms.cxx,v $ + * + * $Revision: 1.1.1.1 $ + * + * last change: $Author: hr $ $Date: 2000-09-18 16:56:52 $ + * + * The Contents of this file are made available subject to the terms of + * either of the following licenses + * + * - GNU Lesser General Public License Version 2.1 + * - Sun Industry Standards Source License Version 1.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * + * Sun Industry Standards Source License Version 1.1 + * ================================================= + * The contents of this file are subject to the Sun Industry Standards + * Source License Version 1.1 (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.openoffice.org/license.html. + * + * Software provided under this License is provided on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, + * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. + * See the License for the specific provisions governing your rights and + * obligations concerning the Software. + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +#include <string.h> // memcpy() + +#ifndef _TOOLS_FSYS_HXX +#include <tools/fsys.hxx> +#endif +#include <tools/debug.hxx> + +#include "stg.hxx" +#include "stgelem.hxx" +#include "stgcache.hxx" +#include "stgstrms.hxx" +#include "stgdir.hxx" +#include "stgio.hxx" +#pragma hdrstop + +#if defined(W31) + #include <tools/svwin.h> + #define memcpy hmemcpy + #define __HUGE _huge +#else + #define __HUGE +#endif + +///////////////////////////// class StgFAT /////////////////////////////// + +// The FAT class performs FAT operations on an underlying storage stream. +// This stream is either the master FAT stream (m == TRUE ) or a normal +// storage stream, which then holds the FAT for small data allocations. + +StgFAT::StgFAT( StgStrm& r, 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( INT32 nByteOff ) +{ + StgPage* pPg = NULL; + // Position within the underlying stream + // use the Pos2Page() method of the stream + if( rStrm.Pos2Page( nByteOff ) ) + { + nOffset = rStrm.GetOffset(); + INT32 nPhysPage = rStrm.GetPage(); + // get the physical page (must be present) + pPg = rStrm.GetIo().Get( nPhysPage, TRUE ); + } + return pPg; +} + +// Get the follow page for a certain FAT page. + +INT32 StgFAT::GetNextPage( 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. + +INT32 StgFAT::FindBlock( INT32& nPgs ) +{ + INT32 nMinStart = STG_EOF, nMinLen = 0; + INT32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL; + INT32 nTmpStart = STG_EOF, nTmpLen = 0; + INT32 nPages = rStrm.GetSize() >> 2; + BOOL bFound = FALSE; + StgPage* pPg; + short nEntry = 0; + for( 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; + } + 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 = TRUE; + else if( nTmpLen >= nMinLen ) + { + // block < requested size + nMinLen = nTmpLen, nMinStart = nTmpStart; + bFound = 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 + INT32 nMinDiff = nPgs - nMinLen; + 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. + +BOOL StgFAT::MakeChain( INT32 nStart, INT32 nPgs ) +{ + INT32 nPos = nStart << 2; + StgPage* pPg = GetPhysPage( nPos ); + if( !pPg || !nPgs ) + return FALSE; + while( --nPgs ) + { + if( nOffset >= nPageSize ) + { + pPg = GetPhysPage( nPos ); + if( !pPg ) + return FALSE; + } + pPg->SetPage( nOffset >> 2, ++nStart ); + nOffset += 4; + nPos += 4; + } + if( nOffset >= nPageSize ) + { + pPg = GetPhysPage( nPos ); + if( !pPg ) + return FALSE; + } + pPg->SetPage( nOffset >> 2, STG_EOF ); + return TRUE; +} + +// Allocate a block of data from the given page number on. +// It the page number is != STG_EOF, chain the block. + +INT32 StgFAT::AllocPages( INT32 nBgn, INT32 nPgs ) +{ + INT32 nOrig = nBgn; + INT32 nLast = nBgn; + INT32 nBegin, nAlloc; + 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 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 + +BOOL StgFAT::InitNew( INT32 nPage1 ) +{ + INT32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries; + 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 ); + for( short i = 0; i < nEntries; i++ ) + pPg->SetPage( i, STG_FREE ); + nPage1++; + } + return TRUE; +} + +// Release a chain + +BOOL StgFAT::FreePages( INT32 nStart, BOOL bAll ) +{ + while( nStart >= 0 ) + { + StgPage* pPg = GetPhysPage( nStart << 2 ); + if( !pPg ) + return 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 = TRUE; + } + return 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. + +BOOL StgStrm::Pos2Page( INT32 nBytePos ) +{ + 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; + INT32 nMask = ~( nPageSize - 1 ); + INT32 nOld = nPos & nMask; + INT32 nNew = nBytePos & nMask; + nOffset = (short) ( nBytePos & ~nMask ); + nPos = nBytePos; + if( nOld == nNew ) + return 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; + 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 BOOL( nRel == 0 && nPage >= 0 ); +} + +// Retrieve the physical page for a given byte offset. + +StgPage* StgStrm::GetPhysPage( INT32 nBytePos, 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. + +BOOL StgStrm::Copy( INT32 nFrom, INT32 nBytes ) +{ + INT32 nTo = nStart; + INT32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize; + while( nPgs-- ) + { + if( nTo < 0 ) + { + rIo.SetError( SVSTREAM_FILEFORMAT_ERROR ); + return FALSE; + } + rIo.Copy( nTo, nFrom ); + if( nFrom >= 0 ) + { + nFrom = pFat->GetNextPage( nFrom ); + if( nFrom < 0 ) + { + rIo.SetError( SVSTREAM_FILEFORMAT_ERROR ); + return FALSE; + } + } + nTo = pFat->GetNextPage( nTo ); + } + return TRUE; +} + +BOOL StgStrm::SetSize( INT32 nBytes ) +{ + // round up to page size + INT32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize; + INT32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize; + if( nNew > nOld ) + { + if( !Pos2Page( nSize ) ) + return FALSE; + INT32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize ); + if( nBgn == STG_EOF ) + return FALSE; + if( nStart == STG_EOF ) + nStart = nPage = nBgn; + } + else if( nNew < nOld ) + { + BOOL bAll = BOOL( nBytes == 0 ); + if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) ) + return 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 TRUE; +} + +// Return the # of allocated pages + +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, TRUE ); + nSize = rIo.aHdr.GetFATSize() * nPageSize; +} + +BOOL StgFATStrm::Pos2Page( 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, FALSE ); + return 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( INT32 nBytePos, BOOL bForce ) +{ + return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce ); +} + +// Get the page number entry for the given page offset. + +INT32 StgFATStrm::GetPage( short nOff, BOOL bMake, USHORT *pnMasterAlloc ) +{ + if( pnMasterAlloc ) *pnMasterAlloc = 0; + if( nOff < rIo.aHdr.GetFAT1Size() ) + return rIo.aHdr.GetFATPage( nOff ); + INT32 nMaxPage = nSize >> 2; + nOff -= rIo.aHdr.GetFAT1Size(); + // Anzahl der Masterpages, durch die wir iterieren muessen + USHORT nMasterCount = ( nPageSize >> 2 ) - 1; + USHORT nBlocks = nOff / nMasterCount; + // Offset in letzter Masterpage + nOff = nOff % nMasterCount; + + StgPage* pOldPage = 0; + StgPage* pMaster = 0; + INT32 nFAT = rIo.aHdr.GetFATChain(); + for( USHORT 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 ); + 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, 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, TRUE ); + 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. + +BOOL StgFATStrm::SetPage( short nOff, INT32 nNewPage ) +{ + BOOL bRes = TRUE; + if( nOff < rIo.aHdr.GetFAT1Size() ) + rIo.aHdr.SetFATPage( nOff, nNewPage ); + else + { + nOff -= rIo.aHdr.GetFAT1Size(); + // Anzahl der Masterpages, durch die wir iterieren muessen + USHORT nMasterCount = ( nPageSize >> 2 ) - 1; + USHORT nBlocks = nOff / nMasterCount; + // Offset in letzter Masterpage + nOff = nOff % nMasterCount; + + StgPage* pMaster = 0; + INT32 nFAT = rIo.aHdr.GetFATChain(); + for( USHORT nCount = 0; nCount <= nBlocks; nCount++ ) + { + if( nFAT == STG_EOF || nFAT == STG_FREE ) + { + pMaster = 0; + break; + } + pMaster = rIo.Get( nFAT, TRUE ); + nFAT = pMaster->GetPage( nMasterCount ); + } + if( pMaster ) + pMaster->SetPage( nOff, nNewPage ); + else + { + rIo.SetError( SVSTREAM_GENERALERROR ); + bRes = FALSE; + } + } + + // lock the page against access + if( bRes ) + { + Pos2Page( nNewPage << 2 ); + StgPage* pPg = rIo.Get( nPage, TRUE ); + if( pPg ) + pPg->SetPage( nOffset >> 2, STG_FAT ); + else + bRes = FALSE; + } + return bRes; +} + +BOOL StgFATStrm::SetSize( INT32 nBytes ) +{ + // 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 + INT32 nPg = 0; + USHORT nMasterAlloc = 0; + nPg = GetPage( nOld, TRUE, &nMasterAlloc ); + if( nPg == STG_EOF ) + return FALSE; + // 4 Bytes have been used for Allocation of each MegaMasterPage + nBytes += nMasterAlloc << 2; + + // find a free page using the FAT allocator + INT32 n = 1; + 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 FALSE; + } + // Set up the page with empty entries + StgPage* pPg = rIo.Copy( nNewPage, STG_FREE ); + 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 + + UINT32 nMax = rIo.aHdr.GetMasters( ); + UINT32 nFAT = rIo.aHdr.GetFATChain(); + if( nMasterAlloc ) + for( USHORT nCount = 0; nCount < nMax; nCount++ ) + { + if( !Pos2Page( nFAT << 2 ) ) + return FALSE; + if( nMax - nCount <= nMasterAlloc ) + { + StgPage* pPg = rIo.Get( nPage, TRUE ); + if( !pPg ) + return FALSE; + pPg->SetPage( nOffset >> 2, STG_MASTER ); + } + StgPage* pPage = rIo.Get( nFAT, TRUE ); + if( !pPage ) return 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 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, INT32 nBgn, 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( INT32 nBgn, INT32 nLen ) +{ + pFat = new StgFAT( *rIo.pFAT, TRUE ); + nStart = nPage = nBgn; + nSize = nLen; + nIncr = 1; + nOffset = 0; + if( nLen < 0 ) + { + // determine the actual size of the stream by scanning + // the FAT chain and counting the # of pages allocated + nSize = 0; + 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. + +BOOL StgDataStrm::SetSize( INT32 nBytes ) +{ + nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr; + INT32 nOldSz = nSize; + if( ( nOldSz != nBytes ) ) + { + if( !StgStrm::SetSize( nBytes ) ) + return FALSE; + INT32 nMaxPage = pFat->GetMaxPage(); + if( nMaxPage > rIo.GetPhysPages() ) + if( !rIo.SetSize( nMaxPage ) ) + return 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 TRUE; +} + +// Get the address of the data byte at a specified offset. +// If bForce = TRUE, a read of non-existent data causes +// a read fault. + +void* StgDataStrm::GetPtr( INT32 nPos, BOOL bForce, BOOL bDirty ) +{ + if( Pos2Page( nPos ) ) + { + StgPage* pPg = rIo.Get( nPage, bForce ); + if( pPg ) + { + pPg->SetOwner( pEntry ); + if( bDirty ) + pPg->SetDirty(); + return ((BYTE *)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. + +INT32 StgDataStrm::Read( void* pBuf, INT32 n ) +{ + if( ( nPos + n ) > nSize ) + n = nSize - nPos; + INT32 nDone = 0; + while( n ) + { + short nBytes = nPageSize - nOffset; + short nRes; + StgPage* pPg; + if( (INT32) nBytes > n ) + nBytes = (short) n; + if( nBytes ) + { + void *p = (BYTE *) 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, FALSE ); + if( !pPg ) + break; + pPg->SetOwner( pEntry ); + memcpy( p, (BYTE*)pPg->GetData() + nOffset, nBytes ); + nRes = nBytes; + } + nDone += nRes; + nPos += nRes; + n -= nRes; + nOffset += nRes; + if( nRes != nBytes ) + break; // read error or EOF + } + // Switch to next page if necessary + if( nOffset >= nPageSize && !Pos2Page( nPos ) ) + break; + } + return nDone; +} + +INT32 StgDataStrm::Write( const void* pBuf, INT32 n ) +{ + INT32 nDone = 0; + if( ( nPos + n ) > nSize ) + { + INT32 nOld = nPos; + if( !SetSize( nPos + n ) ) + return FALSE; + Pos2Page( nOld ); + } + while( n ) + { + short nBytes = nPageSize - nOffset; + short nRes; + StgPage* pPg; + if( (INT32) nBytes > n ) + nBytes = (short) n; + if( nBytes ) + { + const void *p = (const BYTE *) 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, FALSE ); + if( !pPg ) + break; + pPg->SetOwner( pEntry ); + memcpy( (BYTE*)pPg->GetData() + nOffset, p, nBytes ); + pPg->SetDirty(); + nRes = nBytes; + } + nDone += nRes; + nPos += nRes; + n -= nRes; + 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, INT32 nBgn, 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( INT32 nBgn, INT32 nLen ) +{ + pFat = new StgFAT( *rIo.pDataFAT, FALSE ); + pData = rIo.pDataStrm; + 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. + +INT32 StgSmallStrm::Read( void* pBuf, 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( (INT32) nBytes > n ) + nBytes = (short) n; + if( nBytes ) + { + if( !pData->Pos2Page( nPage * nPageSize + nOffset ) ) + break; + // all reading thru the stream + short nRes = (short) pData->Read( (BYTE*)pBuf + nDone, nBytes ); + nDone += nRes; + nPos += nRes; + n -= nRes; + nOffset += nRes; + // read problem? + if( nRes != nBytes ) + break; + } + // Switch to next page if necessary + if( nOffset >= nPageSize && !Pos2Page( nPos ) ) + break; + } + return nDone; +} + +INT32 StgSmallStrm::Write( const void* pBuf, 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 ) + { + INT32 nOld = nPos; + if( !SetSize( nPos + n ) ) + return FALSE; + Pos2Page( nOld ); + } + while( n ) + { + short nBytes = nPageSize - nOffset; + if( (INT32) nBytes > n ) + nBytes = (short) n; + if( nBytes ) + { + // all writing goes thru the stream + INT32 nDataPos = nPage * nPageSize + nOffset; + if( pData->GetSize() < ( nDataPos + nBytes ) ) + if( !pData->SetSize( nDataPos + nBytes ) ) + break; + if( !pData->Pos2Page( nDataPos ) ) + break; + short nRes = (short) pData->Write( (BYTE*)pBuf + nDone, nBytes ); + nDone += nRes; + nPos += nRes; + n -= nRes; + 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( 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 ); +} + +BOOL StgTmpStrm::Copy( StgTmpStrm& rSrc ) +{ + ULONG n = rSrc.GetSize(); + ULONG nCur = rSrc.Tell(); + SetSize( n ); + if( GetError() == SVSTREAM_OK ) + { + void* p = new BYTE[ 4096 ]; + rSrc.Seek( 0L ); + Seek( 0L ); + while( n ) + { + 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 BOOL( n == 0 ); + } + else + return FALSE; +} + +StgTmpStrm::~StgTmpStrm() +{ + if( pStrm ) + { + pStrm->Close(); + DirEntry aEntry( aName ); + aEntry.Kill(); + delete pStrm; + } +} + +ULONG StgTmpStrm::GetSize() +{ + ULONG n; + if( pStrm ) + { + ULONG old = pStrm->Tell(); + n = pStrm->Seek( STREAM_SEEK_TO_END ); + pStrm->Seek( old ); + } + else + n = nEndOfData; + return n; +} + +void StgTmpStrm::SetSize( ULONG n ) +{ + if( pStrm ) + pStrm->SetStreamSize( n ); + else + { + if( n > THRESHOLD ) + { + DirEntry aEntry; + aEntry = aEntry.TempName(); + aName = aEntry.GetFull(); + SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE ); + ULONG nCur = Tell(); + ULONG i = nEndOfData; + if( i ) + { + void* p = new BYTE[ 4096 ]; + Seek( 0L ); + while( i ) + { + 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 ) + { + ULONG nCur = Tell(); + Seek( nEndOfData - 1 ); + *this << (BYTE) 0; + Seek( nCur ); + } + else + nEndOfData = n; + } + } +} + +ULONG StgTmpStrm::GetData( void* pData, ULONG n ) +{ + if( pStrm ) + { + n = pStrm->Read( pData, n ); + SetError( pStrm->GetError() ); + return n; + } + else + return SvMemoryStream::GetData( (sal_Char *)pData, n ); +} + +ULONG StgTmpStrm::PutData( const void* pData, ULONG n ) +{ + UINT32 nCur = Tell(); + 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; +} + +ULONG StgTmpStrm::SeekPos( 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(); +} + |