xref: /AOO41X/main/sot/source/sdstor/stgstrms.cxx (revision 7b6b9ddb4b63a97ea0214b9472b5270bbf674949)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sot.hxx"
26 
27 #include <string.h>     // memcpy()
28 
29 #include <osl/file.hxx>
30 #include <tools/tempfile.hxx>
31 #include <tools/debug.hxx>
32 
33 #include "sot/stg.hxx"
34 #include "stgelem.hxx"
35 #include "stgcache.hxx"
36 #include "stgstrms.hxx"
37 #include "stgdir.hxx"
38 #include "stgio.hxx"
39 
40 #define __HUGE
41 
42 ///////////////////////////// class StgFAT ///////////////////////////////
43 
44 // The FAT class performs FAT operations on an underlying storage stream.
45 // This stream is either the master FAT stream (m == sal_True ) or a normal
46 // storage stream, which then holds the FAT for small data allocations.
47 
48 StgFAT::StgFAT( StgStrm& r, sal_Bool m ) : rStrm( r )
49 {
50     bPhys   = m;
51     nPageSize = rStrm.GetIo().GetPhysPageSize();
52     nEntries  = nPageSize >> 2;
53     nOffset   = 0;
54     nMaxPage  = 0;
55     nLimit    = 0;
56 }
57 
58 // Retrieve the physical page for a given byte offset.
59 
60 StgPage* StgFAT::GetPhysPage( sal_Int32 nByteOff )
61 {
62     StgPage* pPg = NULL;
63     // Position within the underlying stream
64     // use the Pos2Page() method of the stream
65     if( rStrm.Pos2Page( nByteOff ) )
66     {
67         nOffset = rStrm.GetOffset();
68         sal_Int32 nPhysPage = rStrm.GetPage();
69         // get the physical page (must be present)
70         pPg = rStrm.GetIo().Get( nPhysPage, sal_True );
71     }
72     return pPg;
73 }
74 
75 // Get the follow page for a certain FAT page.
76 
77 sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg )
78 {
79     if( nPg >= 0 )
80     {
81       StgPage* pPg = GetPhysPage( nPg << 2 );
82         nPg = pPg ? pPg->GetPage( nOffset >> 2 ) : STG_EOF;
83     }
84     return nPg;
85 }
86 
87 // Find the best fit block for the given size. Return
88 // the starting block and its size or STG_EOF and 0.
89 // nLastPage is a stopper which tells the current
90 // underlying stream size. It is treated as a recommendation
91 // to abort the search to inhibit excessive file growth.
92 
93 sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs )
94 {
95     sal_Int32 nMinStart = STG_EOF, nMinLen = 0;
96     sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
97     sal_Int32 nTmpStart = STG_EOF, nTmpLen = 0;
98     sal_Int32 nPages    = rStrm.GetSize() >> 2;
99     sal_Bool bFound     = sal_False;
100     StgPage* pPg = NULL;
101     short nEntry = 0;
102     for( sal_Int32 i = 0; i < nPages; i++, nEntry++ )
103     {
104         if( !( nEntry % nEntries ) )
105         {
106             // load the next page for that stream
107             nEntry = 0;
108             pPg = GetPhysPage( i << 2 );
109             if( !pPg )
110                 return STG_EOF;
111         }
112         sal_Int32 nCur = pPg->GetPage( nEntry );
113         if( nCur == STG_FREE )
114         {
115             // count the size of this area
116             if( nTmpLen )
117                 nTmpLen++;
118             else
119                 nTmpStart = i,
120                 nTmpLen   = 1;
121             if( nTmpLen == nPgs
122              // If we already did find a block, stop when reaching the limit
123              || ( bFound && ( nEntry >= nLimit ) ) )
124                 break;
125         }
126         else if( nTmpLen )
127         {
128             if( nTmpLen > nPgs && nTmpLen < nMaxLen )
129                 // block > requested size
130                 nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = sal_True;
131             else if( nTmpLen >= nMinLen )
132             {
133                 // block < requested size
134                 nMinLen = nTmpLen, nMinStart = nTmpStart;
135                 bFound = sal_True;
136                 if( nTmpLen == nPgs )
137                     break;
138             }
139             nTmpStart = STG_EOF;
140             nTmpLen   = 0;
141         }
142     }
143     // Determine which block to use.
144     if( nTmpLen )
145     {
146         if( nTmpLen > nPgs  && nTmpLen < nMaxLen )
147             // block > requested size
148             nMaxLen = nTmpLen, nMaxStart = nTmpStart;
149         else if( nTmpLen >= nMinLen )
150             // block < requested size
151             nMinLen = nTmpLen, nMinStart = nTmpStart;
152     }
153     if( nMinStart != STG_EOF && nMaxStart != STG_EOF )
154     {
155         // two areas found; return the best fit area
156         sal_Int32 nMinDiff = nPgs - nMinLen;
157         sal_Int32 nMaxDiff = nMaxLen - nPgs;
158         if( nMinDiff > nMaxDiff )
159             nMinStart = STG_EOF;
160     }
161     if( nMinStart != STG_EOF )
162     {
163         nPgs = nMinLen; return nMinStart;
164     }
165     else
166     {
167         return nMaxStart;
168     }
169 }
170 
171 // Set up the consecutive chain for a given block.
172 
173 sal_Bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs )
174 {
175     sal_Int32 nPos = nStart << 2;
176     StgPage* pPg = GetPhysPage( nPos );
177     if( !pPg || !nPgs )
178         return sal_False;
179     while( --nPgs )
180     {
181         if( nOffset >= nPageSize )
182         {
183             pPg = GetPhysPage( nPos );
184             if( !pPg )
185                 return sal_False;
186         }
187         pPg->SetPage( nOffset >> 2, ++nStart );
188         nOffset += 4;
189         nPos += 4;
190     }
191     if( nOffset >= nPageSize )
192     {
193         pPg = GetPhysPage( nPos );
194         if( !pPg )
195             return sal_False;
196     }
197     pPg->SetPage( nOffset >> 2, STG_EOF );
198     return sal_True;
199 }
200 
201 // Allocate a block of data from the given page number on.
202 // It the page number is != STG_EOF, chain the block.
203 
204 sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs )
205 {
206     sal_Int32 nOrig = nBgn;
207     sal_Int32 nLast = nBgn;
208     sal_Int32 nBegin = STG_EOF;
209     sal_Int32 nAlloc;
210     sal_Int32 nPages = rStrm.GetSize() >> 2;
211     short nPasses = 0;
212     // allow for two passes
213     while( nPasses < 2 )
214     {
215         // try to satisfy the request from the pool of free pages
216         while( nPgs )
217         {
218             nAlloc = nPgs;
219             nBegin = FindBlock( nAlloc );
220             // no more blocks left in present alloc chain
221             if( nBegin == STG_EOF )
222                 break;
223             if( ( nBegin + nAlloc ) > nMaxPage )
224                 nMaxPage = nBegin + nAlloc;
225             if( !MakeChain( nBegin, nAlloc ) )
226                 return STG_EOF;
227             if( nOrig == STG_EOF )
228                 nOrig = nBegin;
229             else
230             {
231                 // Patch the chain
232                 StgPage* pPg = GetPhysPage( nLast << 2 );
233                 if( !pPg )
234                     return STG_EOF;
235                 pPg->SetPage( nOffset >> 2, nBegin );
236             }
237             nLast = nBegin + nAlloc - 1;
238             nPgs -= nAlloc;
239         }
240         if( nPgs && !nPasses )
241         {
242             // we need new, fresh space, so allocate and retry
243             if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
244                 return STG_EOF;
245             if( !bPhys && !InitNew( nPages ) )
246                 return sal_False;
247             nPages = rStrm.GetSize() >> 2;
248             nPasses++;
249         }
250         else
251             break;
252     }
253     // now we should have a chain for the complete block
254     if( nBegin == STG_EOF || nPgs )
255     {
256         rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
257         return STG_EOF; // bad structure
258     }
259     return nOrig;
260 }
261 
262 // Initialize newly allocated pages for a standard FAT stream
263 // It can be assumed that the stream size is always on
264 // a page boundary
265 
266 sal_Bool StgFAT::InitNew( sal_Int32 nPage1 )
267 {
268     sal_Int32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries;
269     if ( n > 0 )
270     {
271         while( n-- )
272         {
273             StgPage* pPg = NULL;
274             // Position within the underlying stream
275             // use the Pos2Page() method of the stream
276             rStrm.Pos2Page( nPage1 << 2 );
277             // Initialize the page
278             pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE );
279             if ( !pPg )
280                 return sal_False;
281             for( short i = 0; i < nEntries; i++ )
282                 pPg->SetPage( i, STG_FREE );
283             nPage1++;
284         }
285     }
286     return sal_True;
287 }
288 
289 // Release a chain
290 
291 sal_Bool StgFAT::FreePages( sal_Int32 nStart, sal_Bool bAll )
292 {
293     while( nStart >= 0 )
294     {
295         StgPage* pPg = GetPhysPage( nStart << 2 );
296         if( !pPg )
297             return sal_False;
298         nStart = pPg->GetPage( nOffset >> 2 );
299         // The first released page is either set to EOF or FREE
300         pPg->SetPage( nOffset >> 2, bAll ? STG_FREE : STG_EOF );
301         bAll = sal_True;
302     }
303     return sal_True;
304 }
305 
306 ///////////////////////////// class StgStrm ////////////////////////////////
307 
308 // The base stream class provides basic functionality for seeking
309 // and accessing the data on a physical basis. It uses the built-in
310 // FAT class for the page allocations.
311 
312 StgStrm::StgStrm( StgIo& r ) : rIo( r )
313 {
314     pFat    = NULL;
315     nStart  = nPage = STG_EOF;
316     nOffset = 0;
317     pEntry  = NULL;
318     nPos = nSize = 0;
319     nPageSize = rIo.GetPhysPageSize();
320 }
321 
322 StgStrm::~StgStrm()
323 {
324     delete pFat;
325 }
326 
327 // Attach the stream to the given entry.
328 
329 void StgStrm::SetEntry( StgDirEntry& r )
330 {
331     r.aEntry.SetLeaf( STG_DATA, nStart );
332     r.aEntry.SetSize( nSize );
333     pEntry = &r;
334     r.SetDirty();
335 }
336 
337 // Compute page number and offset for the given byte position.
338 // If the position is behind the size, set the stream right
339 // behind the EOF.
340 
341 sal_Bool StgStrm::Pos2Page( sal_Int32 nBytePos )
342 {
343     if ( !pFat )
344         return sal_False;
345 
346     sal_Int32 nRel, nBgn;
347     // Values < 0 seek to the end
348     if( nBytePos < 0 || nBytePos >= nSize )
349         nBytePos = nSize;
350     // Adjust the position back to offset 0
351     nPos -= nOffset;
352     sal_Int32 nMask = ~( nPageSize - 1 );
353     sal_Int32 nOld = nPos & nMask;
354     sal_Int32 nNew = nBytePos & nMask;
355     nOffset = (short) ( nBytePos & ~nMask );
356     nPos = nBytePos;
357     if( nOld == nNew )
358         return sal_True;
359     if( nNew > nOld )
360     {
361         // the new position is behind the current, so an incremental
362         // positioning is OK. Set the page relative position
363         nRel = nNew - nOld;
364         nBgn = nPage;
365     }
366     else
367     {
368         // the new position is before the current, so we have to scan
369         // the entire chain.
370         nRel = nNew;
371         nBgn = nStart;
372     }
373     // now, traverse the FAT chain.
374     nRel /= nPageSize;
375     sal_Int32 nLast = STG_EOF;
376     while( nRel && nBgn >= 0 )
377     {
378         nLast = nBgn;
379         nBgn = pFat->GetNextPage( nBgn );
380         nRel--;
381     }
382     // special case: seek to 1st byte of new, unallocated page
383     // (in case the file size is a multiple of the page size)
384     if( nBytePos == nSize && nBgn == STG_EOF && !nRel && !nOffset )
385         nBgn = nLast, nOffset = nPageSize;
386     if( nBgn < 0 && nBgn != STG_EOF )
387     {
388         rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
389         nBgn = STG_EOF;
390         nOffset = nPageSize;
391     }
392     nPage = nBgn;
393     return sal_Bool( nRel == 0 && nPage >= 0 );
394 }
395 
396 // Retrieve the physical page for a given byte offset.
397 
398 StgPage* StgStrm::GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce )
399 {
400     if( !Pos2Page( nBytePos ) )
401         return NULL;
402     return rIo.Get( nPage, bForce );
403 }
404 
405 // Copy an entire stream. Both streams are allocated in the FAT.
406 // The target stream is this stream.
407 
408 sal_Bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes )
409 {
410     if ( !pFat )
411         return sal_False;
412 
413     sal_Int32 nTo = nStart;
414     sal_Int32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize;
415     while( nPgs-- )
416     {
417         if( nTo < 0 )
418         {
419             rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
420             return sal_False;
421         }
422         rIo.Copy( nTo, nFrom );
423         if( nFrom >= 0 )
424         {
425             nFrom = pFat->GetNextPage( nFrom );
426             if( nFrom < 0 )
427             {
428                 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
429                 return sal_False;
430             }
431         }
432         nTo = pFat->GetNextPage( nTo );
433     }
434     return sal_True;
435 }
436 
437 sal_Bool StgStrm::SetSize( sal_Int32 nBytes )
438 {
439     if ( nBytes < 0 || !pFat )
440         return sal_False;
441 
442     // round up to page size
443     sal_Int32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize;
444     sal_Int32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
445     if( nNew > nOld )
446     {
447         if( !Pos2Page( nSize ) )
448             return sal_False;
449         sal_Int32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize );
450         if( nBgn == STG_EOF )
451             return sal_False;
452         if( nStart == STG_EOF )
453             nStart = nPage = nBgn;
454     }
455     else if( nNew < nOld )
456     {
457         sal_Bool bAll = sal_Bool( nBytes == 0 );
458         if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) )
459             return sal_False;
460         if( bAll )
461             nStart = nPage = STG_EOF;
462     }
463     if( pEntry )
464     {
465         // change the dir entry?
466         if( !nSize || !nBytes )
467             pEntry->aEntry.SetLeaf( STG_DATA, nStart );
468         pEntry->aEntry.SetSize( nBytes );
469         pEntry->SetDirty();
470     }
471     nSize = nBytes;
472     pFat->SetLimit( GetPages() );
473     return sal_True;
474 }
475 
476 // Return the # of allocated pages
477 
478 sal_Int32 StgStrm::GetPages()
479 {
480     return ( nSize + nPageSize - 1 ) / nPageSize;
481 }
482 
483 //////////////////////////// class StgFATStrm //////////////////////////////
484 
485 // The FAT stream class provides physical access to the master FAT.
486 // Since this access is implemented as a StgStrm, we can use the
487 // FAT allocator.
488 
489 StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r )
490 {
491     pFat = new StgFAT( *this, sal_True );
492     nSize = rIo.aHdr.GetFATSize() * nPageSize;
493 }
494 
495 sal_Bool StgFATStrm::Pos2Page( sal_Int32 nBytePos )
496 {
497     // Values < 0 seek to the end
498     if( nBytePos < 0 || nBytePos >= nSize  )
499         nBytePos = nSize ? nSize - 1 : 0;
500     nPage   = nBytePos / nPageSize;
501     nOffset = (short) ( nBytePos % nPageSize );
502     nPos    = nBytePos;
503     nPage   = GetPage( (short) nPage, sal_False );
504     return sal_Bool( nPage >= 0 );
505 }
506 
507 // Retrieve the physical page for a given byte offset.
508 // Since Pos2Page() already has computed the physical offset,
509 // use the byte offset directly.
510 
511 StgPage* StgFATStrm::GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce )
512 {
513     OSL_ENSURE( nBytePos >= 0, "The value may not be negative!" );
514     return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce );
515 }
516 
517 // Get the page number entry for the given page offset.
518 
519 sal_Int32 StgFATStrm::GetPage( short nOff, sal_Bool bMake, sal_uInt16 *pnMasterAlloc )
520 {
521     OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
522     if( pnMasterAlloc ) *pnMasterAlloc = 0;
523     if( nOff < rIo.aHdr.GetFAT1Size() )
524         return rIo.aHdr.GetFATPage( nOff );
525     sal_Int32 nMaxPage = nSize >> 2;
526     nOff = nOff - rIo.aHdr.GetFAT1Size();
527     // Anzahl der Masterpages, durch die wir iterieren muessen
528     sal_uInt16 nMasterCount =  ( nPageSize >> 2 ) - 1;
529     sal_uInt16 nBlocks = nOff / nMasterCount;
530     // Offset in letzter Masterpage
531     nOff = nOff % nMasterCount;
532 
533     StgPage* pOldPage = 0;
534     StgPage* pMaster = 0;
535     sal_Int32 nFAT = rIo.aHdr.GetFATChain();
536     for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
537     {
538         if( nFAT == STG_EOF || nFAT == STG_FREE )
539         {
540             if( bMake )
541             {
542                 // create a new master page
543                 nFAT = nMaxPage++;
544                 pMaster = rIo.Copy( nFAT, STG_FREE );
545                 if ( pMaster )
546                 {
547                     for( short k = 0; k < ( nPageSize >> 2 ); k++ )
548                         pMaster->SetPage( k, STG_FREE );
549                     // Verkettung herstellen
550                     if( !pOldPage )
551                         rIo.aHdr.SetFATChain( nFAT );
552                     else
553                         pOldPage->SetPage( nMasterCount, nFAT );
554                     if( nMaxPage >= rIo.GetPhysPages() )
555                         if( !rIo.SetSize( nMaxPage ) )
556                             return STG_EOF;
557                     // mark the page as used
558                     // Platz fuer Masterpage schaffen
559                     if( !pnMasterAlloc ) // Selbst Platz schaffen
560                     {
561                         if( !Pos2Page( nFAT << 2 ) )
562                             return STG_EOF;
563                         StgPage* pPg = rIo.Get( nPage, sal_True );
564                         if( !pPg )
565                             return STG_EOF;
566                         pPg->SetPage( nOffset >> 2, STG_MASTER );
567                     }
568                     else
569                         (*pnMasterAlloc)++;
570                     rIo.aHdr.SetMasters( nCount + 1 );
571                     pOldPage = pMaster;
572                 }
573             }
574         }
575         else
576         {
577             pMaster = rIo.Get( nFAT, sal_True );
578             if ( pMaster )
579             {
580                 nFAT = pMaster->GetPage( nMasterCount );
581                 pOldPage = pMaster;
582             }
583         }
584     }
585     if( pMaster )
586         return pMaster->GetPage( nOff );
587     rIo.SetError( SVSTREAM_GENERALERROR );
588     return STG_EOF;
589 }
590 
591 
592 // Set the page number entry for the given page offset.
593 
594 sal_Bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
595 {
596     OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
597     sal_Bool bRes = sal_True;
598     if( nOff < rIo.aHdr.GetFAT1Size() )
599         rIo.aHdr.SetFATPage( nOff, nNewPage );
600     else
601     {
602         nOff = nOff - rIo.aHdr.GetFAT1Size();
603         // Anzahl der Masterpages, durch die wir iterieren muessen
604         sal_uInt16 nMasterCount =  ( nPageSize >> 2 ) - 1;
605         sal_uInt16 nBlocks = nOff / nMasterCount;
606         // Offset in letzter Masterpage
607         nOff = nOff % nMasterCount;
608 
609         StgPage* pMaster = 0;
610         sal_Int32 nFAT = rIo.aHdr.GetFATChain();
611         for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
612         {
613             if( nFAT == STG_EOF || nFAT == STG_FREE )
614             {
615                 pMaster = 0;
616                 break;
617             }
618             pMaster = rIo.Get( nFAT, sal_True );
619             if ( pMaster )
620                 nFAT = pMaster->GetPage( nMasterCount );
621         }
622         if( pMaster )
623             pMaster->SetPage( nOff, nNewPage );
624         else
625         {
626             rIo.SetError( SVSTREAM_GENERALERROR );
627             bRes = sal_False;
628         }
629     }
630 
631     // lock the page against access
632     if( bRes )
633     {
634         Pos2Page( nNewPage << 2 );
635         StgPage* pPg = rIo.Get( nPage, sal_True );
636         if( pPg )
637             pPg->SetPage( nOffset >> 2, STG_FAT );
638         else
639             bRes = sal_False;
640     }
641     return bRes;
642 }
643 
644 sal_Bool StgFATStrm::SetSize( sal_Int32 nBytes )
645 {
646     if ( nBytes < 0 )
647         return sal_False;
648 
649     // Set the number of entries to a multiple of the page size
650     short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize );
651     short nNew = (short) (
652         ( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ;
653     if( nNew < nOld )
654     {
655         // release master pages
656         for( short i = nNew; i < nOld; i++ )
657             SetPage( i, STG_FREE );
658     }
659     else
660     {
661         while( nOld < nNew )
662         {
663             // allocate master pages
664             // find a free master page slot
665             sal_Int32 nPg = 0;
666             sal_uInt16 nMasterAlloc = 0;
667             nPg = GetPage( nOld, sal_True, &nMasterAlloc );
668             if( nPg == STG_EOF )
669                 return sal_False;
670             // 4 Bytes have been used for Allocation of each MegaMasterPage
671             nBytes += nMasterAlloc << 2;
672 
673             // find a free page using the FAT allocator
674             sal_Int32 n = 1;
675             OSL_ENSURE( pFat, "The pointer is always initializer here!" );
676             sal_Int32 nNewPage = pFat->FindBlock( n );
677             if( nNewPage == STG_EOF )
678             {
679                 // no free pages found; create a new page
680                 // Since all pages are allocated, extend
681                 // the file size for the next page!
682                 nNewPage = nSize >> 2;
683                 // if a MegaMasterPage was created avoid taking
684                 // the same Page
685                 nNewPage += nMasterAlloc;
686                 // adjust the file size if necessary
687                 if( nNewPage >= rIo.GetPhysPages() )
688                     if( !rIo.SetSize( nNewPage + 1 ) )
689                         return sal_False;
690             }
691             // Set up the page with empty entries
692             StgPage* pPg = rIo.Copy( nNewPage, STG_FREE );
693             if ( !pPg )
694                 return sal_False;
695             for( short j = 0; j < ( nPageSize >> 2 ); j++ )
696                 pPg->SetPage( j, STG_FREE );
697 
698             // store the page number into the master FAT
699             // Set the size before so the correct FAT can be found
700             nSize = ( nOld + 1 ) * nPageSize;
701             SetPage( nOld, nNewPage );
702 
703             // MegaMasterPages were created, mark it them as used
704 
705             sal_uInt32 nMax = rIo.aHdr.GetMasters( );
706             sal_uInt32 nFAT = rIo.aHdr.GetFATChain();
707             if( nMasterAlloc )
708                 for( sal_uInt16 nCount = 0; nCount < nMax; nCount++ )
709                 {
710                     if( !Pos2Page( nFAT << 2 ) )
711                         return sal_False;
712                     if( nMax - nCount <= nMasterAlloc )
713                     {
714                         StgPage* piPg = rIo.Get( nPage, sal_True );
715                         if( !piPg )
716                             return sal_False;
717                         piPg->SetPage( nOffset >> 2, STG_MASTER );
718                     }
719                     StgPage* pPage = rIo.Get( nFAT, sal_True );
720                     if( !pPage ) return sal_False;
721                     nFAT = pPage->GetPage( (nPageSize >> 2 ) - 1 );
722                 }
723 
724             nOld++;
725             // We have used up 4 bytes for the STG_FAT entry
726             nBytes += 4;
727             nNew = (short) (
728                 ( nBytes + ( nPageSize - 1 ) ) / nPageSize );
729         }
730     }
731     nSize = nNew * nPageSize;
732     rIo.aHdr.SetFATSize( nNew );
733     return sal_True;
734 }
735 
736 /////////////////////////// class StgDataStrm //////////////////////////////
737 
738 // This class is a normal physical stream which can be initialized
739 // either with an existing dir entry or an existing FAT chain.
740 // The stream has a size increment which normally is 1, but which can be
741 // set to any value is you want the size to be incremented by certain values.
742 
743 StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
744 {
745     Init( nBgn, nLen );
746 }
747 
748 StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
749 {
750     pEntry = &p;
751     Init( p.aEntry.GetLeaf( STG_DATA ),
752           p.aEntry.GetSize() );
753 }
754 
755 void StgDataStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
756 {
757     if ( rIo.pFAT )
758         pFat = new StgFAT( *rIo.pFAT, sal_True );
759 
760     OSL_ENSURE( pFat, "The pointer should not be empty!" );
761 
762     nStart = nPage = nBgn;
763     nSize  = nLen;
764     nIncr  = 1;
765     nOffset = 0;
766     if( nLen < 0 && pFat )
767     {
768         // determine the actual size of the stream by scanning
769         // the FAT chain and counting the # of pages allocated
770         nSize = 0;
771         sal_Int32 nOldBgn = -1;
772         while( nBgn >= 0 && nBgn != nOldBgn )
773         {
774             nOldBgn = nBgn;
775             nBgn = pFat->GetNextPage( nBgn );
776             if( nBgn == nOldBgn )
777                 rIo.SetError( ERRCODE_IO_WRONGFORMAT );
778             nSize += nPageSize;
779         }
780     }
781 }
782 
783 // Set the size of a physical stream.
784 
785 sal_Bool StgDataStrm::SetSize( sal_Int32 nBytes )
786 {
787     if ( !pFat )
788         return sal_False;
789 
790     nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr;
791     sal_Int32 nOldSz = nSize;
792     if( ( nOldSz != nBytes ) )
793     {
794         if( !StgStrm::SetSize( nBytes ) )
795             return sal_False;
796         sal_Int32 nMaxPage = pFat->GetMaxPage();
797         if( nMaxPage > rIo.GetPhysPages() )
798             if( !rIo.SetSize( nMaxPage ) )
799                 return sal_False;
800         // If we only allocated one page or less, create this
801         // page in the cache for faster throughput. The current
802         // position is the former EOF point.
803         if( ( nSize - 1 )  / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 )
804         {
805             Pos2Page( nBytes );
806             if( nPage >= 0 )
807                 rIo.Copy( nPage, STG_FREE );
808         }
809     }
810     return sal_True;
811 }
812 
813 // Get the address of the data byte at a specified offset.
814 // If bForce = sal_True, a read of non-existent data causes
815 // a read fault.
816 
817 void* StgDataStrm::GetPtr( sal_Int32 Pos, sal_Bool bForce, sal_Bool bDirty )
818 {
819     if( Pos2Page( Pos ) )
820     {
821         StgPage* pPg = rIo.Get( nPage, bForce );
822         if( pPg )
823         {
824             pPg->SetOwner( pEntry );
825             if( bDirty )
826                 pPg->SetDirty();
827             return ((sal_uInt8 *)pPg->GetData()) + nOffset;
828         }
829     }
830     return NULL;
831 }
832 
833 // This could easily be adapted to a better algorithm by determining
834 // the amount of consecutable blocks before doing a read. The result
835 // is the number of bytes read. No error is generated on EOF.
836 
837 sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n )
838 {
839     if ( n < 0 )
840         return 0;
841 
842     if( ( nPos + n ) > nSize )
843         n = nSize - nPos;
844     sal_Int32 nDone = 0;
845     while( n )
846     {
847         short nBytes = nPageSize - nOffset;
848         short nRes;
849         StgPage* pPg;
850         if( (sal_Int32) nBytes > n )
851             nBytes = (short) n;
852         if( nBytes )
853         {
854             void *p = (sal_uInt8 *) pBuf + nDone;
855             if( nBytes == nPageSize )
856             {
857                 pPg = rIo.Find( nPage );
858                 if( pPg )
859                 {
860                     // data is present, so use the cached data
861                     pPg->SetOwner( pEntry );
862                     memcpy( p, pPg->GetData(), nBytes );
863                     nRes = nBytes;
864                 }
865                 else
866                     // do a direct (unbuffered) read
867                     nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize;
868             }
869             else
870             {
871                 // partial block read thru the cache.
872                 pPg = rIo.Get( nPage, sal_False );
873                 if( !pPg )
874                     break;
875                 pPg->SetOwner( pEntry );
876                 memcpy( p, (sal_uInt8*)pPg->GetData() + nOffset, nBytes );
877                 nRes = nBytes;
878             }
879             nDone += nRes;
880             nPos += nRes;
881             n -= nRes;
882             nOffset = nOffset + nRes;
883             if( nRes != nBytes )
884                 break;  // read error or EOF
885         }
886         // Switch to next page if necessary
887         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
888             break;
889     }
890     return nDone;
891 }
892 
893 sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n )
894 {
895     if ( n < 0 )
896         return 0;
897 
898     sal_Int32 nDone = 0;
899     if( ( nPos + n ) > nSize )
900     {
901         sal_Int32 nOld = nPos;
902         if( !SetSize( nPos + n ) )
903             return 0;
904         Pos2Page( nOld );
905     }
906     while( n )
907     {
908         short nBytes = nPageSize - nOffset;
909         short nRes;
910         StgPage* pPg;
911         if( (sal_Int32) nBytes > n )
912             nBytes = (short) n;
913         if( nBytes )
914         {
915             const void *p = (const sal_uInt8 *) pBuf + nDone;
916             if( nBytes == nPageSize )
917             {
918                 pPg = rIo.Find( nPage );
919                 if( pPg )
920                 {
921                     // data is present, so use the cached data
922                     pPg->SetOwner( pEntry );
923                     memcpy( pPg->GetData(), p, nBytes );
924                     pPg->SetDirty();
925                     nRes = nBytes;
926                 }
927                 else
928                     // do a direct (unbuffered) write
929                     nRes = (short) rIo.Write( nPage, (void*) p, 1 ) * nPageSize;
930             }
931             else
932             {
933                 // partial block read thru the cache.
934                 pPg = rIo.Get( nPage, sal_False );
935                 if( !pPg )
936                     break;
937                 pPg->SetOwner( pEntry );
938                 memcpy( (sal_uInt8*)pPg->GetData() + nOffset, p, nBytes );
939                 pPg->SetDirty();
940                 nRes = nBytes;
941             }
942             nDone += nRes;
943             nPos += nRes;
944             n -= nRes;
945             nOffset = nOffset + nRes;
946             if( nRes != nBytes )
947                 break;  // read error
948         }
949         // Switch to next page if necessary
950         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
951             break;
952     }
953     return nDone;
954 }
955 
956 //////////////////////////// class StgSmallStream ///////////////////////////
957 
958 // The small stream class provides access to streams with a size < 4096 bytes.
959 // This stream is a StgStream containing small pages. The FAT for this stream
960 // is also a StgStream. The start of the FAT is in the header at DataRootPage,
961 // the stream itself is pointed to by the root entry (it holds start & size).
962 
963 StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
964 {
965     Init( nBgn, nLen );
966 }
967 
968 StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
969 {
970     pEntry = &p;
971     Init( p.aEntry.GetLeaf( STG_DATA ),
972           p.aEntry.GetSize() );
973 }
974 
975 void StgSmallStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
976 {
977     if ( rIo.pDataFAT )
978         pFat = new StgFAT( *rIo.pDataFAT, sal_False );
979     pData = rIo.pDataStrm;
980     OSL_ENSURE( pFat && pData, "The pointers should not be empty!" );
981 
982     nPageSize = rIo.GetDataPageSize();
983     nStart =
984     nPage  = nBgn;
985     nSize  = nLen;
986 }
987 
988 // This could easily be adapted to a better algorithm by determining
989 // the amount of consecutable blocks before doing a read. The result
990 // is the number of bytes read. No error is generated on EOF.
991 
992 sal_Int32 StgSmallStrm::Read( void* pBuf, sal_Int32 n )
993 {
994     // We can safely assume that reads are not huge, since the
995     // small stream is likely to be < 64 KBytes.
996     if( ( nPos + n ) > nSize )
997         n = nSize - nPos;
998     short nDone = 0;
999     while( n )
1000     {
1001         short nBytes = nPageSize - nOffset;
1002         if( (sal_Int32) nBytes > n )
1003             nBytes = (short) n;
1004         if( nBytes )
1005         {
1006             if( !pData || !pData->Pos2Page( nPage * nPageSize + nOffset ) )
1007                 break;
1008             // all reading thru the stream
1009             short nRes = (short) pData->Read( (sal_uInt8*)pBuf + nDone, nBytes );
1010             nDone = nDone + nRes;
1011             nPos += nRes;
1012             n -= nRes;
1013             nOffset = nOffset + nRes;
1014             // read problem?
1015             if( nRes != nBytes )
1016                 break;
1017         }
1018         // Switch to next page if necessary
1019         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1020             break;
1021     }
1022     return nDone;
1023 }
1024 
1025 sal_Int32 StgSmallStrm::Write( const void* pBuf, sal_Int32 n )
1026 {
1027     // you can safely assume that reads are not huge, since the
1028     // small stream is likely to be < 64 KBytes.
1029     short nDone = 0;
1030     if( ( nPos + n ) > nSize )
1031     {
1032         sal_Int32 nOld = nPos;
1033         if( !SetSize( nPos + n ) )
1034             return sal_False;
1035         Pos2Page( nOld );
1036     }
1037     while( n )
1038     {
1039         short nBytes = nPageSize - nOffset;
1040         if( (sal_Int32) nBytes > n )
1041             nBytes = (short) n;
1042         if( nBytes )
1043         {
1044             // all writing goes thru the stream
1045             sal_Int32 nDataPos = nPage * nPageSize + nOffset;
1046             if ( !pData
1047               || ( pData->GetSize() < ( nDataPos + nBytes )
1048                 && !pData->SetSize( nDataPos + nBytes ) ) )
1049                 break;
1050             if( !pData->Pos2Page( nDataPos ) )
1051                 break;
1052             short nRes = (short) pData->Write( (sal_uInt8*)pBuf + nDone, nBytes );
1053             nDone = nDone + nRes;
1054             nPos += nRes;
1055             n -= nRes;
1056             nOffset = nOffset + nRes;
1057             // write problem?
1058             if( nRes != nBytes )
1059                 break;
1060         }
1061         // Switch to next page if necessary
1062         if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1063             break;
1064     }
1065     return nDone;
1066 }
1067 
1068 /////////////////////////// class StgTmpStrm /////////////////////////////
1069 
1070 // The temporary stream uses a memory stream if < 32K, otherwise a
1071 // temporary file.
1072 
1073 #define THRESHOLD 32768L
1074 
1075 StgTmpStrm::StgTmpStrm( sal_uLong nInitSize )
1076           : SvMemoryStream( nInitSize > THRESHOLD
1077                             ? 16
1078                             : ( nInitSize ? nInitSize : 16 ), 4096 )
1079 {
1080     pStrm = NULL;
1081     // this calls FlushData, so all members should be set by this time
1082     SetBufferSize( 0 );
1083     if( nInitSize > THRESHOLD )
1084         SetSize( nInitSize );
1085 }
1086 
1087 sal_Bool StgTmpStrm::Copy( StgTmpStrm& rSrc )
1088 {
1089     sal_uLong n    = rSrc.GetSize();
1090     sal_uLong nCur = rSrc.Tell();
1091     SetSize( n );
1092     if( GetError() == SVSTREAM_OK )
1093     {
1094         sal_uInt8* p = new sal_uInt8[ 4096 ];
1095         rSrc.Seek( 0L );
1096         Seek( 0L );
1097         while( n )
1098         {
1099             sal_uLong nn = n;
1100             if( nn > 4096 )
1101                 nn = 4096;
1102             if( rSrc.Read( p, nn ) != nn )
1103                 break;
1104             if( Write( p, nn ) != nn )
1105                 break;
1106             n -= nn;
1107         }
1108         delete [] p;
1109         rSrc.Seek( nCur );
1110         Seek( nCur );
1111         return sal_Bool( n == 0 );
1112     }
1113     else
1114         return sal_False;
1115 }
1116 
1117 StgTmpStrm::~StgTmpStrm()
1118 {
1119     if( pStrm )
1120     {
1121         pStrm->Close();
1122         osl::File::remove( aName );
1123         delete pStrm;
1124     }
1125 }
1126 
1127 sal_uLong StgTmpStrm::GetSize() const
1128 {
1129     sal_uLong n;
1130     if( pStrm )
1131     {
1132         sal_uLong old = pStrm->Tell();
1133         n = pStrm->Seek( STREAM_SEEK_TO_END );
1134         pStrm->Seek( old );
1135     }
1136     else
1137         n = nEndOfData;
1138     return n;
1139 }
1140 
1141 void StgTmpStrm::SetSize( sal_uLong n )
1142 {
1143     if( pStrm )
1144         pStrm->SetStreamSize( n );
1145     else
1146     {
1147         if( n > THRESHOLD )
1148         {
1149             aName = TempFile::CreateTempName();
1150             SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE );
1151             sal_uLong nCur = Tell();
1152             sal_uLong i = nEndOfData;
1153             if( i )
1154             {
1155                 sal_uInt8* p = new sal_uInt8[ 4096 ];
1156                 Seek( 0L );
1157                 while( i )
1158                 {
1159                     sal_uLong nb = ( i > 4096 ) ? 4096 : i;
1160                     if( Read( p, nb ) == nb
1161                      && s->Write( p, nb ) == nb )
1162                         i -= nb;
1163                     else
1164                         break;
1165                 }
1166                 delete [] p;
1167             }
1168             if( !i && n > nEndOfData )
1169             {
1170                 // We have to write one byte at the end of the file
1171                 // if the file is bigger than the memstream to see
1172                 // if it fits on disk
1173                 s->Seek( n - 1 );
1174                 s->Write( &i, 1 );
1175                 s->Flush();
1176                 if( s->GetError() != SVSTREAM_OK )
1177                     i = 1;
1178             }
1179             Seek( nCur );
1180             s->Seek( nCur );
1181             if( i )
1182             {
1183                 SetError( s->GetError() );
1184                 delete s;
1185                 return;
1186             }
1187             pStrm = s;
1188             // Shrink the memory to 16 bytes, which seems to be the minimum
1189             ReAllocateMemory( - ( (long) nEndOfData - 16 ) );
1190         }
1191         else
1192         {
1193             if( n > nEndOfData )
1194             {
1195                 sal_uLong nCur = Tell();
1196                 Seek( nEndOfData - 1 );
1197                 *this << (sal_uInt8) 0;
1198                 Seek( nCur );
1199             }
1200             else
1201                 nEndOfData = n;
1202         }
1203     }
1204 }
1205 
1206 sal_uLong StgTmpStrm::GetData( void* pData, sal_uLong n )
1207 {
1208     if( pStrm )
1209     {
1210         n = pStrm->Read( pData, n );
1211         SetError( pStrm->GetError() );
1212         return n;
1213     }
1214     else
1215         return SvMemoryStream::GetData( (sal_Char *)pData, n );
1216 }
1217 
1218 sal_uLong StgTmpStrm::PutData( const void* pData, sal_uLong n )
1219 {
1220     sal_uInt32 nCur = Tell();
1221     sal_uInt32 nNew = nCur + n;
1222     if( nNew > THRESHOLD && !pStrm )
1223     {
1224         SetSize( nNew );
1225         if( GetError() != SVSTREAM_OK )
1226             return 0;
1227     }
1228     if( pStrm )
1229     {
1230         nNew = pStrm->Write( pData, n );
1231         SetError( pStrm->GetError() );
1232     }
1233     else
1234         nNew = SvMemoryStream::PutData( (sal_Char*)pData, n );
1235     return nNew;
1236 }
1237 
1238 sal_uLong StgTmpStrm::SeekPos( sal_uLong n )
1239 {
1240     if( n == STREAM_SEEK_TO_END )
1241         n = GetSize();
1242     if( n && n > THRESHOLD && !pStrm )
1243     {
1244         SetSize( n );
1245         if( GetError() != SVSTREAM_OK )
1246             return Tell();
1247         else
1248             return n;
1249     }
1250     else if( pStrm )
1251     {
1252         n = pStrm->Seek( n );
1253         SetError( pStrm->GetError() );
1254         return n;
1255     }
1256     else
1257         return SvMemoryStream::SeekPos( n );
1258 }
1259 
1260 void StgTmpStrm::FlushData()
1261 {
1262     if( pStrm )
1263     {
1264         pStrm->Flush();
1265         SetError( pStrm->GetError() );
1266     }
1267     else
1268         SvMemoryStream::FlushData();
1269 }
1270 
1271