update dev300-m58
[ooovba.git] / sot / source / sdstor / stgstrms.cxx
blob060ca624ea9f4428232cf5d89068b5fd99afb7e3
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: stgstrms.cxx,v $
10 * $Revision: 1.11.8.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sot.hxx"
34 #include <string.h> // memcpy()
36 #include <osl/file.hxx>
37 #include <tools/tempfile.hxx>
38 #include <tools/debug.hxx>
40 #include "stg.hxx"
41 #include "stgelem.hxx"
42 #include "stgcache.hxx"
43 #include "stgstrms.hxx"
44 #include "stgdir.hxx"
45 #include "stgio.hxx"
47 #if defined(W31)
48 #include <tools/svwin.h>
49 #define memcpy hmemcpy
50 #define __HUGE _huge
51 #else
52 #define __HUGE
53 #endif
55 ///////////////////////////// class StgFAT ///////////////////////////////
57 // The FAT class performs FAT operations on an underlying storage stream.
58 // This stream is either the master FAT stream (m == TRUE ) or a normal
59 // storage stream, which then holds the FAT for small data allocations.
61 StgFAT::StgFAT( StgStrm& r, BOOL m ) : rStrm( r )
63 bPhys = m;
64 nPageSize = rStrm.GetIo().GetPhysPageSize();
65 nEntries = nPageSize >> 2;
66 nOffset = 0;
67 nMaxPage = 0;
68 nLimit = 0;
71 // Retrieve the physical page for a given byte offset.
73 StgPage* StgFAT::GetPhysPage( INT32 nByteOff )
75 StgPage* pPg = NULL;
76 // Position within the underlying stream
77 // use the Pos2Page() method of the stream
78 if( rStrm.Pos2Page( nByteOff ) )
80 nOffset = rStrm.GetOffset();
81 INT32 nPhysPage = rStrm.GetPage();
82 // get the physical page (must be present)
83 pPg = rStrm.GetIo().Get( nPhysPage, TRUE );
85 return pPg;
88 // Get the follow page for a certain FAT page.
90 INT32 StgFAT::GetNextPage( INT32 nPg )
92 if( nPg >= 0 )
94 StgPage* pPg = GetPhysPage( nPg << 2 );
95 nPg = pPg ? pPg->GetPage( nOffset >> 2 ) : STG_EOF;
97 return nPg;
100 // Find the best fit block for the given size. Return
101 // the starting block and its size or STG_EOF and 0.
102 // nLastPage is a stopper which tells the current
103 // underlying stream size. It is treated as a recommendation
104 // to abort the search to inhibit excessive file growth.
106 INT32 StgFAT::FindBlock( INT32& nPgs )
108 INT32 nMinStart = STG_EOF, nMinLen = 0;
109 INT32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
110 INT32 nTmpStart = STG_EOF, nTmpLen = 0;
111 INT32 nPages = rStrm.GetSize() >> 2;
112 BOOL bFound = FALSE;
113 StgPage* pPg = NULL;
114 short nEntry = 0;
115 for( INT32 i = 0; i < nPages; i++, nEntry++ )
117 if( !( nEntry % nEntries ) )
119 // load the next page for that stream
120 nEntry = 0;
121 pPg = GetPhysPage( i << 2 );
122 if( !pPg )
123 return STG_EOF;
125 INT32 nCur = pPg->GetPage( nEntry );
126 if( nCur == STG_FREE )
128 // count the size of this area
129 if( nTmpLen )
130 nTmpLen++;
131 else
132 nTmpStart = i,
133 nTmpLen = 1;
134 if( nTmpLen == nPgs
135 // If we already did find a block, stop when reaching the limit
136 || ( bFound && ( nEntry >= nLimit ) ) )
137 break;
139 else if( nTmpLen )
141 if( nTmpLen > nPgs && nTmpLen < nMaxLen )
142 // block > requested size
143 nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = TRUE;
144 else if( nTmpLen >= nMinLen )
146 // block < requested size
147 nMinLen = nTmpLen, nMinStart = nTmpStart;
148 bFound = TRUE;
149 if( nTmpLen == nPgs )
150 break;
152 nTmpStart = STG_EOF;
153 nTmpLen = 0;
156 // Determine which block to use.
157 if( nTmpLen )
159 if( nTmpLen > nPgs && nTmpLen < nMaxLen )
160 // block > requested size
161 nMaxLen = nTmpLen, nMaxStart = nTmpStart;
162 else if( nTmpLen >= nMinLen )
163 // block < requested size
164 nMinLen = nTmpLen, nMinStart = nTmpStart;
166 if( nMinStart != STG_EOF && nMaxStart != STG_EOF )
168 // two areas found; return the best fit area
169 INT32 nMinDiff = nPgs - nMinLen;
170 INT32 nMaxDiff = nMaxLen - nPgs;
171 if( nMinDiff > nMaxDiff )
172 nMinStart = STG_EOF;
174 if( nMinStart != STG_EOF )
176 nPgs = nMinLen; return nMinStart;
178 else
180 return nMaxStart;
184 // Set up the consecutive chain for a given block.
186 BOOL StgFAT::MakeChain( INT32 nStart, INT32 nPgs )
188 INT32 nPos = nStart << 2;
189 StgPage* pPg = GetPhysPage( nPos );
190 if( !pPg || !nPgs )
191 return FALSE;
192 while( --nPgs )
194 if( nOffset >= nPageSize )
196 pPg = GetPhysPage( nPos );
197 if( !pPg )
198 return FALSE;
200 pPg->SetPage( nOffset >> 2, ++nStart );
201 nOffset += 4;
202 nPos += 4;
204 if( nOffset >= nPageSize )
206 pPg = GetPhysPage( nPos );
207 if( !pPg )
208 return FALSE;
210 pPg->SetPage( nOffset >> 2, STG_EOF );
211 return TRUE;
214 // Allocate a block of data from the given page number on.
215 // It the page number is != STG_EOF, chain the block.
217 INT32 StgFAT::AllocPages( INT32 nBgn, INT32 nPgs )
219 INT32 nOrig = nBgn;
220 INT32 nLast = nBgn;
221 INT32 nBegin = STG_EOF;
222 INT32 nAlloc;
223 INT32 nPages = rStrm.GetSize() >> 2;
224 short nPasses = 0;
225 // allow for two passes
226 while( nPasses < 2 )
228 // try to satisfy the request from the pool of free pages
229 while( nPgs )
231 nAlloc = nPgs;
232 nBegin = FindBlock( nAlloc );
233 // no more blocks left in present alloc chain
234 if( nBegin == STG_EOF )
235 break;
236 if( ( nBegin + nAlloc ) > nMaxPage )
237 nMaxPage = nBegin + nAlloc;
238 if( !MakeChain( nBegin, nAlloc ) )
239 return STG_EOF;
240 if( nOrig == STG_EOF )
241 nOrig = nBegin;
242 else
244 // Patch the chain
245 StgPage* pPg = GetPhysPage( nLast << 2 );
246 if( !pPg )
247 return STG_EOF;
248 pPg->SetPage( nOffset >> 2, nBegin );
250 nLast = nBegin + nAlloc - 1;
251 nPgs -= nAlloc;
253 if( nPgs && !nPasses )
255 // we need new, fresh space, so allocate and retry
256 if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
257 return STG_EOF;
258 if( !bPhys && !InitNew( nPages ) )
259 return FALSE;
260 nPages = rStrm.GetSize() >> 2;
261 nPasses++;
263 else
264 break;
266 // now we should have a chain for the complete block
267 if( nBegin == STG_EOF || nPgs )
269 rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
270 return STG_EOF; // bad structure
272 return nOrig;
275 // Initialize newly allocated pages for a standard FAT stream
276 // It can be assumed that the stream size is always on
277 // a page boundary
279 BOOL StgFAT::InitNew( INT32 nPage1 )
281 INT32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries;
282 while( n-- )
284 StgPage* pPg = NULL;
285 // Position within the underlying stream
286 // use the Pos2Page() method of the stream
287 rStrm.Pos2Page( nPage1 << 2 );
288 // Initialize the page
289 pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE );
290 if ( !pPg )
291 return FALSE;
292 for( short i = 0; i < nEntries; i++ )
293 pPg->SetPage( i, STG_FREE );
294 nPage1++;
296 return TRUE;
299 // Release a chain
301 BOOL StgFAT::FreePages( INT32 nStart, BOOL bAll )
303 while( nStart >= 0 )
305 StgPage* pPg = GetPhysPage( nStart << 2 );
306 if( !pPg )
307 return FALSE;
308 nStart = pPg->GetPage( nOffset >> 2 );
309 // The first released page is either set to EOF or FREE
310 pPg->SetPage( nOffset >> 2, bAll ? STG_FREE : STG_EOF );
311 bAll = TRUE;
313 return TRUE;
316 ///////////////////////////// class StgStrm ////////////////////////////////
318 // The base stream class provides basic functionality for seeking
319 // and accessing the data on a physical basis. It uses the built-in
320 // FAT class for the page allocations.
322 StgStrm::StgStrm( StgIo& r ) : rIo( r )
324 pFat = NULL;
325 nStart = nPage = STG_EOF;
326 nOffset = 0;
327 pEntry = NULL;
328 nPos = nSize = 0;
329 nPageSize = rIo.GetPhysPageSize();
332 StgStrm::~StgStrm()
334 delete pFat;
337 // Attach the stream to the given entry.
339 void StgStrm::SetEntry( StgDirEntry& r )
341 r.aEntry.SetLeaf( STG_DATA, nStart );
342 r.aEntry.SetSize( nSize );
343 pEntry = &r;
344 r.SetDirty();
347 // Compute page number and offset for the given byte position.
348 // If the position is behind the size, set the stream right
349 // behind the EOF.
351 BOOL StgStrm::Pos2Page( INT32 nBytePos )
353 INT32 nRel, nBgn;
354 // Values < 0 seek to the end
355 if( nBytePos < 0 || nBytePos >= nSize )
356 nBytePos = nSize;
357 // Adjust the position back to offset 0
358 nPos -= nOffset;
359 INT32 nMask = ~( nPageSize - 1 );
360 INT32 nOld = nPos & nMask;
361 INT32 nNew = nBytePos & nMask;
362 nOffset = (short) ( nBytePos & ~nMask );
363 nPos = nBytePos;
364 if( nOld == nNew )
365 return TRUE;
366 if( nNew > nOld )
368 // the new position is behind the current, so an incremental
369 // positioning is OK. Set the page relative position
370 nRel = nNew - nOld;
371 nBgn = nPage;
373 else
375 // the new position is before the current, so we have to scan
376 // the entire chain.
377 nRel = nNew;
378 nBgn = nStart;
380 // now, traverse the FAT chain.
381 nRel /= nPageSize;
382 INT32 nLast = STG_EOF;
383 while( nRel && nBgn >= 0 )
385 nLast = nBgn;
386 nBgn = pFat->GetNextPage( nBgn );
387 nRel--;
389 // special case: seek to 1st byte of new, unallocated page
390 // (in case the file size is a multiple of the page size)
391 if( nBytePos == nSize && nBgn == STG_EOF && !nRel && !nOffset )
392 nBgn = nLast, nOffset = nPageSize;
393 if( nBgn < 0 && nBgn != STG_EOF )
395 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
396 nBgn = STG_EOF;
397 nOffset = nPageSize;
399 nPage = nBgn;
400 return BOOL( nRel == 0 && nPage >= 0 );
403 // Retrieve the physical page for a given byte offset.
405 StgPage* StgStrm::GetPhysPage( INT32 nBytePos, BOOL bForce )
407 if( !Pos2Page( nBytePos ) )
408 return NULL;
409 return rIo.Get( nPage, bForce );
412 // Copy an entire stream. Both streams are allocated in the FAT.
413 // The target stream is this stream.
415 BOOL StgStrm::Copy( INT32 nFrom, INT32 nBytes )
417 INT32 nTo = nStart;
418 INT32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize;
419 while( nPgs-- )
421 if( nTo < 0 )
423 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
424 return FALSE;
426 rIo.Copy( nTo, nFrom );
427 if( nFrom >= 0 )
429 nFrom = pFat->GetNextPage( nFrom );
430 if( nFrom < 0 )
432 rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
433 return FALSE;
436 nTo = pFat->GetNextPage( nTo );
438 return TRUE;
441 BOOL StgStrm::SetSize( INT32 nBytes )
443 // round up to page size
444 INT32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize;
445 INT32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
446 if( nNew > nOld )
448 if( !Pos2Page( nSize ) )
449 return FALSE;
450 INT32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize );
451 if( nBgn == STG_EOF )
452 return FALSE;
453 if( nStart == STG_EOF )
454 nStart = nPage = nBgn;
456 else if( nNew < nOld )
458 BOOL bAll = BOOL( nBytes == 0 );
459 if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) )
460 return FALSE;
461 if( bAll )
462 nStart = nPage = STG_EOF;
464 if( pEntry )
466 // change the dir entry?
467 if( !nSize || !nBytes )
468 pEntry->aEntry.SetLeaf( STG_DATA, nStart );
469 pEntry->aEntry.SetSize( nBytes );
470 pEntry->SetDirty();
472 nSize = nBytes;
473 pFat->SetLimit( GetPages() );
474 return TRUE;
477 // Return the # of allocated pages
479 INT32 StgStrm::GetPages()
481 return ( nSize + nPageSize - 1 ) / nPageSize;
484 //////////////////////////// class StgFATStrm //////////////////////////////
486 // The FAT stream class provides physical access to the master FAT.
487 // Since this access is implemented as a StgStrm, we can use the
488 // FAT allocator.
490 StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r )
492 pFat = new StgFAT( *this, TRUE );
493 nSize = rIo.aHdr.GetFATSize() * nPageSize;
496 BOOL StgFATStrm::Pos2Page( INT32 nBytePos )
498 // Values < 0 seek to the end
499 if( nBytePos < 0 || nBytePos >= nSize )
500 nBytePos = nSize ? nSize - 1 : 0;
501 nPage = nBytePos / nPageSize;
502 nOffset = (short) ( nBytePos % nPageSize );
503 nPos = nBytePos;
504 nPage = GetPage( (short) nPage, FALSE );
505 return BOOL( nPage >= 0 );
508 // Retrieve the physical page for a given byte offset.
509 // Since Pos2Page() already has computed the physical offset,
510 // use the byte offset directly.
512 StgPage* StgFATStrm::GetPhysPage( INT32 nBytePos, BOOL bForce )
514 return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce );
517 // Get the page number entry for the given page offset.
519 INT32 StgFATStrm::GetPage( short nOff, BOOL bMake, USHORT *pnMasterAlloc )
521 if( pnMasterAlloc ) *pnMasterAlloc = 0;
522 if( nOff < rIo.aHdr.GetFAT1Size() )
523 return rIo.aHdr.GetFATPage( nOff );
524 INT32 nMaxPage = nSize >> 2;
525 nOff = nOff - rIo.aHdr.GetFAT1Size();
526 // Anzahl der Masterpages, durch die wir iterieren muessen
527 USHORT nMasterCount = ( nPageSize >> 2 ) - 1;
528 USHORT nBlocks = nOff / nMasterCount;
529 // Offset in letzter Masterpage
530 nOff = nOff % nMasterCount;
532 StgPage* pOldPage = 0;
533 StgPage* pMaster = 0;
534 INT32 nFAT = rIo.aHdr.GetFATChain();
535 for( USHORT nCount = 0; nCount <= nBlocks; nCount++ )
537 if( nFAT == STG_EOF || nFAT == STG_FREE )
539 if( bMake )
541 // create a new master page
542 nFAT = nMaxPage++;
543 pMaster = rIo.Copy( nFAT, STG_FREE );
544 if ( pMaster )
546 for( short k = 0; k < ( nPageSize >> 2 ); k++ )
547 pMaster->SetPage( k, STG_FREE );
548 // Verkettung herstellen
549 if( !pOldPage )
550 rIo.aHdr.SetFATChain( nFAT );
551 else
552 pOldPage->SetPage( nMasterCount, nFAT );
553 if( nMaxPage >= rIo.GetPhysPages() )
554 if( !rIo.SetSize( nMaxPage ) )
555 return STG_EOF;
556 // mark the page as used
557 // Platz fuer Masterpage schaffen
558 if( !pnMasterAlloc ) // Selbst Platz schaffen
560 if( !Pos2Page( nFAT << 2 ) )
561 return STG_EOF;
562 StgPage* pPg = rIo.Get( nPage, TRUE );
563 if( !pPg )
564 return STG_EOF;
565 pPg->SetPage( nOffset >> 2, STG_MASTER );
567 else
568 (*pnMasterAlloc)++;
569 rIo.aHdr.SetMasters( nCount + 1 );
570 pOldPage = pMaster;
574 else
576 pMaster = rIo.Get( nFAT, TRUE );
577 if ( pMaster )
579 nFAT = pMaster->GetPage( nMasterCount );
580 pOldPage = pMaster;
584 if( pMaster )
585 return pMaster->GetPage( nOff );
586 rIo.SetError( SVSTREAM_GENERALERROR );
587 return STG_EOF;
591 // Set the page number entry for the given page offset.
593 BOOL StgFATStrm::SetPage( short nOff, INT32 nNewPage )
595 BOOL bRes = TRUE;
596 if( nOff < rIo.aHdr.GetFAT1Size() )
597 rIo.aHdr.SetFATPage( nOff, nNewPage );
598 else
600 nOff = nOff - rIo.aHdr.GetFAT1Size();
601 // Anzahl der Masterpages, durch die wir iterieren muessen
602 USHORT nMasterCount = ( nPageSize >> 2 ) - 1;
603 USHORT nBlocks = nOff / nMasterCount;
604 // Offset in letzter Masterpage
605 nOff = nOff % nMasterCount;
607 StgPage* pMaster = 0;
608 INT32 nFAT = rIo.aHdr.GetFATChain();
609 for( USHORT nCount = 0; nCount <= nBlocks; nCount++ )
611 if( nFAT == STG_EOF || nFAT == STG_FREE )
613 pMaster = 0;
614 break;
616 pMaster = rIo.Get( nFAT, TRUE );
617 if ( pMaster )
618 nFAT = pMaster->GetPage( nMasterCount );
620 if( pMaster )
621 pMaster->SetPage( nOff, nNewPage );
622 else
624 rIo.SetError( SVSTREAM_GENERALERROR );
625 bRes = FALSE;
629 // lock the page against access
630 if( bRes )
632 Pos2Page( nNewPage << 2 );
633 StgPage* pPg = rIo.Get( nPage, TRUE );
634 if( pPg )
635 pPg->SetPage( nOffset >> 2, STG_FAT );
636 else
637 bRes = FALSE;
639 return bRes;
642 BOOL StgFATStrm::SetSize( INT32 nBytes )
644 // Set the number of entries to a multiple of the page size
645 short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize );
646 short nNew = (short) (
647 ( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ;
648 if( nNew < nOld )
650 // release master pages
651 for( short i = nNew; i < nOld; i++ )
652 SetPage( i, STG_FREE );
654 else
656 while( nOld < nNew )
658 // allocate master pages
659 // find a free master page slot
660 INT32 nPg = 0;
661 USHORT nMasterAlloc = 0;
662 nPg = GetPage( nOld, TRUE, &nMasterAlloc );
663 if( nPg == STG_EOF )
664 return FALSE;
665 // 4 Bytes have been used for Allocation of each MegaMasterPage
666 nBytes += nMasterAlloc << 2;
668 // find a free page using the FAT allocator
669 INT32 n = 1;
670 INT32 nNewPage = pFat->FindBlock( n );
671 if( nNewPage == STG_EOF )
673 // no free pages found; create a new page
674 // Since all pages are allocated, extend
675 // the file size for the next page!
676 nNewPage = nSize >> 2;
677 // if a MegaMasterPage was created avoid taking
678 // the same Page
679 nNewPage += nMasterAlloc;
680 // adjust the file size if necessary
681 if( nNewPage >= rIo.GetPhysPages() )
682 if( !rIo.SetSize( nNewPage + 1 ) )
683 return FALSE;
685 // Set up the page with empty entries
686 StgPage* pPg = rIo.Copy( nNewPage, STG_FREE );
687 if ( !pPg )
688 return FALSE;
689 for( short j = 0; j < ( nPageSize >> 2 ); j++ )
690 pPg->SetPage( j, STG_FREE );
692 // store the page number into the master FAT
693 // Set the size before so the correct FAT can be found
694 nSize = ( nOld + 1 ) * nPageSize;
695 SetPage( nOld, nNewPage );
697 // MegaMasterPages were created, mark it them as used
699 UINT32 nMax = rIo.aHdr.GetMasters( );
700 UINT32 nFAT = rIo.aHdr.GetFATChain();
701 if( nMasterAlloc )
702 for( USHORT nCount = 0; nCount < nMax; nCount++ )
704 if( !Pos2Page( nFAT << 2 ) )
705 return FALSE;
706 if( nMax - nCount <= nMasterAlloc )
708 StgPage* piPg = rIo.Get( nPage, TRUE );
709 if( !piPg )
710 return FALSE;
711 piPg->SetPage( nOffset >> 2, STG_MASTER );
713 StgPage* pPage = rIo.Get( nFAT, TRUE );
714 if( !pPage ) return FALSE;
715 nFAT = pPage->GetPage( (nPageSize >> 2 ) - 1 );
718 nOld++;
719 // We have used up 4 bytes for the STG_FAT entry
720 nBytes += 4;
721 nNew = (short) (
722 ( nBytes + ( nPageSize - 1 ) ) / nPageSize );
725 nSize = nNew * nPageSize;
726 rIo.aHdr.SetFATSize( nNew );
727 return TRUE;
730 /////////////////////////// class StgDataStrm //////////////////////////////
732 // This class is a normal physical stream which can be initialized
733 // either with an existing dir entry or an existing FAT chain.
734 // The stream has a size increment which normally is 1, but which can be
735 // set to any value is you want the size to be incremented by certain values.
737 StgDataStrm::StgDataStrm( StgIo& r, INT32 nBgn, INT32 nLen ) : StgStrm( r )
739 Init( nBgn, nLen );
742 StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry* p ) : StgStrm( r )
744 pEntry = p;
745 Init( p->aEntry.GetLeaf( STG_DATA ),
746 p->aEntry.GetSize() );
749 void StgDataStrm::Init( INT32 nBgn, INT32 nLen )
751 pFat = new StgFAT( *rIo.pFAT, TRUE );
752 nStart = nPage = nBgn;
753 nSize = nLen;
754 nIncr = 1;
755 nOffset = 0;
756 if( nLen < 0 )
758 // determine the actual size of the stream by scanning
759 // the FAT chain and counting the # of pages allocated
760 nSize = 0;
761 INT32 nOldBgn = -1;
762 while( nBgn >= 0 && nBgn != nOldBgn )
764 nOldBgn = nBgn;
765 nBgn = pFat->GetNextPage( nBgn );
766 if( nBgn == nOldBgn )
767 rIo.SetError( ERRCODE_IO_WRONGFORMAT );
768 nSize += nPageSize;
773 // Set the size of a physical stream.
775 BOOL StgDataStrm::SetSize( INT32 nBytes )
777 nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr;
778 INT32 nOldSz = nSize;
779 if( ( nOldSz != nBytes ) )
781 if( !StgStrm::SetSize( nBytes ) )
782 return FALSE;
783 INT32 nMaxPage = pFat->GetMaxPage();
784 if( nMaxPage > rIo.GetPhysPages() )
785 if( !rIo.SetSize( nMaxPage ) )
786 return FALSE;
787 // If we only allocated one page or less, create this
788 // page in the cache for faster throughput. The current
789 // position is the former EOF point.
790 if( ( nSize - 1 ) / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 )
792 Pos2Page( nBytes );
793 if( nPage >= 0 )
794 rIo.Copy( nPage, STG_FREE );
797 return TRUE;
800 // Get the address of the data byte at a specified offset.
801 // If bForce = TRUE, a read of non-existent data causes
802 // a read fault.
804 void* StgDataStrm::GetPtr( INT32 Pos, BOOL bForce, BOOL bDirty )
806 if( Pos2Page( Pos ) )
808 StgPage* pPg = rIo.Get( nPage, bForce );
809 if( pPg )
811 pPg->SetOwner( pEntry );
812 if( bDirty )
813 pPg->SetDirty();
814 return ((BYTE *)pPg->GetData()) + nOffset;
817 return NULL;
820 // This could easily be adapted to a better algorithm by determining
821 // the amount of consecutable blocks before doing a read. The result
822 // is the number of bytes read. No error is generated on EOF.
824 INT32 StgDataStrm::Read( void* pBuf, INT32 n )
826 if ( n < 0 )
827 return 0;
829 if( ( nPos + n ) > nSize )
830 n = nSize - nPos;
831 INT32 nDone = 0;
832 while( n )
834 short nBytes = nPageSize - nOffset;
835 short nRes;
836 StgPage* pPg;
837 if( (INT32) nBytes > n )
838 nBytes = (short) n;
839 if( nBytes )
841 void *p = (BYTE *) pBuf + nDone;
842 if( nBytes == nPageSize )
844 pPg = rIo.Find( nPage );
845 if( pPg )
847 // data is present, so use the cached data
848 pPg->SetOwner( pEntry );
849 memcpy( p, pPg->GetData(), nBytes );
850 nRes = nBytes;
852 else
853 // do a direct (unbuffered) read
854 nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize;
856 else
858 // partial block read thru the cache.
859 pPg = rIo.Get( nPage, FALSE );
860 if( !pPg )
861 break;
862 pPg->SetOwner( pEntry );
863 memcpy( p, (BYTE*)pPg->GetData() + nOffset, nBytes );
864 nRes = nBytes;
866 nDone += nRes;
867 nPos += nRes;
868 n -= nRes;
869 nOffset = nOffset + nRes;
870 if( nRes != nBytes )
871 break; // read error or EOF
873 // Switch to next page if necessary
874 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
875 break;
877 return nDone;
880 INT32 StgDataStrm::Write( const void* pBuf, INT32 n )
882 INT32 nDone = 0;
883 if( ( nPos + n ) > nSize )
885 INT32 nOld = nPos;
886 if( !SetSize( nPos + n ) )
887 return FALSE;
888 Pos2Page( nOld );
890 while( n )
892 short nBytes = nPageSize - nOffset;
893 short nRes;
894 StgPage* pPg;
895 if( (INT32) nBytes > n )
896 nBytes = (short) n;
897 if( nBytes )
899 const void *p = (const BYTE *) pBuf + nDone;
900 if( nBytes == nPageSize )
902 pPg = rIo.Find( nPage );
903 if( pPg )
905 // data is present, so use the cached data
906 pPg->SetOwner( pEntry );
907 memcpy( pPg->GetData(), p, nBytes );
908 pPg->SetDirty();
909 nRes = nBytes;
911 else
912 // do a direct (unbuffered) write
913 nRes = (short) rIo.Write( nPage, (void*) p, 1 ) * nPageSize;
915 else
917 // partial block read thru the cache.
918 pPg = rIo.Get( nPage, FALSE );
919 if( !pPg )
920 break;
921 pPg->SetOwner( pEntry );
922 memcpy( (BYTE*)pPg->GetData() + nOffset, p, nBytes );
923 pPg->SetDirty();
924 nRes = nBytes;
926 nDone += nRes;
927 nPos += nRes;
928 n -= nRes;
929 nOffset = nOffset + nRes;
930 if( nRes != nBytes )
931 break; // read error
933 // Switch to next page if necessary
934 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
935 break;
937 return nDone;
940 //////////////////////////// class StgSmallStream ///////////////////////////
942 // The small stream class provides access to streams with a size < 4096 bytes.
943 // This stream is a StgStream containing small pages. The FAT for this stream
944 // is also a StgStream. The start of the FAT is in the header at DataRootPage,
945 // the stream itself is pointed to by the root entry (it holds start & size).
947 StgSmallStrm::StgSmallStrm( StgIo& r, INT32 nBgn, INT32 nLen ) : StgStrm( r )
949 Init( nBgn, nLen );
952 StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry* p ) : StgStrm( r )
954 pEntry = p;
955 Init( p->aEntry.GetLeaf( STG_DATA ),
956 p->aEntry.GetSize() );
959 void StgSmallStrm::Init( INT32 nBgn, INT32 nLen )
961 pFat = new StgFAT( *rIo.pDataFAT, FALSE );
962 pData = rIo.pDataStrm;
963 nPageSize = rIo.GetDataPageSize();
964 nStart =
965 nPage = nBgn;
966 nSize = nLen;
969 // This could easily be adapted to a better algorithm by determining
970 // the amount of consecutable blocks before doing a read. The result
971 // is the number of bytes read. No error is generated on EOF.
973 INT32 StgSmallStrm::Read( void* pBuf, INT32 n )
975 // We can safely assume that reads are not huge, since the
976 // small stream is likely to be < 64 KBytes.
977 if( ( nPos + n ) > nSize )
978 n = nSize - nPos;
979 short nDone = 0;
980 while( n )
982 short nBytes = nPageSize - nOffset;
983 if( (INT32) nBytes > n )
984 nBytes = (short) n;
985 if( nBytes )
987 if( !pData->Pos2Page( nPage * nPageSize + nOffset ) )
988 break;
989 // all reading thru the stream
990 short nRes = (short) pData->Read( (BYTE*)pBuf + nDone, nBytes );
991 nDone = nDone + nRes;
992 nPos += nRes;
993 n -= nRes;
994 nOffset = nOffset + nRes;
995 // read problem?
996 if( nRes != nBytes )
997 break;
999 // Switch to next page if necessary
1000 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1001 break;
1003 return nDone;
1006 INT32 StgSmallStrm::Write( const void* pBuf, INT32 n )
1008 // you can safely assume that reads are not huge, since the
1009 // small stream is likely to be < 64 KBytes.
1010 short nDone = 0;
1011 if( ( nPos + n ) > nSize )
1013 INT32 nOld = nPos;
1014 if( !SetSize( nPos + n ) )
1015 return FALSE;
1016 Pos2Page( nOld );
1018 while( n )
1020 short nBytes = nPageSize - nOffset;
1021 if( (INT32) nBytes > n )
1022 nBytes = (short) n;
1023 if( nBytes )
1025 // all writing goes thru the stream
1026 INT32 nDataPos = nPage * nPageSize + nOffset;
1027 if( pData->GetSize() < ( nDataPos + nBytes ) )
1028 if( !pData->SetSize( nDataPos + nBytes ) )
1029 break;
1030 if( !pData->Pos2Page( nDataPos ) )
1031 break;
1032 short nRes = (short) pData->Write( (BYTE*)pBuf + nDone, nBytes );
1033 nDone = nDone + nRes;
1034 nPos += nRes;
1035 n -= nRes;
1036 nOffset = nOffset + nRes;
1037 // write problem?
1038 if( nRes != nBytes )
1039 break;
1041 // Switch to next page if necessary
1042 if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1043 break;
1045 return nDone;
1048 /////////////////////////// class StgTmpStrm /////////////////////////////
1050 // The temporary stream uses a memory stream if < 32K, otherwise a
1051 // temporary file.
1053 #define THRESHOLD 32768L
1055 StgTmpStrm::StgTmpStrm( ULONG nInitSize )
1056 : SvMemoryStream( nInitSize > THRESHOLD
1057 ? 16
1058 : ( nInitSize ? nInitSize : 16 ), 4096 )
1060 pStrm = NULL;
1061 // this calls FlushData, so all members should be set by this time
1062 SetBufferSize( 0 );
1063 if( nInitSize > THRESHOLD )
1064 SetSize( nInitSize );
1067 BOOL StgTmpStrm::Copy( StgTmpStrm& rSrc )
1069 ULONG n = rSrc.GetSize();
1070 ULONG nCur = rSrc.Tell();
1071 SetSize( n );
1072 if( GetError() == SVSTREAM_OK )
1074 BYTE* p = new BYTE[ 4096 ];
1075 rSrc.Seek( 0L );
1076 Seek( 0L );
1077 while( n )
1079 ULONG nn = n;
1080 if( nn > 4096 )
1081 nn = 4096;
1082 if( rSrc.Read( p, nn ) != nn )
1083 break;
1084 if( Write( p, nn ) != nn )
1085 break;
1086 n -= nn;
1088 delete [] p;
1089 rSrc.Seek( nCur );
1090 Seek( nCur );
1091 return BOOL( n == 0 );
1093 else
1094 return FALSE;
1097 StgTmpStrm::~StgTmpStrm()
1099 if( pStrm )
1101 pStrm->Close();
1102 osl::File::remove( aName );
1103 delete pStrm;
1107 ULONG StgTmpStrm::GetSize()
1109 ULONG n;
1110 if( pStrm )
1112 ULONG old = pStrm->Tell();
1113 n = pStrm->Seek( STREAM_SEEK_TO_END );
1114 pStrm->Seek( old );
1116 else
1117 n = nEndOfData;
1118 return n;
1121 void StgTmpStrm::SetSize( ULONG n )
1123 if( pStrm )
1124 pStrm->SetStreamSize( n );
1125 else
1127 if( n > THRESHOLD )
1129 aName = TempFile::CreateTempName();
1130 SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE );
1131 ULONG nCur = Tell();
1132 ULONG i = nEndOfData;
1133 if( i )
1135 BYTE* p = new BYTE[ 4096 ];
1136 Seek( 0L );
1137 while( i )
1139 ULONG nb = ( i > 4096 ) ? 4096 : i;
1140 if( Read( p, nb ) == nb
1141 && s->Write( p, nb ) == nb )
1142 i -= nb;
1143 else
1144 break;
1146 delete [] p;
1148 if( !i && n > nEndOfData )
1150 // We have to write one byte at the end of the file
1151 // if the file is bigger than the memstream to see
1152 // if it fits on disk
1153 s->Seek( n - 1 );
1154 s->Write( &i, 1 );
1155 s->Flush();
1156 if( s->GetError() != SVSTREAM_OK )
1157 i = 1;
1159 Seek( nCur );
1160 s->Seek( nCur );
1161 if( i )
1163 SetError( s->GetError() );
1164 delete s;
1165 return;
1167 pStrm = s;
1168 // Shrink the memory to 16 bytes, which seems to be the minimum
1169 ReAllocateMemory( - ( (long) nEndOfData - 16 ) );
1171 else
1173 if( n > nEndOfData )
1175 ULONG nCur = Tell();
1176 Seek( nEndOfData - 1 );
1177 *this << (BYTE) 0;
1178 Seek( nCur );
1180 else
1181 nEndOfData = n;
1186 ULONG StgTmpStrm::GetData( void* pData, ULONG n )
1188 if( pStrm )
1190 n = pStrm->Read( pData, n );
1191 SetError( pStrm->GetError() );
1192 return n;
1194 else
1195 return SvMemoryStream::GetData( (sal_Char *)pData, n );
1198 ULONG StgTmpStrm::PutData( const void* pData, ULONG n )
1200 UINT32 nCur = Tell();
1201 UINT32 nNew = nCur + n;
1202 if( nNew > THRESHOLD && !pStrm )
1204 SetSize( nNew );
1205 if( GetError() != SVSTREAM_OK )
1206 return 0;
1208 if( pStrm )
1210 nNew = pStrm->Write( pData, n );
1211 SetError( pStrm->GetError() );
1213 else
1214 nNew = SvMemoryStream::PutData( (sal_Char*)pData, n );
1215 return nNew;
1218 ULONG StgTmpStrm::SeekPos( ULONG n )
1220 if( n == STREAM_SEEK_TO_END )
1221 n = GetSize();
1222 if( n && n > THRESHOLD && !pStrm )
1224 SetSize( n );
1225 if( GetError() != SVSTREAM_OK )
1226 return Tell();
1227 else
1228 return n;
1230 else if( pStrm )
1232 n = pStrm->Seek( n );
1233 SetError( pStrm->GetError() );
1234 return n;
1236 else
1237 return SvMemoryStream::SeekPos( n );
1240 void StgTmpStrm::FlushData()
1242 if( pStrm )
1244 pStrm->Flush();
1245 SetError( pStrm->GetError() );
1247 else
1248 SvMemoryStream::FlushData();