dict.c revision a11cbede4711e1efe2d6fb0a8a5b2050b2fe941c
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 return entry->value; 146 } 147 } 148 return NULL; 149} 150 151void * 152dict_find_entry(Dict *d, const void *key) 153{ 154 unsigned int hash; 155 unsigned int bucketpos; 156 struct dict_entry *entry; 157 158 debug(DEBUG_FUNCTION, "dict_find_entry()"); 159 160 hash = d->key2hash(key); 161 bucketpos = hash % DICTTABLESIZE; 162 163 assert(d); 164 for (entry = d->buckets[bucketpos]; entry; entry = entry->next) { 165 if (hash != entry->hash) { 166 continue; 167 } 168 if (!d->key_cmp(key, entry->key)) { 169 break; 170 } 171 } 172 return entry ? entry->value : NULL; 173} 174 175void 176dict_apply_to_all(Dict *d, 177 void (*func) (void *key, void *value, void *data), void *data) { 178 int i; 179 180 debug(DEBUG_FUNCTION, "dict_apply_to_all()"); 181 182 if (!d) { 183 return; 184 } 185 for (i = 0; i < DICTTABLESIZE; i++) { 186 struct dict_entry *entry = d->buckets[i]; 187 while (entry) { 188 func(entry->key, entry->value, data); 189 entry = entry->next; 190 } 191 } 192} 193 194/*****************************************************************************/ 195 196unsigned int 197dict_key2hash_string(const void *key) 198{ 199 const char *s = (const char *)key; 200 unsigned int total = 0, shift = 0; 201 202 assert(key); 203 while (*s) { 204 total = total ^ ((*s) << shift); 205 shift += 5; 206 if (shift > 24) 207 shift -= 24; 208 s++; 209 } 210 return total; 211} 212 213int 214dict_key_cmp_string(const void *key1, const void *key2) 215{ 216 assert(key1); 217 assert(key2); 218 return strcmp((const char *)key1, (const char *)key2); 219} 220 221unsigned int 222dict_key2hash_int(const void *key) 223{ 224 return (unsigned long)key; 225} 226 227int 228dict_key_cmp_int(const void *key1, const void *key2) 229{ 230 return key1 - key2; 231} 232 233Dict * 234dict_clone2(Dict * old, void * (*key_clone)(void *, void *), 235 void * (*value_clone)(void *, void *), void * data) 236{ 237 Dict *d; 238 int i; 239 240 debug(DEBUG_FUNCTION, "dict_clone()"); 241 242 d = malloc(sizeof(Dict)); 243 if (!d) { 244 perror("malloc()"); 245 exit(1); 246 } 247 memcpy(d, old, sizeof(Dict)); 248 for (i = 0; i < DICTTABLESIZE; i++) { /* better use memset()? */ 249 struct dict_entry *de_old; 250 struct dict_entry **de_new; 251 252 de_old = old->buckets[i]; 253 de_new = &d->buckets[i]; 254 while (de_old) { 255 void * nkey, * nval; 256 *de_new = malloc(sizeof(struct dict_entry)); 257 if (!*de_new) { 258 perror("malloc()"); 259 exit(1); 260 } 261 memcpy(*de_new, de_old, sizeof(struct dict_entry)); 262 263 /* The error detection is rather weak :-/ */ 264 nkey = key_clone(de_old->key, data); 265 if (nkey == NULL && de_old->key != NULL) { 266 perror("key_clone"); 267 err: 268 /* XXX Will this actually work? We 269 * simply memcpy the old dictionary 270 * over up there. */ 271 dict_clear(d); 272 free(de_new); 273 return NULL; 274 } 275 276 nval = value_clone(de_old->value, data); 277 if (nval == NULL && de_old->value != NULL) { 278 perror("value_clone"); 279 goto err; 280 } 281 282 (*de_new)->key = nkey; 283 (*de_new)->value = nval; 284 de_new = &(*de_new)->next; 285 de_old = de_old->next; 286 } 287 } 288 return d; 289} 290 291struct wrap_clone_cb 292{ 293 void * (*key_clone)(void *); 294 void * (*value_clone)(void *); 295}; 296 297static void * 298value_clone_1(void * arg, void * data) 299{ 300 return ((struct wrap_clone_cb *)data)->value_clone(arg); 301} 302 303static void * 304key_clone_1(void * arg, void * data) 305{ 306 return ((struct wrap_clone_cb *)data)->key_clone(arg); 307} 308 309Dict * 310dict_clone(Dict * old, void * (*key_clone)(void *), 311 void * (*value_clone)(void *)) 312{ 313 struct wrap_clone_cb cb = { key_clone, value_clone }; 314 return dict_clone2(old, &key_clone_1, &value_clone_1, &cb); 315} 316