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 <o3tl/safeint.hxx>
25 #include <osl/file.hxx>
26 #include <unotools/tempfile.hxx>
28 #include "stgelem.hxx"
29 #include "stgcache.hxx"
30 #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
) : m_rStrm( r
)
44 m_nPageSize
= m_rStrm
.GetIo().GetPhysPageSize();
45 m_nEntries
= m_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( m_rStrm
.Pos2Page( nByteOff
) )
60 m_nOffset
= m_rStrm
.GetOffset();
61 sal_Int32 nPhysPage
= m_rStrm
.GetPage();
62 // get the physical page (must be present)
63 pPg
= m_rStrm
.GetIo().Get( nPhysPage
, true );
68 // Get the follow page for a certain FAT page.
70 sal_Int32
StgFAT::GetNextPage( sal_Int32 nPg
)
74 if (nPg
> (SAL_MAX_INT32
>> 2))
76 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nPg
<< 2 );
77 nPg
= pPg
.is() ? StgCache::GetFromPage( pPg
, m_nOffset
>> 2 ) : STG_EOF
;
82 // Find the best fit block for the given size. Return
83 // the starting block and its size or STG_EOF and 0.
84 // nLastPage is a stopper which tells the current
85 // underlying stream size. It is treated as a recommendation
86 // to abort the search to inhibit excessive file growth.
88 sal_Int32
StgFAT::FindBlock( sal_Int32
& nPgs
)
90 sal_Int32 nMinStart
= STG_EOF
, nMinLen
= 0;
91 sal_Int32 nMaxStart
= STG_EOF
, nMaxLen
= 0x7FFFFFFFL
;
92 sal_Int32 nTmpStart
= STG_EOF
, nTmpLen
= 0;
93 sal_Int32 nPages
= m_rStrm
.GetSize() >> 2;
95 rtl::Reference
< StgPage
> pPg
;
97 for( sal_Int32 i
= 0; i
< nPages
; i
++, nEntry
++ )
99 if( !( nEntry
% m_nEntries
) )
101 // load the next page for that stream
103 pPg
= GetPhysPage( i
<< 2 );
107 sal_Int32 nCur
= StgCache::GetFromPage( pPg
, nEntry
);
108 if( nCur
== STG_FREE
)
110 // count the size of this area
119 // If we already did find a block, stop when reaching the limit
120 || ( bFound
&& ( nEntry
>= m_nLimit
) ) )
125 if( nTmpLen
> nPgs
&& nTmpLen
< nMaxLen
)
127 // block > requested size
129 nMaxStart
= nTmpStart
;
132 else if( nTmpLen
>= nMinLen
)
134 // block < requested size
136 nMinStart
= nTmpStart
;
138 if( nTmpLen
== nPgs
)
145 // Determine which block to use.
148 if( nTmpLen
> nPgs
&& nTmpLen
< nMaxLen
)
150 // block > requested size
152 nMaxStart
= nTmpStart
;
154 else if( nTmpLen
>= nMinLen
)
156 // block < requested size
158 nMinStart
= nTmpStart
;
161 if( nMinStart
!= STG_EOF
&& nMaxStart
!= STG_EOF
)
163 // two areas found; return the best fit area
164 sal_Int32 nMinDiff
= nPgs
- nMinLen
;
165 sal_Int32 nMaxDiff
= nMaxLen
- nPgs
;
166 if( nMinDiff
> nMaxDiff
)
169 if( nMinStart
!= STG_EOF
)
171 nPgs
= nMinLen
; return nMinStart
;
179 // Set up the consecutive chain for a given block.
181 bool StgFAT::MakeChain( sal_Int32 nStart
, sal_Int32 nPgs
)
183 sal_Int32 nPos
= nStart
<< 2;
184 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nPos
);
185 if( !pPg
.is() || !nPgs
)
189 if( m_nOffset
>= m_nPageSize
)
191 pPg
= GetPhysPage( nPos
);
195 m_rStrm
.GetIo().SetToPage( pPg
, m_nOffset
>> 2, ++nStart
);
199 if( m_nOffset
>= m_nPageSize
)
201 pPg
= GetPhysPage( nPos
);
205 m_rStrm
.GetIo().SetToPage( pPg
, m_nOffset
>> 2, STG_EOF
);
209 // Allocate a block of data from the given page number on.
210 // It the page number is != STG_EOF, chain the block.
212 sal_Int32
StgFAT::AllocPages( sal_Int32 nBgn
, sal_Int32 nPgs
)
214 sal_Int32 nOrig
= nBgn
;
215 sal_Int32 nLast
= nBgn
;
216 sal_Int32 nBegin
= STG_EOF
;
218 sal_Int32 nPages
= m_rStrm
.GetSize() >> 2;
220 // allow for two passes
223 // try to satisfy the request from the pool of free pages
227 nBegin
= FindBlock( nAlloc
);
228 // no more blocks left in present alloc chain
229 if( nBegin
== STG_EOF
)
231 if( ( nBegin
+ nAlloc
) > m_nMaxPage
)
232 m_nMaxPage
= nBegin
+ nAlloc
;
233 if( !MakeChain( nBegin
, nAlloc
) )
235 if( nOrig
== STG_EOF
)
240 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nLast
<< 2 );
243 m_rStrm
.GetIo().SetToPage( pPg
, m_nOffset
>> 2, nBegin
);
245 nLast
= nBegin
+ nAlloc
- 1;
248 if( nPgs
&& !nPasses
)
250 // we need new, fresh space, so allocate and retry
251 if( !m_rStrm
.SetSize( ( nPages
+ nPgs
) << 2 ) )
253 if( !m_bPhys
&& !InitNew( nPages
) )
255 // FIXME: this was originally "FALSE", whether or not that
256 // makes sense (or should be STG_EOF instead, say?)
257 nPages
= m_rStrm
.GetSize() >> 2;
263 // now we should have a chain for the complete block
264 if( nBegin
== STG_EOF
|| nPgs
)
266 m_rStrm
.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR
);
267 return STG_EOF
; // bad structure
272 // Initialize newly allocated pages for a standard FAT stream
273 // It can be assumed that the stream size is always on
276 bool StgFAT::InitNew( sal_Int32 nPage1
)
278 sal_Int32 n
= ( ( m_rStrm
.GetSize() >> 2 ) - nPage1
) / m_nEntries
;
283 rtl::Reference
< StgPage
> pPg
;
284 // Position within the underlying stream
285 // use the Pos2Page() method of the stream
286 m_rStrm
.Pos2Page( nPage1
<< 2 );
287 // Initialize the page
288 pPg
= m_rStrm
.GetIo().Copy( m_rStrm
.GetPage() );
291 for( short i
= 0; i
< m_nEntries
; i
++ )
292 m_rStrm
.GetIo().SetToPage( pPg
, i
, STG_FREE
);
301 bool StgFAT::FreePages( sal_Int32 nStart
, bool bAll
)
305 rtl::Reference
< StgPage
> pPg
= GetPhysPage( nStart
<< 2 );
308 nStart
= StgCache::GetFromPage( pPg
, m_nOffset
>> 2 );
309 // The first released page is either set to EOF or FREE
310 m_rStrm
.GetIo().SetToPage( pPg
, m_nOffset
>> 2, bAll
? STG_FREE
: STG_EOF
);
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
)
324 m_bBytePosValid(true),
331 m_nPageSize(m_rIo
.GetPhysPageSize())
339 // Attach the stream to the given entry.
341 void StgStrm::SetEntry( StgDirEntry
& r
)
343 r
.m_aEntry
.SetLeaf( STG_DATA
, m_nStart
);
344 r
.m_aEntry
.SetSize( m_nSize
);
350 * The page chain, is basically a singly linked list of slots each
351 * point to the next page. Instead of traversing the file structure
352 * for this each time build a simple flat in-memory vector list
355 sal_Int32
StgStrm::scanBuildPageChainCache()
359 m_aPagesCache
.reserve(m_nSize
/m_nPageSize
);
360 m_aUsedPageNumbers
.reserve(m_nSize
/m_nPageSize
);
364 sal_Int32 nBgn
= m_nStart
;
365 sal_Int32 nOptSize
= 0;
367 // Track already scanned PageNumbers here and use them to
368 // see if an already counted page is re-visited
369 while( nBgn
>= 0 && !bError
)
371 m_aPagesCache
.push_back(nBgn
);
372 nBgn
= m_pFat
->GetNextPage( nBgn
);
374 //returned second is false if it already exists
375 if (!m_aUsedPageNumbers
.insert(nBgn
).second
)
377 SAL_WARN ("sot", "Error: page number " << nBgn
<< " already in chain for stream");
381 nOptSize
+= m_nPageSize
;
385 SAL_WARN("sot", "returning wrong format error");
386 m_rIo
.SetError( ERRCODE_IO_WRONGFORMAT
);
387 m_aPagesCache
.clear();
388 m_aUsedPageNumbers
.clear();
393 // Compute page number and offset for the given byte position.
394 // If the position is behind the size, set the stream right
396 bool StgStrm::Pos2Page( sal_Int32 nBytePos
)
401 // Values < 0 seek to the end
402 if( nBytePos
< 0 || nBytePos
>= m_nSize
)
404 // Adjust the position back to offset 0
406 sal_Int32 nMask
= ~( m_nPageSize
- 1 );
407 sal_Int32 nOld
= m_nPos
& nMask
;
408 sal_Int32 nNew
= nBytePos
& nMask
;
409 m_nOffset
= static_cast<short>( nBytePos
& ~nMask
);
412 return m_bBytePosValid
;
414 // See fdo#47644 for a .doc with a vast amount of pages where seeking around the
415 // document takes a colossal amount of time
417 // Please Note: we build the pagescache incrementally as we go if necessary,
418 // so that a corrupted FAT doesn't poison the stream state for earlier reads
419 size_t nIdx
= nNew
/ m_nPageSize
;
420 if( nIdx
>= m_aPagesCache
.size() )
422 // Extend the FAT cache ! ...
423 size_t nToAdd
= nIdx
+ 1;
425 if (m_aPagesCache
.empty())
427 m_aPagesCache
.push_back( m_nStart
);
428 assert(m_aUsedPageNumbers
.empty());
429 m_aUsedPageNumbers
.insert(m_nStart
);
432 nToAdd
-= m_aPagesCache
.size();
434 sal_Int32 nBgn
= m_aPagesCache
.back();
436 // Start adding pages while we can
437 while (nToAdd
> 0 && nBgn
>= 0)
439 sal_Int32 nOldBgn
= nBgn
;
440 nBgn
= m_pFat
->GetNextPage(nOldBgn
);
443 //returned second is false if it already exists
444 if (!m_aUsedPageNumbers
.insert(nBgn
).second
)
446 SAL_WARN ("sot", "Error: page number " << nBgn
<< " already in chain for stream");
450 //very much the normal case
451 m_aPagesCache
.push_back(nBgn
);
457 if ( nIdx
> m_aPagesCache
.size() )
459 SAL_WARN("sot", "seek to index " << nIdx
<<
460 " beyond page cache size " << m_aPagesCache
.size());
461 // fdo#84229 - handle seek to end and back as eg. XclImpStream expects
464 // Intriguingly in the past we didn't reset nPos to match the real
465 // length of the stream thus:
466 // nIdx = m_aPagesCache.size();
467 // nPos = nPageSize * nIdx;
468 // so retain this behavior for now.
469 m_bBytePosValid
= false;
473 // special case: seek to 1st byte of new, unallocated page
474 // (in case the file size is a multiple of the page size)
475 if( nBytePos
== m_nSize
&& !m_nOffset
&& nIdx
> 0 && nIdx
== m_aPagesCache
.size() )
478 m_nOffset
= m_nPageSize
;
480 else if ( nIdx
== m_aPagesCache
.size() )
483 m_bBytePosValid
= false;
487 m_nPage
= m_aPagesCache
[ nIdx
];
489 m_bBytePosValid
= m_nPage
>= 0;
490 return m_bBytePosValid
;
493 // Copy an entire stream. Both streams are allocated in the FAT.
494 // The target stream is this stream.
496 bool StgStrm::Copy( sal_Int32 nFrom
, sal_Int32 nBytes
)
501 m_aPagesCache
.clear();
502 m_aUsedPageNumbers
.clear();
504 sal_Int32 nTo
= m_nStart
;
505 sal_Int32 nPgs
= ( nBytes
+ m_nPageSize
- 1 ) / m_nPageSize
;
510 m_rIo
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
513 m_rIo
.Copy( nTo
, nFrom
);
516 nFrom
= m_pFat
->GetNextPage( nFrom
);
519 m_rIo
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
523 nTo
= m_pFat
->GetNextPage( nTo
);
528 bool StgStrm::SetSize( sal_Int32 nBytes
)
530 if ( nBytes
< 0 || !m_pFat
)
533 m_aPagesCache
.clear();
534 m_aUsedPageNumbers
.clear();
536 // round up to page size
537 sal_Int32 nOld
= ( ( m_nSize
+ m_nPageSize
- 1 ) / m_nPageSize
) * m_nPageSize
;
538 sal_Int32 nNew
= ( ( nBytes
+ m_nPageSize
- 1 ) / m_nPageSize
) * m_nPageSize
;
541 if( !Pos2Page( m_nSize
) )
543 sal_Int32 nBgn
= m_pFat
->AllocPages( m_nPage
, ( nNew
- nOld
) / m_nPageSize
);
544 if( nBgn
== STG_EOF
)
546 if( m_nStart
== STG_EOF
)
547 m_nStart
= m_nPage
= nBgn
;
549 else if( nNew
< nOld
)
551 bool bAll
= ( nBytes
== 0 );
552 if( !Pos2Page( nBytes
) || !m_pFat
->FreePages( m_nPage
, bAll
) )
555 m_nStart
= m_nPage
= STG_EOF
;
559 // change the dir entry?
560 if( !m_nSize
|| !nBytes
)
561 m_pEntry
->m_aEntry
.SetLeaf( STG_DATA
, m_nStart
);
562 m_pEntry
->m_aEntry
.SetSize( nBytes
);
563 m_pEntry
->SetDirty();
566 m_pFat
->SetLimit( GetPages() );
570 // Return the # of allocated pages
573 //////////////////////////// class StgFATStrm
575 // The FAT stream class provides physical access to the master FAT.
576 // Since this access is implemented as a StgStrm, we can use the
579 StgFATStrm::StgFATStrm(StgIo
& r
, sal_Int32 nFatStrmSize
) : StgStrm( r
)
581 m_pFat
.reset( new StgFAT( *this, true ) );
582 m_nSize
= nFatStrmSize
;
585 bool StgFATStrm::Pos2Page( sal_Int32 nBytePos
)
587 // Values < 0 seek to the end
588 if( nBytePos
< 0 || nBytePos
>= m_nSize
)
589 nBytePos
= m_nSize
? m_nSize
- 1 : 0;
590 m_nPage
= nBytePos
/ m_nPageSize
;
591 m_nOffset
= static_cast<short>( nBytePos
% m_nPageSize
);
592 m_nPage
= GetPage(m_nPage
, false);
593 bool bValid
= m_nPage
>= 0;
594 SetPos(nBytePos
, bValid
);
598 // Get the page number entry for the given page offset.
600 sal_Int32
StgFATStrm::GetPage(sal_Int32 nOff
, bool bMake
, sal_uInt16
*pnMasterAlloc
)
602 OSL_ENSURE( nOff
>= 0, "The offset may not be negative!" );
603 if( pnMasterAlloc
) *pnMasterAlloc
= 0;
604 if( nOff
< StgHeader::GetFAT1Size() )
605 return m_rIo
.m_aHdr
.GetFATPage( nOff
);
606 sal_Int32 nMaxPage
= m_nSize
>> 2;
607 nOff
= nOff
- StgHeader::GetFAT1Size();
608 // number of master pages that we need to iterate through
609 sal_uInt16 nMasterCount
= ( m_nPageSize
>> 2 ) - 1;
610 sal_uInt16 nBlocks
= nOff
/ nMasterCount
;
611 // offset in the last master page
612 nOff
= nOff
% nMasterCount
;
614 rtl::Reference
< StgPage
> pOldPage
;
615 rtl::Reference
< StgPage
> pMaster
;
616 sal_Int32 nFAT
= m_rIo
.m_aHdr
.GetFATChain();
617 for( sal_uInt16 nCount
= 0; nCount
<= nBlocks
; nCount
++ )
619 if( nFAT
== STG_EOF
|| nFAT
== STG_FREE
)
623 m_aPagesCache
.clear();
624 m_aUsedPageNumbers
.clear();
626 // create a new master page
628 pMaster
= m_rIo
.Copy( nFAT
);
631 for( short k
= 0; k
< static_cast<short>( m_nPageSize
>> 2 ); k
++ )
632 m_rIo
.SetToPage( pMaster
, k
, STG_FREE
);
635 m_rIo
.m_aHdr
.SetFATChain( nFAT
);
637 m_rIo
.SetToPage( pOldPage
, nMasterCount
, nFAT
);
638 if( nMaxPage
>= m_rIo
.GetPhysPages() )
639 if( !m_rIo
.SetSize( nMaxPage
) )
641 // mark the page as used
642 // make space for Masterpage
643 if( !pnMasterAlloc
) // create space oneself
645 if( !Pos2Page( nFAT
<< 2 ) )
647 rtl::Reference
< StgPage
> pPg
= m_rIo
.Get( m_nPage
, true );
650 m_rIo
.SetToPage( pPg
, m_nOffset
>> 2, STG_MASTER
);
654 m_rIo
.m_aHdr
.SetMasters( nCount
+ 1 );
661 pMaster
= m_rIo
.Get( nFAT
, true );
664 nFAT
= StgCache::GetFromPage( pMaster
, nMasterCount
);
670 return StgCache::GetFromPage( pMaster
, nOff
);
671 m_rIo
.SetError( SVSTREAM_GENERALERROR
);
676 // Set the page number entry for the given page offset.
678 bool StgFATStrm::SetPage( short nOff
, sal_Int32 nNewPage
)
680 OSL_ENSURE( nOff
>= 0, "The offset may not be negative!" );
681 m_aPagesCache
.clear();
682 m_aUsedPageNumbers
.clear();
685 if( nOff
< StgHeader::GetFAT1Size() )
686 m_rIo
.m_aHdr
.SetFATPage( nOff
, nNewPage
);
689 nOff
= nOff
- StgHeader::GetFAT1Size();
690 // number of master pages that we need to iterate through
691 sal_uInt16 nMasterCount
= ( m_nPageSize
>> 2 ) - 1;
692 sal_uInt16 nBlocks
= nOff
/ nMasterCount
;
693 // offset in the last master page
694 nOff
= nOff
% nMasterCount
;
696 rtl::Reference
< StgPage
> pMaster
;
697 sal_Int32 nFAT
= m_rIo
.m_aHdr
.GetFATChain();
698 for( sal_uInt16 nCount
= 0; nCount
<= nBlocks
; nCount
++ )
700 if( nFAT
== STG_EOF
|| nFAT
== STG_FREE
)
705 pMaster
= m_rIo
.Get( nFAT
, true );
707 nFAT
= StgCache::GetFromPage( pMaster
, nMasterCount
);
710 m_rIo
.SetToPage( pMaster
, nOff
, nNewPage
);
713 m_rIo
.SetError( SVSTREAM_GENERALERROR
);
718 // lock the page against access
721 Pos2Page( nNewPage
<< 2 );
722 rtl::Reference
< StgPage
> pPg
= m_rIo
.Get( m_nPage
, true );
724 m_rIo
.SetToPage( pPg
, m_nOffset
>> 2, STG_FAT
);
731 bool StgFATStrm::SetSize( sal_Int32 nBytes
)
736 m_aPagesCache
.clear();
737 m_aUsedPageNumbers
.clear();
739 // Set the number of entries to a multiple of the page size
740 short nOld
= static_cast<short>( ( m_nSize
+ ( m_nPageSize
- 1 ) ) / m_nPageSize
);
741 short nNew
= static_cast<short>(
742 ( nBytes
+ ( m_nPageSize
- 1 ) ) / m_nPageSize
) ;
745 // release master pages
746 for( short i
= nNew
; i
< nOld
; i
++ )
747 SetPage( i
, STG_FREE
);
753 // allocate master pages
754 // find a free master page slot
756 sal_uInt16 nMasterAlloc
= 0;
757 nPg
= GetPage( nOld
, true, &nMasterAlloc
);
760 // 4 Bytes have been used for Allocation of each MegaMasterPage
761 nBytes
+= nMasterAlloc
<< 2;
763 // find a free page using the FAT allocator
765 OSL_ENSURE( m_pFat
, "The pointer is always initializer here!" );
766 sal_Int32 nNewPage
= m_pFat
->FindBlock( n
);
767 if( nNewPage
== STG_EOF
)
769 // no free pages found; create a new page
770 // Since all pages are allocated, extend
771 // the file size for the next page!
772 nNewPage
= m_nSize
>> 2;
773 // if a MegaMasterPage was created avoid taking
775 nNewPage
+= nMasterAlloc
;
776 // adjust the file size if necessary
777 if( nNewPage
>= m_rIo
.GetPhysPages() )
778 if( !m_rIo
.SetSize( nNewPage
+ 1 ) )
781 // Set up the page with empty entries
782 rtl::Reference
< StgPage
> pPg
= m_rIo
.Copy( nNewPage
);
785 for( short j
= 0; j
< static_cast<short>( m_nPageSize
>> 2 ); j
++ )
786 m_rIo
.SetToPage( pPg
, j
, STG_FREE
);
788 // store the page number into the master FAT
789 // Set the size before so the correct FAT can be found
790 m_nSize
= ( nOld
+ 1 ) * m_nPageSize
;
791 SetPage( nOld
, nNewPage
);
793 // MegaMasterPages were created, mark it them as used
795 sal_uInt32 nMax
= m_rIo
.m_aHdr
.GetMasters( );
796 sal_uInt32 nFAT
= m_rIo
.m_aHdr
.GetFATChain();
798 for( sal_uInt32 nCount
= 0; nCount
< nMax
; nCount
++ )
800 if( !Pos2Page( nFAT
<< 2 ) )
802 if( nMax
- nCount
<= nMasterAlloc
)
804 rtl::Reference
< StgPage
> piPg
= m_rIo
.Get( m_nPage
, true );
807 m_rIo
.SetToPage( piPg
, m_nOffset
>> 2, STG_MASTER
);
809 rtl::Reference
< StgPage
> pPage
= m_rIo
.Get( nFAT
, true );
810 if( !pPage
.is() ) return false;
811 nFAT
= StgCache::GetFromPage( pPage
, (m_nPageSize
>> 2 ) - 1 );
815 // We have used up 4 bytes for the STG_FAT entry
817 nNew
= static_cast<short>(
818 ( nBytes
+ ( m_nPageSize
- 1 ) ) / m_nPageSize
);
821 m_nSize
= nNew
* m_nPageSize
;
822 m_rIo
.m_aHdr
.SetFATSize( nNew
);
826 /////////////////////////// class StgDataStrm
828 // This class is a normal physical stream which can be initialized
829 // either with an existing dir entry or an existing FAT chain.
830 // The stream has a size increment which normally is 1, but which can be
831 // set to any value is you want the size to be incremented by certain values.
833 StgDataStrm::StgDataStrm( StgIo
& r
, sal_Int32 nBgn
, sal_Int32 nLen
) : StgStrm( r
)
838 StgDataStrm::StgDataStrm( StgIo
& r
, StgDirEntry
& p
) : StgStrm( r
)
841 Init( p
.m_aEntry
.GetLeaf( STG_DATA
),
842 p
.m_aEntry
.GetSize() );
845 void StgDataStrm::Init( sal_Int32 nBgn
, sal_Int32 nLen
)
848 m_pFat
.reset( new StgFAT( *m_rIo
.m_pFAT
, true ) );
850 OSL_ENSURE( m_pFat
, "The pointer should not be empty!" );
852 m_nStart
= m_nPage
= nBgn
;
856 if( nLen
< 0 && m_pFat
)
858 // determine the actual size of the stream by scanning
859 // the FAT chain and counting the # of pages allocated
860 m_nSize
= scanBuildPageChainCache();
864 // Set the size of a physical stream.
866 bool StgDataStrm::SetSize( sal_Int32 nBytes
)
871 nBytes
= ( ( nBytes
+ m_nIncr
- 1 ) / m_nIncr
) * m_nIncr
;
872 sal_Int32 nOldSz
= m_nSize
;
873 if( nOldSz
!= nBytes
)
875 if( !StgStrm::SetSize( nBytes
) )
877 sal_Int32 nMaxPage
= m_pFat
->GetMaxPage();
878 if( nMaxPage
> m_rIo
.GetPhysPages() )
879 if( !m_rIo
.SetSize( nMaxPage
) )
881 // If we only allocated one page or less, create this
882 // page in the cache for faster throughput. The current
883 // position is the former EOF point.
884 if( ( m_nSize
- 1 ) / m_nPageSize
- ( nOldSz
- 1 ) / m_nPageSize
== 1 )
888 m_rIo
.Copy( m_nPage
);
894 // Get the address of the data byte at a specified offset.
895 // If bForce = true, a read of non-existent data causes
898 void* StgDataStrm::GetPtr( sal_Int32 Pos
, bool bDirty
)
900 if( Pos2Page( Pos
) )
902 rtl::Reference
< StgPage
> pPg
= m_rIo
.Get( m_nPage
, true/*bForce*/ );
903 if (pPg
.is() && m_nOffset
< pPg
->GetSize())
906 m_rIo
.SetDirty( pPg
);
907 return static_cast<sal_uInt8
*>(pPg
->GetData()) + m_nOffset
;
913 // This could easily be adapted to a better algorithm by determining
914 // the amount of consecutable blocks before doing a read. The result
915 // is the number of bytes read. No error is generated on EOF.
917 sal_Int32
StgDataStrm::Read( void* pBuf
, sal_Int32 n
)
922 const auto nAvailable
= m_nSize
- GetPos();
928 short nBytes
= m_nPageSize
- m_nOffset
;
929 rtl::Reference
< StgPage
> pPg
;
930 if( static_cast<sal_Int32
>(nBytes
) > n
)
931 nBytes
= static_cast<short>(n
);
935 void *p
= static_cast<sal_uInt8
*>(pBuf
) + nDone
;
936 if( nBytes
== m_nPageSize
)
938 pPg
= m_rIo
.Find( m_nPage
);
941 // data is present, so use the cached data
942 memcpy( p
, pPg
->GetData(), nBytes
);
946 // do a direct (unbuffered) read
947 nRes
= static_cast<short>(m_rIo
.Read( m_nPage
, p
)) * m_nPageSize
;
951 // partial block read through the cache.
952 pPg
= m_rIo
.Get( m_nPage
, false );
955 memcpy( p
, static_cast<sal_uInt8
*>(pPg
->GetData()) + m_nOffset
, nBytes
);
959 SetPos(GetPos() + nRes
, true);
961 m_nOffset
= m_nOffset
+ nRes
;
963 break; // read error or EOF
965 // Switch to next page if necessary
966 if (m_nOffset
>= m_nPageSize
&& !Pos2Page(GetPos()))
972 sal_Int32
StgDataStrm::Write( const void* pBuf
, sal_Int32 n
)
978 if( ( GetPos() + n
) > m_nSize
)
980 sal_Int32 nOld
= GetPos();
981 if( !SetSize( nOld
+ n
) )
987 short nBytes
= m_nPageSize
- m_nOffset
;
988 rtl::Reference
< StgPage
> pPg
;
989 if( static_cast<sal_Int32
>(nBytes
) > n
)
990 nBytes
= static_cast<short>(n
);
994 const void *p
= static_cast<const sal_uInt8
*>(pBuf
) + nDone
;
995 if( nBytes
== m_nPageSize
)
997 pPg
= m_rIo
.Find( m_nPage
);
1000 // data is present, so use the cached data
1001 memcpy( pPg
->GetData(), p
, nBytes
);
1002 m_rIo
.SetDirty( pPg
);
1006 // do a direct (unbuffered) write
1007 nRes
= static_cast<short>(m_rIo
.Write( m_nPage
, p
)) * m_nPageSize
;
1011 // partial block read through the cache.
1012 pPg
= m_rIo
.Get( m_nPage
, false );
1015 memcpy( static_cast<sal_uInt8
*>(pPg
->GetData()) + m_nOffset
, p
, nBytes
);
1016 m_rIo
.SetDirty( pPg
);
1020 SetPos(GetPos() + nRes
, true);
1022 m_nOffset
= m_nOffset
+ nRes
;
1023 if( nRes
!= nBytes
)
1024 break; // read error
1026 // Switch to next page if necessary
1027 if( m_nOffset
>= m_nPageSize
&& !Pos2Page(GetPos()) )
1033 //////////////////////////// class StgSmallStream
1035 // The small stream class provides access to streams with a size < 4096 bytes.
1036 // This stream is a StgStream containing small pages. The FAT for this stream
1037 // is also a StgStream. The start of the FAT is in the header at DataRootPage,
1038 // the stream itself is pointed to by the root entry (it holds start & size).
1040 StgSmallStrm::StgSmallStrm( StgIo
& r
, sal_Int32 nBgn
) : StgStrm( r
)
1045 StgSmallStrm::StgSmallStrm( StgIo
& r
, StgDirEntry
& p
) : StgStrm( r
)
1048 Init( p
.m_aEntry
.GetLeaf( STG_DATA
),
1049 p
.m_aEntry
.GetSize() );
1052 void StgSmallStrm::Init( sal_Int32 nBgn
, sal_Int32 nLen
)
1054 if ( m_rIo
.m_pDataFAT
)
1055 m_pFat
.reset( new StgFAT( *m_rIo
.m_pDataFAT
, false ) );
1056 m_pData
= m_rIo
.m_pDataStrm
;
1057 OSL_ENSURE( m_pFat
&& m_pData
, "The pointers should not be empty!" );
1059 m_nPageSize
= m_rIo
.GetDataPageSize();
1065 // This could easily be adapted to a better algorithm by determining
1066 // the amount of consecutable blocks before doing a read. The result
1067 // is the number of bytes read. No error is generated on EOF.
1069 sal_Int32
StgSmallStrm::Read( void* pBuf
, sal_Int32 n
)
1071 // We can safely assume that reads are not huge, since the
1072 // small stream is likely to be < 64 KBytes.
1073 sal_Int32 nBytePos
= GetPos();
1074 if( ( nBytePos
+ n
) > m_nSize
)
1075 n
= m_nSize
- nBytePos
;
1076 sal_Int32 nDone
= 0;
1079 short nBytes
= m_nPageSize
- m_nOffset
;
1080 if( static_cast<sal_Int32
>(nBytes
) > n
)
1081 nBytes
= static_cast<short>(n
);
1087 if (o3tl::checked_multiply
<sal_Int32
>(m_nPage
, m_nPageSize
, nPos
))
1089 if (!m_pData
->Pos2Page(nPos
+ m_nOffset
))
1091 // all reading through the stream
1092 short nRes
= static_cast<short>(m_pData
->Read( static_cast<sal_uInt8
*>(pBuf
) + nDone
, nBytes
));
1094 SetPos(GetPos() + nRes
, true);
1096 m_nOffset
= m_nOffset
+ nRes
;
1098 if( nRes
!= nBytes
)
1101 // Switch to next page if necessary
1102 if (m_nOffset
>= m_nPageSize
&& !Pos2Page(GetPos()))
1108 sal_Int32
StgSmallStrm::Write( const void* pBuf
, sal_Int32 n
)
1110 // you can safely assume that reads are not huge, since the
1111 // small stream is likely to be < 64 KBytes.
1112 sal_Int32 nDone
= 0;
1113 sal_Int32 nOldPos
= GetPos();
1114 if( ( nOldPos
+ n
) > m_nSize
)
1116 if (!SetSize(nOldPos
+ n
))
1122 short nBytes
= m_nPageSize
- m_nOffset
;
1123 if( static_cast<sal_Int32
>(nBytes
) > n
)
1124 nBytes
= static_cast<short>(n
);
1127 // all writing goes through the stream
1128 sal_Int32 nDataPos
= m_nPage
* m_nPageSize
+ m_nOffset
;
1130 || ( m_pData
->GetSize() < ( nDataPos
+ nBytes
)
1131 && !m_pData
->SetSize( nDataPos
+ nBytes
) ) )
1133 if( !m_pData
->Pos2Page( nDataPos
) )
1135 short nRes
= static_cast<short>(m_pData
->Write( static_cast<sal_uInt8
const *>(pBuf
) + nDone
, nBytes
));
1137 SetPos(GetPos() + nRes
, true);
1139 m_nOffset
= m_nOffset
+ nRes
;
1141 if( nRes
!= nBytes
)
1144 // Switch to next page if necessary
1145 if( m_nOffset
>= m_nPageSize
&& !Pos2Page(GetPos()) )
1151 /////////////////////////// class StgTmpStrm
1153 // The temporary stream uses a memory stream if < 32K, otherwise a
1156 #define THRESHOLD 32768L
1158 StgTmpStrm::StgTmpStrm( sal_uInt64 nInitSize
)
1159 : SvMemoryStream( nInitSize
> THRESHOLD
1161 : ( nInitSize
? nInitSize
: 16 ), 4096 )
1164 // this calls FlushData, so all members should be set by this time
1166 if( nInitSize
> THRESHOLD
)
1167 SetSize( nInitSize
);
1170 bool StgTmpStrm::Copy( StgTmpStrm
& rSrc
)
1172 sal_uInt64 n
= rSrc
.GetSize();
1173 const sal_uInt64 nCur
= rSrc
.Tell();
1175 if( GetError() == ERRCODE_NONE
)
1177 std::unique_ptr
<sal_uInt8
[]> p(new sal_uInt8
[ 4096 ]);
1182 const sal_uInt64 nn
= std::min
<sal_uInt64
>(n
, 4096);
1183 if (rSrc
.ReadBytes( p
.get(), nn
) != nn
)
1185 if (WriteBytes( p
.get(), nn
) != nn
)
1198 StgTmpStrm::~StgTmpStrm()
1203 osl::File::remove( m_aName
);
1208 sal_uInt64
StgTmpStrm::GetSize() const
1213 n
= m_pStrm
->TellEnd();
1220 void StgTmpStrm::SetSize(sal_uInt64 n
)
1223 m_pStrm
->SetStreamSize( n
);
1228 m_aName
= utl::CreateTempURL();
1229 std::unique_ptr
<SvFileStream
> s(new SvFileStream( m_aName
, StreamMode::READWRITE
));
1230 const sal_uInt64 nCur
= Tell();
1231 sal_uInt64 i
= nEndOfData
;
1232 std::unique_ptr
<sal_uInt8
[]> p(new sal_uInt8
[ 4096 ]);
1238 const sal_uInt64 nb
= std::min
<sal_uInt64
>(i
, 4096);
1239 if (ReadBytes(p
.get(), nb
) == nb
1240 && s
->WriteBytes(p
.get(), nb
) == nb
)
1246 if( !i
&& n
> nEndOfData
)
1248 // We have to write one byte at the end of the file
1249 // if the file is bigger than the memstream to see
1250 // if it fits on disk
1251 s
->Seek(nEndOfData
);
1252 memset(p
.get(), 0x00, 4096);
1256 const sal_uInt64 nb
= std::min
<sal_uInt64
>(i
, 4096);
1257 if (s
->WriteBytes(p
.get(), nb
) == nb
)
1263 if( s
->GetError() != ERRCODE_NONE
)
1270 SetError( s
->GetError() );
1273 m_pStrm
= s
.release();
1274 // Shrink the memory to 16 bytes, which seems to be the minimum
1275 ReAllocateMemory( - ( static_cast<tools::Long
>(nEndOfData
) - 16 ) );
1279 if( n
> nEndOfData
)
1281 SvMemoryStream::SetSize(n
);
1289 std::size_t StgTmpStrm::GetData( void* pData
, std::size_t n
)
1293 n
= m_pStrm
->ReadBytes( pData
, n
);
1294 SetError( m_pStrm
->GetError() );
1298 return SvMemoryStream::GetData( pData
, n
);
1301 std::size_t StgTmpStrm::PutData( const void* pData
, std::size_t n
)
1303 sal_uInt32 nCur
= Tell();
1304 sal_uInt32 nNew
= nCur
+ n
;
1305 if( nNew
> THRESHOLD
&& !m_pStrm
)
1308 if( GetError() != ERRCODE_NONE
)
1313 nNew
= m_pStrm
->WriteBytes( pData
, n
);
1314 SetError( m_pStrm
->GetError() );
1317 nNew
= SvMemoryStream::PutData( pData
, n
);
1321 sal_uInt64
StgTmpStrm::SeekPos(sal_uInt64 n
)
1323 // check if a truncated STREAM_SEEK_TO_END was passed
1324 assert(n
!= SAL_MAX_UINT32
);
1325 if( n
== STREAM_SEEK_TO_END
)
1327 if( n
> THRESHOLD
&& !m_pStrm
)
1330 if( GetError() != ERRCODE_NONE
)
1337 n
= m_pStrm
->Seek( n
);
1338 SetError( m_pStrm
->GetError() );
1342 return SvMemoryStream::SeekPos( n
);
1345 void StgTmpStrm::FlushData()
1350 SetError( m_pStrm
->GetError() );
1353 SvMemoryStream::FlushData();
1356 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */