1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: stgcache.cxx,v $
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>
38 #if defined(_MSC_VER) && (_MSC_VER<1200)
39 #include <tools/postsys.h>
41 #include <vos/macros.hxx>
44 #include <osl/endian.h>
45 #include <tools/string.hxx>
48 #include "stgelem.hxx"
49 #include "stgcache.hxx"
50 #include "stgstrms.hxx"
54 /*************************************************************************/
55 //-----------------------------------------------------------------------------
61 NAMESPACE_STD(equal_to
)< INT32
>
64 #pragma warning( disable: 4786 )
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
)
81 pData
= new BYTE
[ nData
];
94 void StgPage::SetPage( short nOff
, INT32 nVal
)
96 if( ( nOff
< (short) ( nData
/ sizeof( INT32
) ) ) && nOff
>= 0 )
99 nVal
= SWAPLONG(nVal
);
101 ((INT32
*) pData
)[ nOff
] = nVal
;
106 //////////////////////////////// class StgCache ////////////////////////////
108 // The disk cache holds the cached sectors. The sector type differ according
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;
122 pCur
= pElem1
= NULL
;
124 nError
= SVSTREAM_OK
;
128 pStorageStream
= NULL
;
131 StgCache::~StgCache()
134 SetStrm( NULL
, FALSE
);
135 delete (UsrStgPagePtr_Impl
*)pLRUCache
;
138 void StgCache::SetPhysPageSize( short n
)
141 ULONG nPos
= pStrm
->Tell();
142 ULONG nFileSize
= pStrm
->Seek( STREAM_SEEK_TO_END
);
143 nPages
= lcl_GetPageCount( nFileSize
, nPageSize
);
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
);
154 // For data security, clear the buffer contents
155 memset( pElem
->pData
, 0, pElem
->nData
);
160 pElem
->pNext1
= pCur
;
161 pElem
->pLast1
= pCur
->pLast1
;
162 pElem
->pNext1
->pLast1
=
163 pElem
->pLast1
->pNext1
= pElem
;
166 pElem
->pNext1
= pElem
->pLast1
= pElem
;
168 pLRUCache
= new UsrStgPagePtr_Impl();
169 (*(UsrStgPagePtr_Impl
*)pLRUCache
)[pElem
->nPage
] = pElem
;
174 pElem1
= pElem
->pNext2
= pElem
->pLast2
= pElem
;
180 if( pElem
->nPage
< p
->nPage
)
183 } while( p
!= pElem1
);
185 pElem
->pLast2
= p
->pLast2
;
186 pElem
->pNext2
->pLast2
=
187 pElem
->pLast2
->pNext2
= pElem
;
188 if( p
->nPage
< pElem1
->nPage
)
194 // Delete the given element
196 void StgCache::Erase( StgPage
* pElem
)
199 pElem
->pNext1
->pLast1
= pElem
->pLast1
;
200 pElem
->pLast1
->pNext1
= pElem
->pNext1
;
202 pCur
= ( pElem
->pNext1
== pElem
) ? NULL
: pElem
->pNext1
;
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
;
213 // remove all cache elements without flushing them
215 void StgCache::Clear()
217 StgPage
* pElem
= pCur
;
220 StgPage
* pDelete
= pElem
;
221 pElem
= pElem
->pNext1
;
224 while( pCur
!= pElem
);
227 delete (UsrStgPagePtr_Impl
*)pLRUCache
;
231 // Look for a cached page
233 StgPage
* StgCache::Find( INT32 nPage
)
237 UsrStgPagePtr_Impl::iterator aIt
= ((UsrStgPagePtr_Impl
*)pLRUCache
)->find( nPage
);
238 if( aIt
!= ((UsrStgPagePtr_Impl
*)pLRUCache
)->end() )
241 StgPage
* pFound
= (*aIt
).second
;
246 pFound
->pNext1
->pLast1
= pFound
->pLast1
;
247 pFound
->pLast1
->pNext1
= pFound
->pNext1
;
249 pFound
->pNext1
= pCur
;
250 pFound
->pLast1
= pCur
->pLast1
;
251 pFound
->pNext1
->pLast1
=
252 pFound
->pLast1
->pNext1
= pFound
;
259 // Load a page into the cache
261 StgPage
* StgCache::Get( INT32 nPage
, BOOL bForce
)
263 StgPage
* p
= Find( nPage
);
267 if( !Read( nPage
, p
->pData
, 1 ) && bForce
)
271 SetError( SVSTREAM_READ_ERROR
);
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
);
288 // old page: we must have this data!
289 StgPage
* q
= Get( nOld
, TRUE
);
291 memcpy( p
->pData
, q
->pData
, p
->nData
);
297 // Flush the cache whose owner is given. NULL flushes all.
299 BOOL
StgCache::Commit( StgDirEntry
* )
306 BOOL b
= Write( p
->nPage
, p
->pData
, 1 );
312 } while( p
!= pElem1
);
314 SetError( pStrm
->GetError() );
321 ErrorBox( NULL
, WB_OK
, String("SO2: Dirty Block in Ordered List") ).Execute();
322 BOOL b
= Write( p
->nPage
, p
->pData
, 1 );
328 } while( p
!= pElem1
);
334 ErrorBox( NULL
, WB_OK
, String("SO2: Dirty Block in LRU List") ).Execute();
335 BOOL b
= Write( p
->nPage
, p
->pData
, 1 );
341 } while( p
!= pElem1
);
346 void StgCache::Revert( StgDirEntry
* )
351 void StgCache::SetStrm( SvStream
* p
, BOOL bMy
)
355 pStorageStream
->ReleaseRef();
356 pStorageStream
= NULL
;
365 void StgCache::SetStrm( UCBStorageStream
* pStgStream
)
368 pStorageStream
->ReleaseRef();
369 pStorageStream
= pStgStream
;
376 if ( pStorageStream
)
378 pStorageStream
->AddRef();
379 pStrm
= pStorageStream
->GetModifySvStream();
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() )
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
);
410 SetError( bAccessDenied
? ERRCODE_IO_ACCESSDENIED
: pStrm
->GetError() );
414 void StgCache::Close()
418 ((SvFileStream
*) pStrm
)->Close();
419 SetError( pStrm
->GetError() );
425 BOOL
StgCache::Read( INT32 nPage
, void* pBuf
, INT32 nPg
)
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
443 nPos
= 0L, nBytes
= 512;
446 if( pStrm
->Tell() != nPos
)
448 if( pStrm
->Seek( nPos
) != nPos
) {
450 ErrorBox( NULL
, WB_OK
, String("SO2: Seek failed") ).Execute();
454 pStrm
->Read( pBuf
, nBytes
);
456 SetError( SVSTREAM_READ_ERROR
);
458 SetError( pStrm
->GetError() );
464 BOOL
StgCache::Write( INT32 nPage
, void* pBuf
, INT32 nPg
)
468 ULONG nPos
= Page2Pos( nPage
);
469 ULONG nBytes
= nPg
* nPageSize
;
470 // fixed address and size for the header
472 nPos
= 0L, nBytes
= 512;
473 if( pStrm
->Tell() != nPos
)
475 if( pStrm
->Seek( nPos
) != nPos
) {
477 ErrorBox( NULL
, WB_OK
, String("SO2: Seek failed") ).Execute();
481 ULONG nRes
= pStrm
->Write( pBuf
, nBytes
);
483 SetError( SVSTREAM_WRITE_ERROR
);
485 SetError( pStrm
->GetError() );
486 #ifdef READ_AFTER_WRITE
490 BOOL bRes
= ( pStrm
->Read( cBuf
, 512 ) == 512 );
492 bRes
= !memcmp( cBuf
, pBuf
, 512 );
495 ErrorBox( NULL
, WB_OK
, String("SO2: Read after Write failed") ).Execute();
496 pStrm
->SetError( SVSTREAM_WRITE_ERROR
);
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() );
516 void StgCache::SetError( ULONG n
)
522 void StgCache::ResetError()
524 nError
= SVSTREAM_OK
;
528 void StgCache::MoveError( StorageBase
& r
)
530 if( nError
!= SVSTREAM_OK
)
532 r
.SetError( nError
);
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;