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"
34 #include <boost/scoped_array.hpp>
36 ///////////////////////////// class StgFAT
38 // The FAT class performs FAT operations on an underlying storage stream.
39 // This stream is either the master FAT stream (m == true ) or a normal
40 // storage stream, which then holds the FAT for small data allocations.
42 StgFAT::StgFAT( StgStrm
& r
, bool m
) : rStrm( r
)
45 nPageSize
= rStrm
.GetIo().GetPhysPageSize();
46 nEntries
= nPageSize
>> 2;
52 // Retrieve the physical page for a given byte offset.
54 rtl::Reference
< StgPage
> StgFAT::GetPhysPage( sal_Int32 nByteOff
)
56 rtl::Reference
< StgPage
> pPg
;
57 // Position within the underlying stream
58 // use the Pos2Page() method of the stream
59 if( rStrm
.Pos2Page( nByteOff
) )
61 nOffset
= rStrm
.GetOffset();
62 sal_Int32 nPhysPage
= rStrm
.GetPage();
63 // get the physical page (must be present)
64 pPg
= rStrm
.GetIo().Get( nPhysPage
, true );
69 // Get the follow page for a certain FAT page.
71 sal_Int32
StgFAT::GetNextPage( sal_Int32 nPg
)
75 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nPg
<< 2 );
76 nPg
= pPg
.is() ? StgCache::GetFromPage( pPg
, nOffset
>> 2 ) : STG_EOF
;
81 // Find the best fit block for the given size. Return
82 // the starting block and its size or STG_EOF and 0.
83 // nLastPage is a stopper which tells the current
84 // underlying stream size. It is treated as a recommendation
85 // to abort the search to inhibit excessive file growth.
87 sal_Int32
StgFAT::FindBlock( sal_Int32
& nPgs
)
89 sal_Int32 nMinStart
= STG_EOF
, nMinLen
= 0;
90 sal_Int32 nMaxStart
= STG_EOF
, nMaxLen
= 0x7FFFFFFFL
;
91 sal_Int32 nTmpStart
= STG_EOF
, nTmpLen
= 0;
92 sal_Int32 nPages
= rStrm
.GetSize() >> 2;
94 rtl::Reference
< StgPage
> pPg
;
96 for( sal_Int32 i
= 0; i
< nPages
; i
++, nEntry
++ )
98 if( !( nEntry
% nEntries
) )
100 // load the next page for that stream
102 pPg
= GetPhysPage( i
<< 2 );
106 sal_Int32 nCur
= StgCache::GetFromPage( pPg
, nEntry
);
107 if( nCur
== STG_FREE
)
109 // count the size of this area
116 // If we already did find a block, stop when reaching the limit
117 || ( bFound
&& ( nEntry
>= nLimit
) ) )
122 if( nTmpLen
> nPgs
&& nTmpLen
< nMaxLen
)
123 // block > requested size
124 nMaxLen
= nTmpLen
, nMaxStart
= nTmpStart
, bFound
= true;
125 else if( nTmpLen
>= nMinLen
)
127 // block < requested size
128 nMinLen
= nTmpLen
, nMinStart
= nTmpStart
;
130 if( nTmpLen
== nPgs
)
137 // Determine which block to use.
140 if( nTmpLen
> nPgs
&& nTmpLen
< nMaxLen
)
141 // block > requested size
142 nMaxLen
= nTmpLen
, nMaxStart
= nTmpStart
;
143 else if( nTmpLen
>= nMinLen
)
144 // block < requested size
145 nMinLen
= nTmpLen
, nMinStart
= nTmpStart
;
147 if( nMinStart
!= STG_EOF
&& nMaxStart
!= STG_EOF
)
149 // two areas found; return the best fit area
150 sal_Int32 nMinDiff
= nPgs
- nMinLen
;
151 sal_Int32 nMaxDiff
= nMaxLen
- nPgs
;
152 if( nMinDiff
> nMaxDiff
)
155 if( nMinStart
!= STG_EOF
)
157 nPgs
= nMinLen
; return nMinStart
;
165 // Set up the consecutive chain for a given block.
167 bool StgFAT::MakeChain( sal_Int32 nStart
, sal_Int32 nPgs
)
169 sal_Int32 nPos
= nStart
<< 2;
170 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nPos
);
171 if( !pPg
.is() || !nPgs
)
175 if( nOffset
>= nPageSize
)
177 pPg
= GetPhysPage( nPos
);
181 rStrm
.GetIo().SetToPage( pPg
, nOffset
>> 2, ++nStart
);
185 if( nOffset
>= nPageSize
)
187 pPg
= GetPhysPage( nPos
);
191 rStrm
.GetIo().SetToPage( pPg
, nOffset
>> 2, STG_EOF
);
195 // Allocate a block of data from the given page number on.
196 // It the page number is != STG_EOF, chain the block.
198 sal_Int32
StgFAT::AllocPages( sal_Int32 nBgn
, sal_Int32 nPgs
)
200 sal_Int32 nOrig
= nBgn
;
201 sal_Int32 nLast
= nBgn
;
202 sal_Int32 nBegin
= STG_EOF
;
204 sal_Int32 nPages
= rStrm
.GetSize() >> 2;
206 // allow for two passes
209 // try to satisfy the request from the pool of free pages
213 nBegin
= FindBlock( nAlloc
);
214 // no more blocks left in present alloc chain
215 if( nBegin
== STG_EOF
)
217 if( ( nBegin
+ nAlloc
) > nMaxPage
)
218 nMaxPage
= nBegin
+ nAlloc
;
219 if( !MakeChain( nBegin
, nAlloc
) )
221 if( nOrig
== STG_EOF
)
226 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nLast
<< 2 );
229 rStrm
.GetIo().SetToPage( pPg
, nOffset
>> 2, nBegin
);
231 nLast
= nBegin
+ nAlloc
- 1;
234 if( nPgs
&& !nPasses
)
236 // we need new, fresh space, so allocate and retry
237 if( !rStrm
.SetSize( ( nPages
+ nPgs
) << 2 ) )
239 if( !bPhys
&& !InitNew( nPages
) )
241 // FIXME: this was originally "FALSE", whether or not that
242 // makes sense (or should be STG_EOF instead, say?)
243 nPages
= rStrm
.GetSize() >> 2;
249 // now we should have a chain for the complete block
250 if( nBegin
== STG_EOF
|| nPgs
)
252 rStrm
.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR
);
253 return STG_EOF
; // bad structure
258 // Initialize newly allocated pages for a standard FAT stream
259 // It can be assumed that the stream size is always on
262 bool StgFAT::InitNew( sal_Int32 nPage1
)
264 sal_Int32 n
= ( ( rStrm
.GetSize() >> 2 ) - nPage1
) / nEntries
;
269 rtl::Reference
< StgPage
> pPg
;
270 // Position within the underlying stream
271 // use the Pos2Page() method of the stream
272 rStrm
.Pos2Page( nPage1
<< 2 );
273 // Initialize the page
274 pPg
= rStrm
.GetIo().Copy( rStrm
.GetPage(), STG_FREE
);
277 for( short i
= 0; i
< nEntries
; i
++ )
278 rStrm
.GetIo().SetToPage( pPg
, i
, STG_FREE
);
287 bool StgFAT::FreePages( sal_Int32 nStart
, bool bAll
)
291 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nStart
<< 2 );
294 nStart
= StgCache::GetFromPage( pPg
, nOffset
>> 2 );
295 // The first released page is either set to EOF or FREE
296 rStrm
.GetIo().SetToPage( pPg
, nOffset
>> 2, bAll
? STG_FREE
: STG_EOF
);
302 ///////////////////////////// class StgStrm
304 // The base stream class provides basic functionality for seeking
305 // and accessing the data on a physical basis. It uses the built-in
306 // FAT class for the page allocations.
308 StgStrm::StgStrm( StgIo
& r
) : rIo( r
)
311 nStart
= nPage
= STG_EOF
;
315 nPageSize
= rIo
.GetPhysPageSize();
323 // Attach the stream to the given entry.
325 void StgStrm::SetEntry( StgDirEntry
& r
)
327 r
.aEntry
.SetLeaf( STG_DATA
, nStart
);
328 r
.aEntry
.SetSize( nSize
);
334 * The page chain, is basically a singly linked list of slots each
335 * point to the next page. Instead of traversing the file structure
336 * for this each time build a simple flat in-memory vector list
339 void StgStrm::scanBuildPageChainCache(sal_Int32
*pOptionalCalcSize
)
342 m_aPagesCache
.reserve(nSize
/nPageSize
);
345 sal_Int32 nBgn
= nStart
;
346 sal_Int32 nOptSize
= 0;
348 // Track already scanned PageNumbers here and use them to
349 // see if an already counted page is re-visited
350 std::set
< sal_Int32
> nUsedPageNumbers
;
352 while( nBgn
>= 0 && !bError
)
355 m_aPagesCache
.push_back(nBgn
);
356 nBgn
= pFat
->GetNextPage( nBgn
);
358 //returned second is false if it already exists
359 if (!nUsedPageNumbers
.insert(nBgn
).second
)
361 SAL_WARN ("sot", "Error: page number " << nBgn
<< " already in chain for stream");
365 nOptSize
+= nPageSize
;
369 SAL_WARN("sot", "returning wrong format error");
370 if (pOptionalCalcSize
)
371 rIo
.SetError( ERRCODE_IO_WRONGFORMAT
);
372 m_aPagesCache
.clear();
374 if (pOptionalCalcSize
)
375 *pOptionalCalcSize
= nOptSize
;
378 // Compute page number and offset for the given byte position.
379 // If the position is behind the size, set the stream right
381 bool StgStrm::Pos2Page( sal_Int32 nBytePos
)
386 // Values < 0 seek to the end
387 if( nBytePos
< 0 || nBytePos
>= nSize
)
389 // Adjust the position back to offset 0
391 sal_Int32 nMask
= ~( nPageSize
- 1 );
392 sal_Int32 nOld
= nPos
& nMask
;
393 sal_Int32 nNew
= nBytePos
& nMask
;
394 nOffset
= (short) ( nBytePos
& ~nMask
);
399 // See fdo#47644 for a .doc with a vast amount of pages where seeking around the
400 // document takes a colossal amount of time
402 // Please Note: we build the pagescache incrementally as we go if necessary,
403 // so that a corrupted FAT doesn't poison the stream state for earlier reads
404 size_t nIdx
= nNew
/ nPageSize
;
405 if( nIdx
>= m_aPagesCache
.size() )
407 // Extend the FAT cache ! ...
408 size_t nToAdd
= nIdx
+ 1;
410 if (m_aPagesCache
.empty())
411 m_aPagesCache
.push_back( nStart
);
413 nToAdd
-= m_aPagesCache
.size();
415 sal_Int32 nBgn
= m_aPagesCache
.back();
417 // Start adding pages while we can
418 while( nToAdd
> 0 && nBgn
>= 0 )
420 nBgn
= pFat
->GetNextPage( nBgn
);
423 m_aPagesCache
.push_back( nBgn
);
429 if ( nIdx
> m_aPagesCache
.size() )
431 SAL_WARN("sot", "seek to index " << nIdx
<<
432 " beyond page cache size " << m_aPagesCache
.size());
433 // fdo#84229 - handle seek to end and back as eg. XclImpStream expects
434 nIdx
= m_aPagesCache
.size();
437 // Intriguingly in the past we didn't reset nPos to match the real
438 // length of the stream thus: nPos = nPageSize * nIdx; so retain
439 // this behavior for now.
443 // special case: seek to 1st byte of new, unallocated page
444 // (in case the file size is a multiple of the page size)
445 if( nBytePos
== nSize
&& !nOffset
&& nIdx
> 0 && nIdx
== m_aPagesCache
.size() )
450 else if ( nIdx
== m_aPagesCache
.size() )
456 nPage
= m_aPagesCache
[ nIdx
];
461 // Retrieve the physical page for a given byte offset.
463 rtl::Reference
< StgPage
> StgStrm::GetPhysPage( sal_Int32 nBytePos
, bool bForce
)
465 if( !Pos2Page( nBytePos
) )
467 return rIo
.Get( nPage
, bForce
);
470 // Copy an entire stream. Both streams are allocated in the FAT.
471 // The target stream is this stream.
473 bool StgStrm::Copy( sal_Int32 nFrom
, sal_Int32 nBytes
)
478 m_aPagesCache
.clear();
480 sal_Int32 nTo
= nStart
;
481 sal_Int32 nPgs
= ( nBytes
+ nPageSize
- 1 ) / nPageSize
;
486 rIo
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
489 rIo
.Copy( nTo
, nFrom
);
492 nFrom
= pFat
->GetNextPage( nFrom
);
495 rIo
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
499 nTo
= pFat
->GetNextPage( nTo
);
504 bool StgStrm::SetSize( sal_Int32 nBytes
)
506 if ( nBytes
< 0 || !pFat
)
509 m_aPagesCache
.clear();
511 // round up to page size
512 sal_Int32 nOld
= ( ( nSize
+ nPageSize
- 1 ) / nPageSize
) * nPageSize
;
513 sal_Int32 nNew
= ( ( nBytes
+ nPageSize
- 1 ) / nPageSize
) * nPageSize
;
516 if( !Pos2Page( nSize
) )
518 sal_Int32 nBgn
= pFat
->AllocPages( nPage
, ( nNew
- nOld
) / nPageSize
);
519 if( nBgn
== STG_EOF
)
521 if( nStart
== STG_EOF
)
522 nStart
= nPage
= nBgn
;
524 else if( nNew
< nOld
)
526 bool bAll
= ( nBytes
== 0 );
527 if( !Pos2Page( nBytes
) || !pFat
->FreePages( nPage
, bAll
) )
530 nStart
= nPage
= STG_EOF
;
534 // change the dir entry?
535 if( !nSize
|| !nBytes
)
536 pEntry
->aEntry
.SetLeaf( STG_DATA
, nStart
);
537 pEntry
->aEntry
.SetSize( nBytes
);
541 pFat
->SetLimit( GetPages() );
545 // Return the # of allocated pages
548 //////////////////////////// class StgFATStrm
550 // The FAT stream class provides physical access to the master FAT.
551 // Since this access is implemented as a StgStrm, we can use the
554 StgFATStrm::StgFATStrm( StgIo
& r
) : StgStrm( r
)
556 pFat
= new StgFAT( *this, true );
557 nSize
= rIo
.aHdr
.GetFATSize() * nPageSize
;
560 bool StgFATStrm::Pos2Page( sal_Int32 nBytePos
)
562 // Values < 0 seek to the end
563 if( nBytePos
< 0 || nBytePos
>= nSize
)
564 nBytePos
= nSize
? nSize
- 1 : 0;
565 nPage
= nBytePos
/ nPageSize
;
566 nOffset
= (short) ( nBytePos
% nPageSize
);
568 nPage
= GetPage( (short) nPage
, false );
572 // Retrieve the physical page for a given byte offset.
573 // Since Pos2Page() already has computed the physical offset,
574 // use the byte offset directly.
576 rtl::Reference
< StgPage
> StgFATStrm::GetPhysPage( sal_Int32 nBytePos
, bool bForce
)
578 OSL_ENSURE( nBytePos
>= 0, "The value may not be negative!" );
579 return rIo
.Get( nBytePos
/ ( nPageSize
>> 2 ), bForce
);
582 // Get the page number entry for the given page offset.
584 sal_Int32
StgFATStrm::GetPage( short nOff
, bool bMake
, sal_uInt16
*pnMasterAlloc
)
586 OSL_ENSURE( nOff
>= 0, "The offset may not be negative!" );
587 if( pnMasterAlloc
) *pnMasterAlloc
= 0;
588 if( nOff
< StgHeader::GetFAT1Size() )
589 return rIo
.aHdr
.GetFATPage( nOff
);
590 sal_Int32 nMaxPage
= nSize
>> 2;
591 nOff
= nOff
- StgHeader::GetFAT1Size();
592 // Anzahl der Masterpages, durch die wir iterieren muessen
593 sal_uInt16 nMasterCount
= ( nPageSize
>> 2 ) - 1;
594 sal_uInt16 nBlocks
= nOff
/ nMasterCount
;
595 // Offset in letzter Masterpage
596 nOff
= nOff
% nMasterCount
;
598 rtl::Reference
< StgPage
> pOldPage
;
599 rtl::Reference
< StgPage
> pMaster
;
600 sal_Int32 nFAT
= rIo
.aHdr
.GetFATChain();
601 for( sal_uInt16 nCount
= 0; nCount
<= nBlocks
; nCount
++ )
603 if( nFAT
== STG_EOF
|| nFAT
== STG_FREE
)
607 m_aPagesCache
.clear();
609 // create a new master page
611 pMaster
= rIo
.Copy( nFAT
, STG_FREE
);
614 for( short k
= 0; k
< ( nPageSize
>> 2 ); k
++ )
615 rIo
.SetToPage( pMaster
, k
, STG_FREE
);
616 // Verkettung herstellen
618 rIo
.aHdr
.SetFATChain( nFAT
);
620 rIo
.SetToPage( pOldPage
, nMasterCount
, nFAT
);
621 if( nMaxPage
>= rIo
.GetPhysPages() )
622 if( !rIo
.SetSize( nMaxPage
) )
624 // mark the page as used
625 // Platz fuer Masterpage schaffen
626 if( !pnMasterAlloc
) // Selbst Platz schaffen
628 if( !Pos2Page( nFAT
<< 2 ) )
630 rtl::Reference
< StgPage
> pPg
= rIo
.Get( nPage
, true );
633 rIo
.SetToPage( pPg
, nOffset
>> 2, STG_MASTER
);
637 rIo
.aHdr
.SetMasters( nCount
+ 1 );
644 pMaster
= rIo
.Get( nFAT
, true );
647 nFAT
= StgCache::GetFromPage( pMaster
, nMasterCount
);
653 return StgCache::GetFromPage( pMaster
, nOff
);
654 rIo
.SetError( SVSTREAM_GENERALERROR
);
659 // Set the page number entry for the given page offset.
661 bool StgFATStrm::SetPage( short nOff
, sal_Int32 nNewPage
)
663 OSL_ENSURE( nOff
>= 0, "The offset may not be negative!" );
664 m_aPagesCache
.clear();
667 if( nOff
< StgHeader::GetFAT1Size() )
668 rIo
.aHdr
.SetFATPage( nOff
, nNewPage
);
671 nOff
= nOff
- StgHeader::GetFAT1Size();
672 // Anzahl der Masterpages, durch die wir iterieren muessen
673 sal_uInt16 nMasterCount
= ( nPageSize
>> 2 ) - 1;
674 sal_uInt16 nBlocks
= nOff
/ nMasterCount
;
675 // Offset in letzter Masterpage
676 nOff
= nOff
% nMasterCount
;
678 rtl::Reference
< StgPage
> pMaster
;
679 sal_Int32 nFAT
= rIo
.aHdr
.GetFATChain();
680 for( sal_uInt16 nCount
= 0; nCount
<= nBlocks
; nCount
++ )
682 if( nFAT
== STG_EOF
|| nFAT
== STG_FREE
)
687 pMaster
= rIo
.Get( nFAT
, true );
689 nFAT
= StgCache::GetFromPage( pMaster
, nMasterCount
);
692 rIo
.SetToPage( pMaster
, nOff
, nNewPage
);
695 rIo
.SetError( SVSTREAM_GENERALERROR
);
700 // lock the page against access
703 Pos2Page( nNewPage
<< 2 );
704 rtl::Reference
< StgPage
> pPg
= rIo
.Get( nPage
, true );
706 rIo
.SetToPage( pPg
, nOffset
>> 2, STG_FAT
);
713 bool StgFATStrm::SetSize( sal_Int32 nBytes
)
718 m_aPagesCache
.clear();
720 // Set the number of entries to a multiple of the page size
721 short nOld
= (short) ( ( nSize
+ ( nPageSize
- 1 ) ) / nPageSize
);
722 short nNew
= (short) (
723 ( nBytes
+ ( nPageSize
- 1 ) ) / nPageSize
) ;
726 // release master pages
727 for( short i
= nNew
; i
< nOld
; i
++ )
728 SetPage( i
, STG_FREE
);
734 // allocate master pages
735 // find a free master page slot
737 sal_uInt16 nMasterAlloc
= 0;
738 nPg
= GetPage( nOld
, true, &nMasterAlloc
);
741 // 4 Bytes have been used for Allocation of each MegaMasterPage
742 nBytes
+= nMasterAlloc
<< 2;
744 // find a free page using the FAT allocator
746 OSL_ENSURE( pFat
, "The pointer is always initializer here!" );
747 sal_Int32 nNewPage
= pFat
->FindBlock( n
);
748 if( nNewPage
== STG_EOF
)
750 // no free pages found; create a new page
751 // Since all pages are allocated, extend
752 // the file size for the next page!
753 nNewPage
= nSize
>> 2;
754 // if a MegaMasterPage was created avoid taking
756 nNewPage
+= nMasterAlloc
;
757 // adjust the file size if necessary
758 if( nNewPage
>= rIo
.GetPhysPages() )
759 if( !rIo
.SetSize( nNewPage
+ 1 ) )
762 // Set up the page with empty entries
763 rtl::Reference
< StgPage
> pPg
= rIo
.Copy( nNewPage
, STG_FREE
);
766 for( short j
= 0; j
< ( nPageSize
>> 2 ); j
++ )
767 rIo
.SetToPage( pPg
, j
, STG_FREE
);
769 // store the page number into the master FAT
770 // Set the size before so the correct FAT can be found
771 nSize
= ( nOld
+ 1 ) * nPageSize
;
772 SetPage( nOld
, nNewPage
);
774 // MegaMasterPages were created, mark it them as used
776 sal_uInt32 nMax
= rIo
.aHdr
.GetMasters( );
777 sal_uInt32 nFAT
= rIo
.aHdr
.GetFATChain();
779 for( sal_uInt16 nCount
= 0; nCount
< nMax
; nCount
++ )
781 if( !Pos2Page( nFAT
<< 2 ) )
783 if( nMax
- nCount
<= nMasterAlloc
)
785 rtl::Reference
< StgPage
> piPg
= rIo
.Get( nPage
, true );
788 rIo
.SetToPage( piPg
, nOffset
>> 2, STG_MASTER
);
790 rtl::Reference
< StgPage
> pPage
= rIo
.Get( nFAT
, true );
791 if( !pPage
.is() ) return false;
792 nFAT
= StgCache::GetFromPage( pPage
, (nPageSize
>> 2 ) - 1 );
796 // We have used up 4 bytes for the STG_FAT entry
799 ( nBytes
+ ( nPageSize
- 1 ) ) / nPageSize
);
802 nSize
= nNew
* nPageSize
;
803 rIo
.aHdr
.SetFATSize( nNew
);
807 /////////////////////////// class StgDataStrm
809 // This class is a normal physical stream which can be initialized
810 // either with an existing dir entry or an existing FAT chain.
811 // The stream has a size increment which normally is 1, but which can be
812 // set to any value is you want the size to be incremented by certain values.
814 StgDataStrm::StgDataStrm( StgIo
& r
, sal_Int32 nBgn
, sal_Int32 nLen
) : StgStrm( r
)
819 StgDataStrm::StgDataStrm( StgIo
& r
, StgDirEntry
& p
) : StgStrm( r
)
822 Init( p
.aEntry
.GetLeaf( STG_DATA
),
823 p
.aEntry
.GetSize() );
826 void StgDataStrm::Init( sal_Int32 nBgn
, sal_Int32 nLen
)
829 pFat
= new StgFAT( *rIo
.pFAT
, true );
831 OSL_ENSURE( pFat
, "The pointer should not be empty!" );
833 nStart
= nPage
= nBgn
;
837 if( nLen
< 0 && pFat
)
839 // determine the actual size of the stream by scanning
840 // the FAT chain and counting the # of pages allocated
841 scanBuildPageChainCache( &nSize
);
845 // Set the size of a physical stream.
847 bool StgDataStrm::SetSize( sal_Int32 nBytes
)
852 nBytes
= ( ( nBytes
+ nIncr
- 1 ) / nIncr
) * nIncr
;
853 sal_Int32 nOldSz
= nSize
;
854 if( ( nOldSz
!= nBytes
) )
856 if( !StgStrm::SetSize( nBytes
) )
858 sal_Int32 nMaxPage
= pFat
->GetMaxPage();
859 if( nMaxPage
> rIo
.GetPhysPages() )
860 if( !rIo
.SetSize( nMaxPage
) )
862 // If we only allocated one page or less, create this
863 // page in the cache for faster throughput. The current
864 // position is the former EOF point.
865 if( ( nSize
- 1 ) / nPageSize
- ( nOldSz
- 1 ) / nPageSize
== 1 )
869 rIo
.Copy( nPage
, STG_FREE
);
875 // Get the address of the data byte at a specified offset.
876 // If bForce = true, a read of non-existent data causes
879 void* StgDataStrm::GetPtr( sal_Int32 Pos
, bool bForce
, bool bDirty
)
881 if( Pos2Page( Pos
) )
883 rtl::Reference
< StgPage
> pPg
= rIo
.Get( nPage
, bForce
);
884 if (pPg
.is() && nOffset
< pPg
->GetSize())
888 return static_cast<sal_uInt8
*>(pPg
->GetData()) + nOffset
;
894 // This could easily be adapted to a better algorithm by determining
895 // the amount of consecutable blocks before doing a read. The result
896 // is the number of bytes read. No error is generated on EOF.
898 sal_Int32
StgDataStrm::Read( void* pBuf
, sal_Int32 n
)
903 if( ( nPos
+ n
) > nSize
)
908 short nBytes
= nPageSize
- nOffset
;
909 rtl::Reference
< StgPage
> pPg
;
910 if( (sal_Int32
) nBytes
> n
)
915 void *p
= static_cast<sal_uInt8
*>(pBuf
) + nDone
;
916 if( nBytes
== nPageSize
)
918 pPg
= rIo
.Find( nPage
);
921 // data is present, so use the cached data
922 memcpy( p
, pPg
->GetData(), nBytes
);
926 // do a direct (unbuffered) read
927 nRes
= (short) rIo
.Read( nPage
, p
, 1 ) * nPageSize
;
931 // partial block read through the cache.
932 pPg
= rIo
.Get( nPage
, false );
935 memcpy( p
, static_cast<sal_uInt8
*>(pPg
->GetData()) + nOffset
, nBytes
);
941 nOffset
= nOffset
+ nRes
;
943 break; // read error or EOF
945 // Switch to next page if necessary
946 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
952 sal_Int32
StgDataStrm::Write( const void* pBuf
, sal_Int32 n
)
958 if( ( nPos
+ n
) > nSize
)
960 sal_Int32 nOld
= nPos
;
961 if( !SetSize( nPos
+ n
) )
967 short nBytes
= nPageSize
- nOffset
;
968 rtl::Reference
< StgPage
> pPg
;
969 if( (sal_Int32
) nBytes
> n
)
974 const void *p
= static_cast<const sal_uInt8
*>(pBuf
) + nDone
;
975 if( nBytes
== nPageSize
)
977 pPg
= rIo
.Find( nPage
);
980 // data is present, so use the cached data
981 memcpy( pPg
->GetData(), p
, nBytes
);
986 // do a direct (unbuffered) write
987 nRes
= (short) rIo
.Write( nPage
, const_cast<void*>(p
), 1 ) * nPageSize
;
991 // partial block read through the cache.
992 pPg
= rIo
.Get( nPage
, false );
995 memcpy( static_cast<sal_uInt8
*>(pPg
->GetData()) + nOffset
, p
, nBytes
);
1002 nOffset
= nOffset
+ nRes
;
1003 if( nRes
!= nBytes
)
1004 break; // read error
1006 // Switch to next page if necessary
1007 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
1013 //////////////////////////// class StgSmallStream
1015 // The small stream class provides access to streams with a size < 4096 bytes.
1016 // This stream is a StgStream containing small pages. The FAT for this stream
1017 // is also a StgStream. The start of the FAT is in the header at DataRootPage,
1018 // the stream itself is pointed to by the root entry (it holds start & size).
1020 StgSmallStrm::StgSmallStrm( StgIo
& r
, sal_Int32 nBgn
, sal_Int32 nLen
) : StgStrm( r
)
1025 StgSmallStrm::StgSmallStrm( StgIo
& r
, StgDirEntry
& p
) : StgStrm( r
)
1028 Init( p
.aEntry
.GetLeaf( STG_DATA
),
1029 p
.aEntry
.GetSize() );
1032 void StgSmallStrm::Init( sal_Int32 nBgn
, sal_Int32 nLen
)
1035 pFat
= new StgFAT( *rIo
.pDataFAT
, false );
1036 pData
= rIo
.pDataStrm
;
1037 OSL_ENSURE( pFat
&& pData
, "The pointers should not be empty!" );
1039 nPageSize
= rIo
.GetDataPageSize();
1045 // This could easily be adapted to a better algorithm by determining
1046 // the amount of consecutable blocks before doing a read. The result
1047 // is the number of bytes read. No error is generated on EOF.
1049 sal_Int32
StgSmallStrm::Read( void* pBuf
, sal_Int32 n
)
1051 // We can safely assume that reads are not huge, since the
1052 // small stream is likely to be < 64 KBytes.
1053 if( ( nPos
+ n
) > nSize
)
1058 short nBytes
= nPageSize
- nOffset
;
1059 if( (sal_Int32
) nBytes
> n
)
1063 if( !pData
|| !pData
->Pos2Page( nPage
* nPageSize
+ nOffset
) )
1065 // all reading through the stream
1066 short nRes
= (short) pData
->Read( static_cast<sal_uInt8
*>(pBuf
) + nDone
, nBytes
);
1067 nDone
= nDone
+ nRes
;
1070 nOffset
= nOffset
+ nRes
;
1072 if( nRes
!= nBytes
)
1075 // Switch to next page if necessary
1076 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
1082 sal_Int32
StgSmallStrm::Write( const void* pBuf
, sal_Int32 n
)
1084 // you can safely assume that reads are not huge, since the
1085 // small stream is likely to be < 64 KBytes.
1087 if( ( nPos
+ n
) > nSize
)
1089 sal_Int32 nOld
= nPos
;
1090 if( !SetSize( nPos
+ n
) )
1096 short nBytes
= nPageSize
- nOffset
;
1097 if( (sal_Int32
) nBytes
> n
)
1101 // all writing goes through the stream
1102 sal_Int32 nDataPos
= nPage
* nPageSize
+ nOffset
;
1104 || ( pData
->GetSize() < ( nDataPos
+ nBytes
)
1105 && !pData
->SetSize( nDataPos
+ nBytes
) ) )
1107 if( !pData
->Pos2Page( nDataPos
) )
1109 short nRes
= (short) pData
->Write( static_cast<sal_uInt8
const *>(pBuf
) + nDone
, nBytes
);
1110 nDone
= nDone
+ nRes
;
1113 nOffset
= nOffset
+ nRes
;
1115 if( nRes
!= nBytes
)
1118 // Switch to next page if necessary
1119 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
1125 /////////////////////////// class StgTmpStrm
1127 // The temporary stream uses a memory stream if < 32K, otherwise a
1130 #define THRESHOLD 32768L
1132 StgTmpStrm::StgTmpStrm( sal_uLong nInitSize
)
1133 : SvMemoryStream( nInitSize
> THRESHOLD
1135 : ( nInitSize
? nInitSize
: 16 ), 4096 )
1138 // this calls FlushData, so all members should be set by this time
1140 if( nInitSize
> THRESHOLD
)
1141 SetSize( nInitSize
);
1144 bool StgTmpStrm::Copy( StgTmpStrm
& rSrc
)
1146 sal_uLong n
= rSrc
.GetSize();
1147 sal_uLong nCur
= rSrc
.Tell();
1149 if( GetError() == SVSTREAM_OK
)
1151 boost::scoped_array
<sal_uInt8
> p(new sal_uInt8
[ 4096 ]);
1159 if( rSrc
.Read( p
.get(), nn
) != nn
)
1161 if( Write( p
.get(), nn
) != nn
)
1174 StgTmpStrm::~StgTmpStrm()
1179 osl::File::remove( aName
);
1184 sal_uLong
StgTmpStrm::GetSize() const
1189 sal_uLong old
= pStrm
->Tell();
1190 n
= pStrm
->Seek( STREAM_SEEK_TO_END
);
1198 void StgTmpStrm::SetSize(sal_uInt64 n
)
1201 pStrm
->SetStreamSize( n
);
1206 aName
= utl::TempFile(0, false).GetURL();
1207 SvFileStream
* s
= new SvFileStream( aName
, STREAM_READWRITE
);
1208 sal_uLong nCur
= Tell();
1209 sal_uLong i
= nEndOfData
;
1212 boost::scoped_array
<sal_uInt8
> p(new sal_uInt8
[ 4096 ]);
1216 sal_uLong nb
= ( i
> 4096 ) ? 4096 : i
;
1217 if( Read( p
.get(), nb
) == nb
1218 && s
->Write( p
.get(), nb
) == nb
)
1224 if( !i
&& n
> nEndOfData
)
1226 // We have to write one byte at the end of the file
1227 // if the file is bigger than the memstream to see
1228 // if it fits on disk
1232 if( s
->GetError() != SVSTREAM_OK
)
1239 SetError( s
->GetError() );
1244 // Shrink the memory to 16 bytes, which seems to be the minimum
1245 ReAllocateMemory( - ( (long) nEndOfData
- 16 ) );
1249 if( n
> nEndOfData
)
1251 SvMemoryStream::SetSize(n
);
1259 sal_uLong
StgTmpStrm::GetData( void* pData
, sal_uLong n
)
1263 n
= pStrm
->Read( pData
, n
);
1264 SetError( pStrm
->GetError() );
1268 return SvMemoryStream::GetData( pData
, n
);
1271 sal_uLong
StgTmpStrm::PutData( const void* pData
, sal_uLong n
)
1273 sal_uInt32 nCur
= Tell();
1274 sal_uInt32 nNew
= nCur
+ n
;
1275 if( nNew
> THRESHOLD
&& !pStrm
)
1278 if( GetError() != SVSTREAM_OK
)
1283 nNew
= pStrm
->Write( pData
, n
);
1284 SetError( pStrm
->GetError() );
1287 nNew
= SvMemoryStream::PutData( pData
, n
);
1291 sal_uInt64
StgTmpStrm::SeekPos(sal_uInt64 n
)
1293 // check if a truncated STREAM_SEEK_TO_END was passed
1294 assert(n
!= SAL_MAX_UINT32
);
1295 if( n
== STREAM_SEEK_TO_END
)
1297 if( n
&& n
> THRESHOLD
&& !pStrm
)
1300 if( GetError() != SVSTREAM_OK
)
1307 n
= pStrm
->Seek( n
);
1308 SetError( pStrm
->GetError() );
1312 return SvMemoryStream::SeekPos( n
);
1315 void StgTmpStrm::FlushData()
1320 SetError( pStrm
->GetError() );
1323 SvMemoryStream::FlushData();
1326 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */