1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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"
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
)
41 , mpData( new sal_uInt8
[ 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
);
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 )
65 nVal
= OSL_SWAPDWORD(nVal
);
67 static_cast<sal_Int32
*>(rPage
->GetData())[ nOff
] = nVal
;
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
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;
90 : nError( SVSTREAM_OK
)
94 , maLRUPages( 8 ) // entries in the LRU lookup
96 , pStorageStream( NULL
)
103 StgCache::~StgCache()
106 SetStrm( NULL
, false );
109 void StgCache::SetPhysPageSize( short n
)
111 OSL_ENSURE( n
>= 512, "Unexpecte page size is provided!" );
115 sal_uLong nPos
= pStrm
->Tell();
116 sal_uLong nFileSize
= pStrm
->Seek( STREAM_SEEK_TO_END
);
117 nPages
= lcl_GetPageCount( nFileSize
, nPageSize
);
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
;
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!" );
137 for ( LRUList::iterator it
= maLRUPages
.begin(); it
!= maLRUPages
.end(); ++it
) {
138 if ( it
->is() && (*it
)->GetPage() == xElem
->GetPage() ) {
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
)
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
)
162 IndexToStgPage::iterator it2
= maDirtyPages
.find( nPage
);
163 if ( it2
!= maDirtyPages
.end() )
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
);
176 if( !Read( nPage
, p
->GetData(), 1 ) && bForce
)
180 SetError( SVSTREAM_READ_ERROR
);
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
);
197 // old page: we must have this data!
198 rtl::Reference
< StgPage
> q
= Get( nOld
, true );
201 OSL_ENSURE( p
->GetSize() == q
->GetSize(), "Unexpected page size!" );
202 memcpy( p
->GetData(), q
->GetData(), p
->GetSize() );
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 ) )
231 maDirtyPages
.clear();
234 SetError( pStrm
->GetError() );
241 void StgCache::SetStrm( SvStream
* p
, bool bMy
)
245 pStorageStream
->ReleaseRef();
246 pStorageStream
= NULL
;
255 void StgCache::SetStrm( UCBStorageStream
* pStgStream
)
258 pStorageStream
->ReleaseRef();
259 pStorageStream
= pStgStream
;
266 if ( pStorageStream
)
268 pStorageStream
->AddFirstRef();
269 pStrm
= pStorageStream
->GetModifySvStream();
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() )
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
);
306 SetError( bAccessDenied
? ERRCODE_IO_ACCESSDENIED
: pStrm
->GetError() );
310 void StgCache::Close()
314 static_cast<SvFileStream
*>(pStrm
)->Close();
315 SetError( pStrm
->GetError() );
321 bool StgCache::Read( sal_Int32 nPage
, void* pBuf
, sal_Int32 nPg
)
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
339 nPos
= 0L, nBytes
= 512;
342 if( pStrm
->Tell() != nPos
)
346 pStrm
->Read( pBuf
, nBytes
);
348 SetError( SVSTREAM_READ_ERROR
);
350 SetError( pStrm
->GetError() );
356 bool StgCache::Write( sal_Int32 nPage
, void* pBuf
, sal_Int32 nPg
)
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
368 nPos
= 0L, nBytes
= 512;
369 if( pStrm
->Tell() != nPos
)
373 sal_uLong nRes
= pStrm
->Write( pBuf
, nBytes
);
375 SetError( SVSTREAM_WRITE_ERROR
);
377 SetError( pStrm
->GetError() );
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() );
395 void StgCache::SetError( sal_uLong n
)
401 void StgCache::ResetError()
403 nError
= SVSTREAM_OK
;
407 void StgCache::MoveError( StorageBase
& r
)
409 if( nError
!= SVSTREAM_OK
)
411 r
.SetError( nError
);
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: */