1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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>
41 #include "stgelem.hxx"
42 #include "stgcache.hxx"
43 #include "stgstrms.hxx"
48 #include <tools/svwin.h>
49 #define memcpy hmemcpy
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
)
64 nPageSize
= rStrm
.GetIo().GetPhysPageSize();
65 nEntries
= nPageSize
>> 2;
71 // Retrieve the physical page for a given byte offset.
73 StgPage
* StgFAT::GetPhysPage( INT32 nByteOff
)
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
);
88 // Get the follow page for a certain FAT page.
90 INT32
StgFAT::GetNextPage( INT32 nPg
)
94 StgPage
* pPg
= GetPhysPage( nPg
<< 2 );
95 nPg
= pPg
? pPg
->GetPage( nOffset
>> 2 ) : STG_EOF
;
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;
115 for( INT32 i
= 0; i
< nPages
; i
++, nEntry
++ )
117 if( !( nEntry
% nEntries
) )
119 // load the next page for that stream
121 pPg
= GetPhysPage( i
<< 2 );
125 INT32 nCur
= pPg
->GetPage( nEntry
);
126 if( nCur
== STG_FREE
)
128 // count the size of this area
135 // If we already did find a block, stop when reaching the limit
136 || ( bFound
&& ( nEntry
>= nLimit
) ) )
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
;
149 if( nTmpLen
== nPgs
)
156 // Determine which block to use.
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
)
174 if( nMinStart
!= STG_EOF
)
176 nPgs
= nMinLen
; return nMinStart
;
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
);
194 if( nOffset
>= nPageSize
)
196 pPg
= GetPhysPage( nPos
);
200 pPg
->SetPage( nOffset
>> 2, ++nStart
);
204 if( nOffset
>= nPageSize
)
206 pPg
= GetPhysPage( nPos
);
210 pPg
->SetPage( nOffset
>> 2, STG_EOF
);
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
)
221 INT32 nBegin
= STG_EOF
;
223 INT32 nPages
= rStrm
.GetSize() >> 2;
225 // allow for two passes
228 // try to satisfy the request from the pool of free pages
232 nBegin
= FindBlock( nAlloc
);
233 // no more blocks left in present alloc chain
234 if( nBegin
== STG_EOF
)
236 if( ( nBegin
+ nAlloc
) > nMaxPage
)
237 nMaxPage
= nBegin
+ nAlloc
;
238 if( !MakeChain( nBegin
, nAlloc
) )
240 if( nOrig
== STG_EOF
)
245 StgPage
* pPg
= GetPhysPage( nLast
<< 2 );
248 pPg
->SetPage( nOffset
>> 2, nBegin
);
250 nLast
= nBegin
+ nAlloc
- 1;
253 if( nPgs
&& !nPasses
)
255 // we need new, fresh space, so allocate and retry
256 if( !rStrm
.SetSize( ( nPages
+ nPgs
) << 2 ) )
258 if( !bPhys
&& !InitNew( nPages
) )
260 nPages
= rStrm
.GetSize() >> 2;
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
275 // Initialize newly allocated pages for a standard FAT stream
276 // It can be assumed that the stream size is always on
279 BOOL
StgFAT::InitNew( INT32 nPage1
)
281 INT32 n
= ( ( rStrm
.GetSize() >> 2 ) - nPage1
) / nEntries
;
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
);
292 for( short i
= 0; i
< nEntries
; i
++ )
293 pPg
->SetPage( i
, STG_FREE
);
301 BOOL
StgFAT::FreePages( INT32 nStart
, BOOL bAll
)
305 StgPage
* pPg
= GetPhysPage( nStart
<< 2 );
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
);
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
)
325 nStart
= nPage
= STG_EOF
;
329 nPageSize
= rIo
.GetPhysPageSize();
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
);
347 // Compute page number and offset for the given byte position.
348 // If the position is behind the size, set the stream right
351 BOOL
StgStrm::Pos2Page( INT32 nBytePos
)
354 // Values < 0 seek to the end
355 if( nBytePos
< 0 || nBytePos
>= nSize
)
357 // Adjust the position back to offset 0
359 INT32 nMask
= ~( nPageSize
- 1 );
360 INT32 nOld
= nPos
& nMask
;
361 INT32 nNew
= nBytePos
& nMask
;
362 nOffset
= (short) ( nBytePos
& ~nMask
);
368 // the new position is behind the current, so an incremental
369 // positioning is OK. Set the page relative position
375 // the new position is before the current, so we have to scan
380 // now, traverse the FAT chain.
382 INT32 nLast
= STG_EOF
;
383 while( nRel
&& nBgn
>= 0 )
386 nBgn
= pFat
->GetNextPage( nBgn
);
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
);
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
) )
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
)
418 INT32 nPgs
= ( nBytes
+ nPageSize
- 1 ) / nPageSize
;
423 rIo
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
426 rIo
.Copy( nTo
, nFrom
);
429 nFrom
= pFat
->GetNextPage( nFrom
);
432 rIo
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
436 nTo
= pFat
->GetNextPage( nTo
);
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
;
448 if( !Pos2Page( nSize
) )
450 INT32 nBgn
= pFat
->AllocPages( nPage
, ( nNew
- nOld
) / nPageSize
);
451 if( nBgn
== STG_EOF
)
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
) )
462 nStart
= nPage
= STG_EOF
;
466 // change the dir entry?
467 if( !nSize
|| !nBytes
)
468 pEntry
->aEntry
.SetLeaf( STG_DATA
, nStart
);
469 pEntry
->aEntry
.SetSize( nBytes
);
473 pFat
->SetLimit( GetPages() );
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
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
);
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
)
541 // create a new master page
543 pMaster
= rIo
.Copy( nFAT
, STG_FREE
);
546 for( short k
= 0; k
< ( nPageSize
>> 2 ); k
++ )
547 pMaster
->SetPage( k
, STG_FREE
);
548 // Verkettung herstellen
550 rIo
.aHdr
.SetFATChain( nFAT
);
552 pOldPage
->SetPage( nMasterCount
, nFAT
);
553 if( nMaxPage
>= rIo
.GetPhysPages() )
554 if( !rIo
.SetSize( nMaxPage
) )
556 // mark the page as used
557 // Platz fuer Masterpage schaffen
558 if( !pnMasterAlloc
) // Selbst Platz schaffen
560 if( !Pos2Page( nFAT
<< 2 ) )
562 StgPage
* pPg
= rIo
.Get( nPage
, TRUE
);
565 pPg
->SetPage( nOffset
>> 2, STG_MASTER
);
569 rIo
.aHdr
.SetMasters( nCount
+ 1 );
576 pMaster
= rIo
.Get( nFAT
, TRUE
);
579 nFAT
= pMaster
->GetPage( nMasterCount
);
585 return pMaster
->GetPage( nOff
);
586 rIo
.SetError( SVSTREAM_GENERALERROR
);
591 // Set the page number entry for the given page offset.
593 BOOL
StgFATStrm::SetPage( short nOff
, INT32 nNewPage
)
596 if( nOff
< rIo
.aHdr
.GetFAT1Size() )
597 rIo
.aHdr
.SetFATPage( nOff
, nNewPage
);
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
)
616 pMaster
= rIo
.Get( nFAT
, TRUE
);
618 nFAT
= pMaster
->GetPage( nMasterCount
);
621 pMaster
->SetPage( nOff
, nNewPage
);
624 rIo
.SetError( SVSTREAM_GENERALERROR
);
629 // lock the page against access
632 Pos2Page( nNewPage
<< 2 );
633 StgPage
* pPg
= rIo
.Get( nPage
, TRUE
);
635 pPg
->SetPage( nOffset
>> 2, STG_FAT
);
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
) ;
650 // release master pages
651 for( short i
= nNew
; i
< nOld
; i
++ )
652 SetPage( i
, STG_FREE
);
658 // allocate master pages
659 // find a free master page slot
661 USHORT nMasterAlloc
= 0;
662 nPg
= GetPage( nOld
, TRUE
, &nMasterAlloc
);
665 // 4 Bytes have been used for Allocation of each MegaMasterPage
666 nBytes
+= nMasterAlloc
<< 2;
668 // find a free page using the FAT allocator
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
679 nNewPage
+= nMasterAlloc
;
680 // adjust the file size if necessary
681 if( nNewPage
>= rIo
.GetPhysPages() )
682 if( !rIo
.SetSize( nNewPage
+ 1 ) )
685 // Set up the page with empty entries
686 StgPage
* pPg
= rIo
.Copy( nNewPage
, STG_FREE
);
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();
702 for( USHORT nCount
= 0; nCount
< nMax
; nCount
++ )
704 if( !Pos2Page( nFAT
<< 2 ) )
706 if( nMax
- nCount
<= nMasterAlloc
)
708 StgPage
* piPg
= rIo
.Get( nPage
, TRUE
);
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 );
719 // We have used up 4 bytes for the STG_FAT entry
722 ( nBytes
+ ( nPageSize
- 1 ) ) / nPageSize
);
725 nSize
= nNew
* nPageSize
;
726 rIo
.aHdr
.SetFATSize( nNew
);
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
)
742 StgDataStrm::StgDataStrm( StgIo
& r
, StgDirEntry
* p
) : StgStrm( r
)
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
;
758 // determine the actual size of the stream by scanning
759 // the FAT chain and counting the # of pages allocated
762 while( nBgn
>= 0 && nBgn
!= nOldBgn
)
765 nBgn
= pFat
->GetNextPage( nBgn
);
766 if( nBgn
== nOldBgn
)
767 rIo
.SetError( ERRCODE_IO_WRONGFORMAT
);
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
) )
783 INT32 nMaxPage
= pFat
->GetMaxPage();
784 if( nMaxPage
> rIo
.GetPhysPages() )
785 if( !rIo
.SetSize( nMaxPage
) )
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 )
794 rIo
.Copy( nPage
, STG_FREE
);
800 // Get the address of the data byte at a specified offset.
801 // If bForce = TRUE, a read of non-existent data causes
804 void* StgDataStrm::GetPtr( INT32 Pos
, BOOL bForce
, BOOL bDirty
)
806 if( Pos2Page( Pos
) )
808 StgPage
* pPg
= rIo
.Get( nPage
, bForce
);
811 pPg
->SetOwner( pEntry
);
814 return ((BYTE
*)pPg
->GetData()) + nOffset
;
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
)
829 if( ( nPos
+ n
) > nSize
)
834 short nBytes
= nPageSize
- nOffset
;
837 if( (INT32
) nBytes
> n
)
841 void *p
= (BYTE
*) pBuf
+ nDone
;
842 if( nBytes
== nPageSize
)
844 pPg
= rIo
.Find( nPage
);
847 // data is present, so use the cached data
848 pPg
->SetOwner( pEntry
);
849 memcpy( p
, pPg
->GetData(), nBytes
);
853 // do a direct (unbuffered) read
854 nRes
= (short) rIo
.Read( nPage
, p
, 1 ) * nPageSize
;
858 // partial block read thru the cache.
859 pPg
= rIo
.Get( nPage
, FALSE
);
862 pPg
->SetOwner( pEntry
);
863 memcpy( p
, (BYTE
*)pPg
->GetData() + nOffset
, nBytes
);
869 nOffset
= nOffset
+ nRes
;
871 break; // read error or EOF
873 // Switch to next page if necessary
874 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
880 INT32
StgDataStrm::Write( const void* pBuf
, INT32 n
)
883 if( ( nPos
+ n
) > nSize
)
886 if( !SetSize( nPos
+ n
) )
892 short nBytes
= nPageSize
- nOffset
;
895 if( (INT32
) nBytes
> n
)
899 const void *p
= (const BYTE
*) pBuf
+ nDone
;
900 if( nBytes
== nPageSize
)
902 pPg
= rIo
.Find( nPage
);
905 // data is present, so use the cached data
906 pPg
->SetOwner( pEntry
);
907 memcpy( pPg
->GetData(), p
, nBytes
);
912 // do a direct (unbuffered) write
913 nRes
= (short) rIo
.Write( nPage
, (void*) p
, 1 ) * nPageSize
;
917 // partial block read thru the cache.
918 pPg
= rIo
.Get( nPage
, FALSE
);
921 pPg
->SetOwner( pEntry
);
922 memcpy( (BYTE
*)pPg
->GetData() + nOffset
, p
, nBytes
);
929 nOffset
= nOffset
+ nRes
;
933 // Switch to next page if necessary
934 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
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
)
952 StgSmallStrm::StgSmallStrm( StgIo
& r
, StgDirEntry
* p
) : StgStrm( r
)
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();
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
)
982 short nBytes
= nPageSize
- nOffset
;
983 if( (INT32
) nBytes
> n
)
987 if( !pData
->Pos2Page( nPage
* nPageSize
+ nOffset
) )
989 // all reading thru the stream
990 short nRes
= (short) pData
->Read( (BYTE
*)pBuf
+ nDone
, nBytes
);
991 nDone
= nDone
+ nRes
;
994 nOffset
= nOffset
+ nRes
;
999 // Switch to next page if necessary
1000 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
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.
1011 if( ( nPos
+ n
) > nSize
)
1014 if( !SetSize( nPos
+ n
) )
1020 short nBytes
= nPageSize
- nOffset
;
1021 if( (INT32
) nBytes
> n
)
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
) )
1030 if( !pData
->Pos2Page( nDataPos
) )
1032 short nRes
= (short) pData
->Write( (BYTE
*)pBuf
+ nDone
, nBytes
);
1033 nDone
= nDone
+ nRes
;
1036 nOffset
= nOffset
+ nRes
;
1038 if( nRes
!= nBytes
)
1041 // Switch to next page if necessary
1042 if( nOffset
>= nPageSize
&& !Pos2Page( nPos
) )
1048 /////////////////////////// class StgTmpStrm /////////////////////////////
1050 // The temporary stream uses a memory stream if < 32K, otherwise a
1053 #define THRESHOLD 32768L
1055 StgTmpStrm::StgTmpStrm( ULONG nInitSize
)
1056 : SvMemoryStream( nInitSize
> THRESHOLD
1058 : ( nInitSize
? nInitSize
: 16 ), 4096 )
1061 // this calls FlushData, so all members should be set by this time
1063 if( nInitSize
> THRESHOLD
)
1064 SetSize( nInitSize
);
1067 BOOL
StgTmpStrm::Copy( StgTmpStrm
& rSrc
)
1069 ULONG n
= rSrc
.GetSize();
1070 ULONG nCur
= rSrc
.Tell();
1072 if( GetError() == SVSTREAM_OK
)
1074 BYTE
* p
= new BYTE
[ 4096 ];
1082 if( rSrc
.Read( p
, nn
) != nn
)
1084 if( Write( p
, nn
) != nn
)
1091 return BOOL( n
== 0 );
1097 StgTmpStrm::~StgTmpStrm()
1102 osl::File::remove( aName
);
1107 ULONG
StgTmpStrm::GetSize()
1112 ULONG old
= pStrm
->Tell();
1113 n
= pStrm
->Seek( STREAM_SEEK_TO_END
);
1121 void StgTmpStrm::SetSize( ULONG n
)
1124 pStrm
->SetStreamSize( n
);
1129 aName
= TempFile::CreateTempName();
1130 SvFileStream
* s
= new SvFileStream( aName
, STREAM_READWRITE
);
1131 ULONG nCur
= Tell();
1132 ULONG i
= nEndOfData
;
1135 BYTE
* p
= new BYTE
[ 4096 ];
1139 ULONG nb
= ( i
> 4096 ) ? 4096 : i
;
1140 if( Read( p
, nb
) == nb
1141 && s
->Write( p
, nb
) == nb
)
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
1156 if( s
->GetError() != SVSTREAM_OK
)
1163 SetError( s
->GetError() );
1168 // Shrink the memory to 16 bytes, which seems to be the minimum
1169 ReAllocateMemory( - ( (long) nEndOfData
- 16 ) );
1173 if( n
> nEndOfData
)
1175 ULONG nCur
= Tell();
1176 Seek( nEndOfData
- 1 );
1186 ULONG
StgTmpStrm::GetData( void* pData
, ULONG n
)
1190 n
= pStrm
->Read( pData
, n
);
1191 SetError( pStrm
->GetError() );
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
)
1205 if( GetError() != SVSTREAM_OK
)
1210 nNew
= pStrm
->Write( pData
, n
);
1211 SetError( pStrm
->GetError() );
1214 nNew
= SvMemoryStream::PutData( (sal_Char
*)pData
, n
);
1218 ULONG
StgTmpStrm::SeekPos( ULONG n
)
1220 if( n
== STREAM_SEEK_TO_END
)
1222 if( n
&& n
> THRESHOLD
&& !pStrm
)
1225 if( GetError() != SVSTREAM_OK
)
1232 n
= pStrm
->Seek( n
);
1233 SetError( pStrm
->GetError() );
1237 return SvMemoryStream::SeekPos( n
);
1240 void StgTmpStrm::FlushData()
1245 SetError( pStrm
->GetError() );
1248 SvMemoryStream::FlushData();