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