dict.c revision 345c9b5195383c7b2d2d9db308e824259323803f
1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <assert.h> 5 6#include "common.h" 7 8/* 9 Dictionary based on code by Morten Eriksen <mortene@sim.no>. 10*/ 11 12struct dict_entry { 13 unsigned int hash; 14 void *key; 15 void *value; 16 struct dict_entry *next; 17}; 18 19/* #define DICTTABLESIZE 97 */ 20#define DICTTABLESIZE 997 /* Semi-randomly selected prime number. */ 21/* #define DICTTABLESIZE 9973 */ 22/* #define DICTTABLESIZE 99991 */ 23/* #define DICTTABLESIZE 999983 */ 24 25struct dict { 26 struct dict_entry *buckets[DICTTABLESIZE]; 27 unsigned int (*key2hash) (const void *); 28 int (*key_cmp) (const void *, const void *); 29}; 30 31Dict * 32dict_init(unsigned int (*key2hash) (const void *), 33 int (*key_cmp) (const void *, const void *)) 34{ 35 Dict *d; 36 int i; 37 38 debug(DEBUG_FUNCTION, "dict_init()"); 39 40 d = malloc(sizeof(Dict)); 41 if (!d) { 42 perror("malloc()"); 43 exit(1); 44 } 45 for (i = 0; i < DICTTABLESIZE; i++) { /* better use memset()? */ 46 d->buckets[i] = NULL; 47 } 48 d->key2hash = key2hash; 49 d->key_cmp = key_cmp; 50 return d; 51} 52 53void 54dict_clear(Dict *d) { 55 int i; 56 struct dict_entry *entry, *nextentry; 57 58 debug(DEBUG_FUNCTION, "dict_clear()"); 59 assert(d); 60 for (i = 0; i < DICTTABLESIZE; i++) { 61 for (entry = d->buckets[i]; entry != NULL; entry = nextentry) { 62 nextentry = entry->next; 63 free(entry); 64 } 65 d->buckets[i] = NULL; 66 } 67 free(d); 68} 69 70int 71dict_enter(Dict *d, void *key, void *value) { 72 struct dict_entry *entry, *newentry; 73 unsigned int hash; 74 unsigned int bucketpos; 75 76 debug(DEBUG_FUNCTION, "dict_enter()"); 77 78 hash = d->key2hash(key); 79 bucketpos = hash % DICTTABLESIZE; 80 81 assert(d); 82 newentry = malloc(sizeof(struct dict_entry)); 83 if (!newentry) { 84 perror("malloc"); 85 exit(1); 86 } 87 88 newentry->hash = hash; 89 newentry->key = key; 90 newentry->value = value; 91 newentry->next = NULL; 92 93 entry = d->buckets[bucketpos]; 94 while (entry && entry->next) 95 entry = entry->next; 96 97 if (entry) 98 entry->next = newentry; 99 else 100 d->buckets[bucketpos] = newentry; 101 102 debug(3, "new dict entry at %p[%d]: (%p,%p)", d, bucketpos, key, value); 103 return 0; 104} 105 106void * 107dict_find_entry(Dict *d, const void *key) 108{ 109 unsigned int hash; 110 unsigned int bucketpos; 111 struct dict_entry *entry; 112 113 debug(DEBUG_FUNCTION, "dict_find_entry()"); 114 115 hash = d->key2hash(key); 116 bucketpos = hash % DICTTABLESIZE; 117 118 assert(d); 119 for (entry = d->buckets[bucketpos]; entry; entry = entry->next) { 120 if (hash != entry->hash) { 121 continue; 122 } 123 if (!d->key_cmp(key, entry->key)) { 124 break; 125 } 126 } 127 return entry ? entry->value : NULL; 128} 129 130void 131dict_apply_to_all(Dict *d, 132 void (*func) (void *key, void *value, void *data), void *data) { 133 int i; 134 135 debug(DEBUG_FUNCTION, "dict_apply_to_all()"); 136 137 if (!d) { 138 return; 139 } 140 for (i = 0; i < DICTTABLESIZE; i++) { 141 struct dict_entry *entry = d->buckets[i]; 142 while (entry) { 143 func(entry->key, entry->value, data); 144 entry = entry->next; 145 } 146 } 147} 148 149/*****************************************************************************/ 150 151unsigned int 152dict_key2hash_string(const void *key) 153{ 154 const char *s = (const char *)key; 155 unsigned int total = 0, shift = 0; 156 157 assert(key); 158 while (*s) { 159 total = total ^ ((*s) << shift); 160 shift += 5; 161 if (shift > 24) 162 shift -= 24; 163 s++; 164 } 165 return total; 166} 167 168int 169dict_key_cmp_string(const void *key1, const void *key2) 170{ 171 assert(key1); 172 assert(key2); 173 return strcmp((const char *)key1, (const char *)key2); 174} 175 176unsigned int 177dict_key2hash_int(const void *key) 178{ 179 return (unsigned long)key; 180} 181 182int 183dict_key_cmp_int(const void *key1, const void *key2) 184{ 185 return key1 - key2; 186} 187 188Dict * 189dict_clone2(Dict * old, void * (*key_clone)(void *, void *), 190 void * (*value_clone)(void *, void *), void * data) 191{ 192 Dict *d; 193 int i; 194 195 debug(DEBUG_FUNCTION, "dict_clone()"); 196 197 d = malloc(sizeof(Dict)); 198 if (!d) { 199 perror("malloc()"); 200 exit(1); 201 } 202 memcpy(d, old, sizeof(Dict)); 203 for (i = 0; i < DICTTABLESIZE; i++) { /* better use memset()? */ 204 struct dict_entry *de_old; 205 struct dict_entry **de_new; 206 207 de_old = old->buckets[i]; 208 de_new = &d->buckets[i]; 209 while (de_old) { 210 void * nkey, * nval; 211 *de_new = malloc(sizeof(struct dict_entry)); 212 if (!*de_new) { 213 perror("malloc()"); 214 exit(1); 215 } 216 memcpy(*de_new, de_old, sizeof(struct dict_entry)); 217 218 /* The error detection is rather weak :-/ */ 219 nkey = key_clone(de_old->key, data); 220 if (nkey == NULL && de_old->key != NULL) { 221 perror("key_clone"); 222 err: 223 /* XXX Will this actually work? We 224 * simply memcpy the old dictionary 225 * over up there. */ 226 dict_clear(d); 227 free(de_new); 228 return NULL; 229 } 230 231 nval = value_clone(de_old->value, data); 232 if (nval == NULL && de_old->value != NULL) { 233 perror("value_clone"); 234 goto err; 235 } 236 237 (*de_new)->key = nkey; 238 (*de_new)->value = nval; 239 de_new = &(*de_new)->next; 240 de_old = de_old->next; 241 } 242 } 243 return d; 244} 245 246struct wrap_clone_cb 247{ 248 void * (*key_clone)(void *); 249 void * (*value_clone)(void *); 250}; 251 252static void * 253value_clone_1(void * arg, void * data) 254{ 255 return ((struct wrap_clone_cb *)data)->value_clone(arg); 256} 257 258static void * 259key_clone_1(void * arg, void * data) 260{ 261 return ((struct wrap_clone_cb *)data)->key_clone(arg); 262} 263 264Dict * 265dict_clone(Dict * old, void * (*key_clone)(void *), 266 void * (*value_clone)(void *)) 267{ 268 struct wrap_clone_cb cb = { key_clone, value_clone }; 269 return dict_clone2(old, &key_clone_1, &value_clone_1, &cb); 270} 271