Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
779130926247c4ca56d244504294941b5c7f0d82
[simgrid.git] / src / xbt / dict.c
1 /* dict - a generic dictionary, variation over hash table                   */
2
3 /* Copyright (c) 2003-2010 Martin Quinson. All rights reserved.             */
4
5 /* This program is free software; you can redistribute it and/or modify it
6  * under the terms of the license (GNU LGPL) which comes with this package. */
7
8 #define DJB2_HASH_FUNCTION
9 //#define FNV_HASH_FUNCTION
10
11 #include <string.h>
12 #include <stdio.h>
13 #include "xbt/ex.h"
14 #include "xbt/log.h"
15 #include "xbt/mallocator.h"
16 #include "xbt_modinter.h"
17 #include "dict_private.h"
18
19 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_dict, xbt,
20                                 "Dictionaries provide the same functionalities than hash tables");
21 /*####[ Private prototypes ]#################################################*/
22
23 static xbt_mallocator_t dict_mallocator = NULL;
24 static void *dict_mallocator_new_f(void);
25 static void dict_mallocator_free_f(void *dict);
26 static void dict_mallocator_reset_f(void *dict);
27
28
29 /*####[ Code ]###############################################################*/
30
31 /**
32  * \brief Constructor
33  * \return pointer to the destination
34  * \see xbt_dict_new_ext(), xbt_dict_free()
35  *
36  * Creates and initialize a new dictionary with a default hashtable size.
37  */
38 xbt_dict_t xbt_dict_new(void)
39 {
40   xbt_dict_t dict;
41
42   if (dict_mallocator == NULL) {
43     /* first run */
44     dict_mallocator = xbt_mallocator_new(256,
45                                          dict_mallocator_new_f,
46                                          dict_mallocator_free_f,
47                                          dict_mallocator_reset_f);
48     dict_elm_mallocator = xbt_mallocator_new(256,
49                                              dict_elm_mallocator_new_f,
50                                              dict_elm_mallocator_free_f,
51                                              dict_elm_mallocator_reset_f);
52   }
53
54   dict = xbt_mallocator_get(dict_mallocator);
55   dict->table_size = 127;
56   dict->table = xbt_new0(xbt_dictelm_t, dict->table_size + 1);
57   dict->count = 0;
58   dict->fill = 0;
59
60   return dict;
61 }
62
63 /**
64  * \brief Destructor
65  * \param dict the dictionary to be freed
66  *
67  * Frees a dictionary with all the data
68  */
69 void xbt_dict_free(xbt_dict_t * dict)
70 {
71   int i;
72   xbt_dictelm_t current, previous;
73   int table_size;
74   xbt_dictelm_t *table;
75
76   //  if ( *dict )  xbt_dict_dump_sizes(*dict);
77
78   if (dict != NULL && *dict != NULL) {
79     table_size = (*dict)->table_size;
80     table = (*dict)->table;
81     /* Warning: the size of the table is 'table_size+1'...
82      * This is because table_size is used as a binary mask in xbt_dict_rehash */
83     for (i = 0; (*dict)->count && i <= table_size; i++) {
84       current = table[i];
85       while (current != NULL) {
86         previous = current;
87         current = current->next;
88         xbt_dictelm_free(previous);
89         (*dict)->count--;
90       }
91     }
92     xbt_free(table);
93     xbt_mallocator_release(dict_mallocator, *dict);
94     *dict = NULL;
95   }
96 }
97
98 /**
99  * Returns the amount of elements in the dict
100  */
101 XBT_INLINE unsigned int xbt_dict_size(xbt_dict_t dict)
102 {
103   return dict->count;
104 }
105
106 /**
107  * Returns the hash code of a string.
108  */
109 static XBT_INLINE unsigned int xbt_dict_hash_ext(const char *str, int str_len)
110 {
111
112
113 #ifdef DJB2_HASH_FUNCTION
114   /* fast implementation of djb2 algorithm */
115   int c;
116   register unsigned int hash = 5381;
117
118   while (str_len--) {
119     c = *str++;
120     hash = ((hash << 5) + hash) + c;    /* hash * 33 + c */
121   }
122 # elif defined(FNV_HASH_FUNCTION)
123   register unsigned int hash = 0x811c9dc5;
124   unsigned char *bp = (unsigned char *) str;    /* start of buffer */
125   unsigned char *be = bp + str_len;     /* beyond end of buffer */
126
127   while (bp < be) {
128     /* multiply by the 32 bit FNV magic prime mod 2^32 */
129     hash +=
130       (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
131
132     /* xor the bottom with the current octet */
133     hash ^= (unsigned int) *bp++;
134   }
135
136 # else
137   register unsigned int hash = 0;
138
139   while (str_len--) {
140     hash += (*str) * (*str);
141     str++;
142   }
143 #endif
144
145   return hash;
146 }
147
148 static XBT_INLINE unsigned int xbt_dict_hash(const char *str)
149 {
150 #ifdef DJB2_HASH_FUNCTION
151   /* fast implementation of djb2 algorithm */
152   int c;
153   register unsigned int hash = 5381;
154
155   while ((c = *str++)) {
156     hash = ((hash << 5) + hash) + c;    /* hash * 33 + c */
157   }
158
159 # elif defined(FNV_HASH_FUNCTION)
160   register unsigned int hash = 0x811c9dc5;
161
162   while (*str) {
163     /* multiply by the 32 bit FNV magic prime mod 2^32 */
164     hash +=
165       (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
166
167     /* xor the bottom with the current byte */
168     hash ^= (unsigned int) *str++;
169   }
170
171 # else
172   register unsigned int hash = 0;
173
174   while (*str) {
175     hash += (*str) * (*str);
176     str++;
177   }
178 #endif
179   return hash;
180 }
181
182 /* Expend the size of the dict */
183 static void xbt_dict_rehash(xbt_dict_t dict)
184 {
185   const int oldsize = dict->table_size + 1;
186   register int newsize = oldsize * 2;
187   register int i;
188   register xbt_dictelm_t *currcell;
189   register xbt_dictelm_t *twincell;
190   register xbt_dictelm_t bucklet;
191   register xbt_dictelm_t *pprev;
192
193   currcell =
194     (xbt_dictelm_t *) xbt_realloc((char *) dict->table,
195                                   newsize * sizeof(xbt_dictelm_t));
196   memset(&currcell[oldsize], 0, oldsize * sizeof(xbt_dictelm_t));       /* zero second half */
197   dict->table_size = --newsize;
198   dict->table = currcell;
199   DEBUG2("REHASH (%d->%d)", oldsize, newsize);
200
201   for (i = 0; i < oldsize; i++, currcell++) {
202     if (!*currcell)             /* empty cell */
203       continue;
204     twincell = currcell + oldsize;
205     for (pprev = currcell, bucklet = *currcell; bucklet; bucklet = *pprev) {
206       /* Since we use "& size" instead of "%size" and since the size was doubled,
207          each bucklet of this cell must either :
208          - stay  in  cell i (ie, currcell)
209          - go to the cell i+oldsize (ie, twincell) */
210       if ((bucklet->hash_code & newsize) != i) {        /* Move to b */
211         *pprev = bucklet->next;
212         bucklet->next = *twincell;
213         if (!*twincell)
214           dict->fill++;
215         *twincell = bucklet;
216         continue;
217       } else {
218         pprev = &bucklet->next;
219       }
220
221     }
222
223     if (!*currcell)             /* everything moved */
224       dict->fill--;
225   }
226 }
227
228 /**
229  * \brief Add data to the dict (arbitrary key)
230  * \param dict the container
231  * \param key the key to set the new data
232  * \param key_len the size of the \a key
233  * \param data the data to add in the dict
234  * \param free_ctn function to call with (\a key as argument) when
235  *        \a key is removed from the dictionary
236  *
237  * Set the \a data in the structure under the \a key, which can be any kind
238  * of data, as long as its length is provided in \a key_len.
239  */
240 XBT_INLINE void xbt_dict_set_ext(xbt_dict_t dict,
241                                  const char *key, int key_len,
242                                  void *data, void_f_pvoid_t free_ctn)
243 {
244
245   unsigned int hash_code = xbt_dict_hash_ext(key, key_len);
246
247   xbt_dictelm_t current, previous = NULL;
248   xbt_assert(dict);
249
250   DEBUG5("ADD %.*s hash = %d, size = %d, & = %d", key_len, key, hash_code,
251          dict->table_size, hash_code & dict->table_size);
252   current = dict->table[hash_code & dict->table_size];
253   while (current != NULL &&
254          (hash_code != current->hash_code || key_len != current->key_len
255           || memcmp(key, current->key, key_len))) {
256     previous = current;
257     current = current->next;
258   }
259
260   if (current == NULL) {
261     /* this key doesn't exist yet */
262     current = xbt_dictelm_new(key, key_len, hash_code, data, free_ctn);
263     dict->count++;
264     if (previous == NULL) {
265       dict->table[hash_code & dict->table_size] = current;
266       dict->fill++;
267       if ((dict->fill * 100) / (dict->table_size + 1) > MAX_FILL_PERCENT)
268         xbt_dict_rehash(dict);
269     } else {
270       previous->next = current;
271     }
272   } else {
273
274     DEBUG6("Replace %.*s by %.*s under key %.*s",
275            key_len, (char *) current->content,
276            key_len, (char *) data, key_len, (char *) key);
277     /* there is already an element with the same key: overwrite it */
278     if (current->content != NULL && current->free_f != NULL) {
279       current->free_f(current->content);
280     }
281     current->content = data;
282     current->free_f = free_ctn;
283   }
284 }
285
286 /**
287  * \brief Add data to the dict (null-terminated key)
288  *
289  * \param dict the dict
290  * \param key the key to set the new data
291  * \param data the data to add in the dict
292  * \param free_ctn function to call with (\a key as argument) when
293  *        \a key is removed from the dictionary
294  *
295  * set the \a data in the structure under the \a key, which is a
296  * null terminated string.
297  */
298 XBT_INLINE void xbt_dict_set(xbt_dict_t dict,
299                   const char *key, void *data, void_f_pvoid_t free_ctn)
300 {
301
302   xbt_dict_set_ext(dict, key, strlen(key), data, free_ctn);
303 }
304
305 /**
306  * \brief Retrieve data from the dict (arbitrary key)
307  *
308  * \param dict the dealer of data
309  * \param key the key to find data
310  * \param key_len the size of the \a key
311  * \return the data that we are looking for
312  *
313  * Search the given \a key. Throws not_found_error when not found.
314  */
315 XBT_INLINE void *xbt_dict_get_ext(xbt_dict_t dict, const char *key, int key_len)
316 {
317
318
319   unsigned int hash_code = xbt_dict_hash_ext(key, key_len);
320   xbt_dictelm_t current;
321
322   xbt_assert(dict);
323
324   current = dict->table[hash_code & dict->table_size];
325   while (current != NULL &&
326          (hash_code != current->hash_code || key_len != current->key_len
327           || memcmp(key, current->key, key_len))) {
328     current = current->next;
329   }
330
331   if (current == NULL)
332     THROW2(not_found_error, 0, "key %.*s not found", key_len, key);
333
334   return current->content;
335 }
336
337 /**
338  * \brief like xbt_dict_get_ext(), but returning NULL when not found
339  */
340 void *xbt_dict_get_or_null_ext(xbt_dict_t dict, const char *key, int key_len)
341 {
342
343   unsigned int hash_code = xbt_dict_hash_ext(key, key_len);
344   xbt_dictelm_t current;
345
346   xbt_assert(dict);
347
348   current = dict->table[hash_code & dict->table_size];
349   while (current != NULL &&
350          (hash_code != current->hash_code || key_len != current->key_len
351           || memcmp(key, current->key, key_len))) {
352     current = current->next;
353   }
354
355   if (current == NULL)
356     return NULL;
357
358   return current->content;
359 }
360
361 /**
362  * @brief retrieve the key associated to that object. Warning, that's a linear search
363  *
364  * Returns NULL if the object cannot be found
365  */
366 char *xbt_dict_get_key(xbt_dict_t dict, const void*data) {
367   int i;
368   xbt_dictelm_t current;
369
370
371   for (i = 0; i <= dict->table_size; i++) {
372     current = dict->table[i];
373     while (current != NULL) {
374       if (current->content == data)
375         return current->key;
376       current = current->next;
377     }
378   }
379
380   return NULL;
381 }
382
383 /**
384  * \brief Retrieve data from the dict (null-terminated key)
385  *
386  * \param dict the dealer of data
387  * \param key the key to find data
388  * \return the data that we are looking for
389  *
390  * Search the given \a key. Throws not_found_error when not found.
391  * Check xbt_dict_get_or_null() for a version returning NULL without exception when
392  * not found.
393  */
394 XBT_INLINE void *xbt_dict_get(xbt_dict_t dict, const char *key)
395 {
396
397   unsigned int hash_code = xbt_dict_hash(key);
398   xbt_dictelm_t current;
399
400   xbt_assert(dict);
401
402   current = dict->table[hash_code & dict->table_size];
403   while (current != NULL
404          && (hash_code != current->hash_code || strcmp(key, current->key)))
405     current = current->next;
406
407   if (current == NULL)
408     THROW1(not_found_error, 0, "key %s not found", key);
409
410   return current->content;
411 }
412
413 /**
414  * \brief like xbt_dict_get(), but returning NULL when not found
415  */
416 XBT_INLINE void *xbt_dict_get_or_null(xbt_dict_t dict, const char *key)
417 {
418   unsigned int hash_code = xbt_dict_hash(key);
419   xbt_dictelm_t current;
420
421   xbt_assert(dict);
422
423   current = dict->table[hash_code & dict->table_size];
424   while (current != NULL &&
425          hash_code != current->hash_code && strcmp(key, current->key))
426     current = current->next;
427
428   if (current == NULL)
429     return NULL;
430
431   return current->content;
432 }
433
434
435 /**
436  * \brief Remove data from the dict (arbitrary key)
437  *
438  * \param dict the trash can
439  * \param key the key of the data to be removed
440  * \param key_len the size of the \a key
441  *
442  * Remove the entry associated with the given \a key (throws not_found)
443  */
444 XBT_INLINE void xbt_dict_remove_ext(xbt_dict_t dict, const char *key, int key_len)
445 {
446
447
448   unsigned int hash_code = xbt_dict_hash_ext(key, key_len);
449   xbt_dictelm_t current, previous = NULL;
450
451   xbt_assert(dict);
452
453   //  fprintf(stderr,"RM %.*s hash = %d, size = %d, & = %d\n",key_len,key,hash_code, dict->table_size, hash_code & dict->table_size);
454   current = dict->table[hash_code & dict->table_size];
455   while (current != NULL &&
456          (hash_code != current->hash_code || key_len != current->key_len
457           || strncmp(key, current->key, key_len))) {
458     previous = current;         /* save the previous node */
459     current = current->next;
460   }
461
462   if (current == NULL)
463     THROW2(not_found_error, 0, "key %.*s not found", key_len, key);
464
465   if (previous != NULL) {
466     previous->next = current->next;
467   } else {
468     dict->table[hash_code & dict->table_size] = current->next;
469   }
470
471   if (!dict->table[hash_code & dict->table_size])
472     dict->fill--;
473
474   xbt_dictelm_free(current);
475   dict->count--;
476 }
477
478
479
480 /**
481  * \brief Remove data from the dict (null-terminated key)
482  *
483  * \param dict the dict
484  * \param key the key of the data to be removed
485  *
486  * Remove the entry associated with the given \a key
487  */
488 XBT_INLINE void xbt_dict_remove(xbt_dict_t dict, const char *key)
489 {
490   xbt_dict_remove_ext(dict, key, strlen(key));
491 }
492
493 /**
494  * \brief Add data to the dict (arbitrary key)
495  * \param dict the container
496  * \param key the key to set the new data
497  * \param key_len the size of the \a key
498  * \param data the data to add in the dict
499  * \param free_ctn function to call with (\a key as argument) when
500  *        \a key is removed from the dictionary
501  *
502  * Set the \a data in the structure under the \a key, which can be any kind
503  * of data, as long as its length is provided in \a key_len.
504  */
505 XBT_INLINE void xbt_dicti_set(xbt_dict_t dict,
506                               uintptr_t key, uintptr_t data) {
507
508   unsigned int hash_code = xbt_dict_hash_ext((void*)&key, sizeof(uintptr_t));
509
510   xbt_dictelm_t current, previous = NULL;
511   xbt_assert(dict);
512
513   DEBUG5("ADD %zu->%zu; hash = %d, size = %d, & = %d", key, data, hash_code,
514          dict->table_size, hash_code & dict->table_size);
515   current = dict->table[hash_code & dict->table_size];
516   while (current != NULL &&
517          (hash_code != current->hash_code || sizeof(uintptr_t) != current->key_len
518           || (((uintptr_t)key) != ((uintptr_t)current->key)) )) {
519     current = current->next;
520   }
521
522   if (current == NULL) {
523     /* this key doesn't exist yet */
524     current = xbt_dictielm_new(key, hash_code, data);
525     dict->count++;
526     if (previous == NULL) {
527       dict->table[hash_code & dict->table_size] = current;
528       dict->fill++;
529       if ((dict->fill * 100) / (dict->table_size + 1) > MAX_FILL_PERCENT)
530         xbt_dict_rehash(dict);
531     } else {
532       previous->next = current;
533     }
534   } else {
535
536     /* there is already an element with the same key: overwrite it */
537     if (current->content != NULL && current->free_f != NULL) {
538       current->free_f(current->content);
539     }
540     current->content = (void*)data;
541     current->free_f = NULL;
542   }
543 }
544
545 /**
546  * \brief Retrieve data from the dict (key considered as a uintptr_t)
547  *
548  * \param dict the dealer of data
549  * \param key the key to find data
550  * \return the data that we are looking for (or 0 if not found)
551  *
552  * Mixing uintptr_t keys with regular keys in the same dict is discouraged
553  */
554 XBT_INLINE uintptr_t xbt_dicti_get(xbt_dict_t dict, uintptr_t key) {
555
556   unsigned int hash_code = xbt_dict_hash_ext(((void*)&key), sizeof(uintptr_t));
557   xbt_dictelm_t current;
558
559   xbt_assert(dict);
560
561   current = dict->table[hash_code & dict->table_size];
562   while (current != NULL &&
563          (hash_code != current->hash_code || sizeof(uintptr_t) != current->key_len
564           || (((uintptr_t)key) != ((uintptr_t)current->key)) )) {
565     current = current->next;
566   }
567
568   if (current == NULL)
569     return 0;
570
571   return (uintptr_t)(current->content);
572 }
573
574 /** Remove a uintptr_t key from the dict */
575 XBT_INLINE void xbt_dicti_remove(xbt_dict_t dict, uintptr_t key) {
576
577   unsigned int hash_code = xbt_dict_hash_ext(((void*)&key), sizeof(uintptr_t));
578   xbt_dictelm_t current, previous = NULL;
579
580
581   current = dict->table[hash_code & dict->table_size];
582   while (current != NULL &&
583          (hash_code != current->hash_code || sizeof(uintptr_t) != current->key_len
584           || (((uintptr_t)key) != ((uintptr_t)current->key)) )) {
585     previous = current;         /* save the previous node */
586     current = current->next;
587   }
588
589   if (current == NULL)
590     THROW1(not_found_error, 0, "key %zu not found", key);
591
592   if (previous != NULL) {
593     previous->next = current->next;
594   } else {
595     dict->table[hash_code & dict->table_size] = current->next;
596   }
597
598   if (!dict->table[hash_code & dict->table_size])
599     dict->fill--;
600
601   xbt_dictelm_free(current);
602   dict->count--;
603 }
604
605
606 /**
607  * \brief Remove all data from the dict
608  * \param dict the dict
609  */
610 void xbt_dict_reset(xbt_dict_t dict)
611 {
612
613   int i;
614   xbt_dictelm_t current, previous = NULL;
615
616   xbt_assert(dict);
617
618   if (dict->count == 0)
619     return;
620
621   for (i = 0; i <= dict->table_size; i++) {
622     current = dict->table[i];
623     while (current != NULL) {
624       previous = current;
625       current = current->next;
626       xbt_dictelm_free(previous);
627     }
628     dict->table[i] = NULL;
629   }
630
631   dict->count = 0;
632   dict->fill = 0;
633 }
634
635 /**
636  * \brief Return the number of elements in the dict.
637  * \param dict a dictionary
638  */
639 XBT_INLINE int xbt_dict_length(xbt_dict_t dict)
640 {
641   xbt_assert(dict);
642
643   return dict->count;
644 }
645
646 /** @brief function to be used in xbt_dict_dump as long as the stored values are strings */
647 void xbt_dict_dump_output_string(void *s)
648 {
649   fputs(s, stdout);
650 }
651
652
653 /**
654  * \brief Outputs the content of the structure (debugging purpose)
655  *
656  * \param dict the exibitionist
657  * \param output a function to dump each data in the tree (check @ref xbt_dict_dump_output_string)
658  *
659  * Outputs the content of the structure. (for debugging purpose). \a output is a
660  * function to output the data. If NULL, data won't be displayed.
661  */
662
663 void xbt_dict_dump(xbt_dict_t dict, void_f_pvoid_t output)
664 {
665   int i;
666   xbt_dictelm_t element;
667   printf("Dict %p:\n", dict);
668   if (dict != NULL) {
669     for (i = 0; i < dict->table_size; i++) {
670       element = dict->table[i];
671       if (element) {
672         printf("[\n");
673         while (element != NULL) {
674           printf(" %s -> '", element->key);
675           if (output != NULL) {
676             (*output) (element->content);
677           }
678           printf("'\n");
679           element = element->next;
680         }
681         printf("]\n");
682       } else {
683         printf("[]\n");
684       }
685     }
686   }
687 }
688
689 xbt_dynar_t all_sizes = NULL;
690 /** @brief shows some debugging info about the bucklet repartition */
691 void xbt_dict_dump_sizes(xbt_dict_t dict)
692 {
693
694   int i;
695   unsigned int count;
696   unsigned int size;
697   xbt_dictelm_t element;
698   xbt_dynar_t sizes = xbt_dynar_new(sizeof(int), NULL);
699
700   printf("Dict %p: %d bucklets, %d used cells (of %d) ", dict, dict->count,
701          dict->fill, dict->table_size);
702   if (dict != NULL) {
703     for (i = 0; i < dict->table_size; i++) {
704       element = dict->table[i];
705       size = 0;
706       if (element) {
707         while (element != NULL) {
708           size++;
709           element = element->next;
710         }
711       }
712       if (xbt_dynar_length(sizes) <= size) {
713         int prevsize = 1;
714         xbt_dynar_set(sizes, size, &prevsize);
715       } else {
716         int prevsize;
717         xbt_dynar_get_cpy(sizes, size, &prevsize);
718         prevsize++;
719         xbt_dynar_set(sizes, size, &prevsize);
720       }
721     }
722     if (!all_sizes)
723       all_sizes = xbt_dynar_new(sizeof(int), NULL);
724
725     xbt_dynar_foreach(sizes, count, size) {
726       /* Copy values of this one into all_sizes */
727       int prevcount;
728       if (xbt_dynar_length(all_sizes) <= count) {
729         prevcount = size;
730         xbt_dynar_set(all_sizes, count, &prevcount);
731       } else {
732         xbt_dynar_get_cpy(all_sizes, count, &prevcount);
733         prevcount += size;
734         xbt_dynar_set(all_sizes, count, &prevcount);
735       }
736
737       /* Report current sizes */
738       if (count == 0)
739         continue;
740       if (size == 0)
741         continue;
742       printf("%delm x %u cells; ", count, size);
743     }
744   }
745   printf("\n");
746   xbt_dynar_free(&sizes);
747 }
748
749 /**
750  * Destroy the dict mallocators.
751  * This is an internal XBT function called by xbt_exit().
752  */
753 void xbt_dict_exit(void)
754 {
755   if (dict_mallocator != NULL) {
756     xbt_mallocator_free(dict_mallocator);
757     dict_mallocator = NULL;
758     xbt_mallocator_free(dict_elm_mallocator);
759     dict_elm_mallocator = NULL;
760   }
761   if (all_sizes) {
762     unsigned int count;
763     int size;
764     double avg = 0;
765     int total_count = 0;
766     printf("Overall stats:");
767     xbt_dynar_foreach(all_sizes, count, size) {
768       if (count == 0)
769         continue;
770       if (size == 0)
771         continue;
772       printf("%delm x %d cells; ", count, size);
773       avg += count * size;
774       total_count += size;
775     }
776     printf("; %f elm per cell\n", avg / (double) total_count);
777   }
778 }
779
780 static void *dict_mallocator_new_f(void)
781 {
782   return xbt_new(s_xbt_dict_t, 1);
783 }
784
785 static void dict_mallocator_free_f(void *dict)
786 {
787   xbt_free(dict);
788 }
789
790 static void dict_mallocator_reset_f(void *dict)
791 {
792   /* nothing to do because all fields are
793    * initialized in xbt_dict_new
794    */
795 }
796
797 #ifdef SIMGRID_TEST
798 #include "xbt.h"
799 #include "xbt/ex.h"
800 #include "portable.h"
801
802 XBT_LOG_EXTERNAL_CATEGORY(xbt_dict);
803 XBT_LOG_DEFAULT_CATEGORY(xbt_dict);
804
805 XBT_TEST_SUITE("dict", "Dict data container");
806
807 static void print_str(void *str)
808 {
809   printf("%s", (char *) PRINTF_STR(str));
810 }
811
812 static void debuged_add_ext(xbt_dict_t head, const char *key,
813                             const char *data_to_fill)
814 {
815   char *data = xbt_strdup(data_to_fill);
816
817   xbt_test_log2("Add %s under %s", PRINTF_STR(data_to_fill), PRINTF_STR(key));
818
819   xbt_dict_set(head, key, data, &free);
820   if (XBT_LOG_ISENABLED(xbt_dict, xbt_log_priority_debug)) {
821     xbt_dict_dump(head, (void (*)(void *)) &printf);
822     fflush(stdout);
823   }
824 }
825
826 static void debuged_add(xbt_dict_t head, const char *key)
827 {
828   debuged_add_ext(head, key, key);
829 }
830
831 static void fill(xbt_dict_t * head)
832 {
833   xbt_test_add0("Fill in the dictionnary");
834
835   *head = xbt_dict_new();
836   debuged_add(*head, "12");
837   debuged_add(*head, "12a");
838   debuged_add(*head, "12b");
839   debuged_add(*head, "123");
840   debuged_add(*head, "123456");
841   /* Child becomes child of what to add */
842   debuged_add(*head, "1234");
843   /* Need of common ancestor */
844   debuged_add(*head, "123457");
845 }
846
847
848 static void search_ext(xbt_dict_t head, const char *key, const char *data)
849 {
850   void *found;
851
852   xbt_test_add1("Search %s", key);
853   found = xbt_dict_get(head, key);
854   xbt_test_log1("Found %s", (char *) found);
855   if (data)
856     xbt_test_assert1(found,
857                      "data do not match expectations: found NULL while searching for %s",
858                      data);
859   if (found)
860     xbt_test_assert2(!strcmp((char *) data, found),
861                      "data do not match expectations: found %s while searching for %s",
862                      (char *) found, data);
863 }
864
865 static void search(xbt_dict_t head, const char *key)
866 {
867   search_ext(head, key, key);
868 }
869
870 static void debuged_remove(xbt_dict_t head, const char *key)
871 {
872
873   xbt_test_add1("Remove '%s'", PRINTF_STR(key));
874   xbt_dict_remove(head, key);
875   /*  xbt_dict_dump(head,(void (*)(void*))&printf); */
876 }
877
878
879 static void traverse(xbt_dict_t head)
880 {
881   xbt_dict_cursor_t cursor = NULL;
882   char *key;
883   char *data;
884   int i = 0;
885
886   xbt_dict_foreach(head, cursor, key, data) {
887     if (!key || !data || strcmp(key, data)) {
888       xbt_test_log3("Seen #%d:  %s->%s", ++i, PRINTF_STR(key),
889                     PRINTF_STR(data));
890     } else {
891       xbt_test_log2("Seen #%d:  %s", ++i, PRINTF_STR(key));
892     }
893     xbt_test_assert2(!data || !strcmp(key, data),
894                      "Key(%s) != value(%s). Aborting", key, data);
895   }
896 }
897
898 static void search_not_found(xbt_dict_t head, const char *data)
899 {
900   int ok = 0;
901   xbt_ex_t e;
902
903   xbt_test_add1("Search %s (expected not to be found)", data);
904
905   TRY {
906     data = xbt_dict_get(head, data);
907     THROW1(unknown_error, 0, "Found something which shouldn't be there (%s)",
908            data);
909   } CATCH(e) {
910     if (e.category != not_found_error)
911       xbt_test_exception(e);
912     xbt_ex_free(e);
913     ok = 1;
914   }
915   xbt_test_assert0(ok, "Exception not raised");
916 }
917
918 static void count(xbt_dict_t dict, int length)
919 {
920   xbt_dict_cursor_t cursor;
921   char *key;
922   void *data;
923   int effective = 0;
924
925
926   xbt_test_add1("Count elements (expecting %d)", length);
927   xbt_test_assert2(xbt_dict_length(dict) == length,
928                    "Announced length(%d) != %d.", xbt_dict_length(dict),
929                    length);
930
931   xbt_dict_foreach(dict, cursor, key, data)
932     effective++;
933
934   xbt_test_assert2(effective == length, "Effective length(%d) != %d.",
935                    effective, length);
936
937 }
938
939 static void count_check_get_key(xbt_dict_t dict, int length)
940 {
941   xbt_dict_cursor_t cursor;
942   char *key,*key2;
943   void *data;
944   int effective = 0;
945
946
947   xbt_test_add1("Count elements (expecting %d), and test the getkey function", length);
948   xbt_test_assert2(xbt_dict_length(dict) == length,
949                    "Announced length(%d) != %d.", xbt_dict_length(dict),
950                    length);
951
952   xbt_dict_foreach(dict, cursor, key, data) {
953     effective++;
954     key2 = xbt_dict_get_key(dict,data);
955     xbt_assert2(!strcmp(key,key2),
956           "The data was registered under %s instead of %s as expected",key2,key);
957   }
958
959   xbt_test_assert2(effective == length, "Effective length(%d) != %d.",
960                    effective, length);
961
962 }
963
964 xbt_ex_t e;
965 xbt_dict_t head = NULL;
966 char *data;
967
968
969 XBT_TEST_UNIT("basic", test_dict_basic,"Basic usage: change, retrieve, traverse")
970 {
971   xbt_test_add0("Traversal the null dictionary");
972   traverse(head);
973
974   xbt_test_add0("Traversal and search the empty dictionary");
975   head = xbt_dict_new();
976   traverse(head);
977   TRY {
978     debuged_remove(head, "12346");
979   } CATCH(e) {
980     if (e.category != not_found_error)
981       xbt_test_exception(e);
982     xbt_ex_free(e);
983   }
984   xbt_dict_free(&head);
985
986   xbt_test_add0("Traverse the full dictionary");
987   fill(&head);
988   count_check_get_key(head, 7);
989
990   debuged_add_ext(head, "toto", "tutu");
991   search_ext(head, "toto", "tutu");
992   debuged_remove(head, "toto");
993
994   search(head, "12a");
995   traverse(head);
996
997   xbt_test_add0("Free the dictionary (twice)");
998   xbt_dict_free(&head);
999   xbt_dict_free(&head);
1000
1001   /* CHANGING */
1002   fill(&head);
1003   count_check_get_key(head, 7);
1004   xbt_test_add0("Change 123 to 'Changed 123'");
1005   xbt_dict_set(head, "123", strdup("Changed 123"), &free);
1006   count_check_get_key(head, 7);
1007
1008   xbt_test_add0("Change 123 back to '123'");
1009   xbt_dict_set(head, "123", strdup("123"), &free);
1010   count_check_get_key(head, 7);
1011
1012   xbt_test_add0("Change 12a to 'Dummy 12a'");
1013   xbt_dict_set(head, "12a", strdup("Dummy 12a"), &free);
1014   count_check_get_key(head, 7);
1015
1016   xbt_test_add0("Change 12a to '12a'");
1017   xbt_dict_set(head, "12a", strdup("12a"), &free);
1018   count_check_get_key(head, 7);
1019
1020   xbt_test_add0("Traverse the resulting dictionary");
1021   traverse(head);
1022
1023   /* RETRIEVE */
1024   xbt_test_add0("Search 123");
1025   data = xbt_dict_get(head, "123");
1026   xbt_test_assert(data);
1027   xbt_test_assert(!strcmp("123", data));
1028
1029   search_not_found(head, "Can't be found");
1030   search_not_found(head, "123 Can't be found");
1031   search_not_found(head, "12345678 NOT");
1032
1033   search(head, "12a");
1034   search(head, "12b");
1035   search(head, "12");
1036   search(head, "123456");
1037   search(head, "1234");
1038   search(head, "123457");
1039
1040   xbt_test_add0("Traverse the resulting dictionary");
1041   traverse(head);
1042
1043   /*  xbt_dict_dump(head,(void (*)(void*))&printf); */
1044
1045   xbt_test_add0("Free the dictionary twice");
1046   xbt_dict_free(&head);
1047   xbt_dict_free(&head);
1048
1049   xbt_test_add0("Traverse the resulting dictionary");
1050   traverse(head);
1051 }
1052
1053 XBT_TEST_UNIT("remove", test_dict_remove, "Removing some values")
1054 {
1055   fill(&head);
1056   count(head, 7);
1057   xbt_test_add0("Remove non existing data");
1058   TRY {
1059     debuged_remove(head, "Does not exist");
1060   }
1061   CATCH(e) {
1062     if (e.category != not_found_error)
1063       xbt_test_exception(e);
1064     xbt_ex_free(e);
1065   }
1066   traverse(head);
1067
1068   xbt_dict_free(&head);
1069
1070   xbt_test_add0
1071     ("Remove each data manually (traversing the resulting dictionary each time)");
1072   fill(&head);
1073   debuged_remove(head, "12a");
1074   traverse(head);
1075   count(head, 6);
1076   debuged_remove(head, "12b");
1077   traverse(head);
1078   count(head, 5);
1079   debuged_remove(head, "12");
1080   traverse(head);
1081   count(head, 4);
1082   debuged_remove(head, "123456");
1083   traverse(head);
1084   count(head, 3);
1085   TRY {
1086     debuged_remove(head, "12346");
1087   }
1088   CATCH(e) {
1089     if (e.category != not_found_error)
1090       xbt_test_exception(e);
1091     xbt_ex_free(e);
1092     traverse(head);
1093   }
1094   debuged_remove(head, "1234");
1095   traverse(head);
1096   debuged_remove(head, "123457");
1097   traverse(head);
1098   debuged_remove(head, "123");
1099   traverse(head);
1100   TRY {
1101     debuged_remove(head, "12346");
1102   }
1103   CATCH(e) {
1104     if (e.category != not_found_error)
1105       xbt_test_exception(e);
1106     xbt_ex_free(e);
1107   }
1108   traverse(head);
1109
1110   xbt_test_add0("Free dict, create new fresh one, and then reset the dict");
1111   xbt_dict_free(&head);
1112   fill(&head);
1113   xbt_dict_reset(head);
1114   count(head, 0);
1115   traverse(head);
1116
1117   xbt_test_add0("Free the dictionary twice");
1118   xbt_dict_free(&head);
1119   xbt_dict_free(&head);
1120 }
1121
1122 XBT_TEST_UNIT("nulldata", test_dict_nulldata, "NULL data management")
1123 {
1124   fill(&head);
1125
1126   xbt_test_add0("Store NULL under 'null'");
1127   xbt_dict_set(head, "null", NULL, NULL);
1128   search_ext(head, "null", NULL);
1129
1130   xbt_test_add0("Check whether I see it while traversing...");
1131   {
1132     xbt_dict_cursor_t cursor = NULL;
1133     char *key;
1134     int found = 0;
1135
1136     xbt_dict_foreach(head, cursor, key, data) {
1137       if (!key || !data || strcmp(key, data)) {
1138         xbt_test_log2("Seen:  %s->%s", PRINTF_STR(key), PRINTF_STR(data));
1139       } else {
1140         xbt_test_log1("Seen:  %s", PRINTF_STR(key));
1141       }
1142
1143       if (!strcmp(key, "null"))
1144         found = 1;
1145     }
1146     xbt_test_assert0(found,
1147                      "the key 'null', associated to NULL is not found");
1148   }
1149   xbt_dict_free(&head);
1150 }
1151
1152 static void debuged_addi(xbt_dict_t head, uintptr_t key, uintptr_t data) {
1153   xbt_test_log2("Add %ld under %ld", data, key);
1154
1155   xbt_dicti_set(head, key, data);
1156   if (XBT_LOG_ISENABLED(xbt_dict, xbt_log_priority_debug)) {
1157     xbt_dict_dump(head, (void (*)(void *)) &printf);
1158     fflush(stdout);
1159   }
1160   uintptr_t stored_data = xbt_dicti_get(head, key);
1161   xbt_test_assert3(stored_data==data,
1162       "Retrieved data (%ld) is not what I just stored (%ld) under key %ld",stored_data,data,key);
1163 }
1164
1165 XBT_TEST_UNIT("dicti", test_dict_scalar, "Scalar data and key management")
1166 {
1167   xbt_test_add0("Fill in the dictionnary");
1168
1169   head = xbt_dict_new();
1170   debuged_addi(head, 12, 12);
1171   debuged_addi(head, 13, 13);
1172   debuged_addi(head, 14, 14);
1173   debuged_addi(head, 15, 15);
1174   /* Change values */
1175   debuged_addi(head, 12, 15);
1176   debuged_addi(head, 15, 2000);
1177   debuged_addi(head, 15, 3000);
1178   /* 0 as key */
1179   debuged_addi(head, 0, 1000);
1180   debuged_addi(head, 0, 2000);
1181   debuged_addi(head, 0, 3000);
1182   /* 0 as value */
1183   debuged_addi(head, 12, 0);
1184   debuged_addi(head, 13, 0);
1185   debuged_addi(head, 12, 0);
1186   debuged_addi(head, 0, 0);
1187
1188   xbt_dict_free(&head);
1189 }
1190
1191 #define NB_ELM 20000
1192 #define SIZEOFKEY 1024
1193 static int countelems(xbt_dict_t head)
1194 {
1195   xbt_dict_cursor_t cursor;
1196   char *key;
1197   void *data;
1198   int res = 0;
1199
1200   xbt_dict_foreach(head, cursor, key, data) {
1201     res++;
1202   }
1203   return res;
1204 }
1205
1206 XBT_TEST_UNIT("crash", test_dict_crash, "Crash test")
1207 {
1208   xbt_dict_t head = NULL;
1209   int i, j, k, nb;
1210   char *key;
1211   void *data;
1212
1213   srand((unsigned int) time(NULL));
1214
1215   for (i = 0; i < 10; i++) {
1216     xbt_test_add2("CRASH test number %d (%d to go)", i + 1, 10 - i - 1);
1217     xbt_test_log0("Fill the struct, count its elems and frees the structure");
1218     xbt_test_log1("using 1000 elements with %d chars long randomized keys.",
1219                   SIZEOFKEY);
1220     head = xbt_dict_new();
1221     /* if (i%10) printf("."); else printf("%d",i/10); fflush(stdout); */
1222     nb = 0;
1223     for (j = 0; j < 1000; j++) {
1224       char *data = NULL;
1225       key = xbt_malloc(SIZEOFKEY);
1226
1227       do {
1228         for (k = 0; k < SIZEOFKEY - 1; k++)
1229           key[k] = rand() % ('z' - 'a') + 'a';
1230         key[k] = '\0';
1231         /*      printf("[%d %s]\n",j,key); */
1232         data = xbt_dict_get_or_null(head, key);
1233       } while (data != NULL);
1234
1235       xbt_dict_set(head, key, key, &free);
1236       data = xbt_dict_get(head, key);
1237       xbt_test_assert2(!strcmp(key, data),
1238                        "Retrieved value (%s) != Injected value (%s)", key,
1239                        data);
1240
1241       count(head, j + 1);
1242     }
1243     /*    xbt_dict_dump(head,(void (*)(void*))&printf); */
1244     traverse(head);
1245     xbt_dict_free(&head);
1246     xbt_dict_free(&head);
1247   }
1248
1249
1250   head = xbt_dict_new();
1251   xbt_test_add1("Fill %d elements, with keys being the number of element",
1252                 NB_ELM);
1253   for (j = 0; j < NB_ELM; j++) {
1254     /* if (!(j%1000)) { printf("."); fflush(stdout); } */
1255
1256     key = xbt_malloc(10);
1257
1258     sprintf(key, "%d", j);
1259     xbt_dict_set(head, key, key, &free);
1260   }
1261   /*xbt_dict_dump(head,(void (*)(void*))&printf); */
1262
1263   xbt_test_add0("Count the elements (retrieving the key and data for each)");
1264   i = countelems(head);
1265   xbt_test_log1("There is %d elements", i);
1266
1267   xbt_test_add1("Search my %d elements 20 times", NB_ELM);
1268   key = xbt_malloc(10);
1269   for (i = 0; i < 20; i++) {
1270     /* if (i%10) printf("."); else printf("%d",i/10); fflush(stdout); */
1271     for (j = 0; j < NB_ELM; j++) {
1272
1273       sprintf(key, "%d", j);
1274       data = xbt_dict_get(head, key);
1275       xbt_test_assert2(!strcmp(key, (char *) data),
1276                        "with get, key=%s != data=%s", key, (char *) data);
1277       data = xbt_dict_get_ext(head, key, strlen(key));
1278       xbt_test_assert2(!strcmp(key, (char *) data),
1279                        "with get_ext, key=%s != data=%s", key, (char *) data);
1280     }
1281   }
1282   free(key);
1283
1284   xbt_test_add1("Remove my %d elements", NB_ELM);
1285   key = xbt_malloc(10);
1286   for (j = 0; j < NB_ELM; j++) {
1287     /* if (!(j%10000)) printf("."); fflush(stdout); */
1288
1289     sprintf(key, "%d", j);
1290     xbt_dict_remove(head, key);
1291   }
1292   free(key);
1293
1294
1295   xbt_test_add0("Free the structure (twice)");
1296   xbt_dict_free(&head);
1297   xbt_dict_free(&head);
1298 }
1299
1300 static void str_free(void *s)
1301 {
1302   char *c = *(char **) s;
1303   free(c);
1304 }
1305
1306 XBT_TEST_UNIT("multicrash", test_dict_multicrash, "Multi-dict crash test")
1307 {
1308
1309 #undef NB_ELM
1310 #define NB_ELM 100              /*00 */
1311 #define DEPTH 5
1312 #define KEY_SIZE 512
1313 #define NB_TEST 20              /*20 */
1314   int verbose = 0;
1315
1316   xbt_dict_t mdict = NULL;
1317   int i, j, k, l;
1318   xbt_dynar_t keys = xbt_dynar_new(sizeof(char *), str_free);
1319   void *data;
1320   char *key;
1321
1322
1323   xbt_test_add0("Generic multicache CRASH test");
1324   xbt_test_log4(" Fill the struct and frees it %d times, using %d elements, "
1325                 "depth of multicache=%d, key size=%d",
1326                 NB_TEST, NB_ELM, DEPTH, KEY_SIZE);
1327
1328   for (l = 0; l < DEPTH; l++) {
1329     key = xbt_malloc(KEY_SIZE);
1330     xbt_dynar_push(keys, &key);
1331   }
1332
1333   for (i = 0; i < NB_TEST; i++) {
1334     mdict = xbt_dict_new();
1335     VERB1("mdict=%p", mdict);
1336     if (verbose > 0)
1337       printf("Test %d\n", i);
1338     /* else if (i%10) printf("."); else printf("%d",i/10); */
1339
1340     for (j = 0; j < NB_ELM; j++) {
1341       if (verbose > 0)
1342         printf("  Add {");
1343
1344       for (l = 0; l < DEPTH; l++) {
1345         key = *(char **) xbt_dynar_get_ptr(keys, l);
1346
1347         for (k = 0; k < KEY_SIZE - 1; k++)
1348           key[k] = rand() % ('z' - 'a') + 'a';
1349
1350         key[k] = '\0';
1351
1352         if (verbose > 0)
1353           printf("%p=%s %s ", key, key, (l < DEPTH - 1 ? ";" : "}"));
1354       }
1355       if (verbose > 0)
1356         printf("in multitree %p.\n", mdict);
1357
1358       xbt_multidict_set(mdict, keys, xbt_strdup(key), free);
1359
1360       data = xbt_multidict_get(mdict, keys);
1361
1362       xbt_test_assert2(data && !strcmp((char *) data, key),
1363                        "Retrieved value (%s) does not match the given one (%s)\n",
1364                        (char *) data, key);
1365     }
1366     xbt_dict_free(&mdict);
1367   }
1368
1369   xbt_dynar_free(&keys);
1370
1371   /*  if (verbose>0)
1372      xbt_dict_dump(mdict,&xbt_dict_print); */
1373
1374   xbt_dict_free(&mdict);
1375   xbt_dynar_free(&keys);
1376
1377 }
1378 #endif /* SIMGRID_TEST */