// 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.
// 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.
// 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 = m_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 ) > m_nMaxPage )
m_nMaxPage = nBegin + nAlloc; if( !MakeChain( nBegin, nAlloc ) ) return STG_EOF; if( nOrig == STG_EOF )
nOrig = nBegin; else
{ // Patch the chain
rtl::Reference< StgPage > pPg = GetPhysPage( nLast << 2 ); if( !pPg.is() ) return STG_EOF;
m_rStrm.GetIo().SetToPage( pPg, m_nOffset >> 2, nBegin );
}
nLast = nBegin + nAlloc - 1;
nPgs -= nAlloc;
} if( nPgs && !nPasses )
{ // we need new, fresh space, so allocate and retry if( !m_rStrm.SetSize( ( nPages + nPgs ) << 2 ) ) return STG_EOF; if( !m_bPhys && !InitNew( nPages ) ) return0; // FIXME: this was originally "FALSE", whether or not that // makes sense (or should be STG_EOF instead, say?)
nPages = m_rStrm.GetSize() >> 2;
nPasses++;
} else break;
} // now we should have a chain for the complete block if( nBegin == STG_EOF || nPgs )
{
m_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( sal_Int32 nPage1 )
{
sal_Int32 n = ( ( m_rStrm.GetSize() >> 2 ) - nPage1 ) / m_nEntries; if ( n > 0 )
{ while( n-- )
{
rtl::Reference< StgPage > pPg; // Position within the underlying stream // use the Pos2Page() method of the stream
m_rStrm.Pos2Page( nPage1 << 2 ); // Initialize the page
pPg = m_rStrm.GetIo().Copy( m_rStrm.GetPage() ); if ( !pPg.is() ) returnfalse; for( short i = 0; i < m_nEntries; i++ )
m_rStrm.GetIo().SetToPage( pPg, i, STG_FREE );
nPage1++;
}
} returntrue;
}
// Release a chain
bool StgFAT::FreePages( sal_Int32 nStart, bool bAll )
{ while( nStart >= 0 )
{
rtl::Reference< StgPage > pPg = GetPhysPage( nStart << 2 ); if( !pPg.is() ) returnfalse;
nStart = StgCache::GetFromPage( pPg, m_nOffset >> 2 ); // The first released page is either set to EOF or FREE
m_rStrm.GetIo().SetToPage( pPg, m_nOffset >> 2, bAll ? STG_FREE : STG_EOF );
bAll = true;
} returntrue;
}
///////////////////////////// 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.
// Track already scanned PageNumbers here and use them to // see if an already counted page is re-visited while( nBgn >= 0 && !bError )
{
m_aPagesCache.push_back(nBgn);
nBgn = m_pFat->GetNextPage( nBgn );
//returned second is false if it already exists if (!m_aUsedPageNumbers.insert(nBgn).second)
{
SAL_WARN ("sot", "Error: page number " << nBgn << " already in chain for stream");
bError = true;
}
// 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( sal_Int32 nBytePos )
{ if ( !m_pFat ) returnfalse;
// Values < 0 seek to the end if( nBytePos < 0 || nBytePos >= m_nSize )
nBytePos = m_nSize; // Adjust the position back to offset 0
m_nPos -= m_nOffset;
sal_Int32 nMask = ~( m_nPageSize - 1 );
sal_Int32 nOld = m_nPos & nMask;
sal_Int32 nNew = nBytePos & nMask;
m_nOffset = static_cast<short>( nBytePos & ~nMask );
m_nPos = nBytePos; if (nOld == nNew) return m_bBytePosValid;
// See fdo#47644 for a .doc with a vast amount of pages where seeking around the // document takes a colossal amount of time
// Please Note: we build the pagescache incrementally as we go if necessary, // so that a corrupted FAT doesn't poison the stream state for earlier reads
size_t nIdx = nNew / m_nPageSize; if( nIdx >= m_aPagesCache.size() )
{ // Extend the FAT cache ! ...
size_t nToAdd = nIdx + 1;
if (m_aPagesCache.empty())
{
m_aPagesCache.push_back( m_nStart );
assert(m_aUsedPageNumbers.empty());
m_aUsedPageNumbers.insert(m_nStart);
}
nToAdd -= m_aPagesCache.size();
sal_Int32 nBgn = m_aPagesCache.back();
// Start adding pages while we can while (nToAdd > 0 && nBgn >= 0)
{
sal_Int32 nOldBgn = nBgn;
nBgn = m_pFat->GetNextPage(nOldBgn); if( nBgn >= 0 )
{ //returned second is false if it already exists if (!m_aUsedPageNumbers.insert(nBgn).second)
{
SAL_WARN ("sot", "Error: page number " << nBgn << " already in chain for stream"); break;
}
//very much the normal case
m_aPagesCache.push_back(nBgn);
--nToAdd;
}
}
}
if ( nIdx > m_aPagesCache.size() )
{
SAL_WARN("sot", "seek to index " << nIdx << " beyond page cache size " << m_aPagesCache.size()); // fdo#84229 - handle seek to end and back as eg. XclImpStream expects
m_nPage = STG_EOF;
m_nOffset = 0; // Intriguingly in the past we didn't reset nPos to match the real // length of the stream thus: // nIdx = m_aPagesCache.size(); // nPos = nPageSize * nIdx; // so retain this behavior for now.
m_bBytePosValid = false; returnfalse;
}
// special case: seek to 1st byte of new, unallocated page // (in case the file size is a multiple of the page size) if( nBytePos == m_nSize && !m_nOffset && nIdx > 0 && nIdx == m_aPagesCache.size() )
{
nIdx--;
m_nOffset = m_nPageSize;
} elseif ( nIdx == m_aPagesCache.size() )
{
m_nPage = STG_EOF;
m_bBytePosValid = false; returnfalse;
}
// Set the page number entry for the given page offset.
bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
{
OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
m_aPagesCache.clear();
m_aUsedPageNumbers.clear();
bool bRes = true; if( nOff < StgHeader::GetFAT1Size() )
m_rIo.m_aHdr.SetFATPage( nOff, nNewPage ); else
{
nOff = nOff - StgHeader::GetFAT1Size(); // number of master pages that we need to iterate through
sal_uInt16 nMasterCount = ( m_nPageSize >> 2 ) - 1;
sal_uInt16 nBlocks = nOff / nMasterCount; // offset in the last master page
nOff = nOff % nMasterCount;
// Set the number of entries to a multiple of the page size short nOld = static_cast<short>( ( m_nSize + ( m_nPageSize - 1 ) ) / m_nPageSize ); short nNew = static_cast<short>(
( nBytes + ( m_nPageSize - 1 ) ) / m_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, true, &nMasterAlloc ); if( nPg == STG_EOF ) returnfalse; // 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( m_pFat, "The pointer is always initializer here!" );
sal_Int32 nNewPage = m_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 = m_nSize >> 2; // if a MegaMasterPage was created avoid taking // the same Page
nNewPage += nMasterAlloc; // adjust the file size if necessary if( nNewPage >= m_rIo.GetPhysPages() ) if( !m_rIo.SetSize( nNewPage + 1 ) ) returnfalse;
} // Set up the page with empty entries
rtl::Reference< StgPage > pPg = m_rIo.Copy( nNewPage ); if ( !pPg.is() ) returnfalse; for( short j = 0; j < static_cast<short>( m_nPageSize >> 2 ); j++ )
m_rIo.SetToPage( pPg, j, STG_FREE );
// store the page number into the master FAT // Set the size before so the correct FAT can be found
m_nSize = ( nOld + 1 ) * m_nPageSize;
SetPage( nOld, nNewPage );
// MegaMasterPages were created, mark it them as used
nOld++; // We have used up 4 bytes for the STG_FAT entry
nBytes += 4;
nNew = static_cast<short>(
( nBytes + ( m_nPageSize - 1 ) ) / m_nPageSize );
}
}
m_nSize = nNew * m_nPageSize;
m_rIo.m_aHdr.SetFATSize( nNew ); returntrue;
}
/////////////////////////// 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.
// 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 ) return0;
constauto nAvailable = m_nSize - GetPos(); if (n > nAvailable)
n = nAvailable;
sal_Int32 nDone = 0; while( n )
{ short nBytes = m_nPageSize - m_nOffset;
rtl::Reference< StgPage > pPg; if( static_cast<sal_Int32>(nBytes) > n )
nBytes = static_cast<short>(n); if( nBytes )
{ short nRes; void *p = static_cast<sal_uInt8 *>(pBuf) + nDone; if( nBytes == m_nPageSize )
{
pPg = m_rIo.Find( m_nPage ); if( pPg.is() )
{ // data is present, so use the cached data
memcpy( p, pPg->GetData(), nBytes );
nRes = nBytes;
} else // do a direct (unbuffered) read
nRes = static_cast<short>(m_rIo.Read( m_nPage, p )) * m_nPageSize;
} else
{ // partial block read through the cache.
pPg = m_rIo.Get( m_nPage, false ); if( !pPg.is() ) break;
memcpy( p, static_cast<sal_uInt8*>(pPg->GetData()) + m_nOffset, nBytes );
nRes = nBytes;
}
nDone += nRes;
SetPos(GetPos() + nRes, true);
n -= nRes;
m_nOffset = m_nOffset + nRes; if( nRes != nBytes ) break; // read error or EOF
} // Switch to next page if necessary if (m_nOffset >= m_nPageSize && !Pos2Page(GetPos())) break;
} return nDone;
}
sal_Int32 StgDataStrm::Write( constvoid* pBuf, sal_Int32 n )
{ if ( n < 0 ) return0;
sal_Int32 nDone = 0; if( ( GetPos() + n ) > m_nSize )
{
sal_Int32 nOld = GetPos(); if( !SetSize( nOld + n ) ) return0;
Pos2Page( nOld );
} while( n )
{ short nBytes = m_nPageSize - m_nOffset;
rtl::Reference< StgPage > pPg; if( static_cast<sal_Int32>(nBytes) > n )
nBytes = static_cast<short>(n); if( nBytes )
{ short nRes; constvoid *p = static_cast<const sal_uInt8 *>(pBuf) + nDone; if( nBytes == m_nPageSize )
{
pPg = m_rIo.Find( m_nPage ); if( pPg.is() )
{ // data is present, so use the cached data
memcpy( pPg->GetData(), p, nBytes );
m_rIo.SetDirty( pPg );
nRes = nBytes;
} else // do a direct (unbuffered) write
nRes = static_cast<short>(m_rIo.Write( m_nPage, p )) * m_nPageSize;
} else
{ // partial block read through the cache.
pPg = m_rIo.Get( m_nPage, false ); if( !pPg.is() ) break;
memcpy( static_cast<sal_uInt8*>(pPg->GetData()) + m_nOffset, p, nBytes );
m_rIo.SetDirty( pPg );
nRes = nBytes;
}
nDone += nRes;
SetPos(GetPos() + nRes, true);
n -= nRes;
m_nOffset = m_nOffset + nRes; if( nRes != nBytes ) break; // read error
} // Switch to next page if necessary if( m_nOffset >= m_nPageSize && !Pos2Page(GetPos()) ) 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).
// 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.
sal_Int32 nBytePos = GetPos(); if( ( nBytePos + n ) > m_nSize )
n = m_nSize - nBytePos;
sal_Int32 nDone = 0; while( n )
{ short nBytes = m_nPageSize - m_nOffset; if( static_cast<sal_Int32>(nBytes) > n )
nBytes = static_cast<short>(n); if( nBytes )
{ if (!m_pData) break;
sal_Int32 nPos; if (o3tl::checked_multiply<sal_Int32>(m_nPage, m_nPageSize, nPos)) break; if (!m_pData->Pos2Page(nPos + m_nOffset)) break; // all reading through the stream short nRes = static_cast<short>(m_pData->Read( static_cast<sal_uInt8*>(pBuf) + nDone, nBytes ));
nDone += nRes;
SetPos(GetPos() + nRes, true);
n -= nRes;
m_nOffset = m_nOffset + nRes; // read problem? if( nRes != nBytes ) break;
} // Switch to next page if necessary if (m_nOffset >= m_nPageSize && !Pos2Page(GetPos())) break;
} return nDone;
}
sal_Int32 StgSmallStrm::Write( constvoid* pBuf, sal_Int32 n )
{ // you can safely assume that reads are not huge, since the // small stream is likely to be < 64 KBytes.
sal_Int32 nDone = 0;
sal_Int32 nOldPos = GetPos(); if( ( nOldPos + n ) > m_nSize )
{ if (!SetSize(nOldPos + n)) return0;
Pos2Page(nOldPos);
} while( n )
{ short nBytes = m_nPageSize - m_nOffset; if( static_cast<sal_Int32>(nBytes) > n )
nBytes = static_cast<short>(n); if( nBytes )
{ // all writing goes through the stream
sal_Int32 nDataPos = m_nPage * m_nPageSize + m_nOffset; if ( !m_pData
|| ( m_pData->GetSize() < ( nDataPos + nBytes )
&& !m_pData->SetSize( nDataPos + nBytes ) ) ) break; if( !m_pData->Pos2Page( nDataPos ) ) break; short nRes = static_cast<short>(m_pData->Write( static_cast<sal_uInt8 const *>(pBuf) + nDone, nBytes ));
nDone += nRes;
SetPos(GetPos() + nRes, true);
n -= nRes;
m_nOffset = m_nOffset + nRes; // write problem? if( nRes != nBytes ) break;
} // Switch to next page if necessary if( m_nOffset >= m_nPageSize && !Pos2Page(GetPos()) ) break;
} return nDone;
}
/////////////////////////// class StgTmpStrm
// The temporary stream uses a memory stream if < 32K, otherwise a // temporary file.
#define THRESHOLD 32768L
StgTmpStrm::StgTmpStrm( sal_uInt64 nInitSize )
: SvMemoryStream( nInitSize > THRESHOLD
? 16
: ( nInitSize ? nInitSize : 16 ), 4096 )
{
m_pStrm = nullptr; // this calls FlushData, so all members should be set by this time
SetBufferSize( 0 ); if( nInitSize > THRESHOLD )
SetSize( nInitSize );
}
bool StgTmpStrm::Copy( StgTmpStrm& rSrc )
{
sal_uInt64 n = rSrc.GetSize(); const sal_uInt64 nCur = rSrc.Tell();
SetSize( n ); if( GetError() == ERRCODE_NONE )
{
std::unique_ptr<sal_uInt8[]> p(new sal_uInt8[ 4096 ]);
rSrc.Seek( 0 );
Seek( 0 ); while( n )
{ const sal_uInt64 nn = std::min<sal_uInt64>(n, 4096); if (rSrc.ReadBytes( p.get(), nn ) != nn) break; if (WriteBytes( p.get(), nn ) != nn) break;
n -= nn;
}
p.reset();
rSrc.Seek( nCur );
Seek( nCur ); return n == 0;
} else returnfalse;
}
void StgTmpStrm::SetSize(sal_uInt64 n)
{ if( m_pStrm )
m_pStrm->SetStreamSize( n ); else
{ if( n > THRESHOLD )
{
m_aName = utl::CreateTempURL();
std::unique_ptr<SvFileStream> s(new SvFileStream( m_aName, StreamMode::READWRITE )); const sal_uInt64 nCur = Tell();
sal_uInt64 i = nEndOfData;
std::unique_ptr<sal_uInt8[]> p(new sal_uInt8[ 4096 ]); if( i )
{
Seek( 0 ); while( i )
{ const sal_uInt64 nb = std::min<sal_uInt64>(i, 4096); if (ReadBytes(p.get(), nb) == nb
&& s->WriteBytes(p.get(), nb) == nb)
i -= nb; else break;
}
} 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(nEndOfData);
memset(p.get(), 0x00, 4096);
i = n - nEndOfData; while (i)
{ const sal_uInt64 nb = std::min<sal_uInt64>(i, 4096); if (s->WriteBytes(p.get(), nb) == nb)
i -= nb; else break; // error
}
s->Flush(); if( s->GetError() != ERRCODE_NONE )
i = 1;
}
Seek( nCur );
s->Seek( nCur ); if( i )
{
SetError( s->GetError() ); return;
}
m_pStrm = std::move(s); // Shrink the memory to 16 bytes, which seems to be the minimum
ReAllocateMemory( - ( static_cast<tools::Long>(nEndOfData) - 16 ) );
} else
{ if( n > nEndOfData )
{
SvMemoryStream::SetSize(n);
} else
nEndOfData = n;
}
}
}
std::size_t StgTmpStrm::GetData( void* pData, std::size_t n )
{ if( m_pStrm )
{
n = m_pStrm->ReadBytes( pData, n );
SetError( m_pStrm->GetError() ); return n;
} else return SvMemoryStream::GetData( pData, n );
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.13Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.