Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <string.h>
21 : #include <osl/endian.h>
22 : #include <tools/solar.h>
23 :
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"
30 :
31 : ////////////////////////////// class StgPage
32 : // This class implements buffer functionality. The cache will always return
33 : // a page buffer, even if a read fails. It is up to the caller to determine
34 : // the correctness of the I/O.
35 :
36 37150 : StgPage::StgPage( short nSize, sal_Int32 nPage )
37 : : mnPage( nPage )
38 37150 : , mpData( new sal_uInt8[ nSize ] )
39 74300 : , mnSize( nSize )
40 : {
41 : OSL_ENSURE( mnSize >= 512, "Unexpected page size is provided!" );
42 : // We will write this data to a permanent file later
43 : // best to clear if first.
44 37150 : memset( mpData, 0, mnSize );
45 37150 : }
46 :
47 111450 : StgPage::~StgPage()
48 : {
49 37150 : delete [] mpData;
50 74300 : }
51 :
52 37150 : rtl::Reference< StgPage > StgPage::Create( short nData, sal_Int32 nPage )
53 : {
54 37150 : return rtl::Reference< StgPage >( new StgPage( nData, nPage ) );
55 : }
56 :
57 191210 : void StgCache::SetToPage ( const rtl::Reference< StgPage > xPage, short nOff, sal_Int32 nVal )
58 : {
59 191210 : if( ( nOff < (short) ( xPage->GetSize() / sizeof( sal_Int32 ) ) ) && nOff >= 0 )
60 : {
61 : #ifdef OSL_BIGENDIAN
62 : nVal = OSL_SWAPDWORD(nVal);
63 : #endif
64 191210 : ((sal_Int32*) xPage->GetData() )[ nOff ] = nVal;
65 191210 : SetDirty( xPage );
66 : }
67 191210 : }
68 :
69 46271 : bool StgPage::IsPageGreater( const StgPage *pA, const StgPage *pB )
70 : {
71 46271 : return pA->mnPage < pB->mnPage;
72 : }
73 :
74 : //////////////////////////////// class StgCache
75 :
76 : // The disk cache holds the cached sectors. The sector type differ according
77 : // to their purpose.
78 :
79 1304 : static sal_Int32 lcl_GetPageCount( sal_uLong nFileSize, short nPageSize )
80 : {
81 : // return (nFileSize >= 512) ? (nFileSize - 512) / nPageSize : 0;
82 : // #i61980# reallife: last page may be incomplete, return number of *started* pages
83 1304 : return (nFileSize >= 512) ? (nFileSize - 512 + nPageSize - 1) / nPageSize : 0;
84 : }
85 :
86 2082 : StgCache::StgCache()
87 : : nError( SVSTREAM_OK )
88 : , nPages( 0 )
89 : , nRef( 0 )
90 : , nReplaceIdx( 0 )
91 : , maLRUPages( 8 ) // entries in the LRU lookup
92 : , nPageSize( 512 )
93 : , pStorageStream( NULL )
94 : , pStrm( NULL )
95 : , bMyStream( false )
96 2082 : , bFile( false )
97 : {
98 2082 : }
99 :
100 4164 : StgCache::~StgCache()
101 : {
102 2082 : Clear();
103 2082 : SetStrm( NULL, false );
104 2082 : }
105 :
106 1270 : void StgCache::SetPhysPageSize( short n )
107 : {
108 : OSL_ENSURE( n >= 512, "Unexpecte page size is provided!" );
109 1270 : if ( n >= 512 )
110 : {
111 1270 : nPageSize = n;
112 1270 : sal_uLong nPos = pStrm->Tell();
113 1270 : sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
114 1270 : nPages = lcl_GetPageCount( nFileSize, nPageSize );
115 1270 : pStrm->Seek( nPos );
116 : }
117 1270 : }
118 :
119 : // Create a new cache element
120 :
121 37150 : rtl::Reference< StgPage > StgCache::Create( sal_Int32 nPg )
122 : {
123 37150 : rtl::Reference< StgPage > xElem( StgPage::Create( nPageSize, nPg ) );
124 37150 : maLRUPages[ nReplaceIdx++ % maLRUPages.size() ] = xElem;
125 37150 : return xElem;
126 : }
127 :
128 : // Delete the given element
129 :
130 0 : void StgCache::Erase( const rtl::Reference< StgPage > &xElem )
131 : {
132 : OSL_ENSURE( xElem.is(), "The pointer should not be NULL!" );
133 0 : if ( xElem.is() ) {
134 0 : for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it ) {
135 0 : if ( it->is() && (*it)->GetPage() == xElem->GetPage() ) {
136 0 : it->clear();
137 0 : break;
138 : }
139 : }
140 : }
141 0 : }
142 :
143 : // remove all cache elements without flushing them
144 :
145 2082 : void StgCache::Clear()
146 : {
147 2082 : maDirtyPages.clear();
148 18738 : for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
149 16656 : it->clear();
150 2082 : }
151 :
152 : // Look for a cached page
153 :
154 2819072 : rtl::Reference< StgPage > StgCache::Find( sal_Int32 nPage )
155 : {
156 14140374 : for ( LRUList::iterator it = maLRUPages.begin(); it != maLRUPages.end(); ++it )
157 13785874 : if ( it->is() && (*it)->GetPage() == nPage )
158 2464572 : return *it;
159 354500 : IndexToStgPage::iterator it2 = maDirtyPages.find( nPage );
160 354500 : if ( it2 != maDirtyPages.end() )
161 174450 : return it2->second;
162 180050 : return rtl::Reference< StgPage >();
163 : }
164 :
165 : // Load a page into the cache
166 :
167 2667148 : rtl::Reference< StgPage > StgCache::Get( sal_Int32 nPage, bool bForce )
168 : {
169 2667148 : rtl::Reference< StgPage > p = Find( nPage );
170 2667148 : if( !p.is() )
171 : {
172 29050 : p = Create( nPage );
173 29050 : if( !Read( nPage, p->GetData(), 1 ) && bForce )
174 : {
175 0 : Erase( p );
176 0 : p.clear();
177 0 : SetError( SVSTREAM_READ_ERROR );
178 : }
179 : }
180 2667148 : return p;
181 : }
182 :
183 : // Copy an existing page into a new page. Use this routine
184 : // to duplicate an existing stream or to create new entries.
185 : // The new page is initially marked dirty. No owner is copied.
186 :
187 8318 : rtl::Reference< StgPage > StgCache::Copy( sal_Int32 nNew, sal_Int32 nOld )
188 : {
189 8318 : rtl::Reference< StgPage > p = Find( nNew );
190 8318 : if( !p.is() )
191 8100 : p = Create( nNew );
192 8318 : if( nOld >= 0 )
193 : {
194 : // old page: we must have this data!
195 0 : rtl::Reference< StgPage > q = Get( nOld, true );
196 0 : if( q.is() )
197 : {
198 : OSL_ENSURE( p->GetSize() == q->GetSize(), "Unexpected page size!" );
199 0 : memcpy( p->GetData(), q->GetData(), p->GetSize() );
200 0 : }
201 : }
202 8318 : SetDirty( p );
203 :
204 8318 : return p;
205 : }
206 :
207 : // Historically this wrote pages in a sorted, ascending order;
208 : // continue that tradition.
209 721 : bool StgCache::Commit()
210 : {
211 721 : if ( Good() ) // otherwise Write does nothing
212 : {
213 721 : std::vector< StgPage * > aToWrite;
214 30141 : for ( IndexToStgPage::iterator aIt = maDirtyPages.begin();
215 20094 : aIt != maDirtyPages.end(); ++aIt )
216 9326 : aToWrite.push_back( aIt->second.get() );
217 :
218 721 : std::sort( aToWrite.begin(), aToWrite.end(), StgPage::IsPageGreater );
219 30117 : for ( std::vector< StgPage * >::iterator aWr = aToWrite.begin();
220 20078 : aWr != aToWrite.end(); ++aWr)
221 : {
222 9326 : const rtl::Reference< StgPage > &pPage = *aWr;
223 9326 : if ( !Write( pPage->GetPage(), pPage->GetData(), 1 ) )
224 8 : return false;
225 10031 : }
226 : }
227 :
228 713 : maDirtyPages.clear();
229 :
230 713 : pStrm->Flush();
231 713 : SetError( pStrm->GetError() );
232 :
233 713 : return true;
234 : }
235 :
236 : // Set a stream
237 :
238 4136 : void StgCache::SetStrm( SvStream* p, bool bMy )
239 : {
240 4136 : if( pStorageStream )
241 : {
242 0 : pStorageStream->ReleaseRef();
243 0 : pStorageStream = NULL;
244 : }
245 :
246 4136 : if( bMyStream )
247 34 : delete pStrm;
248 4136 : pStrm = p;
249 4136 : bMyStream = bMy;
250 4136 : }
251 :
252 0 : void StgCache::SetStrm( UCBStorageStream* pStgStream )
253 : {
254 0 : if( pStorageStream )
255 0 : pStorageStream->ReleaseRef();
256 0 : pStorageStream = pStgStream;
257 :
258 0 : if( bMyStream )
259 0 : delete pStrm;
260 :
261 0 : pStrm = NULL;
262 :
263 0 : if ( pStorageStream )
264 : {
265 0 : pStorageStream->AddFirstRef();
266 0 : pStrm = pStorageStream->GetModifySvStream();
267 : }
268 :
269 0 : bMyStream = false;
270 0 : }
271 :
272 252558 : void StgCache::SetDirty( const rtl::Reference< StgPage > &xPage )
273 : {
274 : assert( IsWritable() );
275 252558 : maDirtyPages[ xPage->GetPage() ] = xPage;
276 252558 : }
277 :
278 : // Open/close the disk file
279 :
280 34 : bool StgCache::Open( const OUString& rName, StreamMode nMode )
281 : {
282 : // do not open in exclusive mode!
283 34 : if( nMode & STREAM_SHARE_DENYALL )
284 0 : nMode = ( ( nMode & ~STREAM_SHARE_DENYALL ) | STREAM_SHARE_DENYWRITE );
285 34 : SvFileStream* pFileStrm = new SvFileStream( rName, nMode );
286 : // SvStream "Feature" Write Open auch erfolgreich, wenns nicht klappt
287 34 : bool bAccessDenied = false;
288 34 : if( ( nMode & STREAM_WRITE ) && !pFileStrm->IsWritable() )
289 : {
290 0 : pFileStrm->Close();
291 0 : bAccessDenied = true;
292 : }
293 34 : SetStrm( pFileStrm, true );
294 34 : if( pFileStrm->IsOpen() )
295 : {
296 34 : sal_uLong nFileSize = pStrm->Seek( STREAM_SEEK_TO_END );
297 34 : nPages = lcl_GetPageCount( nFileSize, nPageSize );
298 34 : pStrm->Seek( 0L );
299 : }
300 : else
301 0 : nPages = 0;
302 34 : bFile = true;
303 34 : SetError( bAccessDenied ? ERRCODE_IO_ACCESSDENIED : pStrm->GetError() );
304 34 : return Good();
305 : }
306 :
307 2020 : void StgCache::Close()
308 : {
309 2020 : if( bFile )
310 : {
311 0 : static_cast<SvFileStream*>(pStrm)->Close();
312 0 : SetError( pStrm->GetError() );
313 : }
314 2020 : }
315 :
316 : // low level I/O
317 :
318 161578 : bool StgCache::Read( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
319 : {
320 161578 : if( Good() )
321 : {
322 : /* #i73846# real life: a storage may refer to a page one-behind the
323 : last valid page (see document attached to the issue). In that case
324 : (if nPage==nPages), just do nothing here and let the caller work on
325 : the empty zero-filled buffer. */
326 161564 : if ( nPage > nPages )
327 0 : SetError( SVSTREAM_READ_ERROR );
328 161564 : else if ( nPage < nPages )
329 : {
330 161564 : sal_uInt32 nPos = Page2Pos( nPage );
331 161564 : sal_Int32 nPg2 = ( ( nPage + nPg ) > nPages ) ? nPages - nPage : nPg;
332 161564 : sal_uInt32 nBytes = nPg2 * nPageSize;
333 : // fixed address and size for the header
334 161564 : if( nPage == -1 )
335 : {
336 0 : nPos = 0L, nBytes = 512;
337 0 : nPg2 = nPg;
338 : }
339 161564 : if( pStrm->Tell() != nPos )
340 : {
341 18177 : pStrm->Seek(nPos);
342 : }
343 161564 : pStrm->Read( pBuf, nBytes );
344 161564 : if ( nPg != nPg2 )
345 0 : SetError( SVSTREAM_READ_ERROR );
346 : else
347 161564 : SetError( pStrm->GetError() );
348 : }
349 : }
350 161578 : return Good();
351 : }
352 :
353 19698 : bool StgCache::Write( sal_Int32 nPage, void* pBuf, sal_Int32 nPg )
354 : {
355 19698 : if( Good() )
356 : {
357 19698 : sal_uInt32 nPos = Page2Pos( nPage );
358 19698 : sal_uInt32 nBytes = 0;
359 19698 : if ( SAL_MAX_INT32 / nPg > nPageSize )
360 19698 : nBytes = nPg * nPageSize;
361 :
362 : // fixed address and size for the header
363 : // nPageSize must be >= 512, otherwise the header can not be written here, we check it on import
364 19698 : if( nPage == -1 )
365 0 : nPos = 0L, nBytes = 512;
366 19698 : if( pStrm->Tell() != nPos )
367 : {
368 2097 : pStrm->Seek(nPos);
369 : }
370 19698 : sal_uLong nRes = pStrm->Write( pBuf, nBytes );
371 19698 : if( nRes != nBytes )
372 0 : SetError( SVSTREAM_WRITE_ERROR );
373 : else
374 19698 : SetError( pStrm->GetError() );
375 : }
376 19698 : return Good();
377 : }
378 :
379 : // set the file size in pages
380 :
381 7347 : bool StgCache::SetSize( sal_Int32 n )
382 : {
383 : // Add the file header
384 7347 : sal_Int32 nSize = n * nPageSize + 512;
385 7347 : pStrm->SetStreamSize( nSize );
386 7347 : SetError( pStrm->GetError() );
387 7347 : if( !nError )
388 7347 : nPages = n;
389 7347 : return Good();
390 : }
391 :
392 213807 : void StgCache::SetError( sal_uLong n )
393 : {
394 213807 : if( n && !nError )
395 226 : nError = n;
396 213807 : }
397 :
398 2724 : void StgCache::ResetError()
399 : {
400 2724 : nError = SVSTREAM_OK;
401 2724 : pStrm->ResetError();
402 2724 : }
403 :
404 2230036 : void StgCache::MoveError( StorageBase& r )
405 : {
406 2230036 : if( nError != SVSTREAM_OK )
407 : {
408 226 : r.SetError( nError );
409 226 : ResetError();
410 : }
411 2230036 : }
412 :
413 : // Utility functions
414 :
415 181262 : sal_Int32 StgCache::Page2Pos( sal_Int32 nPage )
416 : {
417 181262 : if( nPage < 0 ) nPage = 0;
418 181262 : return( nPage * nPageSize ) + nPageSize;
419 : }
420 :
421 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|