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 .
20 #include "sal/config.h"
22 #include "boost/static_assert.hpp"
24 #include "storcach.hxx"
26 #include "sal/types.h"
27 #include "sal/macros.h"
28 #include "rtl/alloc.h"
29 #include "osl/diagnose.h"
31 #include "store/types.h"
33 #include "storbase.hxx"
37 using namespace store
;
39 /*========================================================================
41 * PageCache (non-virtual interface) implementation.
43 *======================================================================*/
45 storeError
PageCache::lookupPageAt (PageHolder
& rxPage
, sal_uInt32 nOffset
)
47 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::lookupPageAt(): invalid Offset");
48 if (nOffset
== STORE_PAGE_NULL
)
49 return store_E_CantSeek
;
51 return lookupPageAt_Impl (rxPage
, nOffset
);
54 storeError
PageCache::insertPageAt (PageHolder
const & rxPage
, sal_uInt32 nOffset
)
56 // [SECURITY:ValInput]
57 PageData
const * pagedata
= rxPage
.get();
58 OSL_PRECOND(!(pagedata
== 0), "store::PageCache::insertPageAt(): invalid Page");
60 return store_E_InvalidParameter
;
62 sal_uInt32
const offset
= pagedata
->location();
63 OSL_PRECOND(!(nOffset
!= offset
), "store::PageCache::insertPageAt(): inconsistent Offset");
64 if (nOffset
!= offset
)
65 return store_E_InvalidParameter
;
67 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::insertPageAt(): invalid Offset");
68 if (nOffset
== STORE_PAGE_NULL
)
69 return store_E_CantSeek
;
71 return insertPageAt_Impl (rxPage
, nOffset
);
74 storeError
PageCache::updatePageAt (PageHolder
const & rxPage
, sal_uInt32 nOffset
)
76 // [SECURITY:ValInput]
77 PageData
const * pagedata
= rxPage
.get();
78 OSL_PRECOND(!(pagedata
== 0), "store::PageCache::updatePageAt(): invalid Page");
80 return store_E_InvalidParameter
;
82 sal_uInt32
const offset
= pagedata
->location();
83 OSL_PRECOND(!(nOffset
!= offset
), "store::PageCache::updatePageAt(): inconsistent Offset");
84 if (nOffset
!= offset
)
85 return store_E_InvalidParameter
;
87 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::updatePageAt(): invalid Offset");
88 if (nOffset
== STORE_PAGE_NULL
)
89 return store_E_CantSeek
;
91 return updatePageAt_Impl (rxPage
, nOffset
);
94 storeError
PageCache::removePageAt (sal_uInt32 nOffset
)
96 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::removePageAt(): invalid Offset");
97 if (nOffset
== STORE_PAGE_NULL
)
98 return store_E_CantSeek
;
100 return removePageAt_Impl (nOffset
);
103 /*========================================================================
107 *======================================================================*/
116 sal_uInt32 m_nOffset
;
121 static void * operator new (size_t, void * p
) { return p
; }
122 static void operator delete (void *, void *) {}
126 explicit Entry (PageHolder
const & rxPage
= PageHolder(), sal_uInt32 nOffset
= STORE_PAGE_NULL
)
127 : m_xPage(rxPage
), m_nOffset(nOffset
), m_pNext(0)
137 /*========================================================================
139 * EntryCache interface.
141 *======================================================================*/
147 rtl_cache_type
* m_entry_cache
;
150 static EntryCache
& get();
152 Entry
* create (PageHolder
const & rxPage
, sal_uInt32 nOffset
);
154 void destroy (Entry
* entry
);
163 /*========================================================================
165 * EntryCache implementation.
167 *======================================================================*/
169 EntryCache
& EntryCache::get()
171 static EntryCache g_entry_cache
;
172 return g_entry_cache
;
175 EntryCache::EntryCache()
177 m_entry_cache
= rtl_cache_create (
178 "store_cache_entry_cache",
190 EntryCache::~EntryCache()
192 rtl_cache_destroy (m_entry_cache
), m_entry_cache
= 0;
195 Entry
* EntryCache::create (PageHolder
const & rxPage
, sal_uInt32 nOffset
)
197 void * pAddr
= rtl_cache_alloc (m_entry_cache
);
201 return new(pAddr
) Entry (rxPage
, nOffset
);
206 void EntryCache::destroy (Entry
* entry
)
214 rtl_cache_free (m_entry_cache
, entry
);
218 /*========================================================================
220 * highbit():= log2() + 1 (complexity O(1))
222 *======================================================================*/
223 static int highbit(sal_Size n
)
229 #if SAL_TYPES_SIZEOFLONG == 8
230 if (n
& 0xffffffff00000000ul
)
247 /*========================================================================
249 * PageCache_Impl implementation.
251 *======================================================================*/
255 class PageCache_Impl
:
256 public store::OStoreObject
,
257 public store::PageCache
261 static size_t const theTableSize
= 32;
262 BOOST_STATIC_ASSERT(STORE_IMPL_ISP2(theTableSize
));
264 Entry
** m_hash_table
;
265 Entry
* m_hash_table_0
[theTableSize
];
268 size_t const m_page_shift
;
270 size_t m_hash_entries
; // total number of entries in table.
274 inline int hash_Impl(sal_uInt32 a
, size_t s
, size_t q
, size_t m
)
276 return ((((a
) + ((a
) >> (s
)) + ((a
) >> ((s
) << 1))) >> (q
)) & (m
));
278 inline int hash_index_Impl (sal_uInt32 nOffset
)
280 return hash_Impl(nOffset
, m_hash_shift
, m_page_shift
, m_hash_size
- 1);
283 Entry
* lookup_Impl (Entry
* entry
, sal_uInt32 nOffset
);
284 void rescale_Impl (sal_Size new_size
);
286 /** PageCache Implementation.
288 virtual storeError
lookupPageAt_Impl (
292 virtual storeError
insertPageAt_Impl (
293 PageHolder
const & rxPage
,
296 virtual storeError
updatePageAt_Impl (
297 PageHolder
const & rxPage
,
300 virtual storeError
removePageAt_Impl (
305 PageCache_Impl (PageCache_Impl
const &);
306 PageCache_Impl
& operator= (PageCache_Impl
const &);
311 explicit PageCache_Impl (sal_uInt16 nPageSize
);
313 /** Delegate multiple inherited IReference.
315 virtual oslInterlockedCount SAL_CALL
acquire();
316 virtual oslInterlockedCount SAL_CALL
release();
321 virtual ~PageCache_Impl (void);
326 PageCache_Impl::PageCache_Impl (sal_uInt16 nPageSize
)
327 : m_hash_table (m_hash_table_0
),
328 m_hash_size (theTableSize
),
329 m_hash_shift (highbit(m_hash_size
) - 1),
330 m_page_shift (highbit(nPageSize
) - 1),
335 static size_t const theSize
= SAL_N_ELEMENTS(m_hash_table_0
);
336 BOOST_STATIC_ASSERT(theSize
== theTableSize
);
337 memset(m_hash_table_0
, 0, sizeof(m_hash_table_0
));
340 PageCache_Impl::~PageCache_Impl()
343 sal_Size i
, n
= m_hash_size
;
344 for (i
= 0; i
< n
; i
++)
347 Entry
* entry
= m_hash_table
[i
];
350 m_hash_table
[i
] = entry
->m_pNext
, entry
->m_pNext
= 0;
351 EntryCache::get().destroy (entry
);
352 entry
= m_hash_table
[i
];
357 double ave
= s_x
/ double(n
);
358 OSL_TRACE("ave hash chain length: %g", ave
);
361 if (m_hash_table
!= m_hash_table_0
)
363 rtl_freeMemory (m_hash_table
);
364 m_hash_table
= m_hash_table_0
;
365 m_hash_size
= theTableSize
;
366 m_hash_shift
= highbit(m_hash_size
) - 1;
368 OSL_TRACE("Hits: %u, Misses: %u", m_nHit
, m_nMissed
);
371 oslInterlockedCount
PageCache_Impl::acquire()
373 return OStoreObject::acquire();
376 oslInterlockedCount
PageCache_Impl::release()
378 return OStoreObject::release();
381 void PageCache_Impl::rescale_Impl (sal_Size new_size
)
383 sal_Size new_bytes
= new_size
* sizeof(Entry
*);
384 Entry
** new_table
= (Entry
**)(rtl_allocateMemory(new_bytes
));
388 Entry
** old_table
= m_hash_table
;
389 sal_Size old_size
= m_hash_size
;
391 OSL_TRACE("ave chain length: %u, total entries: %u [old_size: %u, new_size: %u]",
392 m_hash_entries
>> m_hash_shift
, m_hash_entries
, old_size
, new_size
);
394 memset (new_table
, 0, new_bytes
);
396 m_hash_table
= new_table
;
397 m_hash_size
= new_size
;
398 m_hash_shift
= highbit(m_hash_size
) - 1;
401 for (i
= 0; i
< old_size
; i
++)
403 Entry
* curr
= old_table
[i
];
406 Entry
* next
= curr
->m_pNext
;
407 int index
= hash_index_Impl(curr
->m_nOffset
);
408 curr
->m_pNext
= m_hash_table
[index
], m_hash_table
[index
] = curr
;
413 if (old_table
!= m_hash_table_0
)
416 rtl_freeMemory (old_table
);
421 Entry
* PageCache_Impl::lookup_Impl (Entry
* entry
, sal_uInt32 nOffset
)
423 register int lookups
= 0;
426 if (entry
->m_nOffset
== nOffset
)
430 entry
= entry
->m_pNext
;
434 sal_Size new_size
= m_hash_size
, ave
= m_hash_entries
>> m_hash_shift
;
435 for (; ave
> 4; new_size
*= 2, ave
/= 2)
437 if (new_size
!= m_hash_size
)
438 rescale_Impl (new_size
);
443 storeError
PageCache_Impl::lookupPageAt_Impl (
447 int index
= hash_index_Impl(nOffset
);
448 Entry
const * entry
= lookup_Impl (m_hash_table
[index
], nOffset
);
452 rxPage
= entry
->m_xPage
;
454 // Update stats and leave.
459 // Cache miss. Update stats and leave.
461 return store_E_NotExists
;
464 storeError
PageCache_Impl::insertPageAt_Impl (
465 PageHolder
const & rxPage
,
468 Entry
* entry
= EntryCache::get().create (rxPage
, nOffset
);
472 int index
= hash_index_Impl(nOffset
);
473 entry
->m_pNext
= m_hash_table
[index
], m_hash_table
[index
] = entry
;
475 // Update stats and leave.
479 return store_E_OutOfMemory
;
482 storeError
PageCache_Impl::updatePageAt_Impl (
483 PageHolder
const & rxPage
,
486 int index
= hash_index_Impl(nOffset
);
487 Entry
* entry
= lookup_Impl (m_hash_table
[index
], nOffset
);
490 // Update existing entry.
491 entry
->m_xPage
= rxPage
;
493 // Update stats and leave. // m_nUpdHit += 1;
496 return insertPageAt_Impl (rxPage
, nOffset
);
499 storeError
PageCache_Impl::removePageAt_Impl (
502 Entry
** ppEntry
= &(m_hash_table
[hash_index_Impl(nOffset
)]);
503 while (*ppEntry
!= 0)
505 if ((*ppEntry
)->m_nOffset
== nOffset
)
508 Entry
* entry
= (*ppEntry
);
510 // Dequeue and destroy entry.
511 (*ppEntry
) = entry
->m_pNext
, entry
->m_pNext
= 0;
512 EntryCache::get().destroy (entry
);
514 // Update stats and leave.
518 ppEntry
= &((*ppEntry
)->m_pNext
);
520 return store_E_NotExists
;
523 /*========================================================================
525 * Old OStorePageCache implementation.
527 * (two-way association (sorted address array, LRU chain)).
528 * (external OStorePageData representation).
530 *======================================================================*/
532 /*========================================================================
534 * PageCache factory implementation.
536 *======================================================================*/
540 PageCache_createInstance (
541 rtl::Reference
< store::PageCache
> & rxCache
,
542 sal_uInt16 nPageSize
)
544 rxCache
= new PageCache_Impl (nPageSize
);
546 return store_E_OutOfMemory
;
553 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */