Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <algorithm>
21 :
22 : #include <string.h> // memcpy()
23 : #include <sal/log.hxx>
24 : #include <osl/file.hxx>
25 : #include <tools/tempfile.hxx>
26 :
27 : #include "sot/stg.hxx"
28 : #include "stgelem.hxx"
29 : #include "stgcache.hxx"
30 : #include "stgstrms.hxx"
31 : #include "stgdir.hxx"
32 : #include "stgio.hxx"
33 :
34 : #define __HUGE
35 :
36 : ///////////////////////////// class StgFAT ///////////////////////////////
37 :
38 : // The FAT class performs FAT operations on an underlying storage stream.
39 : // This stream is either the master FAT stream (m == sal_True ) or a normal
40 : // storage stream, which then holds the FAT for small data allocations.
41 :
42 1551 : StgFAT::StgFAT( StgStrm& r, sal_Bool m ) : rStrm( r )
43 : {
44 1551 : bPhys = m;
45 1551 : nPageSize = rStrm.GetIo().GetPhysPageSize();
46 1551 : nEntries = nPageSize >> 2;
47 1551 : nOffset = 0;
48 1551 : nMaxPage = 0;
49 1551 : nLimit = 0;
50 1551 : }
51 :
52 : // Retrieve the physical page for a given byte offset.
53 :
54 36915 : rtl::Reference< StgPage > StgFAT::GetPhysPage( sal_Int32 nByteOff )
55 : {
56 36915 : rtl::Reference< StgPage > pPg;
57 : // Position within the underlying stream
58 : // use the Pos2Page() method of the stream
59 36915 : if( rStrm.Pos2Page( nByteOff ) )
60 : {
61 36915 : nOffset = rStrm.GetOffset();
62 36915 : sal_Int32 nPhysPage = rStrm.GetPage();
63 : // get the physical page (must be present)
64 36915 : pPg = rStrm.GetIo().Get( nPhysPage, sal_True );
65 : }
66 36915 : return pPg;
67 : }
68 :
69 : // Get the follow page for a certain FAT page.
70 :
71 34676 : sal_Int32 StgFAT::GetNextPage( sal_Int32 nPg )
72 : {
73 34676 : if( nPg >= 0 )
74 : {
75 34676 : rtl::Reference< StgPage > pPg = GetPhysPage( nPg << 2 );
76 34676 : nPg = pPg.is() ? rStrm.GetIo().GetFromPage( pPg, nOffset >> 2 ) : STG_EOF;
77 : }
78 34676 : return nPg;
79 : }
80 :
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.
86 :
87 806 : sal_Int32 StgFAT::FindBlock( sal_Int32& nPgs )
88 : {
89 806 : sal_Int32 nMinStart = STG_EOF, nMinLen = 0;
90 806 : sal_Int32 nMaxStart = STG_EOF, nMaxLen = 0x7FFFFFFFL;
91 806 : sal_Int32 nTmpStart = STG_EOF, nTmpLen = 0;
92 806 : sal_Int32 nPages = rStrm.GetSize() >> 2;
93 806 : sal_Bool bFound = sal_False;
94 806 : rtl::Reference< StgPage > pPg;
95 806 : short nEntry = 0;
96 76619 : for( sal_Int32 i = 0; i < nPages; i++, nEntry++ )
97 : {
98 76528 : if( !( nEntry % nEntries ) )
99 : {
100 : // load the next page for that stream
101 1049 : nEntry = 0;
102 1049 : pPg = GetPhysPage( i << 2 );
103 1049 : if( !pPg.is() )
104 0 : return STG_EOF;
105 : }
106 76528 : sal_Int32 nCur = rStrm.GetIo().GetFromPage( pPg, nEntry );
107 76528 : if( nCur == STG_FREE )
108 : {
109 : // count the size of this area
110 4360 : if( nTmpLen )
111 3625 : nTmpLen++;
112 : else
113 : nTmpStart = i,
114 735 : nTmpLen = 1;
115 4360 : if( nTmpLen == nPgs
116 : // If we already did find a block, stop when reaching the limit
117 : || ( bFound && ( nEntry >= nLimit ) ) )
118 715 : break;
119 : }
120 72168 : else if( nTmpLen )
121 : {
122 3 : if( nTmpLen > nPgs && nTmpLen < nMaxLen )
123 : // block > requested size
124 0 : nMaxLen = nTmpLen, nMaxStart = nTmpStart, bFound = sal_True;
125 3 : else if( nTmpLen >= nMinLen )
126 : {
127 : // block < requested size
128 3 : nMinLen = nTmpLen, nMinStart = nTmpStart;
129 3 : bFound = sal_True;
130 3 : if( nTmpLen == nPgs )
131 0 : break;
132 : }
133 3 : nTmpStart = STG_EOF;
134 3 : nTmpLen = 0;
135 : }
136 : }
137 : // Determine which block to use.
138 806 : if( nTmpLen )
139 : {
140 732 : if( nTmpLen > nPgs && nTmpLen < nMaxLen )
141 : // block > requested size
142 0 : nMaxLen = nTmpLen, nMaxStart = nTmpStart;
143 732 : else if( nTmpLen >= nMinLen )
144 : // block < requested size
145 732 : nMinLen = nTmpLen, nMinStart = nTmpStart;
146 : }
147 806 : if( nMinStart != STG_EOF && nMaxStart != STG_EOF )
148 : {
149 : // two areas found; return the best fit area
150 0 : sal_Int32 nMinDiff = nPgs - nMinLen;
151 0 : sal_Int32 nMaxDiff = nMaxLen - nPgs;
152 0 : if( nMinDiff > nMaxDiff )
153 0 : nMinStart = STG_EOF;
154 : }
155 806 : if( nMinStart != STG_EOF )
156 : {
157 732 : nPgs = nMinLen; return nMinStart;
158 : }
159 : else
160 : {
161 74 : return nMaxStart;
162 806 : }
163 : }
164 :
165 : // Set up the consecutive chain for a given block.
166 :
167 732 : sal_Bool StgFAT::MakeChain( sal_Int32 nStart, sal_Int32 nPgs )
168 : {
169 732 : sal_Int32 nPos = nStart << 2;
170 732 : rtl::Reference< StgPage > pPg = GetPhysPage( nPos );
171 732 : if( !pPg.is() || !nPgs )
172 0 : return sal_False;
173 5089 : while( --nPgs )
174 : {
175 3625 : if( nOffset >= nPageSize )
176 : {
177 0 : pPg = GetPhysPage( nPos );
178 0 : if( !pPg.is() )
179 0 : return sal_False;
180 : }
181 3625 : rStrm.GetIo().SetToPage( pPg, nOffset >> 2, ++nStart );
182 3625 : nOffset += 4;
183 3625 : nPos += 4;
184 : }
185 732 : if( nOffset >= nPageSize )
186 : {
187 0 : pPg = GetPhysPage( nPos );
188 0 : if( !pPg.is() )
189 0 : return sal_False;
190 : }
191 732 : rStrm.GetIo().SetToPage( pPg, nOffset >> 2, STG_EOF );
192 732 : return sal_True;
193 : }
194 :
195 : // Allocate a block of data from the given page number on.
196 : // It the page number is != STG_EOF, chain the block.
197 :
198 712 : sal_Int32 StgFAT::AllocPages( sal_Int32 nBgn, sal_Int32 nPgs )
199 : {
200 712 : sal_Int32 nOrig = nBgn;
201 712 : sal_Int32 nLast = nBgn;
202 712 : sal_Int32 nBegin = STG_EOF;
203 : sal_Int32 nAlloc;
204 712 : sal_Int32 nPages = rStrm.GetSize() >> 2;
205 712 : short nPasses = 0;
206 : // allow for two passes
207 1478 : while( nPasses < 2 )
208 : {
209 : // try to satisfy the request from the pool of free pages
210 2264 : while( nPgs )
211 : {
212 786 : nAlloc = nPgs;
213 786 : nBegin = FindBlock( nAlloc );
214 : // no more blocks left in present alloc chain
215 786 : if( nBegin == STG_EOF )
216 54 : break;
217 732 : if( ( nBegin + nAlloc ) > nMaxPage )
218 729 : nMaxPage = nBegin + nAlloc;
219 732 : if( !MakeChain( nBegin, nAlloc ) )
220 0 : return STG_EOF;
221 732 : if( nOrig == STG_EOF )
222 296 : nOrig = nBegin;
223 : else
224 : {
225 : // Patch the chain
226 436 : rtl::Reference< StgPage > pPg = GetPhysPage( nLast << 2 );
227 436 : if( !pPg.is() )
228 0 : return STG_EOF;
229 436 : rStrm.GetIo().SetToPage( pPg, nOffset >> 2, nBegin );
230 : }
231 732 : nLast = nBegin + nAlloc - 1;
232 732 : nPgs -= nAlloc;
233 : }
234 766 : if( nPgs && !nPasses )
235 : {
236 : // we need new, fresh space, so allocate and retry
237 54 : if( !rStrm.SetSize( ( nPages + nPgs ) << 2 ) )
238 0 : return STG_EOF;
239 54 : if( !bPhys && !InitNew( nPages ) )
240 0 : return sal_False;
241 54 : nPages = rStrm.GetSize() >> 2;
242 54 : nPasses++;
243 : }
244 : else
245 712 : break;
246 : }
247 : // now we should have a chain for the complete block
248 712 : if( nBegin == STG_EOF || nPgs )
249 : {
250 0 : rStrm.GetIo().SetError( SVSTREAM_FILEFORMAT_ERROR );
251 0 : return STG_EOF; // bad structure
252 : }
253 712 : return nOrig;
254 : }
255 :
256 : // Initialize newly allocated pages for a standard FAT stream
257 : // It can be assumed that the stream size is always on
258 : // a page boundary
259 :
260 34 : sal_Bool StgFAT::InitNew( sal_Int32 nPage1 )
261 : {
262 34 : sal_Int32 n = ( ( rStrm.GetSize() >> 2 ) - nPage1 ) / nEntries;
263 34 : if ( n > 0 )
264 : {
265 102 : while( n-- )
266 : {
267 34 : rtl::Reference< StgPage > pPg;
268 : // Position within the underlying stream
269 : // use the Pos2Page() method of the stream
270 34 : rStrm.Pos2Page( nPage1 << 2 );
271 : // Initialize the page
272 34 : pPg = rStrm.GetIo().Copy( rStrm.GetPage(), STG_FREE );
273 34 : if ( !pPg.is() )
274 0 : return sal_False;
275 4386 : for( short i = 0; i < nEntries; i++ )
276 4352 : rStrm.GetIo().SetToPage( pPg, i, STG_FREE );
277 34 : nPage1++;
278 34 : }
279 : }
280 34 : return sal_True;
281 : }
282 :
283 : // Release a chain
284 :
285 35 : sal_Bool StgFAT::FreePages( sal_Int32 nStart, sal_Bool bAll )
286 : {
287 92 : while( nStart >= 0 )
288 : {
289 22 : rtl::Reference< StgPage > pPg = GetPhysPage( nStart << 2 );
290 22 : if( !pPg.is() )
291 0 : return sal_False;
292 22 : nStart = rStrm.GetIo().GetFromPage( pPg, nOffset >> 2 );
293 : // The first released page is either set to EOF or FREE
294 22 : rStrm.GetIo().SetToPage( pPg, nOffset >> 2, bAll ? STG_FREE : STG_EOF );
295 22 : bAll = sal_True;
296 22 : }
297 35 : return sal_True;
298 : }
299 :
300 : ///////////////////////////// class StgStrm ////////////////////////////////
301 :
302 : // The base stream class provides basic functionality for seeking
303 : // and accessing the data on a physical basis. It uses the built-in
304 : // FAT class for the page allocations.
305 :
306 1551 : StgStrm::StgStrm( StgIo& r ) : rIo( r )
307 : {
308 1551 : pFat = NULL;
309 1551 : nStart = nPage = STG_EOF;
310 1551 : nOffset = 0;
311 1551 : pEntry = NULL;
312 1551 : nPos = nSize = 0;
313 1551 : nPageSize = rIo.GetPhysPageSize();
314 1551 : }
315 :
316 3102 : StgStrm::~StgStrm()
317 : {
318 1551 : delete pFat;
319 1551 : }
320 :
321 : // Attach the stream to the given entry.
322 :
323 328 : void StgStrm::SetEntry( StgDirEntry& r )
324 : {
325 328 : r.aEntry.SetLeaf( STG_DATA, nStart );
326 328 : r.aEntry.SetSize( nSize );
327 328 : pEntry = &r;
328 328 : r.SetDirty();
329 328 : }
330 :
331 : /*
332 : * The page chain, is basically a singly linked list of slots each
333 : * point to the next page. Instead of traversing the file structure
334 : * for this each time build a simple flat in-memory vector list
335 : * of pages.
336 : */
337 201 : void StgStrm::scanBuildPageChainCache(sal_Int32 *pOptionalCalcSize)
338 : {
339 201 : if (nSize > 0)
340 0 : m_aPagesCache.reserve(nSize/nPageSize);
341 :
342 201 : bool bError = false;
343 201 : sal_Int32 nBgn = nStart;
344 201 : sal_Int32 nOldBgn = -1;
345 201 : sal_Int32 nOptSize = 0;
346 759 : while( nBgn >= 0 && nBgn != nOldBgn )
347 : {
348 357 : if( nBgn >= 0 )
349 357 : m_aPagesCache.push_back(nBgn);
350 357 : nOldBgn = nBgn;
351 357 : nBgn = pFat->GetNextPage( nBgn );
352 357 : if( nBgn == nOldBgn )
353 0 : bError = true;
354 357 : nOptSize += nPageSize;
355 : }
356 201 : if (bError)
357 : {
358 0 : if (pOptionalCalcSize)
359 0 : rIo.SetError( ERRCODE_IO_WRONGFORMAT );
360 0 : m_aPagesCache.clear();
361 : }
362 201 : if (pOptionalCalcSize)
363 201 : *pOptionalCalcSize = nOptSize;
364 201 : }
365 :
366 : // Compute page number and offset for the given byte position.
367 : // If the position is behind the size, set the stream right
368 : // behind the EOF.
369 1758420 : sal_Bool StgStrm::Pos2Page( sal_Int32 nBytePos )
370 : {
371 1758420 : if ( !pFat )
372 0 : return sal_False;
373 :
374 : // Values < 0 seek to the end
375 1758420 : if( nBytePos < 0 || nBytePos >= nSize )
376 26492 : nBytePos = nSize;
377 : // Adjust the position back to offset 0
378 1758420 : nPos -= nOffset;
379 1758420 : sal_Int32 nMask = ~( nPageSize - 1 );
380 1758420 : sal_Int32 nOld = nPos & nMask;
381 1758420 : sal_Int32 nNew = nBytePos & nMask;
382 1758420 : nOffset = (short) ( nBytePos & ~nMask );
383 1758420 : nPos = nBytePos;
384 1758420 : if( nOld == nNew )
385 1710845 : return sal_True;
386 :
387 : // See fdo#47644 for a .doc with a vast amount of pages where seeking around the
388 : // document takes a colossal amount of time
389 : //
390 : // Please Note: we build the pagescache incrementally as we go if necessary,
391 : // so that a corrupted FAT doesn't poison the stream state for earlier reads
392 47575 : size_t nIdx = nNew / nPageSize;
393 47575 : if( nIdx >= m_aPagesCache.size() )
394 : {
395 : // Extend the FAT cache ! ...
396 11982 : size_t nToAdd = nIdx + 1;
397 :
398 11982 : if (m_aPagesCache.empty())
399 1678 : m_aPagesCache.push_back( nStart );
400 :
401 11982 : nToAdd -= m_aPagesCache.size();
402 :
403 11982 : sal_Int32 nBgn = m_aPagesCache.back();
404 :
405 : // Start adding pages while we can
406 58193 : while( nToAdd > 0 && nBgn >= 0 )
407 : {
408 34229 : nBgn = pFat->GetNextPage( nBgn );
409 34229 : if( nBgn >= 0 )
410 : {
411 33260 : m_aPagesCache.push_back( nBgn );
412 33260 : nToAdd--;
413 : }
414 : }
415 : }
416 :
417 47575 : if ( nIdx > m_aPagesCache.size() )
418 : {
419 6 : rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
420 6 : nPage = STG_EOF;
421 6 : nOffset = nPageSize;
422 6 : return sal_False;
423 : }
424 : // special case: seek to 1st byte of new, unallocated page
425 : // (in case the file size is a multiple of the page size)
426 47569 : if( nBytePos == nSize && !nOffset && nIdx > 0 && nIdx == m_aPagesCache.size() )
427 : {
428 963 : nIdx--;
429 963 : nOffset = nPageSize;
430 : }
431 46606 : else if ( nIdx == m_aPagesCache.size() )
432 : {
433 0 : nPage = STG_EOF;
434 0 : return sal_False;
435 : }
436 :
437 47569 : nPage = m_aPagesCache[ nIdx ];
438 :
439 47569 : return nPage >= 0;
440 : }
441 :
442 : // Retrieve the physical page for a given byte offset.
443 :
444 0 : rtl::Reference< StgPage > StgStrm::GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce )
445 : {
446 0 : if( !Pos2Page( nBytePos ) )
447 0 : return NULL;
448 0 : return rIo.Get( nPage, bForce );
449 : }
450 :
451 : // Copy an entire stream. Both streams are allocated in the FAT.
452 : // The target stream is this stream.
453 :
454 35 : sal_Bool StgStrm::Copy( sal_Int32 nFrom, sal_Int32 nBytes )
455 : {
456 35 : if ( !pFat )
457 0 : return sal_False;
458 :
459 35 : m_aPagesCache.clear();
460 :
461 35 : sal_Int32 nTo = nStart;
462 35 : sal_Int32 nPgs = ( nBytes + nPageSize - 1 ) / nPageSize;
463 160 : while( nPgs-- )
464 : {
465 90 : if( nTo < 0 )
466 : {
467 0 : rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
468 0 : return sal_False;
469 : }
470 90 : rIo.Copy( nTo, nFrom );
471 90 : if( nFrom >= 0 )
472 : {
473 0 : nFrom = pFat->GetNextPage( nFrom );
474 0 : if( nFrom < 0 )
475 : {
476 0 : rIo.SetError( SVSTREAM_FILEFORMAT_ERROR );
477 0 : return sal_False;
478 : }
479 : }
480 90 : nTo = pFat->GetNextPage( nTo );
481 : }
482 35 : return sal_True;
483 : }
484 :
485 3770 : sal_Bool StgStrm::SetSize( sal_Int32 nBytes )
486 : {
487 3770 : if ( nBytes < 0 || !pFat )
488 0 : return sal_False;
489 :
490 3770 : m_aPagesCache.clear();
491 :
492 : // round up to page size
493 3770 : sal_Int32 nOld = ( ( nSize + nPageSize - 1 ) / nPageSize ) * nPageSize;
494 3770 : sal_Int32 nNew = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
495 3770 : if( nNew > nOld )
496 : {
497 712 : if( !Pos2Page( nSize ) )
498 0 : return sal_False;
499 712 : sal_Int32 nBgn = pFat->AllocPages( nPage, ( nNew - nOld ) / nPageSize );
500 712 : if( nBgn == STG_EOF )
501 0 : return sal_False;
502 712 : if( nStart == STG_EOF )
503 296 : nStart = nPage = nBgn;
504 : }
505 3058 : else if( nNew < nOld )
506 : {
507 0 : sal_Bool bAll = sal_Bool( nBytes == 0 );
508 0 : if( !Pos2Page( nBytes ) || !pFat->FreePages( nPage, bAll ) )
509 0 : return sal_False;
510 0 : if( bAll )
511 0 : nStart = nPage = STG_EOF;
512 : }
513 3770 : if( pEntry )
514 : {
515 : // change the dir entry?
516 3472 : if( !nSize || !nBytes )
517 245 : pEntry->aEntry.SetLeaf( STG_DATA, nStart );
518 3472 : pEntry->aEntry.SetSize( nBytes );
519 3472 : pEntry->SetDirty();
520 : }
521 3770 : nSize = nBytes;
522 3770 : pFat->SetLimit( GetPages() );
523 3770 : return sal_True;
524 : }
525 :
526 : // Return the # of allocated pages
527 :
528 3802 : sal_Int32 StgStrm::GetPages() const
529 : {
530 3802 : return ( nSize + nPageSize - 1 ) / nPageSize;
531 : }
532 :
533 : //////////////////////////// class StgFATStrm //////////////////////////////
534 :
535 : // The FAT stream class provides physical access to the master FAT.
536 : // Since this access is implemented as a StgStrm, we can use the
537 : // FAT allocator.
538 :
539 102 : StgFATStrm::StgFATStrm( StgIo& r ) : StgStrm( r )
540 : {
541 102 : pFat = new StgFAT( *this, sal_True );
542 102 : nSize = rIo.aHdr.GetFATSize() * nPageSize;
543 102 : }
544 :
545 28007 : sal_Bool StgFATStrm::Pos2Page( sal_Int32 nBytePos )
546 : {
547 : // Values < 0 seek to the end
548 28007 : if( nBytePos < 0 || nBytePos >= nSize )
549 5 : nBytePos = nSize ? nSize - 1 : 0;
550 28007 : nPage = nBytePos / nPageSize;
551 28007 : nOffset = (short) ( nBytePos % nPageSize );
552 28007 : nPos = nBytePos;
553 28007 : nPage = GetPage( (short) nPage, sal_False );
554 28007 : return sal_Bool( nPage >= 0 );
555 : }
556 :
557 : // Retrieve the physical page for a given byte offset.
558 : // Since Pos2Page() already has computed the physical offset,
559 : // use the byte offset directly.
560 :
561 0 : rtl::Reference< StgPage > StgFATStrm::GetPhysPage( sal_Int32 nBytePos, sal_Bool bForce )
562 : {
563 : OSL_ENSURE( nBytePos >= 0, "The value may not be negative!" );
564 0 : return rIo.Get( nBytePos / ( nPageSize >> 2 ), bForce );
565 : }
566 :
567 : // Get the page number entry for the given page offset.
568 :
569 28027 : sal_Int32 StgFATStrm::GetPage( short nOff, sal_Bool bMake, sal_uInt16 *pnMasterAlloc )
570 : {
571 : OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
572 28027 : if( pnMasterAlloc ) *pnMasterAlloc = 0;
573 28027 : if( nOff < rIo.aHdr.GetFAT1Size() )
574 28027 : return rIo.aHdr.GetFATPage( nOff );
575 0 : sal_Int32 nMaxPage = nSize >> 2;
576 0 : nOff = nOff - rIo.aHdr.GetFAT1Size();
577 : // Anzahl der Masterpages, durch die wir iterieren muessen
578 0 : sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1;
579 0 : sal_uInt16 nBlocks = nOff / nMasterCount;
580 : // Offset in letzter Masterpage
581 0 : nOff = nOff % nMasterCount;
582 :
583 0 : rtl::Reference< StgPage > pOldPage;
584 0 : rtl::Reference< StgPage > pMaster;
585 0 : sal_Int32 nFAT = rIo.aHdr.GetFATChain();
586 0 : for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
587 : {
588 0 : if( nFAT == STG_EOF || nFAT == STG_FREE )
589 : {
590 0 : if( bMake )
591 : {
592 0 : m_aPagesCache.clear();
593 :
594 : // create a new master page
595 0 : nFAT = nMaxPage++;
596 0 : pMaster = rIo.Copy( nFAT, STG_FREE );
597 0 : if ( pMaster.is() )
598 : {
599 0 : for( short k = 0; k < ( nPageSize >> 2 ); k++ )
600 0 : rIo.SetToPage( pMaster, k, STG_FREE );
601 : // Verkettung herstellen
602 0 : if( !pOldPage.is() )
603 0 : rIo.aHdr.SetFATChain( nFAT );
604 : else
605 0 : rIo.SetToPage( pOldPage, nMasterCount, nFAT );
606 0 : if( nMaxPage >= rIo.GetPhysPages() )
607 0 : if( !rIo.SetSize( nMaxPage ) )
608 0 : return STG_EOF;
609 : // mark the page as used
610 : // Platz fuer Masterpage schaffen
611 0 : if( !pnMasterAlloc ) // Selbst Platz schaffen
612 : {
613 0 : if( !Pos2Page( nFAT << 2 ) )
614 0 : return STG_EOF;
615 0 : rtl::Reference< StgPage > pPg = rIo.Get( nPage, sal_True );
616 0 : if( !pPg.is() )
617 0 : return STG_EOF;
618 0 : rIo.SetToPage( pPg, nOffset >> 2, STG_MASTER );
619 : }
620 : else
621 0 : (*pnMasterAlloc)++;
622 0 : rIo.aHdr.SetMasters( nCount + 1 );
623 0 : pOldPage = pMaster;
624 : }
625 0 : }
626 : }
627 : else
628 : {
629 0 : pMaster = rIo.Get( nFAT, sal_True );
630 0 : if ( pMaster.is() )
631 : {
632 0 : nFAT = rIo.GetFromPage( pMaster, nMasterCount );
633 0 : pOldPage = pMaster;
634 : }
635 : }
636 : }
637 0 : if( pMaster.is() )
638 0 : return rIo.GetFromPage( pMaster, nOff );
639 0 : rIo.SetError( SVSTREAM_GENERALERROR );
640 0 : return STG_EOF;
641 : }
642 :
643 :
644 : // Set the page number entry for the given page offset.
645 :
646 20 : sal_Bool StgFATStrm::SetPage( short nOff, sal_Int32 nNewPage )
647 : {
648 : OSL_ENSURE( nOff >= 0, "The offset may not be negative!" );
649 20 : m_aPagesCache.clear();
650 :
651 20 : sal_Bool bRes = sal_True;
652 20 : if( nOff < rIo.aHdr.GetFAT1Size() )
653 20 : rIo.aHdr.SetFATPage( nOff, nNewPage );
654 : else
655 : {
656 0 : nOff = nOff - rIo.aHdr.GetFAT1Size();
657 : // Anzahl der Masterpages, durch die wir iterieren muessen
658 0 : sal_uInt16 nMasterCount = ( nPageSize >> 2 ) - 1;
659 0 : sal_uInt16 nBlocks = nOff / nMasterCount;
660 : // Offset in letzter Masterpage
661 0 : nOff = nOff % nMasterCount;
662 :
663 0 : rtl::Reference< StgPage > pMaster;
664 0 : sal_Int32 nFAT = rIo.aHdr.GetFATChain();
665 0 : for( sal_uInt16 nCount = 0; nCount <= nBlocks; nCount++ )
666 : {
667 0 : if( nFAT == STG_EOF || nFAT == STG_FREE )
668 : {
669 0 : pMaster = 0;
670 0 : break;
671 : }
672 0 : pMaster = rIo.Get( nFAT, sal_True );
673 0 : if ( pMaster.is() )
674 0 : nFAT = rIo.GetFromPage( pMaster, nMasterCount );
675 : }
676 0 : if( pMaster.is() )
677 0 : rIo.SetToPage( pMaster, nOff, nNewPage );
678 : else
679 : {
680 0 : rIo.SetError( SVSTREAM_GENERALERROR );
681 0 : bRes = sal_False;
682 0 : }
683 : }
684 :
685 : // lock the page against access
686 20 : if( bRes )
687 : {
688 20 : Pos2Page( nNewPage << 2 );
689 20 : rtl::Reference< StgPage > pPg = rIo.Get( nPage, sal_True );
690 20 : if( pPg.is() )
691 20 : rIo.SetToPage( pPg, nOffset >> 2, STG_FAT );
692 : else
693 0 : bRes = sal_False;
694 : }
695 20 : return bRes;
696 : }
697 :
698 20 : sal_Bool StgFATStrm::SetSize( sal_Int32 nBytes )
699 : {
700 20 : if ( nBytes < 0 )
701 0 : return sal_False;
702 :
703 20 : m_aPagesCache.clear();
704 :
705 : // Set the number of entries to a multiple of the page size
706 20 : short nOld = (short) ( ( nSize + ( nPageSize - 1 ) ) / nPageSize );
707 : short nNew = (short) (
708 20 : ( nBytes + ( nPageSize - 1 ) ) / nPageSize ) ;
709 20 : if( nNew < nOld )
710 : {
711 : // release master pages
712 0 : for( short i = nNew; i < nOld; i++ )
713 0 : SetPage( i, STG_FREE );
714 : }
715 : else
716 : {
717 60 : while( nOld < nNew )
718 : {
719 : // allocate master pages
720 : // find a free master page slot
721 20 : sal_Int32 nPg = 0;
722 20 : sal_uInt16 nMasterAlloc = 0;
723 20 : nPg = GetPage( nOld, sal_True, &nMasterAlloc );
724 20 : if( nPg == STG_EOF )
725 0 : return sal_False;
726 : // 4 Bytes have been used for Allocation of each MegaMasterPage
727 20 : nBytes += nMasterAlloc << 2;
728 :
729 : // find a free page using the FAT allocator
730 20 : sal_Int32 n = 1;
731 : OSL_ENSURE( pFat, "The pointer is always initializer here!" );
732 20 : sal_Int32 nNewPage = pFat->FindBlock( n );
733 20 : if( nNewPage == STG_EOF )
734 : {
735 : // no free pages found; create a new page
736 : // Since all pages are allocated, extend
737 : // the file size for the next page!
738 20 : nNewPage = nSize >> 2;
739 : // if a MegaMasterPage was created avoid taking
740 : // the same Page
741 20 : nNewPage += nMasterAlloc;
742 : // adjust the file size if necessary
743 20 : if( nNewPage >= rIo.GetPhysPages() )
744 20 : if( !rIo.SetSize( nNewPage + 1 ) )
745 0 : return sal_False;
746 : }
747 : // Set up the page with empty entries
748 20 : rtl::Reference< StgPage > pPg = rIo.Copy( nNewPage, STG_FREE );
749 20 : if ( !pPg.is() )
750 0 : return sal_False;
751 2580 : for( short j = 0; j < ( nPageSize >> 2 ); j++ )
752 2560 : rIo.SetToPage( pPg, j, STG_FREE );
753 :
754 : // store the page number into the master FAT
755 : // Set the size before so the correct FAT can be found
756 20 : nSize = ( nOld + 1 ) * nPageSize;
757 20 : SetPage( nOld, nNewPage );
758 :
759 : // MegaMasterPages were created, mark it them as used
760 :
761 20 : sal_uInt32 nMax = rIo.aHdr.GetMasters( );
762 20 : sal_uInt32 nFAT = rIo.aHdr.GetFATChain();
763 20 : if( nMasterAlloc )
764 0 : for( sal_uInt16 nCount = 0; nCount < nMax; nCount++ )
765 : {
766 0 : if( !Pos2Page( nFAT << 2 ) )
767 0 : return sal_False;
768 0 : if( nMax - nCount <= nMasterAlloc )
769 : {
770 0 : rtl::Reference< StgPage > piPg = rIo.Get( nPage, sal_True );
771 0 : if( !piPg.is() )
772 0 : return sal_False;
773 0 : rIo.SetToPage( piPg, nOffset >> 2, STG_MASTER );
774 : }
775 0 : rtl::Reference< StgPage > pPage = rIo.Get( nFAT, sal_True );
776 0 : if( !pPage.is() ) return sal_False;
777 0 : nFAT = rIo.GetFromPage( pPage, (nPageSize >> 2 ) - 1 );
778 0 : }
779 :
780 20 : nOld++;
781 : // We have used up 4 bytes for the STG_FAT entry
782 20 : nBytes += 4;
783 : nNew = (short) (
784 20 : ( nBytes + ( nPageSize - 1 ) ) / nPageSize );
785 20 : }
786 : }
787 20 : nSize = nNew * nPageSize;
788 20 : rIo.aHdr.SetFATSize( nNew );
789 20 : return sal_True;
790 : }
791 :
792 : /////////////////////////// class StgDataStrm //////////////////////////////
793 :
794 : // This class is a normal physical stream which can be initialized
795 : // either with an existing dir entry or an existing FAT chain.
796 : // The stream has a size increment which normally is 1, but which can be
797 : // set to any value is you want the size to be incremented by certain values.
798 :
799 221 : StgDataStrm::StgDataStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
800 : {
801 221 : Init( nBgn, nLen );
802 221 : }
803 :
804 344 : StgDataStrm::StgDataStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
805 : {
806 344 : pEntry = &p;
807 : Init( p.aEntry.GetLeaf( STG_DATA ),
808 344 : p.aEntry.GetSize() );
809 344 : }
810 :
811 565 : void StgDataStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
812 : {
813 565 : if ( rIo.pFAT )
814 565 : pFat = new StgFAT( *rIo.pFAT, sal_True );
815 :
816 : OSL_ENSURE( pFat, "The pointer should not be empty!" );
817 :
818 565 : nStart = nPage = nBgn;
819 565 : nSize = nLen;
820 565 : nIncr = 1;
821 565 : nOffset = 0;
822 565 : if( nLen < 0 && pFat )
823 : {
824 : // determine the actual size of the stream by scanning
825 : // the FAT chain and counting the # of pages allocated
826 201 : scanBuildPageChainCache( &nSize );
827 : }
828 565 : }
829 :
830 : // Set the size of a physical stream.
831 :
832 3297 : sal_Bool StgDataStrm::SetSize( sal_Int32 nBytes )
833 : {
834 3297 : if ( !pFat )
835 0 : return sal_False;
836 :
837 3297 : nBytes = ( ( nBytes + nIncr - 1 ) / nIncr ) * nIncr;
838 3297 : sal_Int32 nOldSz = nSize;
839 3297 : if( ( nOldSz != nBytes ) )
840 : {
841 3297 : if( !StgStrm::SetSize( nBytes ) )
842 0 : return sal_False;
843 3297 : sal_Int32 nMaxPage = pFat->GetMaxPage();
844 3297 : if( nMaxPage > rIo.GetPhysPages() )
845 468 : if( !rIo.SetSize( nMaxPage ) )
846 0 : return sal_False;
847 : // If we only allocated one page or less, create this
848 : // page in the cache for faster throughput. The current
849 : // position is the former EOF point.
850 3297 : if( ( nSize - 1 ) / nPageSize - ( nOldSz - 1 ) / nPageSize == 1 )
851 : {
852 416 : Pos2Page( nBytes );
853 416 : if( nPage >= 0 )
854 416 : rIo.Copy( nPage, STG_FREE );
855 : }
856 : }
857 3297 : return sal_True;
858 : }
859 :
860 : // Get the address of the data byte at a specified offset.
861 : // If bForce = sal_True, a read of non-existent data causes
862 : // a read fault.
863 :
864 1260 : void* StgDataStrm::GetPtr( sal_Int32 Pos, sal_Bool bForce, sal_Bool bDirty )
865 : {
866 1260 : if( Pos2Page( Pos ) )
867 : {
868 1260 : rtl::Reference< StgPage > pPg = rIo.Get( nPage, bForce );
869 1260 : if (pPg.is() && nOffset < pPg->GetSize())
870 : {
871 1260 : if( bDirty )
872 360 : rIo.SetDirty( pPg );
873 1260 : return ((sal_uInt8 *)pPg->GetData()) + nOffset;
874 1260 : }
875 : }
876 0 : return NULL;
877 : }
878 :
879 : // This could easily be adapted to a better algorithm by determining
880 : // the amount of consecutable blocks before doing a read. The result
881 : // is the number of bytes read. No error is generated on EOF.
882 :
883 956739 : sal_Int32 StgDataStrm::Read( void* pBuf, sal_Int32 n )
884 : {
885 956739 : if ( n < 0 )
886 0 : return 0;
887 :
888 956739 : if( ( nPos + n ) > nSize )
889 24599 : n = nSize - nPos;
890 956739 : sal_Int32 nDone = 0;
891 2869307 : while( n )
892 : {
893 955834 : short nBytes = nPageSize - nOffset;
894 955834 : rtl::Reference< StgPage > pPg;
895 955834 : if( (sal_Int32) nBytes > n )
896 929820 : nBytes = (short) n;
897 955834 : if( nBytes )
898 : {
899 : short nRes;
900 955834 : void *p = (sal_uInt8 *) pBuf + nDone;
901 955834 : if( nBytes == nPageSize )
902 : {
903 22410 : pPg = rIo.Find( nPage );
904 22410 : if( pPg.is() )
905 : {
906 : // data is present, so use the cached data
907 193 : memcpy( p, pPg->GetData(), nBytes );
908 193 : nRes = nBytes;
909 : }
910 : else
911 : // do a direct (unbuffered) read
912 22217 : nRes = (short) rIo.Read( nPage, p, 1 ) * nPageSize;
913 : }
914 : else
915 : {
916 : // partial block read thru the cache.
917 933424 : pPg = rIo.Get( nPage, sal_False );
918 933424 : if( !pPg.is() )
919 : break;
920 933424 : memcpy( p, (sal_uInt8*)pPg->GetData() + nOffset, nBytes );
921 933424 : nRes = nBytes;
922 : }
923 955834 : nDone += nRes;
924 955834 : nPos += nRes;
925 955834 : n -= nRes;
926 955834 : nOffset = nOffset + nRes;
927 955834 : if( nRes != nBytes )
928 : break; // read error or EOF
929 : }
930 : // Switch to next page if necessary
931 955829 : if( nOffset >= nPageSize && !Pos2Page( nPos ) )
932 : break;
933 955834 : }
934 956739 : return nDone;
935 : }
936 :
937 3323 : sal_Int32 StgDataStrm::Write( const void* pBuf, sal_Int32 n )
938 : {
939 3323 : if ( n < 0 )
940 0 : return 0;
941 :
942 3323 : sal_Int32 nDone = 0;
943 3323 : if( ( nPos + n ) > nSize )
944 : {
945 0 : sal_Int32 nOld = nPos;
946 0 : if( !SetSize( nPos + n ) )
947 0 : return 0;
948 0 : Pos2Page( nOld );
949 : }
950 10465 : while( n )
951 : {
952 3819 : short nBytes = nPageSize - nOffset;
953 3819 : rtl::Reference< StgPage > pPg;
954 3819 : if( (sal_Int32) nBytes > n )
955 2890 : nBytes = (short) n;
956 3819 : if( nBytes )
957 : {
958 : short nRes;
959 3819 : const void *p = (const sal_uInt8 *) pBuf + nDone;
960 3819 : if( nBytes == nPageSize )
961 : {
962 556 : pPg = rIo.Find( nPage );
963 556 : if( pPg.is() )
964 : {
965 : // data is present, so use the cached data
966 0 : memcpy( pPg->GetData(), p, nBytes );
967 0 : rIo.SetDirty( pPg );
968 0 : nRes = nBytes;
969 : }
970 : else
971 : // do a direct (unbuffered) write
972 556 : nRes = (short) rIo.Write( nPage, (void*) p, 1 ) * nPageSize;
973 : }
974 : else
975 : {
976 : // partial block read thru the cache.
977 3263 : pPg = rIo.Get( nPage, sal_False );
978 3263 : if( !pPg.is() )
979 : break;
980 3263 : memcpy( (sal_uInt8*)pPg->GetData() + nOffset, p, nBytes );
981 3263 : rIo.SetDirty( pPg );
982 3263 : nRes = nBytes;
983 : }
984 3819 : nDone += nRes;
985 3819 : nPos += nRes;
986 3819 : n -= nRes;
987 3819 : nOffset = nOffset + nRes;
988 3819 : if( nRes != nBytes )
989 : break; // read error
990 : }
991 : // Switch to next page if necessary
992 3819 : if( nOffset >= nPageSize && !Pos2Page( nPos ) )
993 : break;
994 3819 : }
995 3323 : return nDone;
996 : }
997 :
998 : //////////////////////////// class StgSmallStream ///////////////////////////
999 :
1000 : // The small stream class provides access to streams with a size < 4096 bytes.
1001 : // This stream is a StgStream containing small pages. The FAT for this stream
1002 : // is also a StgStream. The start of the FAT is in the header at DataRootPage,
1003 : // the stream itself is pointed to by the root entry (it holds start & size).
1004 :
1005 209 : StgSmallStrm::StgSmallStrm( StgIo& r, sal_Int32 nBgn, sal_Int32 nLen ) : StgStrm( r )
1006 : {
1007 209 : Init( nBgn, nLen );
1008 209 : }
1009 :
1010 675 : StgSmallStrm::StgSmallStrm( StgIo& r, StgDirEntry& p ) : StgStrm( r )
1011 : {
1012 675 : pEntry = &p;
1013 : Init( p.aEntry.GetLeaf( STG_DATA ),
1014 675 : p.aEntry.GetSize() );
1015 675 : }
1016 :
1017 884 : void StgSmallStrm::Init( sal_Int32 nBgn, sal_Int32 nLen )
1018 : {
1019 884 : if ( rIo.pDataFAT )
1020 884 : pFat = new StgFAT( *rIo.pDataFAT, sal_False );
1021 884 : pData = rIo.pDataStrm;
1022 : OSL_ENSURE( pFat && pData, "The pointers should not be empty!" );
1023 :
1024 884 : nPageSize = rIo.GetDataPageSize();
1025 : nStart =
1026 884 : nPage = nBgn;
1027 884 : nSize = nLen;
1028 884 : }
1029 :
1030 : // This could easily be adapted to a better algorithm by determining
1031 : // the amount of consecutable blocks before doing a read. The result
1032 : // is the number of bytes read. No error is generated on EOF.
1033 :
1034 3958 : sal_Int32 StgSmallStrm::Read( void* pBuf, sal_Int32 n )
1035 : {
1036 : // We can safely assume that reads are not huge, since the
1037 : // small stream is likely to be < 64 KBytes.
1038 3958 : if( ( nPos + n ) > nSize )
1039 411 : n = nSize - nPos;
1040 3958 : short nDone = 0;
1041 16709 : while( n )
1042 : {
1043 8794 : short nBytes = nPageSize - nOffset;
1044 8794 : if( (sal_Int32) nBytes > n )
1045 3705 : nBytes = (short) n;
1046 8794 : if( nBytes )
1047 : {
1048 8794 : if( !pData || !pData->Pos2Page( nPage * nPageSize + nOffset ) )
1049 0 : break;
1050 : // all reading thru the stream
1051 8794 : short nRes = (short) pData->Read( (sal_uInt8*)pBuf + nDone, nBytes );
1052 8794 : nDone = nDone + nRes;
1053 8794 : nPos += nRes;
1054 8794 : n -= nRes;
1055 8794 : nOffset = nOffset + nRes;
1056 : // read problem?
1057 8794 : if( nRes != nBytes )
1058 1 : break;
1059 : }
1060 : // Switch to next page if necessary
1061 8793 : if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1062 0 : break;
1063 : }
1064 3958 : return nDone;
1065 : }
1066 :
1067 209 : sal_Int32 StgSmallStrm::Write( const void* pBuf, sal_Int32 n )
1068 : {
1069 : // you can safely assume that reads are not huge, since the
1070 : // small stream is likely to be < 64 KBytes.
1071 209 : short nDone = 0;
1072 209 : if( ( nPos + n ) > nSize )
1073 : {
1074 0 : sal_Int32 nOld = nPos;
1075 0 : if( !SetSize( nPos + n ) )
1076 0 : return sal_False;
1077 0 : Pos2Page( nOld );
1078 : }
1079 3661 : while( n )
1080 : {
1081 3243 : short nBytes = nPageSize - nOffset;
1082 3243 : if( (sal_Int32) nBytes > n )
1083 181 : nBytes = (short) n;
1084 3243 : if( nBytes )
1085 : {
1086 : // all writing goes thru the stream
1087 3243 : sal_Int32 nDataPos = nPage * nPageSize + nOffset;
1088 9729 : if ( !pData
1089 3243 : || ( pData->GetSize() < ( nDataPos + nBytes )
1090 3243 : && !pData->SetSize( nDataPos + nBytes ) ) )
1091 0 : break;
1092 3243 : if( !pData->Pos2Page( nDataPos ) )
1093 0 : break;
1094 3243 : short nRes = (short) pData->Write( (sal_uInt8*)pBuf + nDone, nBytes );
1095 3243 : nDone = nDone + nRes;
1096 3243 : nPos += nRes;
1097 3243 : n -= nRes;
1098 3243 : nOffset = nOffset + nRes;
1099 : // write problem?
1100 3243 : if( nRes != nBytes )
1101 0 : break;
1102 : }
1103 : // Switch to next page if necessary
1104 3243 : if( nOffset >= nPageSize && !Pos2Page( nPos ) )
1105 0 : break;
1106 : }
1107 209 : return nDone;
1108 : }
1109 :
1110 : /////////////////////////// class StgTmpStrm /////////////////////////////
1111 :
1112 : // The temporary stream uses a memory stream if < 32K, otherwise a
1113 : // temporary file.
1114 :
1115 : #define THRESHOLD 32768L
1116 :
1117 229 : StgTmpStrm::StgTmpStrm( sal_uLong nInitSize )
1118 : : SvMemoryStream( nInitSize > THRESHOLD
1119 : ? 16
1120 229 : : ( nInitSize ? nInitSize : 16 ), 4096 )
1121 : {
1122 229 : pStrm = NULL;
1123 : // this calls FlushData, so all members should be set by this time
1124 229 : SetBufferSize( 0 );
1125 229 : if( nInitSize > THRESHOLD )
1126 0 : SetSize( nInitSize );
1127 229 : }
1128 :
1129 0 : sal_Bool StgTmpStrm::Copy( StgTmpStrm& rSrc )
1130 : {
1131 0 : sal_uLong n = rSrc.GetSize();
1132 0 : sal_uLong nCur = rSrc.Tell();
1133 0 : SetSize( n );
1134 0 : if( GetError() == SVSTREAM_OK )
1135 : {
1136 0 : sal_uInt8* p = new sal_uInt8[ 4096 ];
1137 0 : rSrc.Seek( 0L );
1138 0 : Seek( 0L );
1139 0 : while( n )
1140 : {
1141 0 : sal_uLong nn = n;
1142 0 : if( nn > 4096 )
1143 0 : nn = 4096;
1144 0 : if( rSrc.Read( p, nn ) != nn )
1145 0 : break;
1146 0 : if( Write( p, nn ) != nn )
1147 0 : break;
1148 0 : n -= nn;
1149 : }
1150 0 : delete [] p;
1151 0 : rSrc.Seek( nCur );
1152 0 : Seek( nCur );
1153 0 : return sal_Bool( n == 0 );
1154 : }
1155 : else
1156 0 : return sal_False;
1157 : }
1158 :
1159 687 : StgTmpStrm::~StgTmpStrm()
1160 : {
1161 229 : if( pStrm )
1162 : {
1163 0 : pStrm->Close();
1164 0 : osl::File::remove( aName );
1165 0 : delete pStrm;
1166 : }
1167 458 : }
1168 :
1169 354 : sal_uLong StgTmpStrm::GetSize() const
1170 : {
1171 : sal_uLong n;
1172 354 : if( pStrm )
1173 : {
1174 0 : sal_uLong old = pStrm->Tell();
1175 0 : n = pStrm->Seek( STREAM_SEEK_TO_END );
1176 0 : pStrm->Seek( old );
1177 : }
1178 : else
1179 354 : n = nEndOfData;
1180 354 : return n;
1181 : }
1182 :
1183 125 : void StgTmpStrm::SetSize( sal_uLong n )
1184 : {
1185 125 : if( pStrm )
1186 0 : pStrm->SetStreamSize( n );
1187 : else
1188 : {
1189 125 : if( n > THRESHOLD )
1190 : {
1191 0 : aName = TempFile::CreateTempName();
1192 0 : SvFileStream* s = new SvFileStream( aName, STREAM_READWRITE );
1193 0 : sal_uLong nCur = Tell();
1194 0 : sal_uLong i = nEndOfData;
1195 0 : if( i )
1196 : {
1197 0 : sal_uInt8* p = new sal_uInt8[ 4096 ];
1198 0 : Seek( 0L );
1199 0 : while( i )
1200 : {
1201 0 : sal_uLong nb = ( i > 4096 ) ? 4096 : i;
1202 0 : if( Read( p, nb ) == nb
1203 0 : && s->Write( p, nb ) == nb )
1204 0 : i -= nb;
1205 : else
1206 0 : break;
1207 : }
1208 0 : delete [] p;
1209 : }
1210 0 : if( !i && n > nEndOfData )
1211 : {
1212 : // We have to write one byte at the end of the file
1213 : // if the file is bigger than the memstream to see
1214 : // if it fits on disk
1215 0 : s->Seek( n - 1 );
1216 0 : s->Write( &i, 1 );
1217 0 : s->Flush();
1218 0 : if( s->GetError() != SVSTREAM_OK )
1219 0 : i = 1;
1220 : }
1221 0 : Seek( nCur );
1222 0 : s->Seek( nCur );
1223 0 : if( i )
1224 : {
1225 0 : SetError( s->GetError() );
1226 0 : delete s;
1227 125 : return;
1228 : }
1229 0 : pStrm = s;
1230 : // Shrink the memory to 16 bytes, which seems to be the minimum
1231 0 : ReAllocateMemory( - ( (long) nEndOfData - 16 ) );
1232 : }
1233 : else
1234 : {
1235 125 : if( n > nEndOfData )
1236 : {
1237 125 : sal_uLong nCur = Tell();
1238 125 : Seek( nEndOfData - 1 );
1239 125 : *this << (sal_uInt8) 0;
1240 125 : Seek( nCur );
1241 : }
1242 : else
1243 0 : nEndOfData = n;
1244 : }
1245 : }
1246 : }
1247 :
1248 289 : sal_uLong StgTmpStrm::GetData( void* pData, sal_uLong n )
1249 : {
1250 289 : if( pStrm )
1251 : {
1252 0 : n = pStrm->Read( pData, n );
1253 0 : SetError( pStrm->GetError() );
1254 0 : return n;
1255 : }
1256 : else
1257 289 : return SvMemoryStream::GetData( (sal_Char *)pData, n );
1258 : }
1259 :
1260 384 : sal_uLong StgTmpStrm::PutData( const void* pData, sal_uLong n )
1261 : {
1262 384 : sal_uInt32 nCur = Tell();
1263 384 : sal_uInt32 nNew = nCur + n;
1264 384 : if( nNew > THRESHOLD && !pStrm )
1265 : {
1266 0 : SetSize( nNew );
1267 0 : if( GetError() != SVSTREAM_OK )
1268 0 : return 0;
1269 : }
1270 384 : if( pStrm )
1271 : {
1272 0 : nNew = pStrm->Write( pData, n );
1273 0 : SetError( pStrm->GetError() );
1274 : }
1275 : else
1276 384 : nNew = SvMemoryStream::PutData( (sal_Char*)pData, n );
1277 384 : return nNew;
1278 : }
1279 :
1280 1113 : sal_uLong StgTmpStrm::SeekPos( sal_uLong n )
1281 : {
1282 1113 : if( n == STREAM_SEEK_TO_END )
1283 125 : n = GetSize();
1284 1113 : if( n && n > THRESHOLD && !pStrm )
1285 : {
1286 0 : SetSize( n );
1287 0 : if( GetError() != SVSTREAM_OK )
1288 0 : return Tell();
1289 : else
1290 0 : return n;
1291 : }
1292 1113 : else if( pStrm )
1293 : {
1294 0 : n = pStrm->Seek( n );
1295 0 : SetError( pStrm->GetError() );
1296 0 : return n;
1297 : }
1298 : else
1299 1113 : return SvMemoryStream::SeekPos( n );
1300 : }
1301 :
1302 0 : void StgTmpStrm::FlushData()
1303 : {
1304 0 : if( pStrm )
1305 : {
1306 0 : pStrm->Flush();
1307 0 : SetError( pStrm->GetError() );
1308 : }
1309 : else
1310 0 : SvMemoryStream::FlushData();
1311 0 : }
1312 :
1313 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|