1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
23 #include <sal/log.hxx>
24 #include <osl/file.hxx>
25 #include <unotools/tempfile.hxx>
28 #include "sot/stg.hxx"
29 #include "stgelem.hxx"
30 #include "stgcache.hxx"
31 #include "stgstrms.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
)
44 nPageSize
= rStrm
.GetIo().GetPhysPageSize();
45 nEntries
= nPageSize
>> 2;
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 );
68 // Get the follow page for a certain FAT page.
70 sal_Int32
StgFAT::GetNextPage( sal_Int32 nPg
)
74 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nPg
<< 2 );
75 nPg
= pPg
.is() ? rStrm
.GetIo().GetFromPage( pPg
, nOffset
>> 2 ) : STG_EOF
;
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;
93 rtl::Reference
< StgPage
> pPg
;
95 for( sal_Int32 i
= 0; i
< nPages
; i
++, nEntry
++ )
97 if( !( nEntry
% nEntries
) )
99 // load the next page for that stream
101 pPg
= GetPhysPage( i
<< 2 );
105 sal_Int32 nCur
= rStrm
.GetIo().GetFromPage( pPg
, nEntry
);
106 if( nCur
== STG_FREE
)
108 // count the size of this area
115 // If we already did find a block, stop when reaching the limit
116 || ( bFound
&& ( nEntry
>= nLimit
) ) )
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
;
129 if( nTmpLen
== nPgs
)
136 // Determine which block to use.
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
)
154 if( nMinStart
!= STG_EOF
)
156 nPgs
= nMinLen
; return nMinStart
;
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
)
174 if( nOffset
>= nPageSize
)
176 pPg
= GetPhysPage( nPos
);
180 rStrm
.GetIo().SetToPage( pPg
, nOffset
>> 2, ++nStart
);
184 if( nOffset
>= nPageSize
)
186 pPg
= GetPhysPage( nPos
);
190 rStrm
.GetIo().SetToPage( pPg
, nOffset
>> 2, STG_EOF
);
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
;
203 sal_Int32 nPages
= rStrm
.GetSize() >> 2;
205 // allow for two passes
208 // try to satisfy the request from the pool of free pages
212 nBegin
= FindBlock( nAlloc
);
213 // no more blocks left in present alloc chain
214 if( nBegin
== STG_EOF
)
216 if( ( nBegin
+ nAlloc
) > nMaxPage
)
217 nMaxPage
= nBegin
+ nAlloc
;
218 if( !MakeChain( nBegin
, nAlloc
) )
220 if( nOrig
== STG_EOF
)
225 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nLast
<< 2 );
228 rStrm
.GetIo().SetToPage( pPg
, nOffset
>> 2, nBegin
);
230 nLast
= nBegin
+ nAlloc
- 1;
233 if( nPgs
&& !nPasses
)
235 // we need new, fresh space, so allocate and retry
236 if( !rStrm
.SetSize( ( nPages
+ nPgs
) << 2 ) )
238 if( !bPhys
&& !InitNew( nPages
) )
240 nPages
= rStrm
.GetSize() >> 2;
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
255 // Initialize newly allocated pages for a standard FAT stream
256 // It can be assumed that the stream size is always on
259 bool StgFAT::InitNew( sal_Int32 nPage1
)
261 sal_Int32 n
= ( ( rStrm
.GetSize() >> 2 ) - nPage1
) / nEntries
;
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
);
274 for( short i
= 0; i
< nEntries
; i
++ )
275 rStrm
.GetIo().SetToPage( pPg
, i
, STG_FREE
);
284 bool StgFAT::FreePages( sal_Int32 nStart
, bool bAll
)
288 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nStart
<< 2 );
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
);
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
)
308 nStart
= nPage
= STG_EOF
;
312 nPageSize
= rIo
.GetPhysPageSize();
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
);
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
336 void StgStrm::scanBuildPageChainCache(sal_Int32
*pOptionalCalcSize
)
339 m_aPagesCache
.reserve(nSize
/nPageSize
);
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
)
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
)
359 nOptSize
+= nPageSize
;
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
374 bool StgStrm::Pos2Page( sal_Int32 nBytePos
)
379 // Values < 0 seek to the end
380 if( nBytePos
< 0 || nBytePos
>= nSize
)
382 // Adjust the position back to offset 0
384 sal_Int32 nMask
= ~( nPageSize
- 1 );
385 sal_Int32 nOld
= nPos
& nMask
;
386 sal_Int32 nNew
= nBytePos
& nMask
;
387 nOffset
= (short) ( nBytePos
& ~nMask
);
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
);
416 m_aPagesCache
.push_back( nBgn
);
422 if ( nIdx
> m_aPagesCache
.size() )
424 rIo
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
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() )
436 else if ( nIdx
== m_aPagesCache
.size() )
442 nPage
= m_aPagesCache
[ nIdx
];
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
) )
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
)
464 m_aPagesCache
.clear();
466 sal_Int32 nTo
= nStart
;
467 sal_Int32 nPgs
= ( nBytes
+ nPageSize
- 1 ) / nPageSize
;
472 rIo
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
475 rIo
.Copy( nTo
, nFrom
);
478 nFrom
= pFat
->GetNextPage( nFrom
);
481 rIo
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
485 nTo
= pFat
->GetNextPage( nTo
);
490 bool StgStrm::SetSize( sal_Int32 nBytes
)
492 if ( nBytes
< 0 || !pFat
)
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
;
502 if( !Pos2Page( nSize
) )
504 sal_Int32 nBgn
= pFat
->AllocPages( nPage
, ( nNew
- nOld
) / nPageSize
);
505 if( nBgn
== STG_EOF
)
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
) )
516 nStart
= nPage
= STG_EOF
;
520 // change the dir entry?
521 if( !nSize
|| !nBytes
)
522 pEntry
->aEntry
.SetLeaf( STG_DATA
, nStart
);
523 pEntry
->aEntry
.SetSize( nBytes
);
527 pFat
->SetLimit( GetPages() );
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
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
);
558 nPage
= GetPage( (short) nPage
, false );
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
)
597 m_aPagesCache
.clear();
599 // create a new master page
601 pMaster
= rIo
.Copy( nFAT
, STG_FREE
);
604 for( short k
= 0; k
< ( nPageSize
>> 2 ); k
++ )
605 rIo
.SetToPage( pMaster
, k
, STG_FREE
);
606 // Verkettung herstellen
608 rIo
.aHdr
.SetFATChain( nFAT
);
610 rIo
.SetToPage( pOldPage
, nMasterCount
, nFAT
);
611 if( nMaxPage
>= rIo
.GetPhysPages() )
612 if( !rIo
.SetSize( nMaxPage
) )
614 // mark the page as used
615 // Platz fuer Masterpage schaffen
616 if( !pnMasterAlloc
) // Selbst Platz schaffen
618 if( !Pos2Page( nFAT
<< 2 ) )
620 rtl::Reference
< StgPage
> pPg
= rIo
.Get( nPage
, true );
623 rIo
.SetToPage( pPg
, nOffset
>> 2, STG_MASTER
);
627 rIo
.aHdr
.SetMasters( nCount
+ 1 );
634 pMaster
= rIo
.Get( nFAT
, true );
637 nFAT
= rIo
.GetFromPage( pMaster
, nMasterCount
);
643 return rIo
.GetFromPage( pMaster
, nOff
);
644 rIo
.SetError( SVSTREAM_GENERALERROR
);
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();
657 if( nOff
< rIo
.aHdr
.GetFAT1Size() )
658 rIo
.aHdr
.SetFATPage( nOff
, nNewPage
);
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
)
677 pMaster
= rIo
.Get( nFAT
, true );
679 nFAT
= rIo
.GetFromPage( pMaster
, nMasterCount
);
682 rIo
.SetToPage( pMaster
, nOff
, nNewPage
);
685 rIo
.SetError( SVSTREAM_GENERALERROR
);
690 // lock the page against access
693 Pos2Page( nNewPage
<< 2 );
694 rtl::Reference
< StgPage
> pPg
= rIo
.Get( nPage
, true );
696 rIo
.SetToPage( pPg
, nOffset
>> 2, STG_FAT
);
703 bool StgFATStrm::SetSize( sal_Int32 nBytes
)
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
) ;
716 // release master pages
717 for( short i
= nNew
; i
< nOld
; i
++ )
718 SetPage( i
, STG_FREE
);
724 // allocate master pages
725 // find a free master page slot
727 sal_uInt16 nMasterAlloc
= 0;
728 nPg
= GetPage( nOld
, true, &nMasterAlloc
);
731 // 4 Bytes have been used for Allocation of each MegaMasterPage
732 nBytes
+= nMasterAlloc
<< 2;
734 // find a free page using the FAT allocator
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
746 nNewPage
+= nMasterAlloc
;
747 // adjust the file size if necessary
748 if( nNewPage
>= rIo
.GetPhysPages() )
749 if( !rIo
.SetSize( nNewPage
+ 1 ) )
752 // Set up the page with empty entries
753 rtl::Reference
< StgPage
> pPg
= rIo
.Copy( nNewPage
, STG_FREE
);
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();
769 for( sal_uInt16 nCount
= 0; nCount
< nMax
; nCount
++ )
771 if( !Pos2Page( nFAT
<< 2 ) )
773 if( nMax
- nCount
<= nMasterAlloc
)
775 rtl::Reference
< StgPage
> piPg
= rIo
.Get( nPage
, true );
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 );
786 // We have used up 4 bytes for the STG_FAT entry
789 ( nBytes
+ ( nPageSize
- 1 ) ) / nPageSize
);
792 nSize
= nNew
* nPageSize
;
793 rIo
.aHdr
.SetFATSize( nNew
);
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
)
809 StgDataStrm::StgDataStrm( StgIo
& r
, StgDirEntry
& p
) : StgStrm( r
)
812 Init( p
.aEntry
.GetLeaf( STG_DATA
),
813 p
.aEntry
.GetSize() );
816 void StgDataStrm::Init( sal_Int32 nBgn
, sal_Int32 nLen
)
819 pFat
= new StgFAT( *rIo
.pFAT
, true );
821 OSL_ENSURE( pFat
, "The pointer should not be empty!" );
823 nStart
= nPage
= nBgn
;
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
)
842 nBytes
= ( ( nBytes
+ nIncr
- 1 ) / nIncr
) * nIncr
;
843 sal_Int32 nOldSz
= nSize
;
844 if( ( nOldSz
!= nBytes
) )
846 if( !StgStrm::SetSize( nBytes
) )
848 sal_Int32 nMaxPage
= pFat
->GetMaxPage();
849 if( nMaxPage
> rIo
.GetPhysPages() )
850 if( !rIo
.SetSize( nMaxPage
) )
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 )
859 rIo
.Copy( nPage
, STG_FREE
);
865 // Get the address of the data byte at a specified offset.
866 // If bForce = true, a read of non-existent data causes
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())
878 return ((sal_uInt8
*)pPg
->GetData()) + nOffset
;
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
)
893 if( ( nPos
+ n
) > nSize
)
898 short nBytes
= nPageSize
- nOffset
;
899 rtl::Reference
< StgPage
> pPg
;
900 if( (sal_Int32
) nBytes
> n
)
905 void *p
= (sal_uInt8
*) pBuf
+ nDone
;
906 if( nBytes
== nPageSize
)
908 pPg
= rIo
.Find( nPage
);
911 // data is present, so use the cached data
912 memcpy( p
, pPg
->GetData(), nBytes
);
916 // do a direct (unbuffered) read
917 nRes
= (short) rIo
.Read( nPage
, p
, 1 ) * nPageSize
;
921 // partial block read thru the cache.
922 pPg
= rIo
.Get( nPage
, false );
925 memcpy( p
, (sal_uInt8
*)pPg
->GetData() + nOffset
, nBytes
);
931 nOffset
= nOffset
+ nRes
;
933 break; // read error or EOF
935 // Switch to next page if necessary
936 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
942 sal_Int32
StgDataStrm::Write( const void* pBuf
, sal_Int32 n
)
948 if( ( nPos
+ n
) > nSize
)
950 sal_Int32 nOld
= nPos
;
951 if( !SetSize( nPos
+ n
) )
957 short nBytes
= nPageSize
- nOffset
;
958 rtl::Reference
< StgPage
> pPg
;
959 if( (sal_Int32
) nBytes
> n
)
964 const void *p
= (const sal_uInt8
*) pBuf
+ nDone
;
965 if( nBytes
== nPageSize
)
967 pPg
= rIo
.Find( nPage
);
970 // data is present, so use the cached data
971 memcpy( pPg
->GetData(), p
, nBytes
);
976 // do a direct (unbuffered) write
977 nRes
= (short) rIo
.Write( nPage
, (void*) p
, 1 ) * nPageSize
;
981 // partial block read thru the cache.
982 pPg
= rIo
.Get( nPage
, false );
985 memcpy( (sal_uInt8
*)pPg
->GetData() + nOffset
, p
, nBytes
);
992 nOffset
= nOffset
+ nRes
;
996 // Switch to next page if necessary
997 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
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
)
1015 StgSmallStrm::StgSmallStrm( StgIo
& r
, StgDirEntry
& p
) : StgStrm( r
)
1018 Init( p
.aEntry
.GetLeaf( STG_DATA
),
1019 p
.aEntry
.GetSize() );
1022 void StgSmallStrm::Init( sal_Int32 nBgn
, sal_Int32 nLen
)
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();
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
)
1048 short nBytes
= nPageSize
- nOffset
;
1049 if( (sal_Int32
) nBytes
> n
)
1053 if( !pData
|| !pData
->Pos2Page( nPage
* nPageSize
+ nOffset
) )
1055 // all reading thru the stream
1056 short nRes
= (short) pData
->Read( (sal_uInt8
*)pBuf
+ nDone
, nBytes
);
1057 nDone
= nDone
+ nRes
;
1060 nOffset
= nOffset
+ nRes
;
1062 if( nRes
!= nBytes
)
1065 // Switch to next page if necessary
1066 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
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.
1077 if( ( nPos
+ n
) > nSize
)
1079 sal_Int32 nOld
= nPos
;
1080 if( !SetSize( nPos
+ n
) )
1086 short nBytes
= nPageSize
- nOffset
;
1087 if( (sal_Int32
) nBytes
> n
)
1091 // all writing goes thru the stream
1092 sal_Int32 nDataPos
= nPage
* nPageSize
+ nOffset
;
1094 || ( pData
->GetSize() < ( nDataPos
+ nBytes
)
1095 && !pData
->SetSize( nDataPos
+ nBytes
) ) )
1097 if( !pData
->Pos2Page( nDataPos
) )
1099 short nRes
= (short) pData
->Write( (sal_uInt8
*)pBuf
+ nDone
, nBytes
);
1100 nDone
= nDone
+ nRes
;
1103 nOffset
= nOffset
+ nRes
;
1105 if( nRes
!= nBytes
)
1108 // Switch to next page if necessary
1109 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
1115 /////////////////////////// class StgTmpStrm /////////////////////////////
1117 // The temporary stream uses a memory stream if < 32K, otherwise a
1120 #define THRESHOLD 32768L
1122 StgTmpStrm::StgTmpStrm( sal_uLong nInitSize
)
1123 : SvMemoryStream( nInitSize
> THRESHOLD
1125 : ( nInitSize
? nInitSize
: 16 ), 4096 )
1128 // this calls FlushData, so all members should be set by this time
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();
1139 if( GetError() == SVSTREAM_OK
)
1141 sal_uInt8
* p
= new sal_uInt8
[ 4096 ];
1149 if( rSrc
.Read( p
, nn
) != nn
)
1151 if( Write( p
, nn
) != nn
)
1164 StgTmpStrm::~StgTmpStrm()
1169 osl::File::remove( aName
);
1174 sal_uLong
StgTmpStrm::GetSize() const
1179 sal_uLong old
= pStrm
->Tell();
1180 n
= pStrm
->Seek( STREAM_SEEK_TO_END
);
1188 void StgTmpStrm::SetSize( sal_uLong n
)
1191 pStrm
->SetStreamSize( n
);
1196 aName
= utl::TempFile::CreateTempName();
1197 SvFileStream
* s
= new SvFileStream( aName
, STREAM_READWRITE
);
1198 sal_uLong nCur
= Tell();
1199 sal_uLong i
= nEndOfData
;
1202 sal_uInt8
* p
= new sal_uInt8
[ 4096 ];
1206 sal_uLong nb
= ( i
> 4096 ) ? 4096 : i
;
1207 if( Read( p
, nb
) == nb
1208 && s
->Write( p
, nb
) == nb
)
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
1223 if( s
->GetError() != SVSTREAM_OK
)
1230 SetError( s
->GetError() );
1235 // Shrink the memory to 16 bytes, which seems to be the minimum
1236 ReAllocateMemory( - ( (long) nEndOfData
- 16 ) );
1240 if( n
> nEndOfData
)
1242 sal_uLong nCur
= Tell();
1243 Seek( nEndOfData
- 1 );
1244 *this << (sal_uInt8
) 0;
1253 sal_uLong
StgTmpStrm::GetData( void* pData
, sal_uLong n
)
1257 n
= pStrm
->Read( pData
, n
);
1258 SetError( pStrm
->GetError() );
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
)
1272 if( GetError() != SVSTREAM_OK
)
1277 nNew
= pStrm
->Write( pData
, n
);
1278 SetError( pStrm
->GetError() );
1281 nNew
= SvMemoryStream::PutData( (sal_Char
*)pData
, n
);
1285 sal_uLong
StgTmpStrm::SeekPos( sal_uLong n
)
1287 if( n
== STREAM_SEEK_TO_END
)
1289 if( n
&& n
> THRESHOLD
&& !pStrm
)
1292 if( GetError() != SVSTREAM_OK
)
1299 n
= pStrm
->Seek( n
);
1300 SetError( pStrm
->GetError() );
1304 return SvMemoryStream::SeekPos( n
);
1307 void StgTmpStrm::FlushData()
1312 SetError( pStrm
->GetError() );
1315 SvMemoryStream::FlushData();
1318 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */