bump product version to 5.0.4.1
[LibreOffice.git] / sot / source / sdstor / stgstrms.cxx
blobcd99b76f73881119477aa76649752ccdb69c5957
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <algorithm>
22 #include <string.h>
23 #include <sal/log.hxx>
24 #include <osl/file.hxx>
25 #include <unotools/tempfile.hxx>
26 #include <set>
28 #include "sot/stg.hxx"
29 #include "stgelem.hxx"
30 #include "stgcache.hxx"
31 #include "stgstrms.hxx"
32 #include "stgdir.hxx"
33 #include "stgio.hxx"
34 #include <boost/scoped_array.hpp>
36 ///////////////////////////// class StgFAT
38 // The FAT class performs FAT operations on an underlying storage stream.
39 // This stream is either the master FAT stream (m == true ) or a normal
40 // storage stream, which then holds the FAT for small data allocations.
42 StgFAT::StgFAT( StgStrm& r, bool m ) : rStrm( r )
44 bPhys = m;
45 nPageSize = rStrm.GetIo().GetPhysPageSize();
46 nEntries = nPageSize >> 2;
47 nOffset = 0;
48 nMaxPage = 0;
49 nLimit = 0;
52 // Retrieve the physical page for a given byte offset.
54 rtl::Reference< StgPage > StgFAT::GetPhysPage( sal_Int32 nByteOff )
56 rtl::Reference< StgPage > pPg;
57 // Position within the underlying stream
58 // use the Pos2Page() method of the stream
59 if( rStrm.Pos2Page( nByteOff ) )
61 nOffset = rStrm.GetOffset();
62 sal_Int32 nPhysPage = rStrm.GetPage();
63 // get the physical page (must be present)
64 pPg = rStrm.GetIo().Get( nPhysPage, true );
66 return pPg;
69 // Get the follow page for a certain FAT page.
71 sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg )
73 if( nPg >= 0 )
75 rtl::Reference< StgPage > pPg = GetPhysPage( nPg << 2 );
76 nPg = pPg.is() ? StgCache::GetFromPage( pPg, nOffset >> 2 ) : STG_EOF;
78 return nPg;
81 // Find the best fit block for the given size. Return
82 // the starting block and its size or STG_EOF and 0.
83 // nLastPage is a stopper which tells the current
84 // underlying stream size. It is treated as a recommendation
85 // to abort the search to inhibit excessive file growth.
87 sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs )
89 sal_Int32 nMinStart = STG_EOF, nMinLen = 0;
90 sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
91 sal_Int32 nTmpStart = STG_EOF, nTmpLen = 0;
92 sal_Int32 nPages = rStrm.GetSize() >> 2;
93 bool bFound = false;
94 rtl::Reference< StgPage > pPg;
95 short nEntry = 0;
96 for( sal_Int32 i = 0; i < nPages; i++, nEntry++ )
98 if( !( nEntry % nEntries ) )
100 // load the next page for that stream
101 nEntry = 0;
102 pPg = GetPhysPage( i << 2 );
103 if( !pPg.is() )
104 return STG_EOF;
106 sal_Int32 nCur = StgCache::GetFromPage( pPg, nEntry );
107 if( nCur == STG_FREE )
109 // count the size of this area
110 if( nTmpLen )
111 nTmpLen++;
112 else
113 nTmpStart = i,
114 nTmpLen = 1;
115 if( nTmpLen == nPgs
116 // If we already did find a block, stop when reaching the limit
117 || ( bFound && ( nEntry >= nLimit ) ) )
118 break;
120 else if( nTmpLen )
122 if( nTmpLen > nPgs && nTmpLen < nMaxLen )
123 // block > requested size
124 nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = true;
125 else if( nTmpLen >= nMinLen )
127 // block < requested size
128 nMinLen = nTmpLen, nMinStart = nTmpStart;
129 bFound = true;
130 if( nTmpLen == nPgs )
131 break;
133 nTmpStart = STG_EOF;
134 nTmpLen = 0;
137 // Determine which block to use.
138 if( nTmpLen )
140 if( nTmpLen > nPgs && nTmpLen < nMaxLen )
141 // block > requested size
142 nMaxLen = nTmpLen, nMaxStart = nTmpStart;
143 else if( nTmpLen >= nMinLen )
144 // block < requested size
145 nMinLen = nTmpLen, nMinStart = nTmpStart;
147 if( nMinStart != STG_EOF && nMaxStart != STG_EOF )
149 // two areas found; return the best fit area
150 sal_Int32 nMinDiff = nPgs - nMinLen;
151 sal_Int32 nMaxDiff = nMaxLen - nPgs;
152 if( nMinDiff > nMaxDiff )
153 nMinStart = STG_EOF;
155 if( nMinStart != STG_EOF )
157 nPgs = nMinLen; return nMinStart;
159 else
161 return nMaxStart;
165 // Set up the consecutive chain for a given block.
167 bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs )
169 sal_Int32 nPos = nStart << 2;
170 rtl::Reference< StgPage > pPg = GetPhysPage( nPos );
171 if( !pPg.is() || !nPgs )
172 return false;
173 while( --nPgs )
175 if( nOffset >= nPageSize )
177 pPg = GetPhysPage( nPos );
178 if( !pPg.is() )
179 return false;
181 rStrm.GetIo().SetToPage( pPg, nOffset >> 2, ++nStart );
182 nOffset += 4;
183 nPos += 4;
185 if( nOffset >= nPageSize )
187 pPg = GetPhysPage( nPos );
188 if( !pPg.is() )
189 return false;
191 rStrm.GetIo().SetToPage( pPg, nOffset >> 2, STG_EOF );
192 return true;
195 // Allocate a block of data from the given page number on.
196 // It the page number is != STG_EOF, chain the block.
198 sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs )
200 sal_Int32 nOrig = nBgn;
201 sal_Int32 nLast = nBgn;
202 sal_Int32 nBegin = STG_EOF;
203 sal_Int32 nAlloc;
204 sal_Int32 nPages = rStrm.GetSize() >> 2;
205 short nPasses = 0;
206 // allow for two passes
207 while( nPasses < 2 )
209 // try to satisfy the request from the pool of free pages
210 while( nPgs )
212 nAlloc = nPgs;
213 nBegin = FindBlock( nAlloc );
214 // no more blocks left in present alloc chain
215 if( nBegin == STG_EOF )
216 break;
217 if( ( nBegin + nAlloc ) > nMaxPage )
218 nMaxPage = nBegin + nAlloc;
219 if( !MakeChain( nBegin, nAlloc ) )
220 return STG_EOF;
221 if( nOrig == STG_EOF )
222 nOrig = nBegin;
223 else
225 // Patch the chain
226 rtl::Reference< StgPage > pPg = GetPhysPage( nLast << 2 );
227 if( !pPg.is() )
228 return STG_EOF;
229 rStrm.GetIo().SetToPage( pPg, nOffset >> 2, nBegin );
231 nLast = nBegin + nAlloc - 1;
232 nPgs -= nAlloc;
234 if( nPgs && !nPasses )
236 // we need new, fresh space, so allocate and retry
237 if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
238 return STG_EOF;
239 if( !bPhys && !InitNew( nPages ) )
240 return 0;
241 // FIXME: this was originally "FALSE", whether or not that
242 // makes sense (or should be STG_EOF instead, say?)
243 nPages = rStrm.GetSize() >> 2;
244 nPasses++;
246 else
247 break;
249 // now we should have a chain for the complete block
250 if( nBegin == STG_EOF || nPgs )
252 rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
253 return STG_EOF; // bad structure
255 return nOrig;
258 // Initialize newly allocated pages for a standard FAT stream
259 // It can be assumed that the stream size is always on
260 // a page boundary
262 bool StgFAT::InitNew( sal_Int32 nPage1 )
264 sal_Int32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries;
265 if ( n > 0 )
267 while( n-- )
269 rtl::Reference< StgPage > pPg;
270 // Position within the underlying stream
271 // use the Pos2Page() method of the stream
272 rStrm.Pos2Page( nPage1 << 2 );
273 // Initialize the page
274 pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE );
275 if ( !pPg.is() )
276 return false;
277 for( short i = 0; i < nEntries; i++ )
278 rStrm.GetIo().SetToPage( pPg, i, STG_FREE );
279 nPage1++;
282 return true;
285 // Release a chain
287 bool StgFAT::FreePages( sal_Int32 nStart, bool bAll )
289 while( nStart >= 0 )
291 rtl::Reference< StgPage > pPg = GetPhysPage( nStart << 2 );
292 if( !pPg.is() )
293 return false;
294 nStart = StgCache::GetFromPage( pPg, nOffset >> 2 );
295 // The first released page is either set to EOF or FREE
296 rStrm.GetIo().SetToPage( pPg, nOffset >> 2, bAll ? STG_FREE : STG_EOF );
297 bAll = true;
299 return true;
302 ///////////////////////////// class StgStrm
304 // The base stream class provides basic functionality for seeking
305 // and accessing the data on a physical basis. It uses the built-in
306 // FAT class for the page allocations.
308 StgStrm::StgStrm( StgIo& r ) : rIo( r )
310 pFat = NULL;
311 nStart = nPage = STG_EOF;
312 nOffset = 0;
313 pEntry = NULL;
314 nPos = nSize = 0;
315 nPageSize = rIo.GetPhysPageSize();
318 StgStrm::~StgStrm()
320 delete pFat;
323 // Attach the stream to the given entry.
325 void StgStrm::SetEntry( StgDirEntry& r )
327 r.aEntry.SetLeaf( STG_DATA, nStart );
328 r.aEntry.SetSize( nSize );
329 pEntry = &r;
330 r.SetDirty();
334 * The page chain, is basically a singly linked list of slots each
335 * point to the next page. Instead of traversing the file structure
336 * for this each time build a simple flat in-memory vector list
337 * of pages.
339 void StgStrm::scanBuildPageChainCache(sal_Int32 *pOptionalCalcSize)
341 if (nSize > 0)
342 m_aPagesCache.reserve(nSize/nPageSize);
344 bool bError = false;
345 sal_Int32 nBgn = nStart;
346 sal_Int32 nOptSize = 0;
348 // Track already scanned PageNumbers here and use them to
349 // see if an already counted page is re-visited
350 std::set< sal_Int32 > nUsedPageNumbers;
352 while( nBgn >= 0 && !bError )
354 if( nBgn >= 0 )
355 m_aPagesCache.push_back(nBgn);
356 nBgn = pFat->GetNextPage( nBgn );
358 //returned second is false if it already exists
359 if (!nUsedPageNumbers.insert(nBgn).second)
361 SAL_WARN ("sot", "Error: page number " << nBgn << " already in chain for stream");
362 bError = true;
365 nOptSize += nPageSize;
367 if (bError)
369 SAL_WARN("sot", "returning wrong format error");
370 if (pOptionalCalcSize)
371 rIo.SetError( ERRCODE_IO_WRONGFORMAT );
372 m_aPagesCache.clear();
374 if (pOptionalCalcSize)
375 *pOptionalCalcSize = nOptSize;
378 // Compute page number and offset for the given byte position.
379 // If the position is behind the size, set the stream right
380 // behind the EOF.
381 bool StgStrm::Pos2Page( sal_Int32 nBytePos )
383 if ( !pFat )
384 return false;
386 // Values < 0 seek to the end
387 if( nBytePos < 0 || nBytePos >= nSize )
388 nBytePos = nSize;
389 // Adjust the position back to offset 0
390 nPos -= nOffset;
391 sal_Int32 nMask = ~( nPageSize - 1 );
392 sal_Int32 nOld = nPos & nMask;
393 sal_Int32 nNew = nBytePos & nMask;
394 nOffset = (short) ( nBytePos & ~nMask );
395 nPos = nBytePos;
396 if( nOld == nNew )
397 return true;
399 // See fdo#47644 for a .doc with a vast amount of pages where seeking around the
400 // document takes a colossal amount of time
402 // Please Note: we build the pagescache incrementally as we go if necessary,
403 // so that a corrupted FAT doesn't poison the stream state for earlier reads
404 size_t nIdx = nNew / nPageSize;
405 if( nIdx >= m_aPagesCache.size() )
407 // Extend the FAT cache ! ...
408 size_t nToAdd = nIdx + 1;
410 if (m_aPagesCache.empty())
411 m_aPagesCache.push_back( nStart );
413 nToAdd -= m_aPagesCache.size();
415 sal_Int32 nBgn = m_aPagesCache.back();
417 // Start adding pages while we can
418 while( nToAdd > 0 && nBgn >= 0 )
420 nBgn = pFat->GetNextPage( nBgn );
421 if( nBgn >= 0 )
423 m_aPagesCache.push_back( nBgn );
424 nToAdd--;
429 if ( nIdx > m_aPagesCache.size() )
431 SAL_WARN("sot", "seek to index " << nIdx <<
432 " beyond page cache size " << m_aPagesCache.size());
433 // fdo#84229 - handle seek to end and back as eg. XclImpStream expects
434 nIdx = m_aPagesCache.size();
435 nPage = STG_EOF;
436 nOffset = 0;
437 // Intriguingly in the past we didn't reset nPos to match the real
438 // length of the stream thus: nPos = nPageSize * nIdx; so retain
439 // this behavior for now.
440 return false;
443 // special case: seek to 1st byte of new, unallocated page
444 // (in case the file size is a multiple of the page size)
445 if( nBytePos == nSize && !nOffset && nIdx > 0 && nIdx == m_aPagesCache.size() )
447 nIdx--;
448 nOffset = nPageSize;
450 else if ( nIdx == m_aPagesCache.size() )
452 nPage = STG_EOF;
453 return false;
456 nPage = m_aPagesCache[ nIdx ];
458 return nPage >= 0;
461 // Retrieve the physical page for a given byte offset.
463 rtl::Reference< StgPage > StgStrm::GetPhysPage( sal_Int32 nBytePos, bool bForce )
465 if( !Pos2Page( nBytePos ) )
466 return NULL;
467 return rIo.Get( nPage, bForce );
470 // Copy an entire stream. Both streams are allocated in the FAT.
471 // The target stream is this stream.
473 bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes )
475 if ( !pFat )
476 return false;
478 m_aPagesCache.clear();
480 sal_Int32 nTo = nStart;
481 sal_Int32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize;
482 while( nPgs-- )
484 if( nTo < 0 )
486 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
487 return false;
489 rIo.Copy( nTo, nFrom );
490 if( nFrom >= 0 )
492 nFrom = pFat->GetNextPage( nFrom );
493 if( nFrom < 0 )
495 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
496 return false;
499 nTo = pFat->GetNextPage( nTo );
501 return true;
504 bool StgStrm::SetSize( sal_Int32 nBytes )
506 if ( nBytes < 0 || !pFat )
507 return false;
509 m_aPagesCache.clear();
511 // round up to page size
512 sal_Int32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize;
513 sal_Int32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
514 if( nNew > nOld )
516 if( !Pos2Page( nSize ) )
517 return false;
518 sal_Int32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize );
519 if( nBgn == STG_EOF )
520 return false;
521 if( nStart == STG_EOF )
522 nStart = nPage = nBgn;
524 else if( nNew < nOld )
526 bool bAll = ( nBytes == 0 );
527 if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) )
528 return false;
529 if( bAll )
530 nStart = nPage = STG_EOF;
532 if( pEntry )
534 // change the dir entry?
535 if( !nSize || !nBytes )
536 pEntry->aEntry.SetLeaf( STG_DATA, nStart );
537 pEntry->aEntry.SetSize( nBytes );
538 pEntry->SetDirty();
540 nSize = nBytes;
541 pFat->SetLimit( GetPages() );
542 return true;
545 // Return the # of allocated pages
548 //////////////////////////// class StgFATStrm
550 // The FAT stream class provides physical access to the master FAT.
551 // Since this access is implemented as a StgStrm, we can use the
552 // FAT allocator.
554 StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r )
556 pFat = new StgFAT( *this, true );
557 nSize = rIo.aHdr.GetFATSize() * nPageSize;
560 bool StgFATStrm::Pos2Page( sal_Int32 nBytePos )
562 // Values < 0 seek to the end
563 if( nBytePos < 0 || nBytePos >= nSize )
564 nBytePos = nSize ? nSize - 1 : 0;
565 nPage = nBytePos / nPageSize;
566 nOffset = (short) ( nBytePos % nPageSize );
567 nPos = nBytePos;
568 nPage = GetPage( (short) nPage, false );
569 return nPage >= 0;
572 // Retrieve the physical page for a given byte offset.
573 // Since Pos2Page() already has computed the physical offset,
574 // use the byte offset directly.
576 rtl::Reference< StgPage > StgFATStrm::GetPhysPage( sal_Int32 nBytePos, bool bForce )
578 OSL_ENSURE( nBytePos >= 0, "The value may not be negative!" );
579 return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce );
582 // Get the page number entry for the given page offset.
584 sal_Int32 StgFATStrm::GetPage( short nOff, bool bMake, sal_uInt16 *pnMasterAlloc )
586 OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
587 if( pnMasterAlloc ) *pnMasterAlloc = 0;
588 if( nOff < StgHeader::GetFAT1Size() )
589 return rIo.aHdr.GetFATPage( nOff );
590 sal_Int32 nMaxPage = nSize >> 2;
591 nOff = nOff - StgHeader::GetFAT1Size();
592 // Anzahl der Masterpages, durch die wir iterieren muessen
593 sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1;
594 sal_uInt16 nBlocks = nOff / nMasterCount;
595 // Offset in letzter Masterpage
596 nOff = nOff % nMasterCount;
598 rtl::Reference< StgPage > pOldPage;
599 rtl::Reference< StgPage > pMaster;
600 sal_Int32 nFAT = rIo.aHdr.GetFATChain();
601 for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
603 if( nFAT == STG_EOF || nFAT == STG_FREE )
605 if( bMake )
607 m_aPagesCache.clear();
609 // create a new master page
610 nFAT = nMaxPage++;
611 pMaster = rIo.Copy( nFAT, STG_FREE );
612 if ( pMaster.is() )
614 for( short k = 0; k < ( nPageSize >> 2 ); k++ )
615 rIo.SetToPage( pMaster, k, STG_FREE );
616 // Verkettung herstellen
617 if( !pOldPage.is() )
618 rIo.aHdr.SetFATChain( nFAT );
619 else
620 rIo.SetToPage( pOldPage, nMasterCount, nFAT );
621 if( nMaxPage >= rIo.GetPhysPages() )
622 if( !rIo.SetSize( nMaxPage ) )
623 return STG_EOF;
624 // mark the page as used
625 // Platz fuer Masterpage schaffen
626 if( !pnMasterAlloc ) // Selbst Platz schaffen
628 if( !Pos2Page( nFAT << 2 ) )
629 return STG_EOF;
630 rtl::Reference< StgPage > pPg = rIo.Get( nPage, true );
631 if( !pPg.is() )
632 return STG_EOF;
633 rIo.SetToPage( pPg, nOffset >> 2, STG_MASTER );
635 else
636 (*pnMasterAlloc)++;
637 rIo.aHdr.SetMasters( nCount + 1 );
638 pOldPage = pMaster;
642 else
644 pMaster = rIo.Get( nFAT, true );
645 if ( pMaster.is() )
647 nFAT = StgCache::GetFromPage( pMaster, nMasterCount );
648 pOldPage = pMaster;
652 if( pMaster.is() )
653 return StgCache::GetFromPage( pMaster, nOff );
654 rIo.SetError( SVSTREAM_GENERALERROR );
655 return STG_EOF;
659 // Set the page number entry for the given page offset.
661 bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
663 OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
664 m_aPagesCache.clear();
666 bool bRes = true;
667 if( nOff < StgHeader::GetFAT1Size() )
668 rIo.aHdr.SetFATPage( nOff, nNewPage );
669 else
671 nOff = nOff - StgHeader::GetFAT1Size();
672 // Anzahl der Masterpages, durch die wir iterieren muessen
673 sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1;
674 sal_uInt16 nBlocks = nOff / nMasterCount;
675 // Offset in letzter Masterpage
676 nOff = nOff % nMasterCount;
678 rtl::Reference< StgPage > pMaster;
679 sal_Int32 nFAT = rIo.aHdr.GetFATChain();
680 for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
682 if( nFAT == STG_EOF || nFAT == STG_FREE )
684 pMaster = 0;
685 break;
687 pMaster = rIo.Get( nFAT, true );
688 if ( pMaster.is() )
689 nFAT = StgCache::GetFromPage( pMaster, nMasterCount );
691 if( pMaster.is() )
692 rIo.SetToPage( pMaster, nOff, nNewPage );
693 else
695 rIo.SetError( SVSTREAM_GENERALERROR );
696 bRes = false;
700 // lock the page against access
701 if( bRes )
703 Pos2Page( nNewPage << 2 );
704 rtl::Reference< StgPage > pPg = rIo.Get( nPage, true );
705 if( pPg.is() )
706 rIo.SetToPage( pPg, nOffset >> 2, STG_FAT );
707 else
708 bRes = false;
710 return bRes;
713 bool StgFATStrm::SetSize( sal_Int32 nBytes )
715 if ( nBytes < 0 )
716 return false;
718 m_aPagesCache.clear();
720 // Set the number of entries to a multiple of the page size
721 short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize );
722 short nNew = (short) (
723 ( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ;
724 if( nNew < nOld )
726 // release master pages
727 for( short i = nNew; i < nOld; i++ )
728 SetPage( i, STG_FREE );
730 else
732 while( nOld < nNew )
734 // allocate master pages
735 // find a free master page slot
736 sal_Int32 nPg = 0;
737 sal_uInt16 nMasterAlloc = 0;
738 nPg = GetPage( nOld, true, &nMasterAlloc );
739 if( nPg == STG_EOF )
740 return false;
741 // 4 Bytes have been used for Allocation of each MegaMasterPage
742 nBytes += nMasterAlloc << 2;
744 // find a free page using the FAT allocator
745 sal_Int32 n = 1;
746 OSL_ENSURE( pFat, "The pointer is always initializer here!" );
747 sal_Int32 nNewPage = pFat->FindBlock( n );
748 if( nNewPage == STG_EOF )
750 // no free pages found; create a new page
751 // Since all pages are allocated, extend
752 // the file size for the next page!
753 nNewPage = nSize >> 2;
754 // if a MegaMasterPage was created avoid taking
755 // the same Page
756 nNewPage += nMasterAlloc;
757 // adjust the file size if necessary
758 if( nNewPage >= rIo.GetPhysPages() )
759 if( !rIo.SetSize( nNewPage + 1 ) )
760 return false;
762 // Set up the page with empty entries
763 rtl::Reference< StgPage > pPg = rIo.Copy( nNewPage, STG_FREE );
764 if ( !pPg.is() )
765 return false;
766 for( short j = 0; j < ( nPageSize >> 2 ); j++ )
767 rIo.SetToPage( pPg, j, STG_FREE );
769 // store the page number into the master FAT
770 // Set the size before so the correct FAT can be found
771 nSize = ( nOld + 1 ) * nPageSize;
772 SetPage( nOld, nNewPage );
774 // MegaMasterPages were created, mark it them as used
776 sal_uInt32 nMax = rIo.aHdr.GetMasters( );
777 sal_uInt32 nFAT = rIo.aHdr.GetFATChain();
778 if( nMasterAlloc )
779 for( sal_uInt16 nCount = 0; nCount < nMax; nCount++ )
781 if( !Pos2Page( nFAT << 2 ) )
782 return false;
783 if( nMax - nCount <= nMasterAlloc )
785 rtl::Reference< StgPage > piPg = rIo.Get( nPage, true );
786 if( !piPg.is() )
787 return false;
788 rIo.SetToPage( piPg, nOffset >> 2, STG_MASTER );
790 rtl::Reference< StgPage > pPage = rIo.Get( nFAT, true );
791 if( !pPage.is() ) return false;
792 nFAT = StgCache::GetFromPage( pPage, (nPageSize >> 2 ) - 1 );
795 nOld++;
796 // We have used up 4 bytes for the STG_FAT entry
797 nBytes += 4;
798 nNew = (short) (
799 ( nBytes + ( nPageSize - 1 ) ) / nPageSize );
802 nSize = nNew * nPageSize;
803 rIo.aHdr.SetFATSize( nNew );
804 return true;
807 /////////////////////////// class StgDataStrm
809 // This class is a normal physical stream which can be initialized
810 // either with an existing dir entry or an existing FAT chain.
811 // The stream has a size increment which normally is 1, but which can be
812 // set to any value is you want the size to be incremented by certain values.
814 StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
816 Init( nBgn, nLen );
819 StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
821 pEntry = &p;
822 Init( p.aEntry.GetLeaf( STG_DATA ),
823 p.aEntry.GetSize() );
826 void StgDataStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
828 if ( rIo.pFAT )
829 pFat = new StgFAT( *rIo.pFAT, true );
831 OSL_ENSURE( pFat, "The pointer should not be empty!" );
833 nStart = nPage = nBgn;
834 nSize = nLen;
835 nIncr = 1;
836 nOffset = 0;
837 if( nLen < 0 && pFat )
839 // determine the actual size of the stream by scanning
840 // the FAT chain and counting the # of pages allocated
841 scanBuildPageChainCache( &nSize );
845 // Set the size of a physical stream.
847 bool StgDataStrm::SetSize( sal_Int32 nBytes )
849 if ( !pFat )
850 return false;
852 nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr;
853 sal_Int32 nOldSz = nSize;
854 if( ( nOldSz != nBytes ) )
856 if( !StgStrm::SetSize( nBytes ) )
857 return false;
858 sal_Int32 nMaxPage = pFat->GetMaxPage();
859 if( nMaxPage > rIo.GetPhysPages() )
860 if( !rIo.SetSize( nMaxPage ) )
861 return false;
862 // If we only allocated one page or less, create this
863 // page in the cache for faster throughput. The current
864 // position is the former EOF point.
865 if( ( nSize - 1 ) / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 )
867 Pos2Page( nBytes );
868 if( nPage >= 0 )
869 rIo.Copy( nPage, STG_FREE );
872 return true;
875 // Get the address of the data byte at a specified offset.
876 // If bForce = true, a read of non-existent data causes
877 // a read fault.
879 void* StgDataStrm::GetPtr( sal_Int32 Pos, bool bForce, bool bDirty )
881 if( Pos2Page( Pos ) )
883 rtl::Reference< StgPage > pPg = rIo.Get( nPage, bForce );
884 if (pPg.is() && nOffset < pPg->GetSize())
886 if( bDirty )
887 rIo.SetDirty( pPg );
888 return static_cast<sal_uInt8 *>(pPg->GetData()) + nOffset;
891 return NULL;
894 // This could easily be adapted to a better algorithm by determining
895 // the amount of consecutable blocks before doing a read. The result
896 // is the number of bytes read. No error is generated on EOF.
898 sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n )
900 if ( n < 0 )
901 return 0;
903 if( ( nPos + n ) > nSize )
904 n = nSize - nPos;
905 sal_Int32 nDone = 0;
906 while( n )
908 short nBytes = nPageSize - nOffset;
909 rtl::Reference< StgPage > pPg;
910 if( (sal_Int32) nBytes > n )
911 nBytes = (short) n;
912 if( nBytes )
914 short nRes;
915 void *p = static_cast<sal_uInt8 *>(pBuf) + nDone;
916 if( nBytes == nPageSize )
918 pPg = rIo.Find( nPage );
919 if( pPg.is() )
921 // data is present, so use the cached data
922 memcpy( p, pPg->GetData(), nBytes );
923 nRes = nBytes;
925 else
926 // do a direct (unbuffered) read
927 nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize;
929 else
931 // partial block read through the cache.
932 pPg = rIo.Get( nPage, false );
933 if( !pPg.is() )
934 break;
935 memcpy( p, static_cast<sal_uInt8*>(pPg->GetData()) + nOffset, nBytes );
936 nRes = nBytes;
938 nDone += nRes;
939 nPos += nRes;
940 n -= nRes;
941 nOffset = nOffset + nRes;
942 if( nRes != nBytes )
943 break; // read error or EOF
945 // Switch to next page if necessary
946 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
947 break;
949 return nDone;
952 sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n )
954 if ( n < 0 )
955 return 0;
957 sal_Int32 nDone = 0;
958 if( ( nPos + n ) > nSize )
960 sal_Int32 nOld = nPos;
961 if( !SetSize( nPos + n ) )
962 return 0;
963 Pos2Page( nOld );
965 while( n )
967 short nBytes = nPageSize - nOffset;
968 rtl::Reference< StgPage > pPg;
969 if( (sal_Int32) nBytes > n )
970 nBytes = (short) n;
971 if( nBytes )
973 short nRes;
974 const void *p = static_cast<const sal_uInt8 *>(pBuf) + nDone;
975 if( nBytes == nPageSize )
977 pPg = rIo.Find( nPage );
978 if( pPg.is() )
980 // data is present, so use the cached data
981 memcpy( pPg->GetData(), p, nBytes );
982 rIo.SetDirty( pPg );
983 nRes = nBytes;
985 else
986 // do a direct (unbuffered) write
987 nRes = (short) rIo.Write( nPage, const_cast<void*>(p), 1 ) * nPageSize;
989 else
991 // partial block read through the cache.
992 pPg = rIo.Get( nPage, false );
993 if( !pPg.is() )
994 break;
995 memcpy( static_cast<sal_uInt8*>(pPg->GetData()) + nOffset, p, nBytes );
996 rIo.SetDirty( pPg );
997 nRes = nBytes;
999 nDone += nRes;
1000 nPos += nRes;
1001 n -= nRes;
1002 nOffset = nOffset + nRes;
1003 if( nRes != nBytes )
1004 break; // read error
1006 // Switch to next page if necessary
1007 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1008 break;
1010 return nDone;
1013 //////////////////////////// class StgSmallStream
1015 // The small stream class provides access to streams with a size < 4096 bytes.
1016 // This stream is a StgStream containing small pages. The FAT for this stream
1017 // is also a StgStream. The start of the FAT is in the header at DataRootPage,
1018 // the stream itself is pointed to by the root entry (it holds start & size).
1020 StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
1022 Init( nBgn, nLen );
1025 StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
1027 pEntry = &p;
1028 Init( p.aEntry.GetLeaf( STG_DATA ),
1029 p.aEntry.GetSize() );
1032 void StgSmallStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
1034 if ( rIo.pDataFAT )
1035 pFat = new StgFAT( *rIo.pDataFAT, false );
1036 pData = rIo.pDataStrm;
1037 OSL_ENSURE( pFat && pData, "The pointers should not be empty!" );
1039 nPageSize = rIo.GetDataPageSize();
1040 nStart =
1041 nPage = nBgn;
1042 nSize = nLen;
1045 // This could easily be adapted to a better algorithm by determining
1046 // the amount of consecutable blocks before doing a read. The result
1047 // is the number of bytes read. No error is generated on EOF.
1049 sal_Int32 StgSmallStrm::Read( void* pBuf, sal_Int32 n )
1051 // We can safely assume that reads are not huge, since the
1052 // small stream is likely to be < 64 KBytes.
1053 if( ( nPos + n ) > nSize )
1054 n = nSize - nPos;
1055 short nDone = 0;
1056 while( n )
1058 short nBytes = nPageSize - nOffset;
1059 if( (sal_Int32) nBytes > n )
1060 nBytes = (short) n;
1061 if( nBytes )
1063 if( !pData || !pData->Pos2Page( nPage * nPageSize + nOffset ) )
1064 break;
1065 // all reading through the stream
1066 short nRes = (short) pData->Read( static_cast<sal_uInt8*>(pBuf) + nDone, nBytes );
1067 nDone = nDone + nRes;
1068 nPos += nRes;
1069 n -= nRes;
1070 nOffset = nOffset + nRes;
1071 // read problem?
1072 if( nRes != nBytes )
1073 break;
1075 // Switch to next page if necessary
1076 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1077 break;
1079 return nDone;
1082 sal_Int32 StgSmallStrm::Write( const void* pBuf, sal_Int32 n )
1084 // you can safely assume that reads are not huge, since the
1085 // small stream is likely to be < 64 KBytes.
1086 short nDone = 0;
1087 if( ( nPos + n ) > nSize )
1089 sal_Int32 nOld = nPos;
1090 if( !SetSize( nPos + n ) )
1091 return 0;
1092 Pos2Page( nOld );
1094 while( n )
1096 short nBytes = nPageSize - nOffset;
1097 if( (sal_Int32) nBytes > n )
1098 nBytes = (short) n;
1099 if( nBytes )
1101 // all writing goes through the stream
1102 sal_Int32 nDataPos = nPage * nPageSize + nOffset;
1103 if ( !pData
1104 || ( pData->GetSize() < ( nDataPos + nBytes )
1105 && !pData->SetSize( nDataPos + nBytes ) ) )
1106 break;
1107 if( !pData->Pos2Page( nDataPos ) )
1108 break;
1109 short nRes = (short) pData->Write( static_cast<sal_uInt8 const *>(pBuf) + nDone, nBytes );
1110 nDone = nDone + nRes;
1111 nPos += nRes;
1112 n -= nRes;
1113 nOffset = nOffset + nRes;
1114 // write problem?
1115 if( nRes != nBytes )
1116 break;
1118 // Switch to next page if necessary
1119 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1120 break;
1122 return nDone;
1125 /////////////////////////// class StgTmpStrm
1127 // The temporary stream uses a memory stream if < 32K, otherwise a
1128 // temporary file.
1130 #define THRESHOLD 32768L
1132 StgTmpStrm::StgTmpStrm( sal_uLong nInitSize )
1133 : SvMemoryStream( nInitSize > THRESHOLD
1134 ? 16
1135 : ( nInitSize ? nInitSize : 16 ), 4096 )
1137 pStrm = NULL;
1138 // this calls FlushData, so all members should be set by this time
1139 SetBufferSize( 0 );
1140 if( nInitSize > THRESHOLD )
1141 SetSize( nInitSize );
1144 bool StgTmpStrm::Copy( StgTmpStrm& rSrc )
1146 sal_uLong n = rSrc.GetSize();
1147 sal_uLong nCur = rSrc.Tell();
1148 SetSize( n );
1149 if( GetError() == SVSTREAM_OK )
1151 boost::scoped_array<sal_uInt8> p(new sal_uInt8[ 4096 ]);
1152 rSrc.Seek( 0L );
1153 Seek( 0L );
1154 while( n )
1156 sal_uLong nn = n;
1157 if( nn > 4096 )
1158 nn = 4096;
1159 if( rSrc.Read( p.get(), nn ) != nn )
1160 break;
1161 if( Write( p.get(), nn ) != nn )
1162 break;
1163 n -= nn;
1165 p.reset();
1166 rSrc.Seek( nCur );
1167 Seek( nCur );
1168 return n == 0;
1170 else
1171 return false;
1174 StgTmpStrm::~StgTmpStrm()
1176 if( pStrm )
1178 pStrm->Close();
1179 osl::File::remove( aName );
1180 delete pStrm;
1184 sal_uLong StgTmpStrm::GetSize() const
1186 sal_uLong n;
1187 if( pStrm )
1189 sal_uLong old = pStrm->Tell();
1190 n = pStrm->Seek( STREAM_SEEK_TO_END );
1191 pStrm->Seek( old );
1193 else
1194 n = nEndOfData;
1195 return n;
1198 void StgTmpStrm::SetSize(sal_uInt64 n)
1200 if( pStrm )
1201 pStrm->SetStreamSize( n );
1202 else
1204 if( n > THRESHOLD )
1206 aName = utl::TempFile(0, false).GetURL();
1207 SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE );
1208 sal_uLong nCur = Tell();
1209 sal_uLong i = nEndOfData;
1210 if( i )
1212 boost::scoped_array<sal_uInt8> p(new sal_uInt8[ 4096 ]);
1213 Seek( 0L );
1214 while( i )
1216 sal_uLong nb = ( i > 4096 ) ? 4096 : i;
1217 if( Read( p.get(), nb ) == nb
1218 && s->Write( p.get(), nb ) == nb )
1219 i -= nb;
1220 else
1221 break;
1224 if( !i && n > nEndOfData )
1226 // We have to write one byte at the end of the file
1227 // if the file is bigger than the memstream to see
1228 // if it fits on disk
1229 s->Seek( n - 1 );
1230 s->Write( &i, 1 );
1231 s->Flush();
1232 if( s->GetError() != SVSTREAM_OK )
1233 i = 1;
1235 Seek( nCur );
1236 s->Seek( nCur );
1237 if( i )
1239 SetError( s->GetError() );
1240 delete s;
1241 return;
1243 pStrm = s;
1244 // Shrink the memory to 16 bytes, which seems to be the minimum
1245 ReAllocateMemory( - ( (long) nEndOfData - 16 ) );
1247 else
1249 if( n > nEndOfData )
1251 SvMemoryStream::SetSize(n);
1253 else
1254 nEndOfData = n;
1259 sal_uLong StgTmpStrm::GetData( void* pData, sal_uLong n )
1261 if( pStrm )
1263 n = pStrm->Read( pData, n );
1264 SetError( pStrm->GetError() );
1265 return n;
1267 else
1268 return SvMemoryStream::GetData( pData, n );
1271 sal_uLong StgTmpStrm::PutData( const void* pData, sal_uLong n )
1273 sal_uInt32 nCur = Tell();
1274 sal_uInt32 nNew = nCur + n;
1275 if( nNew > THRESHOLD && !pStrm )
1277 SetSize( nNew );
1278 if( GetError() != SVSTREAM_OK )
1279 return 0;
1281 if( pStrm )
1283 nNew = pStrm->Write( pData, n );
1284 SetError( pStrm->GetError() );
1286 else
1287 nNew = SvMemoryStream::PutData( pData, n );
1288 return nNew;
1291 sal_uInt64 StgTmpStrm::SeekPos(sal_uInt64 n)
1293 // check if a truncated STREAM_SEEK_TO_END was passed
1294 assert(n != SAL_MAX_UINT32);
1295 if( n == STREAM_SEEK_TO_END )
1296 n = GetSize();
1297 if( n && n > THRESHOLD && !pStrm )
1299 SetSize( n );
1300 if( GetError() != SVSTREAM_OK )
1301 return Tell();
1302 else
1303 return n;
1305 else if( pStrm )
1307 n = pStrm->Seek( n );
1308 SetError( pStrm->GetError() );
1309 return n;
1311 else
1312 return SvMemoryStream::SeekPos( n );
1315 void StgTmpStrm::FlushData()
1317 if( pStrm )
1319 pStrm->Flush();
1320 SetError( pStrm->GetError() );
1322 else
1323 SvMemoryStream::FlushData();
1326 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */