update dev300-m58
[ooovba.git] / sot / source / sdstor / stgcache.cxx
blob0043a479fea4dc7000b03e35c7a495d7f31715e6
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: stgcache.cxx,v $
10 * $Revision: 1.13 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sot.hxx"
34 #if defined(_MSC_VER) && (_MSC_VER<1200)
35 #include <tools/presys.h>
36 #endif
37 #include <hash_map>
38 #if defined(_MSC_VER) && (_MSC_VER<1200)
39 #include <tools/postsys.h>
40 #endif
41 #include <vos/macros.hxx>
43 #include <string.h>
44 #include <osl/endian.h>
45 #include <tools/string.hxx>
47 #include "stg.hxx"
48 #include "stgelem.hxx"
49 #include "stgcache.hxx"
50 #include "stgstrms.hxx"
51 #include "stgdir.hxx"
52 #include "stgio.hxx"
54 /*************************************************************************/
55 //-----------------------------------------------------------------------------
56 typedef std::hash_map
58 INT32,
59 StgPage *,
60 std::hash< INT32 >,
61 NAMESPACE_STD(equal_to)< INT32 >
62 > UsrStgPagePtr_Impl;
63 #ifdef _MSC_VER
64 #pragma warning( disable: 4786 )
65 #endif
67 //#define CHECK_DIRTY 1
68 //#define READ_AFTER_WRITE 1
70 ////////////////////////////// class StgPage /////////////////////////////
71 // This class implements buffer functionality. The cache will always return
72 // a page buffer, even if a read fails. It is up to the caller to determine
73 // the correctness of the I/O.
75 StgPage::StgPage( StgCache* p, short n )
77 pCache = p;
78 nData = n;
79 bDirty = FALSE;
80 nPage = 0;
81 pData = new BYTE[ nData ];
82 pNext1 =
83 pNext2 =
84 pLast1 =
85 pLast2 = NULL;
86 pOwner = NULL;
89 StgPage::~StgPage()
91 delete [] pData;
94 void StgPage::SetPage( short nOff, INT32 nVal )
96 if( ( nOff < (short) ( nData / sizeof( INT32 ) ) ) && nOff >= 0 )
98 #ifdef OSL_BIGENDIAN
99 nVal = SWAPLONG(nVal);
100 #endif
101 ((INT32*) pData )[ nOff ] = nVal;
102 bDirty = TRUE;
106 //////////////////////////////// class StgCache ////////////////////////////
108 // The disk cache holds the cached sectors. The sector type differ according
109 // to their purpose.
111 INT32 lcl_GetPageCount( ULONG nFileSize, short nPageSize )
113 // return (nFileSize >= 512) ? (nFileSize - 512) / nPageSize : 0;
114 // #i61980# reallife: last page may be incomplete, return number of *started* pages
115 return (nFileSize >= 512) ? (nFileSize - 512 + nPageSize - 1) / nPageSize : 0;
118 StgCache::StgCache()
120 nRef = 0;
121 pStrm = NULL;
122 pCur = pElem1 = NULL;
123 nPageSize = 512;
124 nError = SVSTREAM_OK;
125 bMyStream = FALSE;
126 bFile = FALSE;
127 pLRUCache = NULL;
128 pStorageStream = NULL;
131 StgCache::~StgCache()
133 Clear();
134 SetStrm( NULL, FALSE );
135 delete (UsrStgPagePtr_Impl*)pLRUCache;
138 void StgCache::SetPhysPageSize( short n )
140 nPageSize = n;
141 ULONG nPos = pStrm->Tell();
142 ULONG nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
143 nPages = lcl_GetPageCount( nFileSize, nPageSize );
144 pStrm->Seek( nPos );
147 // Create a new cache element
148 // pCur points to this element
150 StgPage* StgCache::Create( INT32 nPg )
152 StgPage* pElem = new StgPage( this, nPageSize );
153 pElem->nPage = nPg;
154 // For data security, clear the buffer contents
155 memset( pElem->pData, 0, pElem->nData );
157 // insert to LRU
158 if( pCur )
160 pElem->pNext1 = pCur;
161 pElem->pLast1 = pCur->pLast1;
162 pElem->pNext1->pLast1 =
163 pElem->pLast1->pNext1 = pElem;
165 else
166 pElem->pNext1 = pElem->pLast1 = pElem;
167 if( !pLRUCache )
168 pLRUCache = new UsrStgPagePtr_Impl();
169 (*(UsrStgPagePtr_Impl*)pLRUCache)[pElem->nPage] = pElem;
170 pCur = pElem;
172 // insert to Sorted
173 if( !pElem1 )
174 pElem1 = pElem->pNext2 = pElem->pLast2 = pElem;
175 else
177 StgPage* p = pElem1;
180 if( pElem->nPage < p->nPage )
181 break;
182 p = p->pNext2;
183 } while( p != pElem1 );
184 pElem->pNext2 = p;
185 pElem->pLast2 = p->pLast2;
186 pElem->pNext2->pLast2 =
187 pElem->pLast2->pNext2 = pElem;
188 if( p->nPage < pElem1->nPage )
189 pElem1 = pElem;
191 return pElem;
194 // Delete the given element
196 void StgCache::Erase( StgPage* pElem )
198 //remove from LRU
199 pElem->pNext1->pLast1 = pElem->pLast1;
200 pElem->pLast1->pNext1 = pElem->pNext1;
201 if( pCur == pElem )
202 pCur = ( pElem->pNext1 == pElem ) ? NULL : pElem->pNext1;
203 if( pLRUCache )
204 ((UsrStgPagePtr_Impl*)pLRUCache)->erase( pElem->nPage );
205 // remove from Sorted
206 pElem->pNext2->pLast2 = pElem->pLast2;
207 pElem->pLast2->pNext2 = pElem->pNext2;
208 if( pElem1 == pElem )
209 pElem1 = ( pElem->pNext2 == pElem ) ? NULL : pElem->pNext2;
210 delete pElem;
213 // remove all cache elements without flushing them
215 void StgCache::Clear()
217 StgPage* pElem = pCur;
218 if( pCur ) do
220 StgPage* pDelete = pElem;
221 pElem = pElem->pNext1;
222 delete pDelete;
224 while( pCur != pElem );
225 pCur = NULL;
226 pElem1 = NULL;
227 delete (UsrStgPagePtr_Impl*)pLRUCache;
228 pLRUCache = NULL;
231 // Look for a cached page
233 StgPage* StgCache::Find( INT32 nPage )
235 if( !pLRUCache )
236 return NULL;
237 UsrStgPagePtr_Impl::iterator aIt = ((UsrStgPagePtr_Impl*)pLRUCache)->find( nPage );
238 if( aIt != ((UsrStgPagePtr_Impl*)pLRUCache)->end() )
240 // page found
241 StgPage* pFound = (*aIt).second;
243 if( pFound != pCur )
245 // remove from LRU
246 pFound->pNext1->pLast1 = pFound->pLast1;
247 pFound->pLast1->pNext1 = pFound->pNext1;
248 // insert to LRU
249 pFound->pNext1 = pCur;
250 pFound->pLast1 = pCur->pLast1;
251 pFound->pNext1->pLast1 =
252 pFound->pLast1->pNext1 = pFound;
254 return pFound;
256 return NULL;
259 // Load a page into the cache
261 StgPage* StgCache::Get( INT32 nPage, BOOL bForce )
263 StgPage* p = Find( nPage );
264 if( !p )
266 p = Create( nPage );
267 if( !Read( nPage, p->pData, 1 ) && bForce )
269 Erase( p );
270 p = NULL;
271 SetError( SVSTREAM_READ_ERROR );
274 return p;
277 // Copy an existing page into a new page. Use this routine
278 // to duplicate an existing stream or to create new entries.
279 // The new page is initially marked dirty. No owner is copied.
281 StgPage* StgCache::Copy( INT32 nNew, INT32 nOld )
283 StgPage* p = Find( nNew );
284 if( !p )
285 p = Create( nNew );
286 if( nOld >= 0 )
288 // old page: we must have this data!
289 StgPage* q = Get( nOld, TRUE );
290 if( q )
291 memcpy( p->pData, q->pData, p->nData );
293 p->SetDirty();
294 return p;
297 // Flush the cache whose owner is given. NULL flushes all.
299 BOOL StgCache::Commit( StgDirEntry* )
301 StgPage* p = pElem1;
302 if( p ) do
304 if( p->bDirty )
306 BOOL b = Write( p->nPage, p->pData, 1 );
307 if( !b )
308 return FALSE;
309 p->bDirty = FALSE;
311 p = p->pNext2;
312 } while( p != pElem1 );
313 pStrm->Flush();
314 SetError( pStrm->GetError() );
315 #ifdef CHECK_DIRTY
316 p = pElem1;
317 if( p ) do
319 if( p->bDirty )
321 ErrorBox( NULL, WB_OK, String("SO2: Dirty Block in Ordered List") ).Execute();
322 BOOL b = Write( p->nPage, p->pData, 1 );
323 if( !b )
324 return FALSE;
325 p->bDirty = FALSE;
327 p = p->pNext2;
328 } while( p != pElem1 );
329 p = pElem1;
330 if( p ) do
332 if( p->bDirty )
334 ErrorBox( NULL, WB_OK, String("SO2: Dirty Block in LRU List") ).Execute();
335 BOOL b = Write( p->nPage, p->pData, 1 );
336 if( !b )
337 return FALSE;
338 p->bDirty = FALSE;
340 p = p->pNext1;
341 } while( p != pElem1 );
342 #endif
343 return TRUE;
346 void StgCache::Revert( StgDirEntry* )
349 // Set a stream
351 void StgCache::SetStrm( SvStream* p, BOOL bMy )
353 if( pStorageStream )
355 pStorageStream->ReleaseRef();
356 pStorageStream = NULL;
359 if( bMyStream )
360 delete pStrm;
361 pStrm = p;
362 bMyStream = bMy;
365 void StgCache::SetStrm( UCBStorageStream* pStgStream )
367 if( pStorageStream )
368 pStorageStream->ReleaseRef();
369 pStorageStream = pStgStream;
371 if( bMyStream )
372 delete pStrm;
374 pStrm = NULL;
376 if ( pStorageStream )
378 pStorageStream->AddRef();
379 pStrm = pStorageStream->GetModifySvStream();
382 bMyStream = FALSE;
385 // Open/close the disk file
387 BOOL StgCache::Open( const String& rName, StreamMode nMode )
389 // do not open in exclusive mode!
390 if( nMode & STREAM_SHARE_DENYALL )
391 nMode = ( ( nMode & ~STREAM_SHARE_DENYALL ) | STREAM_SHARE_DENYWRITE );
392 SvFileStream* pFileStrm = new SvFileStream( rName, nMode );
393 // SvStream "Feature" Write Open auch erfolgreich, wenns nicht klappt
394 BOOL bAccessDenied = FALSE;
395 if( ( nMode & STREAM_WRITE ) && !pFileStrm->IsWritable() )
397 pFileStrm->Close();
398 bAccessDenied = TRUE;
400 SetStrm( pFileStrm, TRUE );
401 if( pFileStrm->IsOpen() )
403 ULONG nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
404 nPages = lcl_GetPageCount( nFileSize, nPageSize );
405 pStrm->Seek( 0L );
407 else
408 nPages = 0;
409 bFile = TRUE;
410 SetError( bAccessDenied ? ERRCODE_IO_ACCESSDENIED : pStrm->GetError() );
411 return Good();
414 void StgCache::Close()
416 if( bFile )
418 ((SvFileStream*) pStrm)->Close();
419 SetError( pStrm->GetError() );
423 // low level I/O
425 BOOL StgCache::Read( INT32 nPage, void* pBuf, INT32 nPg )
427 if( Good() )
429 /* #i73846# real life: a storage may refer to a page one-behind the
430 last valid page (see document attached to the issue). In that case
431 (if nPage==nPages), just do nothing here and let the caller work on
432 the empty zero-filled buffer. */
433 if ( nPage > nPages )
434 SetError( SVSTREAM_READ_ERROR );
435 else if ( nPage < nPages )
437 ULONG nPos = Page2Pos( nPage );
438 INT32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg;
439 ULONG nBytes = nPg2 * nPageSize;
440 // fixed address and size for the header
441 if( nPage == -1 )
443 nPos = 0L, nBytes = 512;
444 nPg2 = nPg;
446 if( pStrm->Tell() != nPos )
448 if( pStrm->Seek( nPos ) != nPos ) {
449 #ifdef CHECK_DIRTY
450 ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute();
451 #endif
454 pStrm->Read( pBuf, nBytes );
455 if ( nPg != nPg2 )
456 SetError( SVSTREAM_READ_ERROR );
457 else
458 SetError( pStrm->GetError() );
461 return Good();
464 BOOL StgCache::Write( INT32 nPage, void* pBuf, INT32 nPg )
466 if( Good() )
468 ULONG nPos = Page2Pos( nPage );
469 ULONG nBytes = nPg * nPageSize;
470 // fixed address and size for the header
471 if( nPage == -1 )
472 nPos = 0L, nBytes = 512;
473 if( pStrm->Tell() != nPos )
475 if( pStrm->Seek( nPos ) != nPos ) {
476 #ifdef CHECK_DIRTY
477 ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute();
478 #endif
481 ULONG nRes = pStrm->Write( pBuf, nBytes );
482 if( nRes != nBytes )
483 SetError( SVSTREAM_WRITE_ERROR );
484 else
485 SetError( pStrm->GetError() );
486 #ifdef READ_AFTER_WRITE
487 BYTE cBuf[ 512 ];
488 pStrm->Flush();
489 pStrm->Seek( nPos );
490 BOOL bRes = ( pStrm->Read( cBuf, 512 ) == 512 );
491 if( bRes )
492 bRes = !memcmp( cBuf, pBuf, 512 );
493 if( !bRes )
495 ErrorBox( NULL, WB_OK, String("SO2: Read after Write failed") ).Execute();
496 pStrm->SetError( SVSTREAM_WRITE_ERROR );
498 #endif
500 return Good();
503 // set the file size in pages
505 BOOL StgCache::SetSize( INT32 n )
507 // Add the file header
508 INT32 nSize = n * nPageSize + 512;
509 pStrm->SetStreamSize( nSize );
510 SetError( pStrm->GetError() );
511 if( !nError )
512 nPages = n;
513 return Good();
516 void StgCache::SetError( ULONG n )
518 if( n && !nError )
519 nError = n;
522 void StgCache::ResetError()
524 nError = SVSTREAM_OK;
525 pStrm->ResetError();
528 void StgCache::MoveError( StorageBase& r )
530 if( nError != SVSTREAM_OK )
532 r.SetError( nError );
533 ResetError();
537 // Utility functions
539 INT32 StgCache::Page2Pos( INT32 nPage )
541 if( nPage < 0 ) nPage = 0;
542 return( nPage * nPageSize ) + nPageSize;
545 INT32 StgCache::Pos2Page( INT32 nPos )
547 return ( ( nPos + nPageSize - 1 ) / nPageSize ) * nPageSize - 1;