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 "storcach.hxx"
24 #include <sal/log.hxx>
25 #include <sal/types.h>
26 #include <sal/macros.h>
27 #include <rtl/alloc.h>
28 #include <osl/diagnose.h>
30 #include <store/types.h>
31 #include "storbase.hxx"
36 using namespace store
;
41 std::shared_ptr
<PageData
> m_xPage
;
45 static void * operator new (size_t, void * p
) { return p
; }
46 static void operator delete (void *, void *) {}
48 explicit Entry (std::shared_ptr
<PageData
> const & rxPage
, sal_uInt32 nOffset
)
49 : m_xPage(rxPage
), m_nOffset(nOffset
), m_pNext(nullptr)
59 rtl_cache_type
* m_entry_cache
;
62 static EntryCache
& get();
64 Entry
* create (std::shared_ptr
<PageData
> const & rxPage
, sal_uInt32 nOffset
);
66 void destroy (Entry
* entry
);
75 EntryCache
& EntryCache::get()
77 static EntryCache g_entry_cache
;
81 EntryCache::EntryCache()
83 m_entry_cache
= rtl_cache_create (
84 "store_cache_entry_cache",
87 nullptr, // constructor
88 nullptr, // destructor
91 nullptr, // default source
96 EntryCache::~EntryCache()
98 rtl_cache_destroy (m_entry_cache
);
99 m_entry_cache
= nullptr;
102 Entry
* EntryCache::create (std::shared_ptr
<PageData
> const & rxPage
, sal_uInt32 nOffset
)
104 void * pAddr
= rtl_cache_alloc (m_entry_cache
);
105 if (pAddr
!= nullptr)
108 return new(pAddr
) Entry (rxPage
, nOffset
);
113 void EntryCache::destroy (Entry
* entry
)
115 if (entry
!= nullptr)
121 rtl_cache_free (m_entry_cache
, entry
);
125 // highbit():= log2() + 1 (complexity O(1))
126 static int highbit(std::size_t n
)
132 if constexpr (sizeof(n
) == 8)
134 if (n
& 0xffffffff00000000)
167 PageCache::PageCache (sal_uInt16 nPageSize
)
168 : m_hash_table (m_hash_table_0
),
169 m_hash_size (theTableSize
),
170 m_hash_shift (highbit(m_hash_size
) - 1),
171 m_page_shift (highbit(nPageSize
) - 1),
176 static size_t const theSize
= SAL_N_ELEMENTS(m_hash_table_0
);
177 static_assert(theSize
== theTableSize
, "must be equal");
180 PageCache::~PageCache()
183 std::size_t i
, n
= m_hash_size
;
184 for (i
= 0; i
< n
; i
++)
187 Entry
* entry
= m_hash_table
[i
];
188 while (entry
!= nullptr)
190 m_hash_table
[i
] = entry
->m_pNext
;
191 entry
->m_pNext
= nullptr;
192 EntryCache::get().destroy (entry
);
193 entry
= m_hash_table
[i
];
198 double ave
= s_x
/ double(n
);
199 SAL_INFO("store", "avg hash chain length: " << ave
);
201 if (m_hash_table
!= m_hash_table_0
)
203 std::free (m_hash_table
);
204 m_hash_table
= m_hash_table_0
;
205 m_hash_size
= theTableSize
;
206 m_hash_shift
= highbit(m_hash_size
) - 1;
208 SAL_INFO("store", "Hits: " << m_nHit
<< ", Misses: " << m_nMissed
);
211 void PageCache::rescale_Impl (std::size_t new_size
)
213 std::size_t new_bytes
= new_size
* sizeof(Entry
*);
214 Entry
** new_table
= static_cast<Entry
**>(std::malloc(new_bytes
));
216 if (new_table
== nullptr)
219 Entry
** old_table
= m_hash_table
;
220 std::size_t old_size
= m_hash_size
;
224 "ave chain length: " << (m_hash_entries
>> m_hash_shift
)
225 << ", total entries: " << m_hash_entries
<< " [old_size: "
226 << old_size
<< " new_size: " << new_size
<< "]");
228 memset (new_table
, 0, new_bytes
);
230 m_hash_table
= new_table
;
231 m_hash_size
= new_size
;
232 m_hash_shift
= highbit(m_hash_size
) - 1;
235 for (i
= 0; i
< old_size
; i
++)
237 Entry
* curr
= old_table
[i
];
238 while (curr
!= nullptr)
240 Entry
* next
= curr
->m_pNext
;
241 int index
= hash_index_Impl(curr
->m_nOffset
);
242 curr
->m_pNext
= m_hash_table
[index
];
243 m_hash_table
[index
] = curr
;
246 old_table
[i
] = nullptr;
248 if (old_table
!= m_hash_table_0
)
251 std::free (old_table
);
255 Entry
* PageCache::lookup_Impl (Entry
* entry
, sal_uInt32 nOffset
)
258 while (entry
!= nullptr)
260 if (entry
->m_nOffset
== nOffset
)
264 entry
= entry
->m_pNext
;
268 std::size_t new_size
= m_hash_size
, ave
= m_hash_entries
>> m_hash_shift
;
269 for (; ave
> 4; new_size
*= 2, ave
/= 2)
271 if (new_size
!= m_hash_size
)
272 rescale_Impl (new_size
);
277 storeError
PageCache::lookupPageAt (std::shared_ptr
<PageData
> & rxPage
, sal_uInt32 nOffset
)
279 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::lookupPageAt(): invalid Offset");
280 if (nOffset
== STORE_PAGE_NULL
)
281 return store_E_CantSeek
;
283 int index
= hash_index_Impl(nOffset
);
284 Entry
const * entry
= lookup_Impl (m_hash_table
[index
], nOffset
);
285 if (entry
!= nullptr)
288 rxPage
= entry
->m_xPage
;
290 // Update stats and leave.
295 // Cache miss. Update stats and leave.
297 return store_E_NotExists
;
300 storeError
PageCache::insertPageAt (std::shared_ptr
<PageData
> const & rxPage
, sal_uInt32 nOffset
)
302 // [SECURITY:ValInput]
303 PageData
const * pagedata
= rxPage
.get();
304 OSL_PRECOND(!(pagedata
== nullptr), "store::PageCache::insertPageAt(): invalid Page");
305 if (pagedata
== nullptr)
306 return store_E_InvalidParameter
;
308 sal_uInt32
const offset
= pagedata
->location();
309 OSL_PRECOND(!(nOffset
!= offset
), "store::PageCache::insertPageAt(): inconsistent Offset");
310 if (nOffset
!= offset
)
311 return store_E_InvalidParameter
;
313 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::insertPageAt(): invalid Offset");
314 if (nOffset
== STORE_PAGE_NULL
)
315 return store_E_CantSeek
;
317 Entry
* entry
= EntryCache::get().create (rxPage
, nOffset
);
318 if (entry
!= nullptr)
321 int index
= hash_index_Impl(nOffset
);
322 entry
->m_pNext
= m_hash_table
[index
];
323 m_hash_table
[index
] = entry
;
325 // Update stats and leave.
329 return store_E_OutOfMemory
;
332 storeError
PageCache::updatePageAt (std::shared_ptr
<PageData
> const & rxPage
, sal_uInt32 nOffset
)
334 // [SECURITY:ValInput]
335 PageData
const * pagedata
= rxPage
.get();
336 OSL_PRECOND(!(pagedata
== nullptr), "store::PageCache::updatePageAt(): invalid Page");
337 if (pagedata
== nullptr)
338 return store_E_InvalidParameter
;
340 sal_uInt32
const offset
= pagedata
->location();
341 OSL_PRECOND(!(nOffset
!= offset
), "store::PageCache::updatePageAt(): inconsistent Offset");
342 if (nOffset
!= offset
)
343 return store_E_InvalidParameter
;
345 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::updatePageAt(): invalid Offset");
346 if (nOffset
== STORE_PAGE_NULL
)
347 return store_E_CantSeek
;
349 int index
= hash_index_Impl(nOffset
);
350 Entry
* entry
= lookup_Impl (m_hash_table
[index
], nOffset
);
351 if (entry
!= nullptr)
353 // Update existing entry.
354 entry
->m_xPage
= rxPage
;
356 // Update stats and leave. // m_nUpdHit += 1;
359 return insertPageAt (rxPage
, nOffset
);
362 storeError
PageCache::removePageAt (sal_uInt32 nOffset
)
364 OSL_PRECOND(!(nOffset
== STORE_PAGE_NULL
), "store::PageCache::removePageAt(): invalid Offset");
365 if (nOffset
== STORE_PAGE_NULL
)
366 return store_E_CantSeek
;
368 Entry
** ppEntry
= &(m_hash_table
[hash_index_Impl(nOffset
)]);
369 while (*ppEntry
!= nullptr)
371 if ((*ppEntry
)->m_nOffset
== nOffset
)
374 Entry
* entry
= *ppEntry
;
376 // Dequeue and destroy entry.
377 (*ppEntry
) = entry
->m_pNext
;
378 entry
->m_pNext
= nullptr;
379 EntryCache::get().destroy (entry
);
381 // Update stats and leave.
385 ppEntry
= &((*ppEntry
)->m_pNext
);
387 return store_E_NotExists
;
392 Old OStorePageCache implementation.
394 (two-way association (sorted address array, LRU chain)).
395 (external PageData representation).
401 PageCache_createInstance (
402 rtl::Reference
< store::PageCache
> & rxCache
,
403 sal_uInt16 nPageSize
)
405 rxCache
= new PageCache (nPageSize
);
407 return store_E_OutOfMemory
;
414 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */