xref: /aoo42x/main/store/source/storcach.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_store.hxx"
30 
31 #include "storcach.hxx"
32 
33 #include "sal/types.h"
34 #include "rtl/alloc.h"
35 #include "osl/diagnose.h"
36 
37 #include "store/types.h"
38 #include "object.hxx"
39 #include "storbase.hxx"
40 
41 #ifndef INCLUDED_STDDEF_H
42 #include <stddef.h>
43 #define INCLUDED_STDDEF_H
44 #endif
45 
46 using namespace store;
47 
48 /*========================================================================
49  *
50  * PageCache (non-virtual interface) implementation.
51  *
52  *======================================================================*/
53 
54 storeError PageCache::lookupPageAt (PageHolder & rxPage, sal_uInt32 nOffset)
55 {
56     OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::PageCache::lookupPageAt(): invalid Offset");
57     if (nOffset == STORE_PAGE_NULL)
58         return store_E_CantSeek;
59 
60     return lookupPageAt_Impl (rxPage, nOffset);
61 }
62 
63 storeError PageCache::insertPageAt (PageHolder const & rxPage, sal_uInt32 nOffset)
64 {
65     // [SECURITY:ValInput]
66     PageData const * pagedata = rxPage.get();
67     OSL_PRECOND(!(pagedata == 0), "store::PageCache::insertPageAt(): invalid Page");
68     if (pagedata == 0)
69         return store_E_InvalidParameter;
70 
71     sal_uInt32 const offset = pagedata->location();
72     OSL_PRECOND(!(nOffset != offset), "store::PageCache::insertPageAt(): inconsistent Offset");
73     if (nOffset != offset)
74         return store_E_InvalidParameter;
75 
76     OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::PageCache::insertPageAt(): invalid Offset");
77     if (nOffset == STORE_PAGE_NULL)
78         return store_E_CantSeek;
79 
80     return insertPageAt_Impl (rxPage, nOffset);
81 }
82 
83 storeError PageCache::updatePageAt (PageHolder const & rxPage, sal_uInt32 nOffset)
84 {
85     // [SECURITY:ValInput]
86     PageData const * pagedata = rxPage.get();
87     OSL_PRECOND(!(pagedata == 0), "store::PageCache::updatePageAt(): invalid Page");
88     if (pagedata == 0)
89         return store_E_InvalidParameter;
90 
91     sal_uInt32 const offset = pagedata->location();
92     OSL_PRECOND(!(nOffset != offset), "store::PageCache::updatePageAt(): inconsistent Offset");
93     if (nOffset != offset)
94         return store_E_InvalidParameter;
95 
96     OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::PageCache::updatePageAt(): invalid Offset");
97     if (nOffset == STORE_PAGE_NULL)
98         return store_E_CantSeek;
99 
100     return updatePageAt_Impl (rxPage, nOffset);
101 }
102 
103 storeError PageCache::removePageAt (sal_uInt32 nOffset)
104 {
105     OSL_PRECOND(!(nOffset == STORE_PAGE_NULL), "store::PageCache::removePageAt(): invalid Offset");
106     if (nOffset == STORE_PAGE_NULL)
107         return store_E_CantSeek;
108 
109     return removePageAt_Impl (nOffset);
110 }
111 
112 /*========================================================================
113  *
114  * Entry.
115  *
116  *======================================================================*/
117 namespace
118 {
119 
120 struct Entry
121 {
122     /** Representation.
123      */
124     PageHolder m_xPage;
125     sal_uInt32 m_nOffset;
126     Entry *    m_pNext;
127 
128     /** Allocation.
129      */
130     static void * operator new (size_t, void * p) { return p; }
131     static void   operator delete (void *, void *) {}
132 
133     /** Construction.
134      */
135     explicit Entry (PageHolder const & rxPage = PageHolder(), sal_uInt32 nOffset = STORE_PAGE_NULL)
136         : m_xPage(rxPage), m_nOffset(nOffset), m_pNext(0)
137     {}
138 
139     /** Destruction.
140      */
141     ~Entry() {}
142 };
143 
144 } // namespace
145 
146 /*========================================================================
147  *
148  * EntryCache interface.
149  *
150  *======================================================================*/
151 namespace
152 {
153 
154 class EntryCache
155 {
156     rtl_cache_type * m_entry_cache;
157 
158 public:
159     static EntryCache & get();
160 
161     Entry * create (PageHolder const & rxPage, sal_uInt32 nOffset);
162 
163     void destroy (Entry * entry);
164 
165 protected:
166     EntryCache();
167     ~EntryCache();
168 };
169 
170 } // namespace
171 
172 /*========================================================================
173  *
174  * EntryCache implementation.
175  *
176  *======================================================================*/
177 
178 EntryCache & EntryCache::get()
179 {
180     static EntryCache g_entry_cache;
181     return g_entry_cache;
182 }
183 
184 EntryCache::EntryCache()
185 {
186     m_entry_cache = rtl_cache_create (
187         "store_cache_entry_cache",
188         sizeof(Entry),
189         0, // objalign
190         0, // constructor
191         0, // destructor
192         0, // reclaim
193         0, // userarg
194         0, // default source
195         0  // flags
196         );
197 }
198 
199 EntryCache::~EntryCache()
200 {
201     rtl_cache_destroy (m_entry_cache), m_entry_cache = 0;
202 }
203 
204 Entry * EntryCache::create (PageHolder const & rxPage, sal_uInt32 nOffset)
205 {
206     void * pAddr = rtl_cache_alloc (m_entry_cache);
207     if (pAddr != 0)
208     {
209         // construct.
210         return new(pAddr) Entry (rxPage, nOffset);
211     }
212     return 0;
213 }
214 
215 void EntryCache::destroy (Entry * entry)
216 {
217     if (entry != 0)
218     {
219         // destruct.
220         entry->~Entry();
221 
222         // return to cache.
223         rtl_cache_free (m_entry_cache, entry);
224     }
225 }
226 
227 /*========================================================================
228  *
229  * highbit():= log2() + 1 (complexity O(1))
230  *
231  *======================================================================*/
232 static int highbit(sal_Size n)
233 {
234     register int k = 1;
235 
236     if (n == 0)
237         return (0);
238 #if SAL_TYPES_SIZEOFLONG == 8
239     if (n & 0xffffffff00000000ul)
240         k |= 32, n >>= 32;
241 #endif
242     if (n & 0xffff0000)
243         k |= 16, n >>= 16;
244     if (n & 0xff00)
245         k |= 8, n >>= 8;
246     if (n & 0xf0)
247         k |= 4, n >>= 4;
248     if (n & 0x0c)
249         k |= 2, n >>= 2;
250     if (n & 0x02)
251         k++;
252 
253     return (k);
254 }
255 
256 /*========================================================================
257  *
258  * PageCache_Impl implementation.
259  *
260  *======================================================================*/
261 namespace store
262 {
263 
264 class PageCache_Impl :
265     public store::OStoreObject,
266     public store::PageCache
267 {
268     /** Representation.
269      */
270     static size_t const theTableSize = 32;
271     STORE_STATIC_ASSERT(STORE_IMPL_ISP2(theTableSize));
272 
273     Entry **     m_hash_table;
274     Entry *      m_hash_table_0[theTableSize];
275     size_t       m_hash_size;
276     size_t       m_hash_shift;
277     size_t const m_page_shift;
278 
279     size_t       m_hash_entries; // total number of entries in table.
280     size_t       m_nHit;
281     size_t       m_nMissed;
282 
283     inline int hash_Impl(sal_uInt32 a, size_t s, size_t q, size_t m)
284     {
285         return ((((a) + ((a) >> (s)) + ((a) >> ((s) << 1))) >> (q)) & (m));
286     }
287     inline int hash_index_Impl (sal_uInt32 nOffset)
288     {
289         return hash_Impl(nOffset, m_hash_shift, m_page_shift, m_hash_size - 1);
290     }
291 
292     Entry * lookup_Impl (Entry * entry, sal_uInt32 nOffset);
293     void rescale_Impl (sal_Size new_size);
294 
295     /** PageCache Implementation.
296      */
297     virtual storeError lookupPageAt_Impl (
298         PageHolder & rxPage,
299         sal_uInt32   nOffset);
300 
301     virtual storeError insertPageAt_Impl (
302         PageHolder const & rxPage,
303         sal_uInt32         nOffset);
304 
305     virtual storeError updatePageAt_Impl (
306         PageHolder const & rxPage,
307         sal_uInt32         nOffset);
308 
309     virtual storeError removePageAt_Impl (
310         sal_uInt32 nOffset);
311 
312     /** Not implemented.
313      */
314     PageCache_Impl (PageCache_Impl const &);
315     PageCache_Impl & operator= (PageCache_Impl const &);
316 
317 public:
318     /** Construction.
319      */
320     explicit PageCache_Impl (sal_uInt16 nPageSize);
321 
322     /** Delegate multiple inherited IReference.
323      */
324     virtual oslInterlockedCount SAL_CALL acquire();
325     virtual oslInterlockedCount SAL_CALL release();
326 
327 protected:
328     /** Destruction.
329      */
330     virtual ~PageCache_Impl (void);
331 };
332 
333 } // namespace store
334 
335 PageCache_Impl::PageCache_Impl (sal_uInt16 nPageSize)
336     : m_hash_table   (m_hash_table_0),
337       m_hash_size    (theTableSize),
338       m_hash_shift   (highbit(m_hash_size) - 1),
339       m_page_shift   (highbit(nPageSize) - 1),
340       m_hash_entries (0),
341       m_nHit         (0),
342       m_nMissed      (0)
343 {
344     static size_t const theSize = sizeof(m_hash_table_0) / sizeof(m_hash_table_0[0]);
345     STORE_STATIC_ASSERT(theSize == theTableSize);
346     memset(m_hash_table_0, 0, sizeof(m_hash_table_0));
347 }
348 
349 PageCache_Impl::~PageCache_Impl()
350 {
351     double s_x = 0.0, s_xx = 0.0;
352     sal_Size i, n = m_hash_size;
353     for (i = 0; i < n; i++)
354     {
355         int x = 0;
356         Entry * entry = m_hash_table[i];
357         while (entry != 0)
358         {
359             m_hash_table[i] = entry->m_pNext, entry->m_pNext = 0;
360             EntryCache::get().destroy (entry);
361             entry = m_hash_table[i];
362             x += 1;
363         }
364         s_x  += double(x);
365         s_xx += double(x) * double(x);
366     }
367     double ave = s_x / double(n);
368     OSL_TRACE("ave hash chain length: %g", ave);
369     (void) ave;
370 
371     if (m_hash_table != m_hash_table_0)
372     {
373         rtl_freeMemory (m_hash_table);
374         m_hash_table = m_hash_table_0;
375         m_hash_size  = theTableSize;
376         m_hash_shift = highbit(m_hash_size) - 1;
377     }
378     OSL_TRACE("Hits: %u, Misses: %u", m_nHit, m_nMissed);
379 }
380 
381 oslInterlockedCount PageCache_Impl::acquire()
382 {
383     return OStoreObject::acquire();
384 }
385 
386 oslInterlockedCount PageCache_Impl::release()
387 {
388     return OStoreObject::release();
389 }
390 
391 void PageCache_Impl::rescale_Impl (sal_Size new_size)
392 {
393     sal_Size new_bytes = new_size * sizeof(Entry*);
394     Entry ** new_table = (Entry**)(rtl_allocateMemory(new_bytes));
395 
396     if (new_table != 0)
397     {
398         Entry ** old_table = m_hash_table;
399         sal_Size old_size  = m_hash_size;
400 
401         OSL_TRACE("ave chain length: %u, total entries: %u [old_size: %u, new_size: %u]",
402                   m_hash_entries >> m_hash_shift, m_hash_entries, old_size, new_size);
403 
404         memset (new_table, 0, new_bytes);
405 
406         m_hash_table = new_table;
407         m_hash_size  = new_size;
408         m_hash_shift = highbit(m_hash_size) - 1;
409 
410         sal_Size i;
411         for (i = 0; i < old_size; i++)
412         {
413             Entry * curr = old_table[i];
414             while (curr != 0)
415             {
416                 Entry * next = curr->m_pNext;
417                 int index = hash_index_Impl(curr->m_nOffset);
418                 curr->m_pNext = m_hash_table[index], m_hash_table[index] = curr;
419                 curr = next;
420             }
421             old_table[i] = 0;
422         }
423         if (old_table != m_hash_table_0)
424         {
425             //
426             rtl_freeMemory (old_table);
427         }
428     }
429 }
430 
431 Entry * PageCache_Impl::lookup_Impl (Entry * entry, sal_uInt32 nOffset)
432 {
433     register int lookups = 0;
434     while (entry != 0)
435     {
436         if (entry->m_nOffset == nOffset)
437             break;
438 
439         lookups += 1;
440         entry = entry->m_pNext;
441     }
442     if (lookups > 2)
443     {
444         sal_Size new_size = m_hash_size, ave = m_hash_entries >> m_hash_shift;
445         for (; ave > 4; new_size *= 2, ave /= 2)
446             continue;
447         if (new_size != m_hash_size)
448             rescale_Impl (new_size);
449     }
450     return entry;
451 }
452 
453 storeError PageCache_Impl::lookupPageAt_Impl (
454     PageHolder & rxPage,
455     sal_uInt32   nOffset)
456 {
457     int index = hash_index_Impl(nOffset);
458     Entry const * entry = lookup_Impl (m_hash_table[index], nOffset);
459     if (entry != 0)
460     {
461         // Existing entry.
462         rxPage = entry->m_xPage;
463 
464         // Update stats and leave.
465         m_nHit += 1;
466         return store_E_None;
467     }
468 
469     // Cache miss. Update stats and leave.
470     m_nMissed += 1;
471     return store_E_NotExists;
472 }
473 
474 storeError PageCache_Impl::insertPageAt_Impl (
475     PageHolder const & rxPage,
476     sal_uInt32         nOffset)
477 {
478     Entry * entry = EntryCache::get().create (rxPage, nOffset);
479     if (entry != 0)
480     {
481         // Insert new entry.
482         int index = hash_index_Impl(nOffset);
483         entry->m_pNext = m_hash_table[index], m_hash_table[index] = entry;
484 
485         // Update stats and leave.
486         m_hash_entries += 1;
487         return store_E_None;
488     }
489     return store_E_OutOfMemory;
490 }
491 
492 storeError PageCache_Impl::updatePageAt_Impl (
493     PageHolder const & rxPage,
494     sal_uInt32         nOffset)
495 {
496     int index = hash_index_Impl(nOffset);
497     Entry *  entry  = lookup_Impl (m_hash_table[index], nOffset);
498     if (entry != 0)
499     {
500         // Update existing entry.
501         entry->m_xPage = rxPage;
502 
503         // Update stats and leave. // m_nUpdHit += 1;
504         return store_E_None;
505     }
506     return insertPageAt_Impl (rxPage, nOffset);
507 }
508 
509 storeError PageCache_Impl::removePageAt_Impl (
510     sal_uInt32 nOffset)
511 {
512     Entry ** ppEntry = &(m_hash_table[hash_index_Impl(nOffset)]);
513     while (*ppEntry != 0)
514     {
515         if ((*ppEntry)->m_nOffset == nOffset)
516         {
517             // Existing entry.
518             Entry * entry = (*ppEntry);
519 
520             // Dequeue and destroy entry.
521             (*ppEntry) = entry->m_pNext, entry->m_pNext = 0;
522             EntryCache::get().destroy (entry);
523 
524             // Update stats and leave.
525             m_hash_entries -= 1;
526             return store_E_None;
527         }
528         ppEntry = &((*ppEntry)->m_pNext);
529     }
530     return store_E_NotExists;
531 }
532 
533 /*========================================================================
534  *
535  * Old OStorePageCache implementation.
536  *
537  * (two-way association (sorted address array, LRU chain)).
538  * (external OStorePageData representation).
539  *
540  *======================================================================*/
541 
542 /*========================================================================
543  *
544  * PageCache factory implementation.
545  *
546  *======================================================================*/
547 namespace store {
548 
549 storeError
550 PageCache_createInstance (
551     rtl::Reference< store::PageCache > & rxCache,
552     sal_uInt16                           nPageSize)
553 {
554     rxCache = new PageCache_Impl (nPageSize);
555     if (!rxCache.is())
556         return store_E_OutOfMemory;
557 
558     return store_E_None;
559 }
560 
561 } // namespace store
562