bump product version to 4.2.0.1
[LibreOffice.git] / sot / source / sdstor / stgstrms.cxx
blobd4115e5e472460ac6d3db52f932961298e9c99e7
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"
35 ///////////////////////////// class StgFAT ///////////////////////////////
37 // The FAT class performs FAT operations on an underlying storage stream.
38 // This stream is either the master FAT stream (m == true ) or a normal
39 // storage stream, which then holds the FAT for small data allocations.
41 StgFAT::StgFAT( StgStrm& r, bool m ) : rStrm( r )
43 bPhys = m;
44 nPageSize = rStrm.GetIo().GetPhysPageSize();
45 nEntries = nPageSize >> 2;
46 nOffset = 0;
47 nMaxPage = 0;
48 nLimit = 0;
51 // Retrieve the physical page for a given byte offset.
53 rtl::Reference< StgPage > StgFAT::GetPhysPage( sal_Int32 nByteOff )
55 rtl::Reference< StgPage > pPg;
56 // Position within the underlying stream
57 // use the Pos2Page() method of the stream
58 if( rStrm.Pos2Page( nByteOff ) )
60 nOffset = rStrm.GetOffset();
61 sal_Int32 nPhysPage = rStrm.GetPage();
62 // get the physical page (must be present)
63 pPg = rStrm.GetIo().Get( nPhysPage, true );
65 return pPg;
68 // Get the follow page for a certain FAT page.
70 sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg )
72 if( nPg >= 0 )
74 rtl::Reference< StgPage > pPg = GetPhysPage( nPg << 2 );
75 nPg = pPg.is() ? rStrm.GetIo().GetFromPage( pPg, nOffset >> 2 ) : STG_EOF;
77 return nPg;
80 // Find the best fit block for the given size. Return
81 // the starting block and its size or STG_EOF and 0.
82 // nLastPage is a stopper which tells the current
83 // underlying stream size. It is treated as a recommendation
84 // to abort the search to inhibit excessive file growth.
86 sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs )
88 sal_Int32 nMinStart = STG_EOF, nMinLen = 0;
89 sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
90 sal_Int32 nTmpStart = STG_EOF, nTmpLen = 0;
91 sal_Int32 nPages = rStrm.GetSize() >> 2;
92 bool bFound = false;
93 rtl::Reference< StgPage > pPg;
94 short nEntry = 0;
95 for( sal_Int32 i = 0; i < nPages; i++, nEntry++ )
97 if( !( nEntry % nEntries ) )
99 // load the next page for that stream
100 nEntry = 0;
101 pPg = GetPhysPage( i << 2 );
102 if( !pPg.is() )
103 return STG_EOF;
105 sal_Int32 nCur = rStrm.GetIo().GetFromPage( pPg, nEntry );
106 if( nCur == STG_FREE )
108 // count the size of this area
109 if( nTmpLen )
110 nTmpLen++;
111 else
112 nTmpStart = i,
113 nTmpLen = 1;
114 if( nTmpLen == nPgs
115 // If we already did find a block, stop when reaching the limit
116 || ( bFound && ( nEntry >= nLimit ) ) )
117 break;
119 else if( nTmpLen )
121 if( nTmpLen > nPgs && nTmpLen < nMaxLen )
122 // block > requested size
123 nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = true;
124 else if( nTmpLen >= nMinLen )
126 // block < requested size
127 nMinLen = nTmpLen, nMinStart = nTmpStart;
128 bFound = true;
129 if( nTmpLen == nPgs )
130 break;
132 nTmpStart = STG_EOF;
133 nTmpLen = 0;
136 // Determine which block to use.
137 if( nTmpLen )
139 if( nTmpLen > nPgs && nTmpLen < nMaxLen )
140 // block > requested size
141 nMaxLen = nTmpLen, nMaxStart = nTmpStart;
142 else if( nTmpLen >= nMinLen )
143 // block < requested size
144 nMinLen = nTmpLen, nMinStart = nTmpStart;
146 if( nMinStart != STG_EOF && nMaxStart != STG_EOF )
148 // two areas found; return the best fit area
149 sal_Int32 nMinDiff = nPgs - nMinLen;
150 sal_Int32 nMaxDiff = nMaxLen - nPgs;
151 if( nMinDiff > nMaxDiff )
152 nMinStart = STG_EOF;
154 if( nMinStart != STG_EOF )
156 nPgs = nMinLen; return nMinStart;
158 else
160 return nMaxStart;
164 // Set up the consecutive chain for a given block.
166 bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs )
168 sal_Int32 nPos = nStart << 2;
169 rtl::Reference< StgPage > pPg = GetPhysPage( nPos );
170 if( !pPg.is() || !nPgs )
171 return false;
172 while( --nPgs )
174 if( nOffset >= nPageSize )
176 pPg = GetPhysPage( nPos );
177 if( !pPg.is() )
178 return false;
180 rStrm.GetIo().SetToPage( pPg, nOffset >> 2, ++nStart );
181 nOffset += 4;
182 nPos += 4;
184 if( nOffset >= nPageSize )
186 pPg = GetPhysPage( nPos );
187 if( !pPg.is() )
188 return false;
190 rStrm.GetIo().SetToPage( pPg, nOffset >> 2, STG_EOF );
191 return true;
194 // Allocate a block of data from the given page number on.
195 // It the page number is != STG_EOF, chain the block.
197 sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs )
199 sal_Int32 nOrig = nBgn;
200 sal_Int32 nLast = nBgn;
201 sal_Int32 nBegin = STG_EOF;
202 sal_Int32 nAlloc;
203 sal_Int32 nPages = rStrm.GetSize() >> 2;
204 short nPasses = 0;
205 // allow for two passes
206 while( nPasses < 2 )
208 // try to satisfy the request from the pool of free pages
209 while( nPgs )
211 nAlloc = nPgs;
212 nBegin = FindBlock( nAlloc );
213 // no more blocks left in present alloc chain
214 if( nBegin == STG_EOF )
215 break;
216 if( ( nBegin + nAlloc ) > nMaxPage )
217 nMaxPage = nBegin + nAlloc;
218 if( !MakeChain( nBegin, nAlloc ) )
219 return STG_EOF;
220 if( nOrig == STG_EOF )
221 nOrig = nBegin;
222 else
224 // Patch the chain
225 rtl::Reference< StgPage > pPg = GetPhysPage( nLast << 2 );
226 if( !pPg.is() )
227 return STG_EOF;
228 rStrm.GetIo().SetToPage( pPg, nOffset >> 2, nBegin );
230 nLast = nBegin + nAlloc - 1;
231 nPgs -= nAlloc;
233 if( nPgs && !nPasses )
235 // we need new, fresh space, so allocate and retry
236 if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
237 return STG_EOF;
238 if( !bPhys && !InitNew( nPages ) )
239 return false;
240 nPages = rStrm.GetSize() >> 2;
241 nPasses++;
243 else
244 break;
246 // now we should have a chain for the complete block
247 if( nBegin == STG_EOF || nPgs )
249 rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
250 return STG_EOF; // bad structure
252 return nOrig;
255 // Initialize newly allocated pages for a standard FAT stream
256 // It can be assumed that the stream size is always on
257 // a page boundary
259 bool StgFAT::InitNew( sal_Int32 nPage1 )
261 sal_Int32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries;
262 if ( n > 0 )
264 while( n-- )
266 rtl::Reference< StgPage > pPg;
267 // Position within the underlying stream
268 // use the Pos2Page() method of the stream
269 rStrm.Pos2Page( nPage1 << 2 );
270 // Initialize the page
271 pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE );
272 if ( !pPg.is() )
273 return false;
274 for( short i = 0; i < nEntries; i++ )
275 rStrm.GetIo().SetToPage( pPg, i, STG_FREE );
276 nPage1++;
279 return true;
282 // Release a chain
284 bool StgFAT::FreePages( sal_Int32 nStart, bool bAll )
286 while( nStart >= 0 )
288 rtl::Reference< StgPage > pPg = GetPhysPage( nStart << 2 );
289 if( !pPg.is() )
290 return false;
291 nStart = rStrm.GetIo().GetFromPage( pPg, nOffset >> 2 );
292 // The first released page is either set to EOF or FREE
293 rStrm.GetIo().SetToPage( pPg, nOffset >> 2, bAll ? STG_FREE : STG_EOF );
294 bAll = true;
296 return true;
299 ///////////////////////////// class StgStrm ////////////////////////////////
301 // The base stream class provides basic functionality for seeking
302 // and accessing the data on a physical basis. It uses the built-in
303 // FAT class for the page allocations.
305 StgStrm::StgStrm( StgIo& r ) : rIo( r )
307 pFat = NULL;
308 nStart = nPage = STG_EOF;
309 nOffset = 0;
310 pEntry = NULL;
311 nPos = nSize = 0;
312 nPageSize = rIo.GetPhysPageSize();
315 StgStrm::~StgStrm()
317 delete pFat;
320 // Attach the stream to the given entry.
322 void StgStrm::SetEntry( StgDirEntry& r )
324 r.aEntry.SetLeaf( STG_DATA, nStart );
325 r.aEntry.SetSize( nSize );
326 pEntry = &r;
327 r.SetDirty();
331 * The page chain, is basically a singly linked list of slots each
332 * point to the next page. Instead of traversing the file structure
333 * for this each time build a simple flat in-memory vector list
334 * of pages.
336 void StgStrm::scanBuildPageChainCache(sal_Int32 *pOptionalCalcSize)
338 if (nSize > 0)
339 m_aPagesCache.reserve(nSize/nPageSize);
341 bool bError = false;
342 sal_Int32 nBgn = nStart;
343 sal_Int32 nOptSize = 0;
345 // Track already scanned PageNumbers here and use them to
346 // see if an already counted page is re-visited
347 std::set< sal_Int32 > nUsedPageNumbers;
349 while( nBgn >= 0 && !bError )
351 if( nBgn >= 0 )
352 m_aPagesCache.push_back(nBgn);
353 nBgn = pFat->GetNextPage( nBgn );
355 //returned second is false if it already exists
356 if (!nUsedPageNumbers.insert(nBgn).second)
357 bError = true;
359 nOptSize += nPageSize;
361 if (bError)
363 if (pOptionalCalcSize)
364 rIo.SetError( ERRCODE_IO_WRONGFORMAT );
365 m_aPagesCache.clear();
367 if (pOptionalCalcSize)
368 *pOptionalCalcSize = nOptSize;
371 // Compute page number and offset for the given byte position.
372 // If the position is behind the size, set the stream right
373 // behind the EOF.
374 bool StgStrm::Pos2Page( sal_Int32 nBytePos )
376 if ( !pFat )
377 return false;
379 // Values < 0 seek to the end
380 if( nBytePos < 0 || nBytePos >= nSize )
381 nBytePos = nSize;
382 // Adjust the position back to offset 0
383 nPos -= nOffset;
384 sal_Int32 nMask = ~( nPageSize - 1 );
385 sal_Int32 nOld = nPos & nMask;
386 sal_Int32 nNew = nBytePos & nMask;
387 nOffset = (short) ( nBytePos & ~nMask );
388 nPos = nBytePos;
389 if( nOld == nNew )
390 return true;
392 // See fdo#47644 for a .doc with a vast amount of pages where seeking around the
393 // document takes a colossal amount of time
395 // Please Note: we build the pagescache incrementally as we go if necessary,
396 // so that a corrupted FAT doesn't poison the stream state for earlier reads
397 size_t nIdx = nNew / nPageSize;
398 if( nIdx >= m_aPagesCache.size() )
400 // Extend the FAT cache ! ...
401 size_t nToAdd = nIdx + 1;
403 if (m_aPagesCache.empty())
404 m_aPagesCache.push_back( nStart );
406 nToAdd -= m_aPagesCache.size();
408 sal_Int32 nBgn = m_aPagesCache.back();
410 // Start adding pages while we can
411 while( nToAdd > 0 && nBgn >= 0 )
413 nBgn = pFat->GetNextPage( nBgn );
414 if( nBgn >= 0 )
416 m_aPagesCache.push_back( nBgn );
417 nToAdd--;
422 if ( nIdx > m_aPagesCache.size() )
424 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
425 nPage = STG_EOF;
426 nOffset = nPageSize;
427 return false;
429 // special case: seek to 1st byte of new, unallocated page
430 // (in case the file size is a multiple of the page size)
431 if( nBytePos == nSize && !nOffset && nIdx > 0 && nIdx == m_aPagesCache.size() )
433 nIdx--;
434 nOffset = nPageSize;
436 else if ( nIdx == m_aPagesCache.size() )
438 nPage = STG_EOF;
439 return false;
442 nPage = m_aPagesCache[ nIdx ];
444 return nPage >= 0;
447 // Retrieve the physical page for a given byte offset.
449 rtl::Reference< StgPage > StgStrm::GetPhysPage( sal_Int32 nBytePos, bool bForce )
451 if( !Pos2Page( nBytePos ) )
452 return NULL;
453 return rIo.Get( nPage, bForce );
456 // Copy an entire stream. Both streams are allocated in the FAT.
457 // The target stream is this stream.
459 bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes )
461 if ( !pFat )
462 return false;
464 m_aPagesCache.clear();
466 sal_Int32 nTo = nStart;
467 sal_Int32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize;
468 while( nPgs-- )
470 if( nTo < 0 )
472 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
473 return false;
475 rIo.Copy( nTo, nFrom );
476 if( nFrom >= 0 )
478 nFrom = pFat->GetNextPage( nFrom );
479 if( nFrom < 0 )
481 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
482 return false;
485 nTo = pFat->GetNextPage( nTo );
487 return true;
490 bool StgStrm::SetSize( sal_Int32 nBytes )
492 if ( nBytes < 0 || !pFat )
493 return false;
495 m_aPagesCache.clear();
497 // round up to page size
498 sal_Int32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize;
499 sal_Int32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
500 if( nNew > nOld )
502 if( !Pos2Page( nSize ) )
503 return false;
504 sal_Int32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize );
505 if( nBgn == STG_EOF )
506 return false;
507 if( nStart == STG_EOF )
508 nStart = nPage = nBgn;
510 else if( nNew < nOld )
512 bool bAll = ( nBytes == 0 );
513 if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) )
514 return false;
515 if( bAll )
516 nStart = nPage = STG_EOF;
518 if( pEntry )
520 // change the dir entry?
521 if( !nSize || !nBytes )
522 pEntry->aEntry.SetLeaf( STG_DATA, nStart );
523 pEntry->aEntry.SetSize( nBytes );
524 pEntry->SetDirty();
526 nSize = nBytes;
527 pFat->SetLimit( GetPages() );
528 return true;
531 // Return the # of allocated pages
533 sal_Int32 StgStrm::GetPages() const
535 return ( nSize + nPageSize - 1 ) / nPageSize;
538 //////////////////////////// class StgFATStrm //////////////////////////////
540 // The FAT stream class provides physical access to the master FAT.
541 // Since this access is implemented as a StgStrm, we can use the
542 // FAT allocator.
544 StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r )
546 pFat = new StgFAT( *this, true );
547 nSize = rIo.aHdr.GetFATSize() * nPageSize;
550 bool StgFATStrm::Pos2Page( sal_Int32 nBytePos )
552 // Values < 0 seek to the end
553 if( nBytePos < 0 || nBytePos >= nSize )
554 nBytePos = nSize ? nSize - 1 : 0;
555 nPage = nBytePos / nPageSize;
556 nOffset = (short) ( nBytePos % nPageSize );
557 nPos = nBytePos;
558 nPage = GetPage( (short) nPage, false );
559 return nPage >= 0;
562 // Retrieve the physical page for a given byte offset.
563 // Since Pos2Page() already has computed the physical offset,
564 // use the byte offset directly.
566 rtl::Reference< StgPage > StgFATStrm::GetPhysPage( sal_Int32 nBytePos, bool bForce )
568 OSL_ENSURE( nBytePos >= 0, "The value may not be negative!" );
569 return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce );
572 // Get the page number entry for the given page offset.
574 sal_Int32 StgFATStrm::GetPage( short nOff, bool bMake, sal_uInt16 *pnMasterAlloc )
576 OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
577 if( pnMasterAlloc ) *pnMasterAlloc = 0;
578 if( nOff < rIo.aHdr.GetFAT1Size() )
579 return rIo.aHdr.GetFATPage( nOff );
580 sal_Int32 nMaxPage = nSize >> 2;
581 nOff = nOff - rIo.aHdr.GetFAT1Size();
582 // Anzahl der Masterpages, durch die wir iterieren muessen
583 sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1;
584 sal_uInt16 nBlocks = nOff / nMasterCount;
585 // Offset in letzter Masterpage
586 nOff = nOff % nMasterCount;
588 rtl::Reference< StgPage > pOldPage;
589 rtl::Reference< StgPage > pMaster;
590 sal_Int32 nFAT = rIo.aHdr.GetFATChain();
591 for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
593 if( nFAT == STG_EOF || nFAT == STG_FREE )
595 if( bMake )
597 m_aPagesCache.clear();
599 // create a new master page
600 nFAT = nMaxPage++;
601 pMaster = rIo.Copy( nFAT, STG_FREE );
602 if ( pMaster.is() )
604 for( short k = 0; k < ( nPageSize >> 2 ); k++ )
605 rIo.SetToPage( pMaster, k, STG_FREE );
606 // Verkettung herstellen
607 if( !pOldPage.is() )
608 rIo.aHdr.SetFATChain( nFAT );
609 else
610 rIo.SetToPage( pOldPage, nMasterCount, nFAT );
611 if( nMaxPage >= rIo.GetPhysPages() )
612 if( !rIo.SetSize( nMaxPage ) )
613 return STG_EOF;
614 // mark the page as used
615 // Platz fuer Masterpage schaffen
616 if( !pnMasterAlloc ) // Selbst Platz schaffen
618 if( !Pos2Page( nFAT << 2 ) )
619 return STG_EOF;
620 rtl::Reference< StgPage > pPg = rIo.Get( nPage, true );
621 if( !pPg.is() )
622 return STG_EOF;
623 rIo.SetToPage( pPg, nOffset >> 2, STG_MASTER );
625 else
626 (*pnMasterAlloc)++;
627 rIo.aHdr.SetMasters( nCount + 1 );
628 pOldPage = pMaster;
632 else
634 pMaster = rIo.Get( nFAT, true );
635 if ( pMaster.is() )
637 nFAT = rIo.GetFromPage( pMaster, nMasterCount );
638 pOldPage = pMaster;
642 if( pMaster.is() )
643 return rIo.GetFromPage( pMaster, nOff );
644 rIo.SetError( SVSTREAM_GENERALERROR );
645 return STG_EOF;
649 // Set the page number entry for the given page offset.
651 bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
653 OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
654 m_aPagesCache.clear();
656 bool bRes = true;
657 if( nOff < rIo.aHdr.GetFAT1Size() )
658 rIo.aHdr.SetFATPage( nOff, nNewPage );
659 else
661 nOff = nOff - rIo.aHdr.GetFAT1Size();
662 // Anzahl der Masterpages, durch die wir iterieren muessen
663 sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1;
664 sal_uInt16 nBlocks = nOff / nMasterCount;
665 // Offset in letzter Masterpage
666 nOff = nOff % nMasterCount;
668 rtl::Reference< StgPage > pMaster;
669 sal_Int32 nFAT = rIo.aHdr.GetFATChain();
670 for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
672 if( nFAT == STG_EOF || nFAT == STG_FREE )
674 pMaster = 0;
675 break;
677 pMaster = rIo.Get( nFAT, true );
678 if ( pMaster.is() )
679 nFAT = rIo.GetFromPage( pMaster, nMasterCount );
681 if( pMaster.is() )
682 rIo.SetToPage( pMaster, nOff, nNewPage );
683 else
685 rIo.SetError( SVSTREAM_GENERALERROR );
686 bRes = false;
690 // lock the page against access
691 if( bRes )
693 Pos2Page( nNewPage << 2 );
694 rtl::Reference< StgPage > pPg = rIo.Get( nPage, true );
695 if( pPg.is() )
696 rIo.SetToPage( pPg, nOffset >> 2, STG_FAT );
697 else
698 bRes = false;
700 return bRes;
703 bool StgFATStrm::SetSize( sal_Int32 nBytes )
705 if ( nBytes < 0 )
706 return false;
708 m_aPagesCache.clear();
710 // Set the number of entries to a multiple of the page size
711 short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize );
712 short nNew = (short) (
713 ( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ;
714 if( nNew < nOld )
716 // release master pages
717 for( short i = nNew; i < nOld; i++ )
718 SetPage( i, STG_FREE );
720 else
722 while( nOld < nNew )
724 // allocate master pages
725 // find a free master page slot
726 sal_Int32 nPg = 0;
727 sal_uInt16 nMasterAlloc = 0;
728 nPg = GetPage( nOld, true, &nMasterAlloc );
729 if( nPg == STG_EOF )
730 return false;
731 // 4 Bytes have been used for Allocation of each MegaMasterPage
732 nBytes += nMasterAlloc << 2;
734 // find a free page using the FAT allocator
735 sal_Int32 n = 1;
736 OSL_ENSURE( pFat, "The pointer is always initializer here!" );
737 sal_Int32 nNewPage = pFat->FindBlock( n );
738 if( nNewPage == STG_EOF )
740 // no free pages found; create a new page
741 // Since all pages are allocated, extend
742 // the file size for the next page!
743 nNewPage = nSize >> 2;
744 // if a MegaMasterPage was created avoid taking
745 // the same Page
746 nNewPage += nMasterAlloc;
747 // adjust the file size if necessary
748 if( nNewPage >= rIo.GetPhysPages() )
749 if( !rIo.SetSize( nNewPage + 1 ) )
750 return false;
752 // Set up the page with empty entries
753 rtl::Reference< StgPage > pPg = rIo.Copy( nNewPage, STG_FREE );
754 if ( !pPg.is() )
755 return false;
756 for( short j = 0; j < ( nPageSize >> 2 ); j++ )
757 rIo.SetToPage( pPg, j, STG_FREE );
759 // store the page number into the master FAT
760 // Set the size before so the correct FAT can be found
761 nSize = ( nOld + 1 ) * nPageSize;
762 SetPage( nOld, nNewPage );
764 // MegaMasterPages were created, mark it them as used
766 sal_uInt32 nMax = rIo.aHdr.GetMasters( );
767 sal_uInt32 nFAT = rIo.aHdr.GetFATChain();
768 if( nMasterAlloc )
769 for( sal_uInt16 nCount = 0; nCount < nMax; nCount++ )
771 if( !Pos2Page( nFAT << 2 ) )
772 return false;
773 if( nMax - nCount <= nMasterAlloc )
775 rtl::Reference< StgPage > piPg = rIo.Get( nPage, true );
776 if( !piPg.is() )
777 return false;
778 rIo.SetToPage( piPg, nOffset >> 2, STG_MASTER );
780 rtl::Reference< StgPage > pPage = rIo.Get( nFAT, true );
781 if( !pPage.is() ) return false;
782 nFAT = rIo.GetFromPage( pPage, (nPageSize >> 2 ) - 1 );
785 nOld++;
786 // We have used up 4 bytes for the STG_FAT entry
787 nBytes += 4;
788 nNew = (short) (
789 ( nBytes + ( nPageSize - 1 ) ) / nPageSize );
792 nSize = nNew * nPageSize;
793 rIo.aHdr.SetFATSize( nNew );
794 return true;
797 /////////////////////////// class StgDataStrm //////////////////////////////
799 // This class is a normal physical stream which can be initialized
800 // either with an existing dir entry or an existing FAT chain.
801 // The stream has a size increment which normally is 1, but which can be
802 // set to any value is you want the size to be incremented by certain values.
804 StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
806 Init( nBgn, nLen );
809 StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
811 pEntry = &p;
812 Init( p.aEntry.GetLeaf( STG_DATA ),
813 p.aEntry.GetSize() );
816 void StgDataStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
818 if ( rIo.pFAT )
819 pFat = new StgFAT( *rIo.pFAT, true );
821 OSL_ENSURE( pFat, "The pointer should not be empty!" );
823 nStart = nPage = nBgn;
824 nSize = nLen;
825 nIncr = 1;
826 nOffset = 0;
827 if( nLen < 0 && pFat )
829 // determine the actual size of the stream by scanning
830 // the FAT chain and counting the # of pages allocated
831 scanBuildPageChainCache( &nSize );
835 // Set the size of a physical stream.
837 bool StgDataStrm::SetSize( sal_Int32 nBytes )
839 if ( !pFat )
840 return false;
842 nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr;
843 sal_Int32 nOldSz = nSize;
844 if( ( nOldSz != nBytes ) )
846 if( !StgStrm::SetSize( nBytes ) )
847 return false;
848 sal_Int32 nMaxPage = pFat->GetMaxPage();
849 if( nMaxPage > rIo.GetPhysPages() )
850 if( !rIo.SetSize( nMaxPage ) )
851 return false;
852 // If we only allocated one page or less, create this
853 // page in the cache for faster throughput. The current
854 // position is the former EOF point.
855 if( ( nSize - 1 ) / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 )
857 Pos2Page( nBytes );
858 if( nPage >= 0 )
859 rIo.Copy( nPage, STG_FREE );
862 return true;
865 // Get the address of the data byte at a specified offset.
866 // If bForce = true, a read of non-existent data causes
867 // a read fault.
869 void* StgDataStrm::GetPtr( sal_Int32 Pos, bool bForce, bool bDirty )
871 if( Pos2Page( Pos ) )
873 rtl::Reference< StgPage > pPg = rIo.Get( nPage, bForce );
874 if (pPg.is() && nOffset < pPg->GetSize())
876 if( bDirty )
877 rIo.SetDirty( pPg );
878 return ((sal_uInt8 *)pPg->GetData()) + nOffset;
881 return NULL;
884 // This could easily be adapted to a better algorithm by determining
885 // the amount of consecutable blocks before doing a read. The result
886 // is the number of bytes read. No error is generated on EOF.
888 sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n )
890 if ( n < 0 )
891 return 0;
893 if( ( nPos + n ) > nSize )
894 n = nSize - nPos;
895 sal_Int32 nDone = 0;
896 while( n )
898 short nBytes = nPageSize - nOffset;
899 rtl::Reference< StgPage > pPg;
900 if( (sal_Int32) nBytes > n )
901 nBytes = (short) n;
902 if( nBytes )
904 short nRes;
905 void *p = (sal_uInt8 *) pBuf + nDone;
906 if( nBytes == nPageSize )
908 pPg = rIo.Find( nPage );
909 if( pPg.is() )
911 // data is present, so use the cached data
912 memcpy( p, pPg->GetData(), nBytes );
913 nRes = nBytes;
915 else
916 // do a direct (unbuffered) read
917 nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize;
919 else
921 // partial block read thru the cache.
922 pPg = rIo.Get( nPage, false );
923 if( !pPg.is() )
924 break;
925 memcpy( p, (sal_uInt8*)pPg->GetData() + nOffset, nBytes );
926 nRes = nBytes;
928 nDone += nRes;
929 nPos += nRes;
930 n -= nRes;
931 nOffset = nOffset + nRes;
932 if( nRes != nBytes )
933 break; // read error or EOF
935 // Switch to next page if necessary
936 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
937 break;
939 return nDone;
942 sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n )
944 if ( n < 0 )
945 return 0;
947 sal_Int32 nDone = 0;
948 if( ( nPos + n ) > nSize )
950 sal_Int32 nOld = nPos;
951 if( !SetSize( nPos + n ) )
952 return 0;
953 Pos2Page( nOld );
955 while( n )
957 short nBytes = nPageSize - nOffset;
958 rtl::Reference< StgPage > pPg;
959 if( (sal_Int32) nBytes > n )
960 nBytes = (short) n;
961 if( nBytes )
963 short nRes;
964 const void *p = (const sal_uInt8 *) pBuf + nDone;
965 if( nBytes == nPageSize )
967 pPg = rIo.Find( nPage );
968 if( pPg.is() )
970 // data is present, so use the cached data
971 memcpy( pPg->GetData(), p, nBytes );
972 rIo.SetDirty( pPg );
973 nRes = nBytes;
975 else
976 // do a direct (unbuffered) write
977 nRes = (short) rIo.Write( nPage, (void*) p, 1 ) * nPageSize;
979 else
981 // partial block read thru the cache.
982 pPg = rIo.Get( nPage, false );
983 if( !pPg.is() )
984 break;
985 memcpy( (sal_uInt8*)pPg->GetData() + nOffset, p, nBytes );
986 rIo.SetDirty( pPg );
987 nRes = nBytes;
989 nDone += nRes;
990 nPos += nRes;
991 n -= nRes;
992 nOffset = nOffset + nRes;
993 if( nRes != nBytes )
994 break; // read error
996 // Switch to next page if necessary
997 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
998 break;
1000 return nDone;
1003 //////////////////////////// class StgSmallStream ///////////////////////////
1005 // The small stream class provides access to streams with a size < 4096 bytes.
1006 // This stream is a StgStream containing small pages. The FAT for this stream
1007 // is also a StgStream. The start of the FAT is in the header at DataRootPage,
1008 // the stream itself is pointed to by the root entry (it holds start & size).
1010 StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
1012 Init( nBgn, nLen );
1015 StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
1017 pEntry = &p;
1018 Init( p.aEntry.GetLeaf( STG_DATA ),
1019 p.aEntry.GetSize() );
1022 void StgSmallStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
1024 if ( rIo.pDataFAT )
1025 pFat = new StgFAT( *rIo.pDataFAT, false );
1026 pData = rIo.pDataStrm;
1027 OSL_ENSURE( pFat && pData, "The pointers should not be empty!" );
1029 nPageSize = rIo.GetDataPageSize();
1030 nStart =
1031 nPage = nBgn;
1032 nSize = nLen;
1035 // This could easily be adapted to a better algorithm by determining
1036 // the amount of consecutable blocks before doing a read. The result
1037 // is the number of bytes read. No error is generated on EOF.
1039 sal_Int32 StgSmallStrm::Read( void* pBuf, sal_Int32 n )
1041 // We can safely assume that reads are not huge, since the
1042 // small stream is likely to be < 64 KBytes.
1043 if( ( nPos + n ) > nSize )
1044 n = nSize - nPos;
1045 short nDone = 0;
1046 while( n )
1048 short nBytes = nPageSize - nOffset;
1049 if( (sal_Int32) nBytes > n )
1050 nBytes = (short) n;
1051 if( nBytes )
1053 if( !pData || !pData->Pos2Page( nPage * nPageSize + nOffset ) )
1054 break;
1055 // all reading thru the stream
1056 short nRes = (short) pData->Read( (sal_uInt8*)pBuf + nDone, nBytes );
1057 nDone = nDone + nRes;
1058 nPos += nRes;
1059 n -= nRes;
1060 nOffset = nOffset + nRes;
1061 // read problem?
1062 if( nRes != nBytes )
1063 break;
1065 // Switch to next page if necessary
1066 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1067 break;
1069 return nDone;
1072 sal_Int32 StgSmallStrm::Write( const void* pBuf, sal_Int32 n )
1074 // you can safely assume that reads are not huge, since the
1075 // small stream is likely to be < 64 KBytes.
1076 short nDone = 0;
1077 if( ( nPos + n ) > nSize )
1079 sal_Int32 nOld = nPos;
1080 if( !SetSize( nPos + n ) )
1081 return false;
1082 Pos2Page( nOld );
1084 while( n )
1086 short nBytes = nPageSize - nOffset;
1087 if( (sal_Int32) nBytes > n )
1088 nBytes = (short) n;
1089 if( nBytes )
1091 // all writing goes thru the stream
1092 sal_Int32 nDataPos = nPage * nPageSize + nOffset;
1093 if ( !pData
1094 || ( pData->GetSize() < ( nDataPos + nBytes )
1095 && !pData->SetSize( nDataPos + nBytes ) ) )
1096 break;
1097 if( !pData->Pos2Page( nDataPos ) )
1098 break;
1099 short nRes = (short) pData->Write( (sal_uInt8*)pBuf + nDone, nBytes );
1100 nDone = nDone + nRes;
1101 nPos += nRes;
1102 n -= nRes;
1103 nOffset = nOffset + nRes;
1104 // write problem?
1105 if( nRes != nBytes )
1106 break;
1108 // Switch to next page if necessary
1109 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1110 break;
1112 return nDone;
1115 /////////////////////////// class StgTmpStrm /////////////////////////////
1117 // The temporary stream uses a memory stream if < 32K, otherwise a
1118 // temporary file.
1120 #define THRESHOLD 32768L
1122 StgTmpStrm::StgTmpStrm( sal_uLong nInitSize )
1123 : SvMemoryStream( nInitSize > THRESHOLD
1124 ? 16
1125 : ( nInitSize ? nInitSize : 16 ), 4096 )
1127 pStrm = NULL;
1128 // this calls FlushData, so all members should be set by this time
1129 SetBufferSize( 0 );
1130 if( nInitSize > THRESHOLD )
1131 SetSize( nInitSize );
1134 bool StgTmpStrm::Copy( StgTmpStrm& rSrc )
1136 sal_uLong n = rSrc.GetSize();
1137 sal_uLong nCur = rSrc.Tell();
1138 SetSize( n );
1139 if( GetError() == SVSTREAM_OK )
1141 sal_uInt8* p = new sal_uInt8[ 4096 ];
1142 rSrc.Seek( 0L );
1143 Seek( 0L );
1144 while( n )
1146 sal_uLong nn = n;
1147 if( nn > 4096 )
1148 nn = 4096;
1149 if( rSrc.Read( p, nn ) != nn )
1150 break;
1151 if( Write( p, nn ) != nn )
1152 break;
1153 n -= nn;
1155 delete [] p;
1156 rSrc.Seek( nCur );
1157 Seek( nCur );
1158 return n == 0;
1160 else
1161 return false;
1164 StgTmpStrm::~StgTmpStrm()
1166 if( pStrm )
1168 pStrm->Close();
1169 osl::File::remove( aName );
1170 delete pStrm;
1174 sal_uLong StgTmpStrm::GetSize() const
1176 sal_uLong n;
1177 if( pStrm )
1179 sal_uLong old = pStrm->Tell();
1180 n = pStrm->Seek( STREAM_SEEK_TO_END );
1181 pStrm->Seek( old );
1183 else
1184 n = nEndOfData;
1185 return n;
1188 void StgTmpStrm::SetSize( sal_uLong n )
1190 if( pStrm )
1191 pStrm->SetStreamSize( n );
1192 else
1194 if( n > THRESHOLD )
1196 aName = utl::TempFile::CreateTempName();
1197 SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE );
1198 sal_uLong nCur = Tell();
1199 sal_uLong i = nEndOfData;
1200 if( i )
1202 sal_uInt8* p = new sal_uInt8[ 4096 ];
1203 Seek( 0L );
1204 while( i )
1206 sal_uLong nb = ( i > 4096 ) ? 4096 : i;
1207 if( Read( p, nb ) == nb
1208 && s->Write( p, nb ) == nb )
1209 i -= nb;
1210 else
1211 break;
1213 delete [] p;
1215 if( !i && n > nEndOfData )
1217 // We have to write one byte at the end of the file
1218 // if the file is bigger than the memstream to see
1219 // if it fits on disk
1220 s->Seek( n - 1 );
1221 s->Write( &i, 1 );
1222 s->Flush();
1223 if( s->GetError() != SVSTREAM_OK )
1224 i = 1;
1226 Seek( nCur );
1227 s->Seek( nCur );
1228 if( i )
1230 SetError( s->GetError() );
1231 delete s;
1232 return;
1234 pStrm = s;
1235 // Shrink the memory to 16 bytes, which seems to be the minimum
1236 ReAllocateMemory( - ( (long) nEndOfData - 16 ) );
1238 else
1240 if( n > nEndOfData )
1242 sal_uLong nCur = Tell();
1243 Seek( nEndOfData - 1 );
1244 *this << (sal_uInt8) 0;
1245 Seek( nCur );
1247 else
1248 nEndOfData = n;
1253 sal_uLong StgTmpStrm::GetData( void* pData, sal_uLong n )
1255 if( pStrm )
1257 n = pStrm->Read( pData, n );
1258 SetError( pStrm->GetError() );
1259 return n;
1261 else
1262 return SvMemoryStream::GetData( (sal_Char *)pData, n );
1265 sal_uLong StgTmpStrm::PutData( const void* pData, sal_uLong n )
1267 sal_uInt32 nCur = Tell();
1268 sal_uInt32 nNew = nCur + n;
1269 if( nNew > THRESHOLD && !pStrm )
1271 SetSize( nNew );
1272 if( GetError() != SVSTREAM_OK )
1273 return 0;
1275 if( pStrm )
1277 nNew = pStrm->Write( pData, n );
1278 SetError( pStrm->GetError() );
1280 else
1281 nNew = SvMemoryStream::PutData( (sal_Char*)pData, n );
1282 return nNew;
1285 sal_uLong StgTmpStrm::SeekPos( sal_uLong n )
1287 if( n == STREAM_SEEK_TO_END )
1288 n = GetSize();
1289 if( n && n > THRESHOLD && !pStrm )
1291 SetSize( n );
1292 if( GetError() != SVSTREAM_OK )
1293 return Tell();
1294 else
1295 return n;
1297 else if( pStrm )
1299 n = pStrm->Seek( n );
1300 SetError( pStrm->GetError() );
1301 return n;
1303 else
1304 return SvMemoryStream::SeekPos( n );
1307 void StgTmpStrm::FlushData()
1309 if( pStrm )
1311 pStrm->Flush();
1312 SetError( pStrm->GetError() );
1314 else
1315 SvMemoryStream::FlushData();
1318 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */