fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / sot / source / sdstor / stgcache.cxx
blob0a2371c02ccfd642161ce2604eb274f3a35ecc8d
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 <tools/string.hxx>
24 #include "sot/stg.hxx"
25 #include "stgelem.hxx"
26 #include "stgcache.hxx"
27 #include "stgstrms.hxx"
28 #include "stgdir.hxx"
29 #include "stgio.hxx"
31 //#define CHECK_DIRTY 1
32 //#define READ_AFTER_WRITE 1
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 : mnRefCount( 0 )
41 , mnPage( nPage )
42 , mpData( new sal_uInt8[ nSize ] )
43 , mnSize( nSize )
45 OSL_ENSURE( mnSize >= 512, "Unexpected page size is provided!" );
46 // We will write this data to a permanent file later
47 // best to clear if first.
48 memset( mpData, 0, mnSize );
51 StgPage::~StgPage()
53 delete [] mpData;
56 rtl::Reference< StgPage > StgPage::Create( short nData, sal_Int32 nPage )
58 return rtl::Reference< StgPage >( new StgPage( nData, nPage ) );
61 void StgCache::SetToPage ( const rtl::Reference< StgPage > xPage, short nOff, sal_Int32 nVal )
63 if( ( nOff < (short) ( xPage->GetSize() / sizeof( sal_Int32 ) ) ) && nOff >= 0 )
65 #ifdef OSL_BIGENDIAN
66 nVal = OSL_SWAPDWORD(nVal);
67 #endif
68 ((sal_Int32*) xPage->GetData() )[ nOff ] = nVal;
69 SetDirty( xPage );
73 bool StgPage::IsPageGreater( const StgPage *pA, const StgPage *pB )
75 return pA->mnPage < pB->mnPage;
78 //////////////////////////////// class StgCache ////////////////////////////
80 // The disk cache holds the cached sectors. The sector type differ according
81 // to their purpose.
83 static sal_Int32 lcl_GetPageCount( sal_uLong nFileSize, short nPageSize )
85 // return (nFileSize >= 512) ? (nFileSize - 512) / nPageSize : 0;
86 // #i61980# reallife: last page may be incomplete, return number of *started* pages
87 return (nFileSize >= 512) ? (nFileSize - 512 + nPageSize - 1) / nPageSize : 0;
90 StgCache::StgCache()
91 : nError( SVSTREAM_OK )
92 , nPages( 0 )
93 , nRef( 0 )
94 , nReplaceIdx( 0 )
95 , maLRUPages( 8 ) // entries in the LRU lookup
96 , nPageSize( 512 )
97 , pStorageStream( NULL )
98 , pStrm( NULL )
99 , bMyStream( sal_False )
100 , bFile( sal_False )
104 StgCache::~StgCache()
106 Clear();
107 SetStrm( NULL, sal_False );
110 void StgCache::SetPhysPageSize( short n )
112 OSL_ENSURE( n >= 512, "Unexpecte page size is provided!" );
113 if ( n >= 512 )
115 nPageSize = n;
116 sal_uLong nPos = pStrm->Tell();
117 sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
118 nPages = lcl_GetPageCount( nFileSize, nPageSize );
119 pStrm->Seek( nPos );
123 // Create a new cache element
125 rtl::Reference< StgPage > StgCache::Create( sal_Int32 nPg )
127 rtl::Reference< StgPage > xElem( StgPage::Create( nPageSize, nPg ) );
128 maLRUPages[ nReplaceIdx++ % maLRUPages.size() ] = xElem;
129 return xElem;
132 // Delete the given element
134 void StgCache::Erase( const rtl::Reference< StgPage > &xElem )
136 OSL_ENSURE( xElem.is(), "The pointer should not be NULL!" );
137 if ( xElem.is() ) {
138 for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it ) {
139 if ( it->is() && (*it)->GetPage() == xElem->GetPage() ) {
140 it->clear();
141 break;
147 // remove all cache elements without flushing them
149 void StgCache::Clear()
151 maDirtyPages.clear();
152 for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
153 it->clear();
156 // Look for a cached page
158 rtl::Reference< StgPage > StgCache::Find( sal_Int32 nPage )
160 for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
161 if ( it->is() && (*it)->GetPage() == nPage )
162 return *it;
163 IndexToStgPage::iterator it2 = maDirtyPages.find( nPage );
164 if ( it2 != maDirtyPages.end() )
165 return it2->second;
166 return rtl::Reference< StgPage >();
169 // Load a page into the cache
171 rtl::Reference< StgPage > StgCache::Get( sal_Int32 nPage, sal_Bool bForce )
173 rtl::Reference< StgPage > p = Find( nPage );
174 if( !p.is() )
176 p = Create( nPage );
177 if( !Read( nPage, p->GetData(), 1 ) && bForce )
179 Erase( p );
180 p.clear();
181 SetError( SVSTREAM_READ_ERROR );
184 return p;
187 // Copy an existing page into a new page. Use this routine
188 // to duplicate an existing stream or to create new entries.
189 // The new page is initially marked dirty. No owner is copied.
191 rtl::Reference< StgPage > StgCache::Copy( sal_Int32 nNew, sal_Int32 nOld )
193 rtl::Reference< StgPage > p = Find( nNew );
194 if( !p.is() )
195 p = Create( nNew );
196 if( nOld >= 0 )
198 // old page: we must have this data!
199 rtl::Reference< StgPage > q = Get( nOld, sal_True );
200 if( q.is() )
202 OSL_ENSURE( p->GetSize() == q->GetSize(), "Unexpected page size!" );
203 memcpy( p->GetData(), q->GetData(), p->GetSize() );
206 SetDirty( p );
208 return p;
211 // Historically this wrote pages in a sorted, ascending order;
212 // continue that tradition.
213 sal_Bool StgCache::Commit()
215 if ( Good() ) // otherwise Write does nothing
217 std::vector< StgPage * > aToWrite;
218 for ( IndexToStgPage::iterator aIt = maDirtyPages.begin();
219 aIt != maDirtyPages.end(); ++aIt )
220 aToWrite.push_back( aIt->second.get() );
222 std::sort( aToWrite.begin(), aToWrite.end(), StgPage::IsPageGreater );
223 for ( std::vector< StgPage * >::iterator aWr = aToWrite.begin();
224 aWr != aToWrite.end(); ++aWr)
226 const rtl::Reference< StgPage > &pPage = *aWr;
227 if ( !Write( pPage->GetPage(), pPage->GetData(), 1 ) )
228 return sal_False;
232 maDirtyPages.clear();
234 pStrm->Flush();
235 SetError( pStrm->GetError() );
237 return sal_True;
240 // Set a stream
242 void StgCache::SetStrm( SvStream* p, sal_Bool bMy )
244 if( pStorageStream )
246 pStorageStream->ReleaseRef();
247 pStorageStream = NULL;
250 if( bMyStream )
251 delete pStrm;
252 pStrm = p;
253 bMyStream = bMy;
256 void StgCache::SetStrm( UCBStorageStream* pStgStream )
258 if( pStorageStream )
259 pStorageStream->ReleaseRef();
260 pStorageStream = pStgStream;
262 if( bMyStream )
263 delete pStrm;
265 pStrm = NULL;
267 if ( pStorageStream )
269 pStorageStream->AddRef();
270 pStrm = pStorageStream->GetModifySvStream();
273 bMyStream = sal_False;
276 void StgCache::SetDirty( const rtl::Reference< StgPage > &xPage )
278 assert( IsWritable() );
279 maDirtyPages[ xPage->GetPage() ] = xPage;
282 // Open/close the disk file
284 sal_Bool StgCache::Open( const String& rName, StreamMode nMode )
286 // do not open in exclusive mode!
287 if( nMode & STREAM_SHARE_DENYALL )
288 nMode = ( ( nMode & ~STREAM_SHARE_DENYALL ) | STREAM_SHARE_DENYWRITE );
289 SvFileStream* pFileStrm = new SvFileStream( rName, nMode );
290 // SvStream "Feature" Write Open auch erfolgreich, wenns nicht klappt
291 sal_Bool bAccessDenied = sal_False;
292 if( ( nMode & STREAM_WRITE ) && !pFileStrm->IsWritable() )
294 pFileStrm->Close();
295 bAccessDenied = sal_True;
297 SetStrm( pFileStrm, sal_True );
298 if( pFileStrm->IsOpen() )
300 sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
301 nPages = lcl_GetPageCount( nFileSize, nPageSize );
302 pStrm->Seek( 0L );
304 else
305 nPages = 0;
306 bFile = sal_True;
307 SetError( bAccessDenied ? ERRCODE_IO_ACCESSDENIED : pStrm->GetError() );
308 return Good();
311 void StgCache::Close()
313 if( bFile )
315 ((SvFileStream*) pStrm)->Close();
316 SetError( pStrm->GetError() );
320 // low level I/O
322 sal_Bool StgCache::Read( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
324 if( Good() )
326 /* #i73846# real life: a storage may refer to a page one-behind the
327 last valid page (see document attached to the issue). In that case
328 (if nPage==nPages), just do nothing here and let the caller work on
329 the empty zero-filled buffer. */
330 if ( nPage > nPages )
331 SetError( SVSTREAM_READ_ERROR );
332 else if ( nPage < nPages )
334 sal_uLong nPos = Page2Pos( nPage );
335 sal_Int32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg;
336 sal_uLong nBytes = nPg2 * nPageSize;
337 // fixed address and size for the header
338 if( nPage == -1 )
340 nPos = 0L, nBytes = 512;
341 nPg2 = nPg;
343 if( pStrm->Tell() != nPos )
345 if( pStrm->Seek( nPos ) != nPos ) {
346 #ifdef CHECK_DIRTY
347 ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute();
348 #endif
351 pStrm->Read( pBuf, nBytes );
352 if ( nPg != nPg2 )
353 SetError( SVSTREAM_READ_ERROR );
354 else
355 SetError( pStrm->GetError() );
358 return Good();
361 sal_Bool StgCache::Write( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
363 if( Good() )
365 sal_uLong nPos = Page2Pos( nPage );
366 sal_uLong nBytes = 0;
367 if ( SAL_MAX_INT32 / nPg > nPageSize )
368 nBytes = nPg * nPageSize;
370 // fixed address and size for the header
371 // nPageSize must be >= 512, otherwise the header can not be written here, we check it on import
372 if( nPage == -1 )
373 nPos = 0L, nBytes = 512;
374 if( pStrm->Tell() != nPos )
376 if( pStrm->Seek( nPos ) != nPos ) {
377 #ifdef CHECK_DIRTY
378 ErrorBox( NULL, WB_OK, String("SO2: Seek failed") ).Execute();
379 #endif
382 sal_uLong nRes = pStrm->Write( pBuf, nBytes );
383 if( nRes != nBytes )
384 SetError( SVSTREAM_WRITE_ERROR );
385 else
386 SetError( pStrm->GetError() );
387 #ifdef READ_AFTER_WRITE
388 sal_uInt8 cBuf[ 512 ];
389 pStrm->Flush();
390 pStrm->Seek( nPos );
391 sal_Bool bRes = ( pStrm->Read( cBuf, 512 ) == 512 );
392 if( bRes )
393 bRes = !memcmp( cBuf, pBuf, 512 );
394 if( !bRes )
396 ErrorBox( NULL, WB_OK, String("SO2: Read after Write failed") ).Execute();
397 pStrm->SetError( SVSTREAM_WRITE_ERROR );
399 #endif
401 return Good();
404 // set the file size in pages
406 sal_Bool StgCache::SetSize( sal_Int32 n )
408 // Add the file header
409 sal_Int32 nSize = n * nPageSize + 512;
410 pStrm->SetStreamSize( nSize );
411 SetError( pStrm->GetError() );
412 if( !nError )
413 nPages = n;
414 return Good();
417 void StgCache::SetError( sal_uLong n )
419 if( n && !nError )
420 nError = n;
423 void StgCache::ResetError()
425 nError = SVSTREAM_OK;
426 pStrm->ResetError();
429 void StgCache::MoveError( StorageBase& r )
431 if( nError != SVSTREAM_OK )
433 r.SetError( nError );
434 ResetError();
438 // Utility functions
440 sal_Int32 StgCache::Page2Pos( sal_Int32 nPage )
442 if( nPage < 0 ) nPage = 0;
443 return( nPage * nPageSize ) + nPageSize;
446 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */