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