summaryrefslogtreecommitdiff
path: root/sot/source/sdstor/stgcache.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sot/source/sdstor/stgcache.cxx')
-rw-r--r--sot/source/sdstor/stgcache.cxx547
1 files changed, 547 insertions, 0 deletions
diff --git a/sot/source/sdstor/stgcache.cxx b/sot/source/sdstor/stgcache.cxx
new file mode 100644
index 000000000000..b3eca5aac8bf
--- /dev/null
+++ b/sot/source/sdstor/stgcache.cxx
@@ -0,0 +1,547 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sot.hxx"
+
+#if defined(_MSC_VER) && (_MSC_VER<1200)
+#include <tools/presys.h>
+#endif
+#include <hash_map>
+#if defined(_MSC_VER) && (_MSC_VER<1200)
+#include <tools/postsys.h>
+#endif
+
+#include <string.h>
+#include <osl/endian.h>
+#include <tools/string.hxx>
+
+#include "stg.hxx"
+#include "stgelem.hxx"
+#include "stgcache.hxx"
+#include "stgstrms.hxx"
+#include "stgdir.hxx"
+#include "stgio.hxx"
+
+/*************************************************************************/
+//-----------------------------------------------------------------------------
+typedef std::hash_map
+<
+ INT32,
+ StgPage *,
+ std::hash< INT32 >,
+ std::equal_to< INT32 >
+> UsrStgPagePtr_Impl;
+#ifdef _MSC_VER
+#pragma warning( disable: 4786 )
+#endif
+
+//#define CHECK_DIRTY 1
+//#define READ_AFTER_WRITE 1
+
+////////////////////////////// class StgPage /////////////////////////////
+// This class implements buffer functionality. The cache will always return
+// a page buffer, even if a read fails. It is up to the caller to determine
+// the correctness of the I/O.
+
+StgPage::StgPage( StgCache* p, short n )
+{
+ pCache = p;
+ nData = n;
+ bDirty = FALSE;
+ nPage = 0;
+ pData = new BYTE[ nData ];
+ pNext1 =
+ pNext2 =
+ pLast1 =
+ pLast2 = NULL;
+ pOwner = NULL;
+}
+
+StgPage::~StgPage()
+{
+ delete [] pData;
+}
+
+void StgPage::SetPage( short nOff, INT32 nVal )
+{
+ if( ( nOff < (short) ( nData / sizeof( INT32 ) ) ) && nOff >= 0 )
+ {
+#ifdef OSL_BIGENDIAN
+ nVal = SWAPLONG(nVal);
+#endif
+ ((INT32*) pData )[ nOff ] = nVal;
+ bDirty = TRUE;
+ }
+}
+
+//////////////////////////////// class StgCache ////////////////////////////
+
+// The disk cache holds the cached sectors. The sector type differ according
+// to their purpose.
+
+INT32 lcl_GetPageCount( ULONG nFileSize, short nPageSize )
+{
+// return (nFileSize >= 512) ? (nFileSize - 512) / nPageSize : 0;
+ // #i61980# reallife: last page may be incomplete, return number of *started* pages
+ return (nFileSize >= 512) ? (nFileSize - 512 + nPageSize - 1) / nPageSize : 0;
+}
+
+StgCache::StgCache()
+{
+ nRef = 0;
+ pStrm = NULL;
+ pCur = pElem1 = NULL;
+ nPageSize = 512;
+ nError = SVSTREAM_OK;
+ bMyStream = FALSE;
+ bFile = FALSE;
+ pLRUCache = NULL;
+ pStorageStream = NULL;
+}
+
+StgCache::~StgCache()
+{
+ Clear();
+ SetStrm( NULL, FALSE );
+ delete (UsrStgPagePtr_Impl*)pLRUCache;
+}
+
+void StgCache::SetPhysPageSize( short n )
+{
+ nPageSize = n;
+ ULONG nPos = pStrm->Tell();
+ ULONG nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
+ nPages = lcl_GetPageCount( nFileSize, nPageSize );
+ pStrm->Seek( nPos );
+}
+
+// Create a new cache element
+// pCur points to this element
+
+StgPage* StgCache::Create( INT32 nPg )
+{
+ StgPage* pElem = new StgPage( this, nPageSize );
+ pElem->nPage = nPg;
+ // For data security, clear the buffer contents
+ memset( pElem->pData, 0, pElem->nData );
+
+ // insert to LRU
+ if( pCur )
+ {
+ pElem->pNext1 = pCur;
+ pElem->pLast1 = pCur->pLast1;
+ pElem->pNext1->pLast1 =
+ pElem->pLast1->pNext1 = pElem;
+ }
+ else
+ pElem->pNext1 = pElem->pLast1 = pElem;
+ if( !pLRUCache )
+ pLRUCache = new UsrStgPagePtr_Impl();
+ (*(UsrStgPagePtr_Impl*)pLRUCache)[pElem->nPage] = pElem;
+ pCur = pElem;
+
+ // insert to Sorted
+ if( !pElem1 )
+ pElem1 = pElem->pNext2 = pElem->pLast2 = pElem;
+ else
+ {
+ StgPage* p = pElem1;
+ do
+ {
+ if( pElem->nPage < p->nPage )
+ break;
+ p = p->pNext2;
+ } while( p != pElem1 );
+ pElem->pNext2 = p;
+ pElem->pLast2 = p->pLast2;
+ pElem->pNext2->pLast2 =
+ pElem->pLast2->pNext2 = pElem;
+ if( p->nPage < pElem1->nPage )
+ pElem1 = pElem;
+ }
+ return pElem;
+}
+
+// Delete the given element
+
+void StgCache::Erase( StgPage* pElem )
+{
+ //remove from LRU
+ pElem->pNext1->pLast1 = pElem->pLast1;
+ pElem->pLast1->pNext1 = pElem->pNext1;
+ if( pCur == pElem )
+ pCur = ( pElem->pNext1 == pElem ) ? NULL : pElem->pNext1;
+ if( pLRUCache )
+ ((UsrStgPagePtr_Impl*)pLRUCache)->erase( pElem->nPage );
+ // remove from Sorted
+ pElem->pNext2->pLast2 = pElem->pLast2;
+ pElem->pLast2->pNext2 = pElem->pNext2;
+ if( pElem1 == pElem )
+ pElem1 = ( pElem->pNext2 == pElem ) ? NULL : pElem->pNext2;
+ delete pElem;
+}
+
+// remove all cache elements without flushing them
+
+void StgCache::Clear()
+{
+ StgPage* pElem = pCur;
+ if( pCur ) do
+ {
+ StgPage* pDelete = pElem;
+ pElem = pElem->pNext1;
+ delete pDelete;
+ }
+ while( pCur != pElem );
+ pCur = NULL;
+ pElem1 = NULL;
+ delete (UsrStgPagePtr_Impl*)pLRUCache;
+ pLRUCache = NULL;
+}
+
+// Look for a cached page
+
+StgPage* StgCache::Find( INT32 nPage )
+{
+ if( !pLRUCache )
+ return NULL;
+ UsrStgPagePtr_Impl::iterator aIt = ((UsrStgPagePtr_Impl*)pLRUCache)->find( nPage );
+ if( aIt != ((UsrStgPagePtr_Impl*)pLRUCache)->end() )
+ {
+ // page found
+ StgPage* pFound = (*aIt).second;
+
+ if( pFound != pCur )
+ {
+ // remove from LRU
+ pFound->pNext1->pLast1 = pFound->pLast1;
+ pFound->pLast1->pNext1 = pFound->pNext1;
+ // insert to LRU
+ pFound->pNext1 = pCur;
+ pFound->pLast1 = pCur->pLast1;
+ pFound->pNext1->pLast1 =
+ pFound->pLast1->pNext1 = pFound;
+ }
+ return pFound;
+ }
+ return NULL;
+}
+
+// Load a page into the cache
+
+StgPage* StgCache::Get( INT32 nPage, BOOL bForce )
+{
+ StgPage* p = Find( nPage );
+ if( !p )
+ {
+ p = Create( nPage );
+ if( !Read( nPage, p->pData, 1 ) && bForce )
+ {
+ Erase( p );
+ p = NULL;
+ SetError( SVSTREAM_READ_ERROR );
+ }
+ }
+ return p;
+}
+
+// Copy an existing page into a new page. Use this routine
+// to duplicate an existing stream or to create new entries.
+// The new page is initially marked dirty. No owner is copied.
+
+StgPage* StgCache::Copy( INT32 nNew, INT32 nOld )
+{
+ StgPage* p = Find( nNew );
+ if( !p )
+ p = Create( nNew );
+ if( nOld >= 0 )
+ {
+ // old page: we must have this data!
+ StgPage* q = Get( nOld, TRUE );
+ if( q )
+ memcpy( p->pData, q->pData, p->nData );
+ }
+ p->SetDirty();
+ return p;
+}
+
+// Flush the cache whose owner is given. NULL flushes all.
+
+BOOL StgCache::Commit( StgDirEntry* )
+{
+ StgPage* p = pElem1;
+ if( p ) do
+ {
+ if( p->bDirty )
+ {
+ BOOL b = Write( p->nPage, p->pData, 1 );
+ if( !b )
+ return FALSE;
+ p->bDirty = FALSE;
+ }
+ p = p->pNext2;
+ } while( p != pElem1 );
+ pStrm->Flush();
+ SetError( pStrm->GetError() );
+#ifdef CHECK_DIRTY
+ p = pElem1;
+ if( p ) do
+ {
+ if( p->bDirty )
+ {
+ ErrorBox( NULL, WB_OK, String("SO2: Dirty Block in Ordered List") ).Execute();
+ BOOL b = Write( p->nPage, p->pData, 1 );
+ if( !b )
+ return FALSE;
+ p->bDirty = FALSE;
+ }
+ p = p->pNext2;
+ } while( p != pElem1 );
+ p = pElem1;
+ if( p ) do
+ {
+ if( p->bDirty )
+ {
+ ErrorBox( NULL, WB_OK, String("SO2: Dirty Block in LRU List") ).Execute();
+ BOOL b = Write( p->nPage, p->pData, 1 );
+ if( !b )
+ return FALSE;
+ p->bDirty = FALSE;
+ }
+ p = p->pNext1;
+ } while( p != pElem1 );
+#endif
+ return TRUE;
+}
+
+void StgCache::Revert( StgDirEntry* )
+{}
+
+// Set a stream
+
+void StgCache::SetStrm( SvStream* p, BOOL bMy )
+{
+ if( pStorageStream )
+ {
+ pStorageStream->ReleaseRef();
+ pStorageStream = NULL;
+ }
+
+ if( bMyStream )
+ delete pStrm;
+ pStrm = p;
+ bMyStream = bMy;
+}
+
+void StgCache::SetStrm( UCBStorageStream* pStgStream )
+{
+ if( pStorageStream )
+ pStorageStream->ReleaseRef();
+ pStorageStream = pStgStream;
+
+ if( bMyStream )
+ delete pStrm;
+
+ pStrm = NULL;
+
+ if ( pStorageStream )
+ {
+ pStorageStream->AddRef();
+ pStrm = pStorageStream->GetModifySvStream();
+ }
+
+ bMyStream = FALSE;
+}
+
+// Open/close the disk file
+
+BOOL StgCache::Open( const String& rName, StreamMode nMode )
+{
+ // do not open in exclusive mode!
+ if( nMode & STREAM_SHARE_DENYALL )
+ nMode = ( ( nMode & ~STREAM_SHARE_DENYALL ) | STREAM_SHARE_DENYWRITE );
+ SvFileStream* pFileStrm = new SvFileStream( rName, nMode );
+ // SvStream "Feature" Write Open auch erfolgreich, wenns nicht klappt
+ BOOL bAccessDenied = FALSE;
+ if( ( nMode & STREAM_WRITE ) && !pFileStrm->IsWritable() )
+ {
+ pFileStrm->Close();
+ bAccessDenied = TRUE;
+ }
+ SetStrm( pFileStrm, TRUE );
+ if( pFileStrm->IsOpen() )
+ {
+ ULONG nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
+ nPages = lcl_GetPageCount( nFileSize, nPageSize );
+ pStrm->Seek( 0L );
+ }
+ else
+ nPages = 0;
+ bFile = TRUE;
+ SetError( bAccessDenied ? ERRCODE_IO_ACCESSDENIED : pStrm->GetError() );
+ return Good();
+}
+
+void StgCache::Close()
+{
+ if( bFile )
+ {
+ ((SvFileStream*) pStrm)->Close();
+ SetError( pStrm->GetError() );
+ }
+}
+
+// low level I/O
+
+BOOL StgCache::Read( INT32 nPage, void* pBuf, INT32 nPg )
+{
+ if( Good() )
+ {
+ /* #i73846# real life: a storage may refer to a page one-behind the
+ last valid page (see document attached to the issue). In that case
+ (if nPage==nPages), just do nothing here and let the caller work on
+ the empty zero-filled buffer. */
+ if ( nPage > nPages )
+ SetError( SVSTREAM_READ_ERROR );
+ else if ( nPage < nPages )
+ {
+ ULONG nPos = Page2Pos( nPage );
+ INT32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg;
+ ULONG nBytes = nPg2 * nPageSize;
+ // fixed address and size for the header
+ if( nPage == -1 )
+ {
+ nPos = 0L, nBytes = 512;
+ nPg2 = nPg;
+ }
+ if( pStrm->Tell() != nPos )
+ {
+ if( pStrm->Seek( nPos ) != nPos ) {
+ #ifdef CHECK_DIRTY
+ ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute();
+ #endif
+ }
+ }
+ pStrm->Read( pBuf, nBytes );
+ if ( nPg != nPg2 )
+ SetError( SVSTREAM_READ_ERROR );
+ else
+ SetError( pStrm->GetError() );
+ }
+ }
+ return Good();
+}
+
+BOOL StgCache::Write( INT32 nPage, void* pBuf, INT32 nPg )
+{
+ if( Good() )
+ {
+ ULONG nPos = Page2Pos( nPage );
+ ULONG nBytes = nPg * nPageSize;
+ // fixed address and size for the header
+ if( nPage == -1 )
+ nPos = 0L, nBytes = 512;
+ if( pStrm->Tell() != nPos )
+ {
+ if( pStrm->Seek( nPos ) != nPos ) {
+#ifdef CHECK_DIRTY
+ ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute();
+#endif
+ }
+ }
+ ULONG nRes = pStrm->Write( pBuf, nBytes );
+ if( nRes != nBytes )
+ SetError( SVSTREAM_WRITE_ERROR );
+ else
+ SetError( pStrm->GetError() );
+#ifdef READ_AFTER_WRITE
+ BYTE cBuf[ 512 ];
+ pStrm->Flush();
+ pStrm->Seek( nPos );
+ BOOL bRes = ( pStrm->Read( cBuf, 512 ) == 512 );
+ if( bRes )
+ bRes = !memcmp( cBuf, pBuf, 512 );
+ if( !bRes )
+ {
+ ErrorBox( NULL, WB_OK, String("SO2: Read after Write failed") ).Execute();
+ pStrm->SetError( SVSTREAM_WRITE_ERROR );
+ }
+#endif
+ }
+ return Good();
+}
+
+// set the file size in pages
+
+BOOL StgCache::SetSize( INT32 n )
+{
+ // Add the file header
+ INT32 nSize = n * nPageSize + 512;
+ pStrm->SetStreamSize( nSize );
+ SetError( pStrm->GetError() );
+ if( !nError )
+ nPages = n;
+ return Good();
+}
+
+void StgCache::SetError( ULONG n )
+{
+ if( n && !nError )
+ nError = n;
+}
+
+void StgCache::ResetError()
+{
+ nError = SVSTREAM_OK;
+ pStrm->ResetError();
+}
+
+void StgCache::MoveError( StorageBase& r )
+{
+ if( nError != SVSTREAM_OK )
+ {
+ r.SetError( nError );
+ ResetError();
+ }
+}
+
+// Utility functions
+
+INT32 StgCache::Page2Pos( INT32 nPage )
+{
+ if( nPage < 0 ) nPage = 0;
+ return( nPage * nPageSize ) + nPageSize;
+}
+
+INT32 StgCache::Pos2Page( INT32 nPos )
+{
+ return ( ( nPos + nPageSize - 1 ) / nPageSize ) * nPageSize - 1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */