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/noncopyable.hpp"
24 #include "storcach.hxx"
26 #include "sal/log.hxx"
27 #include "sal/types.h"
28 #include "sal/macros.h"
29 #include "rtl/alloc.h"
30 #include "osl/diagnose.h"
32 #include "store/types.h"
34 #include "storbase.hxx"
38 using namespace store
;
40 // PageCache (non-virtual interface) implementation.
41 storeError
PageCache::lookupPageAt (PageHolder
& rxPage
, sal_uInt32 nOffset
)
43 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::lookupPageAt(): invalid Offset");
44 if (nOffset
== STORE_PAGE_NULL
)
45 return store_E_CantSeek
;
47 return lookupPageAt_Impl (rxPage
, nOffset
);
50 storeError
PageCache::insertPageAt (PageHolder
const & rxPage
, sal_uInt32 nOffset
)
52 // [SECURITY:ValInput]
53 PageData
const * pagedata
= rxPage
.get();
54 OSL_PRECOND(!(pagedata
== 0), "store::PageCache::insertPageAt(): invalid Page");
56 return store_E_InvalidParameter
;
58 sal_uInt32
const offset
= pagedata
->location();
59 OSL_PRECOND(!(nOffset
!= offset
), "store::PageCache::insertPageAt(): inconsistent Offset");
60 if (nOffset
!= offset
)
61 return store_E_InvalidParameter
;
63 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::insertPageAt(): invalid Offset");
64 if (nOffset
== STORE_PAGE_NULL
)
65 return store_E_CantSeek
;
67 return insertPageAt_Impl (rxPage
, nOffset
);
70 storeError
PageCache::updatePageAt (PageHolder
const & rxPage
, sal_uInt32 nOffset
)
72 // [SECURITY:ValInput]
73 PageData
const * pagedata
= rxPage
.get();
74 OSL_PRECOND(!(pagedata
== 0), "store::PageCache::updatePageAt(): invalid Page");
76 return store_E_InvalidParameter
;
78 sal_uInt32
const offset
= pagedata
->location();
79 OSL_PRECOND(!(nOffset
!= offset
), "store::PageCache::updatePageAt(): inconsistent Offset");
80 if (nOffset
!= offset
)
81 return store_E_InvalidParameter
;
83 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::updatePageAt(): invalid Offset");
84 if (nOffset
== STORE_PAGE_NULL
)
85 return store_E_CantSeek
;
87 return updatePageAt_Impl (rxPage
, nOffset
);
90 storeError
PageCache::removePageAt (sal_uInt32 nOffset
)
92 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::removePageAt(): invalid Offset");
93 if (nOffset
== STORE_PAGE_NULL
)
94 return store_E_CantSeek
;
96 return removePageAt_Impl (nOffset
);
107 sal_uInt32 m_nOffset
;
111 static void * operator new (size_t, void * p
) { return p
; }
112 static void operator delete (void *, void *) {}
115 explicit Entry (PageHolder
const & rxPage
= PageHolder(), sal_uInt32 nOffset
= STORE_PAGE_NULL
)
116 : m_xPage(rxPage
), m_nOffset(nOffset
), m_pNext(0)
125 // EntryCache interface
131 rtl_cache_type
* m_entry_cache
;
134 static EntryCache
& get();
136 Entry
* create (PageHolder
const & rxPage
, sal_uInt32 nOffset
);
138 void destroy (Entry
* entry
);
147 // EntryCache implementation
148 EntryCache
& EntryCache::get()
150 static EntryCache g_entry_cache
;
151 return g_entry_cache
;
154 EntryCache::EntryCache()
156 m_entry_cache
= rtl_cache_create (
157 "store_cache_entry_cache",
169 EntryCache::~EntryCache()
171 rtl_cache_destroy (m_entry_cache
), m_entry_cache
= 0;
174 Entry
* EntryCache::create (PageHolder
const & rxPage
, sal_uInt32 nOffset
)
176 void * pAddr
= rtl_cache_alloc (m_entry_cache
);
180 return new(pAddr
) Entry (rxPage
, nOffset
);
185 void EntryCache::destroy (Entry
* entry
)
193 rtl_cache_free (m_entry_cache
, entry
);
197 // highbit():= log2() + 1 (complexity O(1))
198 static int highbit(sal_Size n
)
204 #if SAL_TYPES_SIZEOFLONG == 8
205 if (n
& 0xffffffff00000000ul
)
222 //PageCache_Impl implementation
226 class PageCache_Impl
:
227 public store::OStoreObject
,
228 public store::PageCache
,
229 private boost::noncopyable
232 static size_t const theTableSize
= 32;
233 static_assert(STORE_IMPL_ISP2(theTableSize
), "must be the case");
235 Entry
** m_hash_table
;
236 Entry
* m_hash_table_0
[theTableSize
];
239 size_t const m_page_shift
;
241 size_t m_hash_entries
; // total number of entries in table.
245 static inline int hash_Impl(sal_uInt32 a
, size_t s
, size_t q
, size_t m
)
247 return ((((a
) + ((a
) >> (s
)) + ((a
) >> ((s
) << 1))) >> (q
)) & (m
));
249 inline int hash_index_Impl (sal_uInt32 nOffset
)
251 return hash_Impl(nOffset
, m_hash_shift
, m_page_shift
, m_hash_size
- 1);
254 Entry
* lookup_Impl (Entry
* entry
, sal_uInt32 nOffset
);
255 void rescale_Impl (sal_Size new_size
);
257 // PageCache Implementation
258 virtual storeError
lookupPageAt_Impl (
260 sal_uInt32 nOffset
) SAL_OVERRIDE
;
262 virtual storeError
insertPageAt_Impl (
263 PageHolder
const & rxPage
,
264 sal_uInt32 nOffset
) SAL_OVERRIDE
;
266 virtual storeError
updatePageAt_Impl (
267 PageHolder
const & rxPage
,
268 sal_uInt32 nOffset
) SAL_OVERRIDE
;
270 virtual storeError
removePageAt_Impl (
271 sal_uInt32 nOffset
) SAL_OVERRIDE
;
275 explicit PageCache_Impl (sal_uInt16 nPageSize
);
279 virtual ~PageCache_Impl();
284 PageCache_Impl::PageCache_Impl (sal_uInt16 nPageSize
)
285 : m_hash_table (m_hash_table_0
),
286 m_hash_size (theTableSize
),
287 m_hash_shift (highbit(m_hash_size
) - 1),
288 m_page_shift (highbit(nPageSize
) - 1),
293 static size_t const theSize
= SAL_N_ELEMENTS(m_hash_table_0
);
294 static_assert(theSize
== theTableSize
, "must be equal");
295 memset(m_hash_table_0
, 0, sizeof(m_hash_table_0
));
298 PageCache_Impl::~PageCache_Impl()
301 sal_Size i
, n
= m_hash_size
;
302 for (i
= 0; i
< n
; i
++)
305 Entry
* entry
= m_hash_table
[i
];
308 m_hash_table
[i
] = entry
->m_pNext
, entry
->m_pNext
= 0;
309 EntryCache::get().destroy (entry
);
310 entry
= m_hash_table
[i
];
315 double ave
= s_x
/ double(n
);
316 OSL_TRACE("ave hash chain length: %g", ave
);
319 if (m_hash_table
!= m_hash_table_0
)
321 rtl_freeMemory (m_hash_table
);
322 m_hash_table
= m_hash_table_0
;
323 m_hash_size
= theTableSize
;
324 m_hash_shift
= highbit(m_hash_size
) - 1;
326 OSL_TRACE("Hits: %zu, Misses: %zu", m_nHit
, m_nMissed
);
329 void PageCache_Impl::rescale_Impl (sal_Size new_size
)
331 sal_Size new_bytes
= new_size
* sizeof(Entry
*);
332 Entry
** new_table
= static_cast<Entry
**>(rtl_allocateMemory(new_bytes
));
336 Entry
** old_table
= m_hash_table
;
337 sal_Size old_size
= m_hash_size
;
341 "ave chain length: " << (m_hash_entries
>> m_hash_shift
)
342 << ", total entries: " << m_hash_entries
<< " [old_size: "
343 << old_size
<< " new_size: " << new_size
<< "]");
345 memset (new_table
, 0, new_bytes
);
347 m_hash_table
= new_table
;
348 m_hash_size
= new_size
;
349 m_hash_shift
= highbit(m_hash_size
) - 1;
352 for (i
= 0; i
< old_size
; i
++)
354 Entry
* curr
= old_table
[i
];
357 Entry
* next
= curr
->m_pNext
;
358 int index
= hash_index_Impl(curr
->m_nOffset
);
359 curr
->m_pNext
= m_hash_table
[index
], m_hash_table
[index
] = curr
;
364 if (old_table
!= m_hash_table_0
)
367 rtl_freeMemory (old_table
);
372 Entry
* PageCache_Impl::lookup_Impl (Entry
* entry
, sal_uInt32 nOffset
)
377 if (entry
->m_nOffset
== nOffset
)
381 entry
= entry
->m_pNext
;
385 sal_Size new_size
= m_hash_size
, ave
= m_hash_entries
>> m_hash_shift
;
386 for (; ave
> 4; new_size
*= 2, ave
/= 2)
388 if (new_size
!= m_hash_size
)
389 rescale_Impl (new_size
);
394 storeError
PageCache_Impl::lookupPageAt_Impl (
398 int index
= hash_index_Impl(nOffset
);
399 Entry
const * entry
= lookup_Impl (m_hash_table
[index
], nOffset
);
403 rxPage
= entry
->m_xPage
;
405 // Update stats and leave.
410 // Cache miss. Update stats and leave.
412 return store_E_NotExists
;
415 storeError
PageCache_Impl::insertPageAt_Impl (
416 PageHolder
const & rxPage
,
419 Entry
* entry
= EntryCache::get().create (rxPage
, nOffset
);
423 int index
= hash_index_Impl(nOffset
);
424 entry
->m_pNext
= m_hash_table
[index
], m_hash_table
[index
] = entry
;
426 // Update stats and leave.
430 return store_E_OutOfMemory
;
433 storeError
PageCache_Impl::updatePageAt_Impl (
434 PageHolder
const & rxPage
,
437 int index
= hash_index_Impl(nOffset
);
438 Entry
* entry
= lookup_Impl (m_hash_table
[index
], nOffset
);
441 // Update existing entry.
442 entry
->m_xPage
= rxPage
;
444 // Update stats and leave. // m_nUpdHit += 1;
447 return insertPageAt_Impl (rxPage
, nOffset
);
450 storeError
PageCache_Impl::removePageAt_Impl (
453 Entry
** ppEntry
= &(m_hash_table
[hash_index_Impl(nOffset
)]);
454 while (*ppEntry
!= 0)
456 if ((*ppEntry
)->m_nOffset
== nOffset
)
459 Entry
* entry
= (*ppEntry
);
461 // Dequeue and destroy entry.
462 (*ppEntry
) = entry
->m_pNext
, entry
->m_pNext
= 0;
463 EntryCache::get().destroy (entry
);
465 // Update stats and leave.
469 ppEntry
= &((*ppEntry
)->m_pNext
);
471 return store_E_NotExists
;
476 * Old OStorePageCache implementation.
478 * (two-way association (sorted address array, LRU chain)).
479 * (external OStorePageData representation).
485 * PageCache factory implementation.
491 PageCache_createInstance (
492 rtl::Reference
< store::PageCache
> & rxCache
,
493 sal_uInt16 nPageSize
)
495 rxCache
= new PageCache_Impl (nPageSize
);
497 return store_E_OutOfMemory
;
504 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */