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