u_cache.c revision d00cbf46cde0edee6d8f2c08e14458ef92ff0fbe
1/************************************************************************** 2 * 3 * Copyright 2008 VMware, Inc. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28/** 29 * @file 30 * Simple cache implementation. 31 * 32 * We simply have fixed size array, and destroy previous values on collision. 33 * 34 * @author Jose Fonseca <jfonseca@vmware.com> 35 */ 36 37 38#include "pipe/p_compiler.h" 39#include "util/u_debug.h" 40 41#include "util/u_math.h" 42#include "util/u_memory.h" 43#include "util/u_cache.h" 44 45 46struct util_cache_entry 47{ 48 void *key; 49 void *value; 50 51#ifdef DEBUG 52 unsigned count; 53#endif 54}; 55 56 57struct util_cache 58{ 59 /** Hash function */ 60 uint32_t (*hash)(const void *key); 61 62 /** Compare two keys */ 63 int (*compare)(const void *key1, const void *key2); 64 65 /** Destroy a (key, value) pair */ 66 void (*destroy)(void *key, void *value); 67 68 uint32_t size; 69 70 struct util_cache_entry *entries; 71 72#ifdef DEBUG 73 unsigned count; 74#endif 75}; 76 77 78struct util_cache * 79util_cache_create(uint32_t (*hash)(const void *key), 80 int (*compare)(const void *key1, const void *key2), 81 void (*destroy)(void *key, void *value), 82 uint32_t size) 83{ 84 struct util_cache *cache; 85 86 cache = CALLOC_STRUCT(util_cache); 87 if(!cache) 88 return NULL; 89 90 cache->hash = hash; 91 cache->compare = compare; 92 cache->destroy = destroy; 93 cache->size = size; 94 95 cache->entries = CALLOC(size, sizeof(struct util_cache_entry)); 96 if(!cache->entries) { 97 FREE(cache); 98 return NULL; 99 } 100 101 return cache; 102} 103 104 105static INLINE struct util_cache_entry * 106util_cache_entry_get(struct util_cache *cache, 107 const void *key) 108{ 109 uint32_t hash; 110 111 hash = cache->hash(key); 112 113 return &cache->entries[hash % cache->size]; 114} 115 116static INLINE void 117util_cache_entry_destroy(struct util_cache *cache, 118 struct util_cache_entry *entry) 119{ 120 void *key = entry->key; 121 void *value = entry->value; 122 123 entry->key = NULL; 124 entry->value = NULL; 125 126 if(key || value) 127 if(cache->destroy) 128 cache->destroy(key, value); 129} 130 131 132void 133util_cache_set(struct util_cache *cache, 134 void *key, 135 void *value) 136{ 137 struct util_cache_entry *entry; 138 139 assert(cache); 140 if (!cache) 141 return; 142 143 entry = util_cache_entry_get(cache, key); 144 util_cache_entry_destroy(cache, entry); 145 146#ifdef DEBUG 147 ++entry->count; 148 ++cache->count; 149#endif 150 151 entry->key = key; 152 entry->value = value; 153} 154 155 156void * 157util_cache_get(struct util_cache *cache, 158 const void *key) 159{ 160 struct util_cache_entry *entry; 161 162 assert(cache); 163 if (!cache) 164 return NULL; 165 166 entry = util_cache_entry_get(cache, key); 167 if(!entry->key && !entry->value) 168 return NULL; 169 170 if(cache->compare(key, entry->key) != 0) 171 return NULL; 172 173 return entry->value; 174} 175 176 177void 178util_cache_clear(struct util_cache *cache) 179{ 180 uint32_t i; 181 182 assert(cache); 183 if (!cache) 184 return; 185 186 for(i = 0; i < cache->size; ++i) 187 util_cache_entry_destroy(cache, &cache->entries[i]); 188} 189 190 191void 192util_cache_destroy(struct util_cache *cache) 193{ 194 assert(cache); 195 if (!cache) 196 return; 197 198#ifdef DEBUG 199 if(cache->count >= 20*cache->size) { 200 /* Normal approximation of the Poisson distribution */ 201 double mean = (double)cache->count/(double)cache->size; 202 double stddev = sqrt(mean); 203 unsigned i; 204 for(i = 0; i < cache->size; ++i) { 205 double z = fabs(cache->entries[i].count - mean)/stddev; 206 /* This assert should not fail 99.9999998027% of the times, unless 207 * the hash function is a poor one */ 208 assert(z <= 6.0); 209 } 210 } 211#endif 212 213 util_cache_clear(cache); 214 215 FREE(cache->entries); 216 FREE(cache); 217} 218 219 220void 221util_cache_remove(struct util_cache *cache, 222 const void *key) 223{ 224 struct util_cache_entry *entry; 225 uint32_t hash; 226 227 assert(cache); 228 if (!cache) 229 return; 230 231 hash = cache->hash(key); 232 233 entry = util_cache_entry_get(cache, hash, key); 234 if (!entry) 235 return; 236 237 if (entry->state == FILLED) 238 util_cache_entry_destroy(cache, entry); 239} 240