1bf215546Sopenharmony_ci/**************************************************************************
2bf215546Sopenharmony_ci *
3bf215546Sopenharmony_ci * Copyright 2008 VMware, Inc.
4bf215546Sopenharmony_ci * All Rights Reserved.
5bf215546Sopenharmony_ci *
6bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
7bf215546Sopenharmony_ci * copy of this software and associated documentation files (the
8bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
9bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
10bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
11bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
12bf215546Sopenharmony_ci * the following conditions:
13bf215546Sopenharmony_ci *
14bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the
15bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
16bf215546Sopenharmony_ci * of the Software.
17bf215546Sopenharmony_ci *
18bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19bf215546Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20bf215546Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21bf215546Sopenharmony_ci * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22bf215546Sopenharmony_ci * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23bf215546Sopenharmony_ci * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24bf215546Sopenharmony_ci * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25bf215546Sopenharmony_ci *
26bf215546Sopenharmony_ci **************************************************************************/
27bf215546Sopenharmony_ci
28bf215546Sopenharmony_ci/**
29bf215546Sopenharmony_ci * @file
30bf215546Sopenharmony_ci * Improved cache implementation.
31bf215546Sopenharmony_ci *
32bf215546Sopenharmony_ci * Fixed size array with linear probing on collision and LRU eviction
33bf215546Sopenharmony_ci * on full.
34bf215546Sopenharmony_ci *
35bf215546Sopenharmony_ci * @author Jose Fonseca <jfonseca@vmware.com>
36bf215546Sopenharmony_ci */
37bf215546Sopenharmony_ci
38bf215546Sopenharmony_ci
39bf215546Sopenharmony_ci#include "pipe/p_compiler.h"
40bf215546Sopenharmony_ci#include "util/u_debug.h"
41bf215546Sopenharmony_ci
42bf215546Sopenharmony_ci#include "util/u_math.h"
43bf215546Sopenharmony_ci#include "util/u_memory.h"
44bf215546Sopenharmony_ci#include "util/u_cache.h"
45bf215546Sopenharmony_ci#include "util/list.h"
46bf215546Sopenharmony_ci
47bf215546Sopenharmony_cistruct util_cache_entry
48bf215546Sopenharmony_ci{
49bf215546Sopenharmony_ci   enum { EMPTY = 0, FILLED, DELETED } state;
50bf215546Sopenharmony_ci   uint32_t hash;
51bf215546Sopenharmony_ci
52bf215546Sopenharmony_ci   struct list_head list;
53bf215546Sopenharmony_ci
54bf215546Sopenharmony_ci   void *key;
55bf215546Sopenharmony_ci   void *value;
56bf215546Sopenharmony_ci
57bf215546Sopenharmony_ci#ifdef DEBUG
58bf215546Sopenharmony_ci   unsigned count;
59bf215546Sopenharmony_ci#endif
60bf215546Sopenharmony_ci};
61bf215546Sopenharmony_ci
62bf215546Sopenharmony_ci
63bf215546Sopenharmony_cistruct util_cache
64bf215546Sopenharmony_ci{
65bf215546Sopenharmony_ci   /** Hash function */
66bf215546Sopenharmony_ci   uint32_t (*hash)(const void *key);
67bf215546Sopenharmony_ci
68bf215546Sopenharmony_ci   /** Compare two keys */
69bf215546Sopenharmony_ci   int (*compare)(const void *key1, const void *key2);
70bf215546Sopenharmony_ci
71bf215546Sopenharmony_ci   /** Destroy a (key, value) pair */
72bf215546Sopenharmony_ci   void (*destroy)(void *key, void *value);
73bf215546Sopenharmony_ci
74bf215546Sopenharmony_ci   /** Max entries in the cache */
75bf215546Sopenharmony_ci   uint32_t size;
76bf215546Sopenharmony_ci
77bf215546Sopenharmony_ci   /** Array [size] of entries */
78bf215546Sopenharmony_ci   struct util_cache_entry *entries;
79bf215546Sopenharmony_ci
80bf215546Sopenharmony_ci   /** Number of entries in the cache */
81bf215546Sopenharmony_ci   unsigned count;
82bf215546Sopenharmony_ci
83bf215546Sopenharmony_ci   /** Head of list, sorted from Least-recently used to Most-recently used */
84bf215546Sopenharmony_ci   struct util_cache_entry lru;
85bf215546Sopenharmony_ci};
86bf215546Sopenharmony_ci
87bf215546Sopenharmony_ci
88bf215546Sopenharmony_cistatic void
89bf215546Sopenharmony_ciensure_sanity(const struct util_cache *cache);
90bf215546Sopenharmony_ci
91bf215546Sopenharmony_ci#define CACHE_DEFAULT_ALPHA 2
92bf215546Sopenharmony_ci
93bf215546Sopenharmony_ci/**
94bf215546Sopenharmony_ci * Create a new cache with 'size' entries.  Also provide functions for
95bf215546Sopenharmony_ci * hashing keys, comparing keys and destroying (key,value) pairs.
96bf215546Sopenharmony_ci */
97bf215546Sopenharmony_cistruct util_cache *
98bf215546Sopenharmony_ciutil_cache_create(uint32_t (*hash)(const void *key),
99bf215546Sopenharmony_ci                  int (*compare)(const void *key1, const void *key2),
100bf215546Sopenharmony_ci                  void (*destroy)(void *key, void *value),
101bf215546Sopenharmony_ci                  uint32_t size)
102bf215546Sopenharmony_ci{
103bf215546Sopenharmony_ci   struct util_cache *cache;
104bf215546Sopenharmony_ci
105bf215546Sopenharmony_ci   cache = CALLOC_STRUCT(util_cache);
106bf215546Sopenharmony_ci   if (!cache)
107bf215546Sopenharmony_ci      return NULL;
108bf215546Sopenharmony_ci
109bf215546Sopenharmony_ci   cache->hash = hash;
110bf215546Sopenharmony_ci   cache->compare = compare;
111bf215546Sopenharmony_ci   cache->destroy = destroy;
112bf215546Sopenharmony_ci
113bf215546Sopenharmony_ci   list_inithead(&cache->lru.list);
114bf215546Sopenharmony_ci
115bf215546Sopenharmony_ci   size *= CACHE_DEFAULT_ALPHA;
116bf215546Sopenharmony_ci   cache->size = size;
117bf215546Sopenharmony_ci
118bf215546Sopenharmony_ci   cache->entries = CALLOC(size, sizeof(struct util_cache_entry));
119bf215546Sopenharmony_ci   if (!cache->entries) {
120bf215546Sopenharmony_ci      FREE(cache);
121bf215546Sopenharmony_ci      return NULL;
122bf215546Sopenharmony_ci   }
123bf215546Sopenharmony_ci
124bf215546Sopenharmony_ci   ensure_sanity(cache);
125bf215546Sopenharmony_ci   return cache;
126bf215546Sopenharmony_ci}
127bf215546Sopenharmony_ci
128bf215546Sopenharmony_ci
129bf215546Sopenharmony_ci/**
130bf215546Sopenharmony_ci * Try to find a cache entry, given the key and hash of the key.
131bf215546Sopenharmony_ci */
132bf215546Sopenharmony_cistatic struct util_cache_entry *
133bf215546Sopenharmony_ciutil_cache_entry_get(struct util_cache *cache,
134bf215546Sopenharmony_ci                     uint32_t hash,
135bf215546Sopenharmony_ci                     const void *key)
136bf215546Sopenharmony_ci{
137bf215546Sopenharmony_ci   struct util_cache_entry *first_unfilled = NULL;
138bf215546Sopenharmony_ci   uint32_t index = hash % cache->size;
139bf215546Sopenharmony_ci   uint32_t probe;
140bf215546Sopenharmony_ci
141bf215546Sopenharmony_ci   /* Probe until we find either a matching FILLED entry or an EMPTY
142bf215546Sopenharmony_ci    * slot (which has never been occupied).
143bf215546Sopenharmony_ci    *
144bf215546Sopenharmony_ci    * Deleted or non-matching slots are not indicative of completion
145bf215546Sopenharmony_ci    * as a previous linear probe for the same key could have continued
146bf215546Sopenharmony_ci    * past this point.
147bf215546Sopenharmony_ci    */
148bf215546Sopenharmony_ci   for (probe = 0; probe < cache->size; probe++) {
149bf215546Sopenharmony_ci      uint32_t i = (index + probe) % cache->size;
150bf215546Sopenharmony_ci      struct util_cache_entry *current = &cache->entries[i];
151bf215546Sopenharmony_ci
152bf215546Sopenharmony_ci      if (current->state == FILLED) {
153bf215546Sopenharmony_ci         if (current->hash == hash &&
154bf215546Sopenharmony_ci             cache->compare(key, current->key) == 0)
155bf215546Sopenharmony_ci            return current;
156bf215546Sopenharmony_ci      }
157bf215546Sopenharmony_ci      else {
158bf215546Sopenharmony_ci         if (!first_unfilled)
159bf215546Sopenharmony_ci            first_unfilled = current;
160bf215546Sopenharmony_ci
161bf215546Sopenharmony_ci         if (current->state == EMPTY)
162bf215546Sopenharmony_ci            return first_unfilled;
163bf215546Sopenharmony_ci      }
164bf215546Sopenharmony_ci   }
165bf215546Sopenharmony_ci
166bf215546Sopenharmony_ci   return NULL;
167bf215546Sopenharmony_ci}
168bf215546Sopenharmony_ci
169bf215546Sopenharmony_cistatic inline void
170bf215546Sopenharmony_ciutil_cache_entry_destroy(struct util_cache *cache,
171bf215546Sopenharmony_ci                         struct util_cache_entry *entry)
172bf215546Sopenharmony_ci{
173bf215546Sopenharmony_ci   void *key = entry->key;
174bf215546Sopenharmony_ci   void *value = entry->value;
175bf215546Sopenharmony_ci
176bf215546Sopenharmony_ci   entry->key = NULL;
177bf215546Sopenharmony_ci   entry->value = NULL;
178bf215546Sopenharmony_ci
179bf215546Sopenharmony_ci   if (entry->state == FILLED) {
180bf215546Sopenharmony_ci      list_del(&entry->list);
181bf215546Sopenharmony_ci      cache->count--;
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_ci      if (cache->destroy)
184bf215546Sopenharmony_ci         cache->destroy(key, value);
185bf215546Sopenharmony_ci
186bf215546Sopenharmony_ci      entry->state = DELETED;
187bf215546Sopenharmony_ci   }
188bf215546Sopenharmony_ci}
189bf215546Sopenharmony_ci
190bf215546Sopenharmony_ci
191bf215546Sopenharmony_ci/**
192bf215546Sopenharmony_ci * Insert an entry in the cache, given the key and value.
193bf215546Sopenharmony_ci */
194bf215546Sopenharmony_civoid
195bf215546Sopenharmony_ciutil_cache_set(struct util_cache *cache,
196bf215546Sopenharmony_ci               void *key,
197bf215546Sopenharmony_ci               void *value)
198bf215546Sopenharmony_ci{
199bf215546Sopenharmony_ci   struct util_cache_entry *entry;
200bf215546Sopenharmony_ci   uint32_t hash;
201bf215546Sopenharmony_ci
202bf215546Sopenharmony_ci   assert(cache);
203bf215546Sopenharmony_ci   if (!cache)
204bf215546Sopenharmony_ci      return;
205bf215546Sopenharmony_ci   hash = cache->hash(key);
206bf215546Sopenharmony_ci   entry = util_cache_entry_get(cache, hash, key);
207bf215546Sopenharmony_ci   if (!entry)
208bf215546Sopenharmony_ci      entry = list_last_entry(&cache->lru.list, struct util_cache_entry, list);
209bf215546Sopenharmony_ci
210bf215546Sopenharmony_ci   if (cache->count >= cache->size / CACHE_DEFAULT_ALPHA)
211bf215546Sopenharmony_ci      util_cache_entry_destroy(cache, list_last_entry(&cache->lru.list, struct util_cache_entry, list));
212bf215546Sopenharmony_ci
213bf215546Sopenharmony_ci   util_cache_entry_destroy(cache, entry);
214bf215546Sopenharmony_ci
215bf215546Sopenharmony_ci#ifdef DEBUG
216bf215546Sopenharmony_ci   ++entry->count;
217bf215546Sopenharmony_ci#endif
218bf215546Sopenharmony_ci
219bf215546Sopenharmony_ci   entry->key = key;
220bf215546Sopenharmony_ci   entry->hash = hash;
221bf215546Sopenharmony_ci   entry->value = value;
222bf215546Sopenharmony_ci   entry->state = FILLED;
223bf215546Sopenharmony_ci   list_add(&cache->lru.list, &entry->list);
224bf215546Sopenharmony_ci   cache->count++;
225bf215546Sopenharmony_ci
226bf215546Sopenharmony_ci   ensure_sanity(cache);
227bf215546Sopenharmony_ci}
228bf215546Sopenharmony_ci
229bf215546Sopenharmony_ci
230bf215546Sopenharmony_ci/**
231bf215546Sopenharmony_ci * Search the cache for an entry with the given key.  Return the corresponding
232bf215546Sopenharmony_ci * value or NULL if not found.
233bf215546Sopenharmony_ci */
234bf215546Sopenharmony_civoid *
235bf215546Sopenharmony_ciutil_cache_get(struct util_cache *cache,
236bf215546Sopenharmony_ci               const void *key)
237bf215546Sopenharmony_ci{
238bf215546Sopenharmony_ci   struct util_cache_entry *entry;
239bf215546Sopenharmony_ci   uint32_t hash;
240bf215546Sopenharmony_ci
241bf215546Sopenharmony_ci   assert(cache);
242bf215546Sopenharmony_ci   if (!cache)
243bf215546Sopenharmony_ci      return NULL;
244bf215546Sopenharmony_ci   hash = cache->hash(key);
245bf215546Sopenharmony_ci   entry = util_cache_entry_get(cache, hash, key);
246bf215546Sopenharmony_ci   if (!entry)
247bf215546Sopenharmony_ci      return NULL;
248bf215546Sopenharmony_ci
249bf215546Sopenharmony_ci   if (entry->state == FILLED) {
250bf215546Sopenharmony_ci      list_move_to(&cache->lru.list, &entry->list);
251bf215546Sopenharmony_ci   }
252bf215546Sopenharmony_ci
253bf215546Sopenharmony_ci   return entry->value;
254bf215546Sopenharmony_ci}
255bf215546Sopenharmony_ci
256bf215546Sopenharmony_ci
257bf215546Sopenharmony_ci/**
258bf215546Sopenharmony_ci * Remove all entries from the cache.  The 'destroy' function will be called
259bf215546Sopenharmony_ci * for each entry's (key, value).
260bf215546Sopenharmony_ci */
261bf215546Sopenharmony_civoid
262bf215546Sopenharmony_ciutil_cache_clear(struct util_cache *cache)
263bf215546Sopenharmony_ci{
264bf215546Sopenharmony_ci   uint32_t i;
265bf215546Sopenharmony_ci
266bf215546Sopenharmony_ci   assert(cache);
267bf215546Sopenharmony_ci   if (!cache)
268bf215546Sopenharmony_ci      return;
269bf215546Sopenharmony_ci
270bf215546Sopenharmony_ci   for (i = 0; i < cache->size; ++i) {
271bf215546Sopenharmony_ci      util_cache_entry_destroy(cache, &cache->entries[i]);
272bf215546Sopenharmony_ci      cache->entries[i].state = EMPTY;
273bf215546Sopenharmony_ci   }
274bf215546Sopenharmony_ci
275bf215546Sopenharmony_ci   assert(cache->count == 0);
276bf215546Sopenharmony_ci   assert(list_is_empty(&cache->lru.list));
277bf215546Sopenharmony_ci   ensure_sanity(cache);
278bf215546Sopenharmony_ci}
279bf215546Sopenharmony_ci
280bf215546Sopenharmony_ci
281bf215546Sopenharmony_ci/**
282bf215546Sopenharmony_ci * Destroy the cache and all entries.
283bf215546Sopenharmony_ci */
284bf215546Sopenharmony_civoid
285bf215546Sopenharmony_ciutil_cache_destroy(struct util_cache *cache)
286bf215546Sopenharmony_ci{
287bf215546Sopenharmony_ci   assert(cache);
288bf215546Sopenharmony_ci   if (!cache)
289bf215546Sopenharmony_ci      return;
290bf215546Sopenharmony_ci
291bf215546Sopenharmony_ci#ifdef DEBUG
292bf215546Sopenharmony_ci   if (cache->count >= 20*cache->size) {
293bf215546Sopenharmony_ci      /* Normal approximation of the Poisson distribution */
294bf215546Sopenharmony_ci      double mean = (double)cache->count/(double)cache->size;
295bf215546Sopenharmony_ci      double stddev = sqrt(mean);
296bf215546Sopenharmony_ci      unsigned i;
297bf215546Sopenharmony_ci      for (i = 0; i < cache->size; ++i) {
298bf215546Sopenharmony_ci         double z = fabs(cache->entries[i].count - mean)/stddev;
299bf215546Sopenharmony_ci         /* This assert should not fail 99.9999998027% of the times, unless
300bf215546Sopenharmony_ci          * the hash function is a poor one */
301bf215546Sopenharmony_ci         assert(z <= 6.0);
302bf215546Sopenharmony_ci      }
303bf215546Sopenharmony_ci   }
304bf215546Sopenharmony_ci#endif
305bf215546Sopenharmony_ci
306bf215546Sopenharmony_ci   util_cache_clear(cache);
307bf215546Sopenharmony_ci
308bf215546Sopenharmony_ci   FREE(cache->entries);
309bf215546Sopenharmony_ci   FREE(cache);
310bf215546Sopenharmony_ci}
311bf215546Sopenharmony_ci
312bf215546Sopenharmony_ci
313bf215546Sopenharmony_ci/**
314bf215546Sopenharmony_ci * Remove the cache entry which matches the given key.
315bf215546Sopenharmony_ci */
316bf215546Sopenharmony_civoid
317bf215546Sopenharmony_ciutil_cache_remove(struct util_cache *cache,
318bf215546Sopenharmony_ci                  const void *key)
319bf215546Sopenharmony_ci{
320bf215546Sopenharmony_ci   struct util_cache_entry *entry;
321bf215546Sopenharmony_ci   uint32_t hash;
322bf215546Sopenharmony_ci
323bf215546Sopenharmony_ci   assert(cache);
324bf215546Sopenharmony_ci   if (!cache)
325bf215546Sopenharmony_ci      return;
326bf215546Sopenharmony_ci
327bf215546Sopenharmony_ci   hash = cache->hash(key);
328bf215546Sopenharmony_ci
329bf215546Sopenharmony_ci   entry = util_cache_entry_get(cache, hash, key);
330bf215546Sopenharmony_ci   if (!entry)
331bf215546Sopenharmony_ci      return;
332bf215546Sopenharmony_ci
333bf215546Sopenharmony_ci   if (entry->state == FILLED)
334bf215546Sopenharmony_ci      util_cache_entry_destroy(cache, entry);
335bf215546Sopenharmony_ci
336bf215546Sopenharmony_ci   ensure_sanity(cache);
337bf215546Sopenharmony_ci}
338bf215546Sopenharmony_ci
339bf215546Sopenharmony_ci
340bf215546Sopenharmony_cistatic void
341bf215546Sopenharmony_ciensure_sanity(const struct util_cache *cache)
342bf215546Sopenharmony_ci{
343bf215546Sopenharmony_ci#ifdef DEBUG
344bf215546Sopenharmony_ci   unsigned i, cnt = 0;
345bf215546Sopenharmony_ci
346bf215546Sopenharmony_ci   assert(cache);
347bf215546Sopenharmony_ci   for (i = 0; i < cache->size; i++) {
348bf215546Sopenharmony_ci      struct util_cache_entry *header = &cache->entries[i];
349bf215546Sopenharmony_ci
350bf215546Sopenharmony_ci      assert(header);
351bf215546Sopenharmony_ci      assert(header->state == FILLED ||
352bf215546Sopenharmony_ci             header->state == EMPTY ||
353bf215546Sopenharmony_ci             header->state == DELETED);
354bf215546Sopenharmony_ci      if (header->state == FILLED) {
355bf215546Sopenharmony_ci         cnt++;
356bf215546Sopenharmony_ci         assert(header->hash == cache->hash(header->key));
357bf215546Sopenharmony_ci      }
358bf215546Sopenharmony_ci   }
359bf215546Sopenharmony_ci
360bf215546Sopenharmony_ci   assert(cnt == cache->count);
361bf215546Sopenharmony_ci   assert(cache->size >= cnt);
362bf215546Sopenharmony_ci
363bf215546Sopenharmony_ci   if (cache->count == 0) {
364bf215546Sopenharmony_ci      assert (list_is_empty(&cache->lru.list));
365bf215546Sopenharmony_ci   }
366bf215546Sopenharmony_ci   else {
367bf215546Sopenharmony_ci      struct util_cache_entry *header =
368bf215546Sopenharmony_ci         list_entry(&cache->lru, struct util_cache_entry, list);
369bf215546Sopenharmony_ci
370bf215546Sopenharmony_ci      assert (header);
371bf215546Sopenharmony_ci      assert (!list_is_empty(&cache->lru.list));
372bf215546Sopenharmony_ci
373bf215546Sopenharmony_ci      for (i = 0; i < cache->count; i++)
374bf215546Sopenharmony_ci         header = list_entry(&header, struct util_cache_entry, list);
375bf215546Sopenharmony_ci
376bf215546Sopenharmony_ci      assert(header == &cache->lru);
377bf215546Sopenharmony_ci   }
378bf215546Sopenharmony_ci#endif
379bf215546Sopenharmony_ci
380bf215546Sopenharmony_ci   (void)cache;
381bf215546Sopenharmony_ci}
382