dict.c revision d3202de1176057520f49b5e6231b8774f6b6b31f
1/* 2 * This file is part of ltrace. 3 * Copyright (C) 2011,2012 Petr Machata 4 * Copyright (C) 2003,2004,2008,2009 Juan Cespedes 5 * Copyright (C) 2006 Ian Wienand 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of the 10 * License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 * 02110-1301 USA 21 */ 22 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <assert.h> 27 28#include "common.h" 29 30/* 31 * Dictionary based on code by Morten Eriksen <mortene@sim.no>. 32 */ 33 34struct dict_entry { 35 unsigned int hash; 36 void *key; 37 void *value; 38 struct dict_entry *next; 39}; 40 41/* #define DICTTABLESIZE 97 */ 42#define DICTTABLESIZE 997 /* Semi-randomly selected prime number. */ 43/* #define DICTTABLESIZE 9973 */ 44/* #define DICTTABLESIZE 99991 */ 45/* #define DICTTABLESIZE 999983 */ 46 47struct dict { 48 struct dict_entry *buckets[DICTTABLESIZE]; 49 unsigned int (*key2hash) (const void *); 50 int (*key_cmp) (const void *, const void *); 51}; 52 53Dict * 54dict_init(unsigned int (*key2hash) (const void *), 55 int (*key_cmp) (const void *, const void *)) 56{ 57 Dict *d; 58 int i; 59 60 debug(DEBUG_FUNCTION, "dict_init()"); 61 62 d = malloc(sizeof(Dict)); 63 if (!d) { 64 perror("malloc()"); 65 exit(1); 66 } 67 for (i = 0; i < DICTTABLESIZE; i++) { /* better use memset()? */ 68 d->buckets[i] = NULL; 69 } 70 d->key2hash = key2hash; 71 d->key_cmp = key_cmp; 72 return d; 73} 74 75void 76dict_clear(Dict *d) { 77 int i; 78 struct dict_entry *entry, *nextentry; 79 80 debug(DEBUG_FUNCTION, "dict_clear()"); 81 assert(d); 82 for (i = 0; i < DICTTABLESIZE; i++) { 83 for (entry = d->buckets[i]; entry != NULL; entry = nextentry) { 84 nextentry = entry->next; 85 free(entry); 86 } 87 d->buckets[i] = NULL; 88 } 89 free(d); 90} 91 92int 93dict_enter(Dict *d, void *key, void *value) { 94 struct dict_entry *entry, *newentry; 95 unsigned int hash; 96 unsigned int bucketpos; 97 98 debug(DEBUG_FUNCTION, "dict_enter()"); 99 100 hash = d->key2hash(key); 101 bucketpos = hash % DICTTABLESIZE; 102 103 assert(d); 104 newentry = malloc(sizeof(struct dict_entry)); 105 if (!newentry) { 106 perror("malloc"); 107 exit(1); 108 } 109 110 newentry->hash = hash; 111 newentry->key = key; 112 newentry->value = value; 113 newentry->next = NULL; 114 115 entry = d->buckets[bucketpos]; 116 while (entry && entry->next) 117 entry = entry->next; 118 119 if (entry) 120 entry->next = newentry; 121 else 122 d->buckets[bucketpos] = newentry; 123 124 debug(3, "new dict entry at %p[%d]: (%p,%p)", d, bucketpos, key, value); 125 return 0; 126} 127 128void * 129dict_remove(Dict *d, void *key) 130{ 131 assert(d != NULL); 132 debug(DEBUG_FUNCTION, "dict_remove(%p)", key); 133 134 unsigned int hash = d->key2hash(key); 135 unsigned int bucketpos = hash % DICTTABLESIZE; 136 137 struct dict_entry **entryp; 138 for (entryp = &d->buckets[bucketpos]; (*entryp) != NULL; 139 entryp = &(*entryp)->next) { 140 struct dict_entry *entry = *entryp; 141 if (hash != entry->hash) 142 continue; 143 if (d->key_cmp(key, entry->key) == 0) { 144 *entryp = entry->next; 145 void *value = entry->value; 146 free(entry); 147 return value; 148 } 149 } 150 return NULL; 151} 152 153void * 154dict_find_entry(Dict *d, const void *key) 155{ 156 unsigned int hash; 157 unsigned int bucketpos; 158 struct dict_entry *entry; 159 160 debug(DEBUG_FUNCTION, "dict_find_entry()"); 161 162 hash = d->key2hash(key); 163 bucketpos = hash % DICTTABLESIZE; 164 165 assert(d); 166 for (entry = d->buckets[bucketpos]; entry; entry = entry->next) { 167 if (hash != entry->hash) { 168 continue; 169 } 170 if (!d->key_cmp(key, entry->key)) { 171 break; 172 } 173 } 174 return entry ? entry->value : NULL; 175} 176 177void 178dict_apply_to_all(Dict *d, 179 void (*func) (void *key, void *value, void *data), void *data) { 180 int i; 181 182 debug(DEBUG_FUNCTION, "dict_apply_to_all()"); 183 184 if (!d) { 185 return; 186 } 187 for (i = 0; i < DICTTABLESIZE; i++) { 188 struct dict_entry *entry = d->buckets[i]; 189 while (entry) { 190 func(entry->key, entry->value, data); 191 entry = entry->next; 192 } 193 } 194} 195 196/*****************************************************************************/ 197 198unsigned int 199dict_key2hash_string(const void *key) 200{ 201 const char *s = (const char *)key; 202 unsigned int total = 0, shift = 0; 203 204 assert(key); 205 while (*s) { 206 total = total ^ ((*s) << shift); 207 shift += 5; 208 if (shift > 24) 209 shift -= 24; 210 s++; 211 } 212 return total; 213} 214 215int 216dict_key_cmp_string(const void *key1, const void *key2) 217{ 218 assert(key1); 219 assert(key2); 220 return strcmp((const char *)key1, (const char *)key2); 221} 222 223unsigned int 224dict_key2hash_int(const void *key) 225{ 226 return (unsigned long)key; 227} 228 229int 230dict_key_cmp_int(const void *key1, const void *key2) 231{ 232 return key1 - key2; 233} 234 235Dict * 236dict_clone2(Dict * old, void * (*key_clone)(void *, void *), 237 void * (*value_clone)(void *, void *), void * data) 238{ 239 Dict *d; 240 int i; 241 242 debug(DEBUG_FUNCTION, "dict_clone()"); 243 244 d = malloc(sizeof(Dict)); 245 if (!d) { 246 perror("malloc()"); 247 exit(1); 248 } 249 memcpy(d, old, sizeof(Dict)); 250 for (i = 0; i < DICTTABLESIZE; i++) { /* better use memset()? */ 251 struct dict_entry *de_old; 252 struct dict_entry **de_new; 253 254 de_old = old->buckets[i]; 255 de_new = &d->buckets[i]; 256 while (de_old) { 257 void * nkey, * nval; 258 *de_new = malloc(sizeof(struct dict_entry)); 259 if (!*de_new) { 260 perror("malloc()"); 261 exit(1); 262 } 263 memcpy(*de_new, de_old, sizeof(struct dict_entry)); 264 265 /* The error detection is rather weak :-/ */ 266 nkey = key_clone(de_old->key, data); 267 if (nkey == NULL && de_old->key != NULL) { 268 perror("key_clone"); 269 err: 270 /* XXX Will this actually work? We 271 * simply memcpy the old dictionary 272 * over up there. */ 273 dict_clear(d); 274 free(de_new); 275 return NULL; 276 } 277 278 nval = value_clone(de_old->value, data); 279 if (nval == NULL && de_old->value != NULL) { 280 perror("value_clone"); 281 goto err; 282 } 283 284 (*de_new)->key = nkey; 285 (*de_new)->value = nval; 286 de_new = &(*de_new)->next; 287 de_old = de_old->next; 288 } 289 } 290 return d; 291} 292 293struct wrap_clone_cb 294{ 295 void * (*key_clone)(void *); 296 void * (*value_clone)(void *); 297}; 298 299static void * 300value_clone_1(void * arg, void * data) 301{ 302 return ((struct wrap_clone_cb *)data)->value_clone(arg); 303} 304 305static void * 306key_clone_1(void * arg, void * data) 307{ 308 return ((struct wrap_clone_cb *)data)->key_clone(arg); 309} 310 311Dict * 312dict_clone(Dict * old, void * (*key_clone)(void *), 313 void * (*value_clone)(void *)) 314{ 315 struct wrap_clone_cb cb = { key_clone, value_clone }; 316 return dict_clone2(old, &key_clone_1, &value_clone_1, &cb); 317} 318