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