fixed crash when saving empty menu items in wprefs
[wmaker-crm.git] / WINGs / hashtable.c
blobfc5f0f8ab3cc9251a3a0baad6eb4315c09cf35bf
3 #include <sys/types.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <stdio.h>
8 #include "WUtil.h"
13 #define INITIAL_CAPACITY 23
16 #if defined(__GNUC__) && !defined(__STRICT_ANSI__)
17 # define INLINE inline
18 #else
19 # define INLINE
20 #endif
23 typedef struct HashItem {
24 const void *key;
25 const void *data;
27 struct HashItem *next; /* collided item list */
28 } HashItem;
31 typedef struct W_HashTable {
32 WMHashTableCallbacks callbacks;
34 unsigned itemCount;
35 unsigned size; /* table size */
37 HashItem **table;
38 } HashTable;
43 #define HASH(table, key) (((table)->callbacks.hash ? \
44 (*(table)->callbacks.hash)(key) : hashPtr(key)) % (table)->size)
46 #define DUPKEY(table, key) ((table)->callbacks.retainKey ? \
47 (*(table)->callbacks.retainKey)(key) : (key))
49 #define RELKEY(table, key) if ((table)->callbacks.releaseKey) \
50 (*(table)->callbacks.releaseKey)(key)
55 static INLINE unsigned
56 hashString(const char *key)
58 unsigned ret = 0;
59 unsigned ctr = 0;
61 while (*key) {
62 ret ^= *(char*)key++ << ctr;
63 ctr = (ctr + 1) % sizeof (char *);
66 return ret;
71 static INLINE unsigned
72 hashPtr(const void *key)
74 return ((size_t)key / sizeof(char*));
80 static void
81 rellocateItem(WMHashTable *table, HashItem *item)
83 unsigned h;
85 h = HASH(table, item->key);
87 item->next = table->table[h];
88 table->table[h] = item;
92 static void
93 rebuildTable(WMHashTable *table)
95 HashItem *next;
96 HashItem **oldArray;
97 int i;
98 int oldSize;
99 int newSize;
101 oldArray = table->table;
102 oldSize = table->size;
104 newSize = table->size*2;
106 table->table = wmalloc(sizeof(char*)*newSize);
107 memset(table->table, 0, sizeof(char*)*newSize);
108 table->size = newSize;
110 for (i = 0; i < oldSize; i++) {
111 while (oldArray[i]!=NULL) {
112 next = oldArray[i]->next;
113 rellocateItem(table, oldArray[i]);
114 oldArray[i] = next;
117 wfree(oldArray);
122 WMHashTable*
123 WMCreateHashTable(WMHashTableCallbacks callbacks)
125 HashTable *table;
127 table = wmalloc(sizeof(HashTable));
128 memset(table, 0, sizeof(HashTable));
130 table->callbacks = callbacks;
132 table->size = INITIAL_CAPACITY;
134 table->table = wmalloc(sizeof(HashItem*)*table->size);
135 memset(table->table, 0, sizeof(HashItem*)*table->size);
137 return table;
141 void
142 WMResetHashTable(WMHashTable *table)
144 HashItem *item, *tmp;
145 int i;
147 for (i = 0; i < table->size; i++) {
148 item = table->table[i];
149 while (item) {
150 tmp = item->next;
151 RELKEY(table, item);
152 wfree(item);
153 item = tmp;
157 table->itemCount = 0;
159 if (table->size > INITIAL_CAPACITY) {
160 wfree(table->table);
161 table->size = INITIAL_CAPACITY;
162 table->table = wmalloc(sizeof(HashItem*)*table->size);
164 memset(table->table, 0, sizeof(HashItem*)*table->size);
168 void
169 WMFreeHashTable(WMHashTable *table)
171 HashItem *item, *tmp;
172 int i;
174 for (i = 0; i < table->size; i++) {
175 item = table->table[i];
176 while (item) {
177 tmp = item->next;
178 RELKEY(table, item->key);
179 wfree(item);
180 item = tmp;
183 wfree(table->table);
184 wfree(table);
189 void*
190 WMHashGet(WMHashTable *table, const void *key)
192 unsigned h;
193 HashItem *item;
195 h = HASH(table, key);
196 item = table->table[h];
198 if (table->callbacks.keyIsEqual) {
199 while (item) {
200 if ((*table->callbacks.keyIsEqual)(key, item->key)) {
201 break;
203 item = item->next;
205 } else {
206 while (item) {
207 if (key == item->key) {
208 break;
210 item = item->next;
213 if (item)
214 return (void*)item->data;
215 else
216 return NULL;
221 void*
222 WMHashInsert(WMHashTable *table, const void *key, const void *data)
224 unsigned h;
225 HashItem *item;
226 int replacing = 0;
228 h = HASH(table, key);
229 /* look for the entry */
230 item = table->table[h];
231 if (table->callbacks.keyIsEqual) {
232 while (item) {
233 if ((*table->callbacks.keyIsEqual)(key, item->key)) {
234 replacing = 1;
235 break;
237 item = item->next;
239 } else {
240 while (item) {
241 if (key == item->key) {
242 replacing = 1;
243 break;
245 item = item->next;
249 if (replacing) {
250 const void *old;
252 old = item->data;
253 item->data = data;
254 RELKEY(table, item->key);
255 item->key = DUPKEY(table, key);
257 return (void*)old;
258 } else {
259 HashItem *nitem;
261 nitem = wmalloc(sizeof(HashItem));
262 nitem->key = DUPKEY(table, key);
263 nitem->data = data;
264 nitem->next = table->table[h];
265 table->table[h] = nitem;
267 table->itemCount++;
270 /* OPTIMIZE: put this in an idle handler.*/
271 if (table->itemCount > table->size) {
272 #ifdef DEBUG0
273 printf("rebuilding hash table...\n");
274 #endif
275 rebuildTable(table);
276 #ifdef DEBUG0
277 printf("finished rebuild.\n");
278 #endif
281 return NULL;
285 static HashItem*
286 deleteFromList(HashTable *table, HashItem *item, const void *key)
288 HashItem *next;
290 if (item==NULL)
291 return NULL;
293 if ((table->callbacks.keyIsEqual
294 && (*table->callbacks.keyIsEqual)(key, item->key))
295 || (!table->callbacks.keyIsEqual && key==item->key)) {
297 next = item->next;
298 RELKEY(table, item->key);
299 wfree(item);
301 table->itemCount--;
303 return next;
306 item->next = deleteFromList(table, item->next, key);
308 return item;
312 void
313 WMHashRemove(WMHashTable *table, const void *key)
315 unsigned h;
317 h = HASH(table, key);
319 table->table[h] = deleteFromList(table, table->table[h], key);
323 WMHashEnumerator
324 WMEnumerateHashTable(WMHashTable *table)
326 WMHashEnumerator enumerator;
328 enumerator.table = table;
329 enumerator.index = 0;
330 enumerator.nextItem = table->table[0];
332 return enumerator;
337 void*
338 WMNextHashEnumeratorItem(WMHashEnumerator *enumerator)
340 const void *data = NULL;
342 /* this assumes the table doesn't change between
343 * WMEnumerateHashTable() and WMNextHashEnumeratorItem() calls */
345 if (enumerator->nextItem==NULL) {
346 HashTable *table = enumerator->table;
347 while (++enumerator->index < table->size) {
348 if (table->table[enumerator->index]!=NULL) {
349 enumerator->nextItem = table->table[enumerator->index];
350 break;
355 if (enumerator->nextItem) {
356 data = ((HashItem*)enumerator->nextItem)->data;
357 enumerator->nextItem = ((HashItem*)enumerator->nextItem)->next;
360 return (void*)data;
364 void*
365 WMNextHashEnumeratorKey(WMHashEnumerator *enumerator)
367 const void *key = NULL;
369 /* this assumes the table doesn't change between
370 * WMEnumerateHashTable() and WMNextHashEnumeratorKey() calls */
372 if (enumerator->nextItem==NULL) {
373 HashTable *table = enumerator->table;
374 while (++enumerator->index < table->size) {
375 if (table->table[enumerator->index]!=NULL) {
376 enumerator->nextItem = table->table[enumerator->index];
377 break;
382 if (enumerator->nextItem) {
383 key = ((HashItem*)enumerator->nextItem)->key;
384 enumerator->nextItem = ((HashItem*)enumerator->nextItem)->next;
387 return (void*)key;
391 unsigned
392 WMCountHashTable(WMHashTable *table)
394 return table->itemCount;
398 static Bool
399 compareStrings(const char *key1, const char *key2)
401 return strcmp(key1, key2)==0;
405 typedef unsigned (*hashFunc)(const void*);
406 typedef Bool (*isEqualFunc)(const void*, const void*);
407 typedef void* (*retainFunc)(const void*);
408 typedef void (*releaseFunc)(const void*);
411 const WMHashTableCallbacks WMIntHashCallbacks = {
412 NULL,
413 NULL,
414 NULL,
415 NULL
418 const WMHashTableCallbacks WMStringHashCallbacks = {
419 (hashFunc)hashString,
420 (isEqualFunc)compareStrings,
421 (retainFunc)wstrdup,
422 (releaseFunc)wfree
427 const WMHashTableCallbacks WMStringPointerHashCallbacks = {
428 (hashFunc)hashString,
429 (isEqualFunc)compareStrings,
430 NULL,
431 NULL