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