bump product version to 5.0.4.1
[LibreOffice.git] / sot / source / sdstor / stgcache.cxx
blobe54ffa4f14af751686e15e7ff283f4f09511826b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <string.h>
21 #include <osl/endian.h>
22 #include <osl/diagnose.h>
23 #include <tools/solar.h>
25 #include "sot/stg.hxx"
26 #include "stgelem.hxx"
27 #include "stgcache.hxx"
28 #include "stgstrms.hxx"
29 #include "stgdir.hxx"
30 #include "stgio.hxx"
32 #include <algorithm>
34 ////////////////////////////// class StgPage
35 // This class implements buffer functionality. The cache will always return
36 // a page buffer, even if a read fails. It is up to the caller to determine
37 // the correctness of the I/O.
39 StgPage::StgPage( short nSize, sal_Int32 nPage )
40 : mnPage( nPage )
41 , mpData( new sal_uInt8[ nSize ] )
42 , mnSize( nSize )
44 OSL_ENSURE( mnSize >= 512, "Unexpected page size is provided!" );
45 // We will write this data to a permanent file later
46 // best to clear if first.
47 memset( mpData, 0, mnSize );
50 StgPage::~StgPage()
52 delete [] mpData;
55 rtl::Reference< StgPage > StgPage::Create( short nData, sal_Int32 nPage )
57 return rtl::Reference< StgPage >( new StgPage( nData, nPage ) );
60 void StgCache::SetToPage ( const rtl::Reference< StgPage >& rPage, short nOff, sal_Int32 nVal )
62 if( ( nOff < (short) ( rPage->GetSize() / sizeof( sal_Int32 ) ) ) && nOff >= 0 )
64 #ifdef OSL_BIGENDIAN
65 nVal = OSL_SWAPDWORD(nVal);
66 #endif
67 static_cast<sal_Int32*>(rPage->GetData())[ nOff ] = nVal;
68 SetDirty( rPage );
72 bool StgPage::IsPageGreater( const StgPage *pA, const StgPage *pB )
74 return pA->mnPage < pB->mnPage;
77 //////////////////////////////// class StgCache
79 // The disk cache holds the cached sectors. The sector type differ according
80 // to their purpose.
82 static sal_Int32 lcl_GetPageCount( sal_uLong nFileSize, short nPageSize )
84 // return (nFileSize >= 512) ? (nFileSize - 512) / nPageSize : 0;
85 // #i61980# reallife: last page may be incomplete, return number of *started* pages
86 return (nFileSize >= 512) ? (nFileSize - 512 + nPageSize - 1) / nPageSize : 0;
89 StgCache::StgCache()
90 : nError( SVSTREAM_OK )
91 , nPages( 0 )
92 , nRef( 0 )
93 , nReplaceIdx( 0 )
94 , maLRUPages( 8 ) // entries in the LRU lookup
95 , nPageSize( 512 )
96 , pStorageStream( NULL )
97 , pStrm( NULL )
98 , bMyStream( false )
99 , bFile( false )
103 StgCache::~StgCache()
105 Clear();
106 SetStrm( NULL, false );
109 void StgCache::SetPhysPageSize( short n )
111 OSL_ENSURE( n >= 512, "Unexpecte page size is provided!" );
112 if ( n >= 512 )
114 nPageSize = n;
115 sal_uLong nPos = pStrm->Tell();
116 sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
117 nPages = lcl_GetPageCount( nFileSize, nPageSize );
118 pStrm->Seek( nPos );
122 // Create a new cache element
124 rtl::Reference< StgPage > StgCache::Create( sal_Int32 nPg )
126 rtl::Reference< StgPage > xElem( StgPage::Create( nPageSize, nPg ) );
127 maLRUPages[ nReplaceIdx++ % maLRUPages.size() ] = xElem;
128 return xElem;
131 // Delete the given element
133 void StgCache::Erase( const rtl::Reference< StgPage > &xElem )
135 OSL_ENSURE( xElem.is(), "The pointer should not be NULL!" );
136 if ( xElem.is() ) {
137 for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it ) {
138 if ( it->is() && (*it)->GetPage() == xElem->GetPage() ) {
139 it->clear();
140 break;
146 // remove all cache elements without flushing them
148 void StgCache::Clear()
150 maDirtyPages.clear();
151 for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
152 it->clear();
155 // Look for a cached page
157 rtl::Reference< StgPage > StgCache::Find( sal_Int32 nPage )
159 for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
160 if ( it->is() && (*it)->GetPage() == nPage )
161 return *it;
162 IndexToStgPage::iterator it2 = maDirtyPages.find( nPage );
163 if ( it2 != maDirtyPages.end() )
164 return it2->second;
165 return rtl::Reference< StgPage >();
168 // Load a page into the cache
170 rtl::Reference< StgPage > StgCache::Get( sal_Int32 nPage, bool bForce )
172 rtl::Reference< StgPage > p = Find( nPage );
173 if( !p.is() )
175 p = Create( nPage );
176 if( !Read( nPage, p->GetData(), 1 ) && bForce )
178 Erase( p );
179 p.clear();
180 SetError( SVSTREAM_READ_ERROR );
183 return p;
186 // Copy an existing page into a new page. Use this routine
187 // to duplicate an existing stream or to create new entries.
188 // The new page is initially marked dirty. No owner is copied.
190 rtl::Reference< StgPage > StgCache::Copy( sal_Int32 nNew, sal_Int32 nOld )
192 rtl::Reference< StgPage > p = Find( nNew );
193 if( !p.is() )
194 p = Create( nNew );
195 if( nOld >= 0 )
197 // old page: we must have this data!
198 rtl::Reference< StgPage > q = Get( nOld, true );
199 if( q.is() )
201 OSL_ENSURE( p->GetSize() == q->GetSize(), "Unexpected page size!" );
202 memcpy( p->GetData(), q->GetData(), p->GetSize() );
205 SetDirty( p );
207 return p;
210 // Historically this wrote pages in a sorted, ascending order;
211 // continue that tradition.
212 bool StgCache::Commit()
214 if ( Good() ) // otherwise Write does nothing
216 std::vector< StgPage * > aToWrite;
217 for ( IndexToStgPage::iterator aIt = maDirtyPages.begin();
218 aIt != maDirtyPages.end(); ++aIt )
219 aToWrite.push_back( aIt->second.get() );
221 std::sort( aToWrite.begin(), aToWrite.end(), StgPage::IsPageGreater );
222 for ( std::vector< StgPage * >::iterator aWr = aToWrite.begin();
223 aWr != aToWrite.end(); ++aWr)
225 const rtl::Reference< StgPage > &pPage = *aWr;
226 if ( !Write( pPage->GetPage(), pPage->GetData(), 1 ) )
227 return false;
231 maDirtyPages.clear();
233 pStrm->Flush();
234 SetError( pStrm->GetError() );
236 return true;
239 // Set a stream
241 void StgCache::SetStrm( SvStream* p, bool bMy )
243 if( pStorageStream )
245 pStorageStream->ReleaseRef();
246 pStorageStream = NULL;
249 if( bMyStream )
250 delete pStrm;
251 pStrm = p;
252 bMyStream = bMy;
255 void StgCache::SetStrm( UCBStorageStream* pStgStream )
257 if( pStorageStream )
258 pStorageStream->ReleaseRef();
259 pStorageStream = pStgStream;
261 if( bMyStream )
262 delete pStrm;
264 pStrm = NULL;
266 if ( pStorageStream )
268 pStorageStream->AddFirstRef();
269 pStrm = pStorageStream->GetModifySvStream();
272 bMyStream = false;
275 void StgCache::SetDirty( const rtl::Reference< StgPage > &rPage )
277 assert( IsWritable() );
278 maDirtyPages[ rPage->GetPage() ] = rPage;
281 // Open/close the disk file
283 bool StgCache::Open( const OUString& rName, StreamMode nMode )
285 // do not open in exclusive mode!
286 if( nMode & StreamMode::SHARE_DENYALL )
287 nMode = ( ( nMode & ~StreamMode::SHARE_DENYALL ) | StreamMode::SHARE_DENYWRITE );
288 SvFileStream* pFileStrm = new SvFileStream( rName, nMode );
289 // SvStream "Feature" Write Open auch erfolgreich, wenns nicht klappt
290 bool bAccessDenied = false;
291 if( ( nMode & StreamMode::WRITE ) && !pFileStrm->IsWritable() )
293 pFileStrm->Close();
294 bAccessDenied = true;
296 SetStrm( pFileStrm, true );
297 if( pFileStrm->IsOpen() )
299 sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
300 nPages = lcl_GetPageCount( nFileSize, nPageSize );
301 pStrm->Seek( 0L );
303 else
304 nPages = 0;
305 bFile = true;
306 SetError( bAccessDenied ? ERRCODE_IO_ACCESSDENIED : pStrm->GetError() );
307 return Good();
310 void StgCache::Close()
312 if( bFile )
314 static_cast<SvFileStream*>(pStrm)->Close();
315 SetError( pStrm->GetError() );
319 // low level I/O
321 bool StgCache::Read( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
323 if( Good() )
325 /* #i73846# real life: a storage may refer to a page one-behind the
326 last valid page (see document attached to the issue). In that case
327 (if nPage==nPages), just do nothing here and let the caller work on
328 the empty zero-filled buffer. */
329 if ( nPage > nPages )
330 SetError( SVSTREAM_READ_ERROR );
331 else if ( nPage < nPages )
333 sal_uInt32 nPos = Page2Pos( nPage );
334 sal_Int32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg;
335 sal_uInt32 nBytes = nPg2 * nPageSize;
336 // fixed address and size for the header
337 if( nPage == -1 )
339 nPos = 0L, nBytes = 512;
340 nPg2 = nPg;
342 if( pStrm->Tell() != nPos )
344 pStrm->Seek(nPos);
346 pStrm->Read( pBuf, nBytes );
347 if ( nPg != nPg2 )
348 SetError( SVSTREAM_READ_ERROR );
349 else
350 SetError( pStrm->GetError() );
353 return Good();
356 bool StgCache::Write( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
358 if( Good() )
360 sal_uInt32 nPos = Page2Pos( nPage );
361 sal_uInt32 nBytes = 0;
362 if ( SAL_MAX_INT32 / nPg > nPageSize )
363 nBytes = nPg * nPageSize;
365 // fixed address and size for the header
366 // nPageSize must be >= 512, otherwise the header can not be written here, we check it on import
367 if( nPage == -1 )
368 nPos = 0L, nBytes = 512;
369 if( pStrm->Tell() != nPos )
371 pStrm->Seek(nPos);
373 sal_uLong nRes = pStrm->Write( pBuf, nBytes );
374 if( nRes != nBytes )
375 SetError( SVSTREAM_WRITE_ERROR );
376 else
377 SetError( pStrm->GetError() );
379 return Good();
382 // set the file size in pages
384 bool StgCache::SetSize( sal_Int32 n )
386 // Add the file header
387 sal_Int32 nSize = n * nPageSize + 512;
388 pStrm->SetStreamSize( nSize );
389 SetError( pStrm->GetError() );
390 if( !nError )
391 nPages = n;
392 return Good();
395 void StgCache::SetError( sal_uLong n )
397 if( n && !nError )
398 nError = n;
401 void StgCache::ResetError()
403 nError = SVSTREAM_OK;
404 pStrm->ResetError();
407 void StgCache::MoveError( StorageBase& r )
409 if( nError != SVSTREAM_OK )
411 r.SetError( nError );
412 ResetError();
416 // Utility functions
418 sal_Int32 StgCache::Page2Pos( sal_Int32 nPage )
420 if( nPage < 0 ) nPage = 0;
421 return( nPage * nPageSize ) + nPageSize;
424 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */