Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge changes of maxmin_selective_update branch into the trunk.
[simgrid.git] / src / xbt / dict.c
1 /* $Id$ */
2
3 /* dict - a generic dictionary, variation over the B-tree concept          */
4
5 /* Copyright (c) 2003,2004 Martin Quinson. All rights reserved.             */
6
7 /* This program is free software; you can redistribute it and/or modify it
8  * under the terms of the license (GNU LGPL) which comes with this package. */
9
10 #define DJB2_HASH_FUNCTION
11 //#define FNV_HASH_FUNCTION
12
13 #include <string.h>
14 #include <stdio.h>
15 #include "xbt/ex.h"
16 #include "xbt/log.h"
17 #include "xbt/mallocator.h"
18 #include "xbt_modinter.h"
19 #include "dict_private.h"
20
21 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_dict, xbt,
22                                 "Dictionaries provide the same functionalities than hash tables");
23 /*####[ Private prototypes ]#################################################*/
24
25 static xbt_mallocator_t dict_mallocator = NULL;
26 static void *dict_mallocator_new_f(void);
27 static void dict_mallocator_free_f(void *dict);
28 static void dict_mallocator_reset_f(void *dict);
29
30
31 /*####[ Code ]###############################################################*/
32
33 /**
34  * \brief Constructor
35  * \return pointer to the destination
36  * \see xbt_dict_new_ext(), xbt_dict_free()
37  *
38  * Creates and initialize a new dictionary with a default hashtable size.
39  */
40 xbt_dict_t xbt_dict_new(void)
41 {
42   xbt_dict_t dict;
43
44   if (dict_mallocator == NULL) {
45     /* first run */
46     dict_mallocator = xbt_mallocator_new(256,
47                                          dict_mallocator_new_f,
48                                          dict_mallocator_free_f,
49                                          dict_mallocator_reset_f);
50     dict_elm_mallocator = xbt_mallocator_new(256,
51                                              dict_elm_mallocator_new_f,
52                                              dict_elm_mallocator_free_f,
53                                              dict_elm_mallocator_reset_f);
54   }
55
56   dict = xbt_mallocator_get(dict_mallocator);
57   dict->table_size = 127;
58   dict->table = xbt_new0(xbt_dictelm_t, dict->table_size + 1);
59   dict->count = 0;
60   dict->fill = 0;
61
62   return dict;
63 }
64
65 /**
66  * \brief Destructor
67  * \param dict the dictionary to be freed
68  *
69  * Frees a dictionary with all the data
70  */
71 void xbt_dict_free(xbt_dict_t * dict)
72 {
73   int i;
74   xbt_dictelm_t current, previous;
75   int table_size;
76   xbt_dictelm_t *table;
77
78   //  if ( *dict )  xbt_dict_dump_sizes(*dict);
79
80   if (dict != NULL && *dict != NULL) {
81     table_size = (*dict)->table_size;
82     table = (*dict)->table;
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 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 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 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   unsigned int hash_code = xbt_dict_hash_ext(key, key_len);
343   xbt_dictelm_t current;
344
345   xbt_assert(dict);
346
347   current = dict->table[hash_code & dict->table_size];
348   while (current != NULL &&
349          (hash_code != current->hash_code || key_len != current->key_len
350           || memcmp(key, current->key, key_len))) {
351     current = current->next;
352   }
353
354   if (current == NULL)
355     return NULL;
356
357   return current->content;
358 }
359
360 /**
361  * @brief retrieve the key associated to that object. Warning, that's a linear search
362  *
363  * Returns NULL if the object cannot be found
364  */
365 char *xbt_dict_get_key(xbt_dict_t dict, const void*data) {
366   int i;
367   xbt_dictelm_t current;
368
369
370   for (i = 0; i <= dict->table_size; i++) {
371     current = dict->table[i];
372     while (current != NULL) {
373       if (current->content == data)
374         return current->key;
375       current = current->next;
376     }
377   }
378
379   return NULL;
380 }
381
382 /**
383  * \brief Retrieve data from the dict (null-terminated key)
384  *
385  * \param dict the dealer of data
386  * \param key the key to find data
387  * \return the data that we are looking for
388  *
389  * Search the given \a key. Throws not_found_error when not found.
390  * Check xbt_dict_get_or_null() for a version returning NULL without exception when
391  * not found.
392  */
393 void *xbt_dict_get(xbt_dict_t dict, const char *key)
394 {
395
396   unsigned int hash_code = xbt_dict_hash(key);
397   xbt_dictelm_t current;
398
399   xbt_assert(dict);
400
401   current = dict->table[hash_code & dict->table_size];
402   while (current != NULL
403          && (hash_code != current->hash_code || strcmp(key, current->key)))
404     current = current->next;
405
406   if (current == NULL)
407     THROW1(not_found_error, 0, "key %s not found", key);
408
409   return current->content;
410 }
411
412 /**
413  * \brief like xbt_dict_get(), but returning NULL when not found
414  */
415 void *xbt_dict_get_or_null(xbt_dict_t dict, const char *key)
416 {
417   unsigned int hash_code = xbt_dict_hash(key);
418   xbt_dictelm_t current;
419
420   xbt_assert(dict);
421
422   current = dict->table[hash_code & dict->table_size];
423   while (current != NULL &&
424          hash_code != current->hash_code && strcmp(key, current->key))
425     current = current->next;
426
427   if (current == NULL)
428     return NULL;
429
430   return current->content;
431 }
432
433
434 /**
435  * \brief Remove data from the dict (arbitrary key)
436  *
437  * \param dict the trash can
438  * \param key the key of the data to be removed
439  * \param key_len the size of the \a key
440  *
441  * Remove the entry associated with the given \a key (throws not_found)
442  */
443 void xbt_dict_remove_ext(xbt_dict_t dict, const char *key, int key_len)
444 {
445
446
447   unsigned int hash_code = xbt_dict_hash_ext(key, key_len);
448   xbt_dictelm_t current, previous = NULL;
449
450   xbt_assert(dict);
451
452   //  fprintf(stderr,"RM %.*s hash = %d, size = %d, & = %d\n",key_len,key,hash_code, dict->table_size, hash_code & dict->table_size);
453   current = dict->table[hash_code & dict->table_size];
454   while (current != NULL &&
455          (hash_code != current->hash_code || key_len != current->key_len
456           || strncmp(key, current->key, key_len))) {
457     previous = current;         /* save the previous node */
458     current = current->next;
459   }
460
461   if (current == NULL)
462     THROW2(not_found_error, 0, "key %.*s not found", key_len, key);
463
464   if (previous != NULL) {
465     previous->next = current->next;
466   } else {
467     dict->table[hash_code & dict->table_size] = current->next;
468   }
469
470   if (!dict->table[hash_code & dict->table_size])
471     dict->fill--;
472
473   xbt_dictelm_free(current);
474   dict->count--;
475 }
476
477 /**
478  * \brief Remove data from the dict (null-terminated key)
479  *
480  * \param dict the dict
481  * \param key the key of the data to be removed
482  *
483  * Remove the entry associated with the given \a key
484  */
485 void xbt_dict_remove(xbt_dict_t dict, const char *key)
486 {
487   xbt_dict_remove_ext(dict, key, strlen(key));
488 }
489
490 /**
491  * \brief Remove all data from the dict
492  * \param dict the dict
493  */
494 void xbt_dict_reset(xbt_dict_t dict)
495 {
496
497   int i;
498   xbt_dictelm_t current, previous = NULL;
499
500   xbt_assert(dict);
501
502   if (dict->count == 0)
503     return;
504
505   for (i = 0; i <= dict->table_size; i++) {
506     current = dict->table[i];
507     while (current != NULL) {
508       previous = current;
509       current = current->next;
510       xbt_dictelm_free(previous);
511     }
512     dict->table[i] = NULL;
513   }
514
515   dict->count = 0;
516   dict->fill = 0;
517 }
518
519 /**
520  * \brief Return the number of elements in the dict.
521  * \param dict a dictionary
522  */
523 int xbt_dict_length(xbt_dict_t dict)
524 {
525   xbt_assert(dict);
526
527   return dict->count;
528 }
529
530 /** @brief function to be used in xbt_dict_dump as long as the stored values are strings */
531 void xbt_dict_dump_output_string(void *s)
532 {
533   fputs(s, stdout);
534 }
535
536
537 /**
538  * \brief Outputs the content of the structure (debugging purpose)
539  *
540  * \param dict the exibitionist
541  * \param output a function to dump each data in the tree (check @ref xbt_dict_dump_output_string)
542  *
543  * Outputs the content of the structure. (for debugging purpose). \a output is a
544  * function to output the data. If NULL, data won't be displayed.
545  */
546
547 void xbt_dict_dump(xbt_dict_t dict, void_f_pvoid_t output)
548 {
549   int i;
550   xbt_dictelm_t element;
551   printf("Dict %p:\n", dict);
552   if (dict != NULL) {
553     for (i = 0; i < dict->table_size; i++) {
554       element = dict->table[i];
555       if (element) {
556         printf("[\n");
557         while (element != NULL) {
558           printf(" %s -> '", element->key);
559           if (output != NULL) {
560             (*output) (element->content);
561           }
562           printf("'\n");
563           element = element->next;
564         }
565         printf("]\n");
566       } else {
567         printf("[]\n");
568       }
569     }
570   }
571 }
572
573 xbt_dynar_t all_sizes = NULL;
574 /** @brief shows some debugging info about the bucklet repartition */
575 void xbt_dict_dump_sizes(xbt_dict_t dict)
576 {
577
578   int i;
579   unsigned int count;
580   unsigned int size;
581   xbt_dictelm_t element;
582   xbt_dynar_t sizes = xbt_dynar_new(sizeof(int), NULL);
583
584   printf("Dict %p: %d bucklets, %d used cells (of %d) ", dict, dict->count,
585          dict->fill, dict->table_size);
586   if (dict != NULL) {
587     for (i = 0; i < dict->table_size; i++) {
588       element = dict->table[i];
589       size = 0;
590       if (element) {
591         while (element != NULL) {
592           size++;
593           element = element->next;
594         }
595       }
596       if (xbt_dynar_length(sizes) <= size) {
597         int prevsize = 1;
598         xbt_dynar_set(sizes, size, &prevsize);
599       } else {
600         int prevsize;
601         xbt_dynar_get_cpy(sizes, size, &prevsize);
602         prevsize++;
603         xbt_dynar_set(sizes, size, &prevsize);
604       }
605     }
606     if (!all_sizes)
607       all_sizes = xbt_dynar_new(sizeof(int), NULL);
608
609     xbt_dynar_foreach(sizes, count, size) {
610       /* Copy values of this one into all_sizes */
611       int prevcount;
612       if (xbt_dynar_length(all_sizes) <= count) {
613         prevcount = size;
614         xbt_dynar_set(all_sizes, count, &prevcount);
615       } else {
616         xbt_dynar_get_cpy(all_sizes, count, &prevcount);
617         prevcount += size;
618         xbt_dynar_set(all_sizes, count, &prevcount);
619       }
620
621       /* Report current sizes */
622       if (count == 0)
623         continue;
624       if (size == 0)
625         continue;
626       printf("%delm x %u cells; ", count, size);
627     }
628   }
629   printf("\n");
630   xbt_dynar_free(&sizes);
631 }
632
633 /**
634  * Destroy the dict mallocators.
635  * This is an internal XBT function called by xbt_exit().
636  */
637 void xbt_dict_exit(void)
638 {
639   if (dict_mallocator != NULL) {
640     xbt_mallocator_free(dict_mallocator);
641     dict_mallocator = NULL;
642     xbt_mallocator_free(dict_elm_mallocator);
643     dict_elm_mallocator = NULL;
644   }
645   if (all_sizes) {
646     unsigned int count;
647     int size;
648     double avg = 0;
649     int total_count = 0;
650     printf("Overall stats:");
651     xbt_dynar_foreach(all_sizes, count, size) {
652       if (count == 0)
653         continue;
654       if (size == 0)
655         continue;
656       printf("%delm x %d cells; ", count, size);
657       avg += count * size;
658       total_count += size;
659     }
660     printf("; %f elm per cell\n", avg / (double) total_count);
661   }
662 }
663
664 static void *dict_mallocator_new_f(void)
665 {
666   return xbt_new(s_xbt_dict_t, 1);
667 }
668
669 static void dict_mallocator_free_f(void *dict)
670 {
671   xbt_free(dict);
672 }
673
674 static void dict_mallocator_reset_f(void *dict)
675 {
676   /* nothing to do because all fields are
677    * initialized in xbt_dict_new
678    */
679 }
680
681 #ifdef SIMGRID_TEST
682 #include "xbt.h"
683 #include "xbt/ex.h"
684 #include "portable.h"
685
686 XBT_LOG_EXTERNAL_CATEGORY(xbt_dict);
687 XBT_LOG_DEFAULT_CATEGORY(xbt_dict);
688
689 XBT_TEST_SUITE("dict", "Dict data container");
690
691 static void print_str(void *str)
692 {
693   printf("%s", (char *) PRINTF_STR(str));
694 }
695
696 static void debuged_add_ext(xbt_dict_t head, const char *key,
697                             const char *data_to_fill)
698 {
699   char *data = xbt_strdup(data_to_fill);
700
701   xbt_test_log2("Add %s under %s", PRINTF_STR(data_to_fill), PRINTF_STR(key));
702
703   xbt_dict_set(head, key, data, &free);
704   if (XBT_LOG_ISENABLED(xbt_dict, xbt_log_priority_debug)) {
705     xbt_dict_dump(head, (void (*)(void *)) &printf);
706     fflush(stdout);
707   }
708 }
709
710 static void debuged_add(xbt_dict_t head, const char *key)
711 {
712   debuged_add_ext(head, key, key);
713 }
714
715 static void fill(xbt_dict_t * head)
716 {
717   xbt_test_add0("Fill in the dictionnary");
718
719   *head = xbt_dict_new();
720   debuged_add(*head, "12");
721   debuged_add(*head, "12a");
722   debuged_add(*head, "12b");
723   debuged_add(*head, "123");
724   debuged_add(*head, "123456");
725   /* Child becomes child of what to add */
726   debuged_add(*head, "1234");
727   /* Need of common ancestor */
728   debuged_add(*head, "123457");
729 }
730
731
732 static void search_ext(xbt_dict_t head, const char *key, const char *data)
733 {
734   void *found;
735
736   xbt_test_add1("Search %s", key);
737   found = xbt_dict_get(head, key);
738   xbt_test_log1("Found %s", (char *) found);
739   if (data)
740     xbt_test_assert1(found,
741                      "data do not match expectations: found NULL while searching for %s",
742                      data);
743   if (found)
744     xbt_test_assert2(!strcmp((char *) data, found),
745                      "data do not match expectations: found %s while searching for %s",
746                      (char *) found, data);
747 }
748
749 static void search(xbt_dict_t head, const char *key)
750 {
751   search_ext(head, key, key);
752 }
753
754 static void debuged_remove(xbt_dict_t head, const char *key)
755 {
756
757   xbt_test_add1("Remove '%s'", PRINTF_STR(key));
758   xbt_dict_remove(head, key);
759   /*  xbt_dict_dump(head,(void (*)(void*))&printf); */
760 }
761
762
763 static void traverse(xbt_dict_t head)
764 {
765   xbt_dict_cursor_t cursor = NULL;
766   char *key;
767   char *data;
768   int i = 0;
769
770   xbt_dict_foreach(head, cursor, key, data) {
771     if (!key || !data || strcmp(key, data)) {
772       xbt_test_log3("Seen #%d:  %s->%s", ++i, PRINTF_STR(key),
773                     PRINTF_STR(data));
774     } else {
775       xbt_test_log2("Seen #%d:  %s", ++i, PRINTF_STR(key));
776     }
777     xbt_test_assert2(!data || !strcmp(key, data),
778                      "Key(%s) != value(%s). Aborting", key, data);
779   }
780 }
781
782 static void search_not_found(xbt_dict_t head, const char *data)
783 {
784   int ok = 0;
785   xbt_ex_t e;
786
787   xbt_test_add1("Search %s (expected not to be found)", data);
788
789   TRY {
790     data = xbt_dict_get(head, data);
791     THROW1(unknown_error, 0, "Found something which shouldn't be there (%s)",
792            data);
793   } CATCH(e) {
794     if (e.category != not_found_error)
795       xbt_test_exception(e);
796     xbt_ex_free(e);
797     ok = 1;
798   }
799   xbt_test_assert0(ok, "Exception not raised");
800 }
801
802 static void count(xbt_dict_t dict, int length)
803 {
804   xbt_dict_cursor_t cursor;
805   char *key;
806   void *data;
807   int effective = 0;
808
809
810   xbt_test_add1("Count elements (expecting %d)", length);
811   xbt_test_assert2(xbt_dict_length(dict) == length,
812                    "Announced length(%d) != %d.", xbt_dict_length(dict),
813                    length);
814
815   xbt_dict_foreach(dict, cursor, key, data)
816     effective++;
817
818   xbt_test_assert2(effective == length, "Effective length(%d) != %d.",
819                    effective, length);
820
821 }
822
823 static void count_check_get_key(xbt_dict_t dict, int length)
824 {
825   xbt_dict_cursor_t cursor;
826   char *key,*key2;
827   void *data;
828   int effective = 0;
829
830
831   xbt_test_add1("Count elements (expecting %d), and test the getkey function", length);
832   xbt_test_assert2(xbt_dict_length(dict) == length,
833                    "Announced length(%d) != %d.", xbt_dict_length(dict),
834                    length);
835
836   xbt_dict_foreach(dict, cursor, key, data) {
837     effective++;
838     key2 = xbt_dict_get_key(dict,data);
839     xbt_assert2(!strcmp(key,key2),
840           "The data was registered under %s instead of %s as expected",key2,key);
841   }
842
843   xbt_test_assert2(effective == length, "Effective length(%d) != %d.",
844                    effective, length);
845
846 }
847
848 xbt_ex_t e;
849 xbt_dict_t head = NULL;
850 char *data;
851
852
853 XBT_TEST_UNIT("basic", test_dict_basic,"Basic usage: change, retrieve, traverse")
854 {
855   xbt_test_add0("Traversal the null dictionary");
856   traverse(head);
857
858   xbt_test_add0("Traversal and search the empty dictionary");
859   head = xbt_dict_new();
860   traverse(head);
861   TRY {
862     debuged_remove(head, "12346");
863   } CATCH(e) {
864     if (e.category != not_found_error)
865       xbt_test_exception(e);
866     xbt_ex_free(e);
867   }
868   xbt_dict_free(&head);
869
870   xbt_test_add0("Traverse the full dictionary");
871   fill(&head);
872   count_check_get_key(head, 7);
873
874   debuged_add_ext(head, "toto", "tutu");
875   search_ext(head, "toto", "tutu");
876   debuged_remove(head, "toto");
877
878   search(head, "12a");
879   traverse(head);
880
881   xbt_test_add0("Free the dictionary (twice)");
882   xbt_dict_free(&head);
883   xbt_dict_free(&head);
884
885   /* CHANGING */
886   fill(&head);
887   count_check_get_key(head, 7);
888   xbt_test_add0("Change 123 to 'Changed 123'");
889   xbt_dict_set(head, "123", strdup("Changed 123"), &free);
890   count_check_get_key(head, 7);
891
892   xbt_test_add0("Change 123 back to '123'");
893   xbt_dict_set(head, "123", strdup("123"), &free);
894   count_check_get_key(head, 7);
895
896   xbt_test_add0("Change 12a to 'Dummy 12a'");
897   xbt_dict_set(head, "12a", strdup("Dummy 12a"), &free);
898   count_check_get_key(head, 7);
899
900   xbt_test_add0("Change 12a to '12a'");
901   xbt_dict_set(head, "12a", strdup("12a"), &free);
902   count_check_get_key(head, 7);
903
904   xbt_test_add0("Traverse the resulting dictionary");
905   traverse(head);
906
907   /* RETRIEVE */
908   xbt_test_add0("Search 123");
909   data = xbt_dict_get(head, "123");
910   xbt_test_assert(data);
911   xbt_test_assert(!strcmp("123", data));
912
913   search_not_found(head, "Can't be found");
914   search_not_found(head, "123 Can't be found");
915   search_not_found(head, "12345678 NOT");
916
917   search(head, "12a");
918   search(head, "12b");
919   search(head, "12");
920   search(head, "123456");
921   search(head, "1234");
922   search(head, "123457");
923
924   xbt_test_add0("Traverse the resulting dictionary");
925   traverse(head);
926
927   /*  xbt_dict_dump(head,(void (*)(void*))&printf); */
928
929   xbt_test_add0("Free the dictionary twice");
930   xbt_dict_free(&head);
931   xbt_dict_free(&head);
932
933   xbt_test_add0("Traverse the resulting dictionary");
934   traverse(head);
935 }
936
937 XBT_TEST_UNIT("remove", test_dict_remove, "Removing some values")
938 {
939   fill(&head);
940   count(head, 7);
941   xbt_test_add0("Remove non existing data");
942   TRY {
943     debuged_remove(head, "Does not exist");
944   }
945   CATCH(e) {
946     if (e.category != not_found_error)
947       xbt_test_exception(e);
948     xbt_ex_free(e);
949   }
950   traverse(head);
951
952   xbt_dict_free(&head);
953
954   xbt_test_add0
955     ("Remove each data manually (traversing the resulting dictionary each time)");
956   fill(&head);
957   debuged_remove(head, "12a");
958   traverse(head);
959   count(head, 6);
960   debuged_remove(head, "12b");
961   traverse(head);
962   count(head, 5);
963   debuged_remove(head, "12");
964   traverse(head);
965   count(head, 4);
966   debuged_remove(head, "123456");
967   traverse(head);
968   count(head, 3);
969   TRY {
970     debuged_remove(head, "12346");
971   }
972   CATCH(e) {
973     if (e.category != not_found_error)
974       xbt_test_exception(e);
975     xbt_ex_free(e);
976     traverse(head);
977   }
978   debuged_remove(head, "1234");
979   traverse(head);
980   debuged_remove(head, "123457");
981   traverse(head);
982   debuged_remove(head, "123");
983   traverse(head);
984   TRY {
985     debuged_remove(head, "12346");
986   }
987   CATCH(e) {
988     if (e.category != not_found_error)
989       xbt_test_exception(e);
990     xbt_ex_free(e);
991   }
992   traverse(head);
993
994   xbt_test_add0("Free dict, create new fresh one, and then reset the dict");
995   xbt_dict_free(&head);
996   fill(&head);
997   xbt_dict_reset(head);
998   count(head, 0);
999   traverse(head);
1000
1001   xbt_test_add0("Free the dictionary twice");
1002   xbt_dict_free(&head);
1003   xbt_dict_free(&head);
1004 }
1005
1006 XBT_TEST_UNIT("nulldata", test_dict_nulldata, "NULL data management")
1007 {
1008   fill(&head);
1009
1010   xbt_test_add0("Store NULL under 'null'");
1011   xbt_dict_set(head, "null", NULL, NULL);
1012   search_ext(head, "null", NULL);
1013
1014   xbt_test_add0("Check whether I see it while traversing...");
1015   {
1016     xbt_dict_cursor_t cursor = NULL;
1017     char *key;
1018     int found = 0;
1019
1020     xbt_dict_foreach(head, cursor, key, data) {
1021       if (!key || !data || strcmp(key, data)) {
1022         xbt_test_log2("Seen:  %s->%s", PRINTF_STR(key), PRINTF_STR(data));
1023       } else {
1024         xbt_test_log1("Seen:  %s", PRINTF_STR(key));
1025       }
1026
1027       if (!strcmp(key, "null"))
1028         found = 1;
1029     }
1030     xbt_test_assert0(found,
1031                      "the key 'null', associated to NULL is not found");
1032   }
1033   xbt_dict_free(&head);
1034 }
1035
1036 #define NB_ELM 20000
1037 #define SIZEOFKEY 1024
1038 static int countelems(xbt_dict_t head)
1039 {
1040   xbt_dict_cursor_t cursor;
1041   char *key;
1042   void *data;
1043   int res = 0;
1044
1045   xbt_dict_foreach(head, cursor, key, data) {
1046     res++;
1047   }
1048   return res;
1049 }
1050
1051 XBT_TEST_UNIT("crash", test_dict_crash, "Crash test")
1052 {
1053   xbt_dict_t head = NULL;
1054   int i, j, k, nb;
1055   char *key;
1056   void *data;
1057
1058   srand((unsigned int) time(NULL));
1059
1060   for (i = 0; i < 10; i++) {
1061     xbt_test_add2("CRASH test number %d (%d to go)", i + 1, 10 - i - 1);
1062     xbt_test_log0("Fill the struct, count its elems and frees the structure");
1063     xbt_test_log1("using 1000 elements with %d chars long randomized keys.",
1064                   SIZEOFKEY);
1065     head = xbt_dict_new();
1066     /* if (i%10) printf("."); else printf("%d",i/10); fflush(stdout); */
1067     nb = 0;
1068     for (j = 0; j < 1000; j++) {
1069       char *data = NULL;
1070       key = xbt_malloc(SIZEOFKEY);
1071
1072       do {
1073         for (k = 0; k < SIZEOFKEY - 1; k++)
1074           key[k] = rand() % ('z' - 'a') + 'a';
1075         key[k] = '\0';
1076         /*      printf("[%d %s]\n",j,key); */
1077         data = xbt_dict_get_or_null(head, key);
1078       } while (data != NULL);
1079
1080       xbt_dict_set(head, key, key, &free);
1081       data = xbt_dict_get(head, key);
1082       xbt_test_assert2(!strcmp(key, data),
1083                        "Retrieved value (%s) != Injected value (%s)", key,
1084                        data);
1085
1086       count(head, j + 1);
1087     }
1088     /*    xbt_dict_dump(head,(void (*)(void*))&printf); */
1089     traverse(head);
1090     xbt_dict_free(&head);
1091     xbt_dict_free(&head);
1092   }
1093
1094
1095   head = xbt_dict_new();
1096   xbt_test_add1("Fill %d elements, with keys being the number of element",
1097                 NB_ELM);
1098   for (j = 0; j < NB_ELM; j++) {
1099     /* if (!(j%1000)) { printf("."); fflush(stdout); } */
1100
1101     key = xbt_malloc(10);
1102
1103     sprintf(key, "%d", j);
1104     xbt_dict_set(head, key, key, &free);
1105   }
1106   /*xbt_dict_dump(head,(void (*)(void*))&printf); */
1107
1108   xbt_test_add0("Count the elements (retrieving the key and data for each)");
1109   i = countelems(head);
1110   xbt_test_log1("There is %d elements", i);
1111
1112   xbt_test_add1("Search my %d elements 20 times", NB_ELM);
1113   key = xbt_malloc(10);
1114   for (i = 0; i < 20; i++) {
1115     /* if (i%10) printf("."); else printf("%d",i/10); fflush(stdout); */
1116     for (j = 0; j < NB_ELM; j++) {
1117
1118       sprintf(key, "%d", j);
1119       data = xbt_dict_get(head, key);
1120       xbt_test_assert2(!strcmp(key, (char *) data),
1121                        "with get, key=%s != data=%s", key, (char *) data);
1122       data = xbt_dict_get_ext(head, key, strlen(key));
1123       xbt_test_assert2(!strcmp(key, (char *) data),
1124                        "with get_ext, key=%s != data=%s", key, (char *) data);
1125     }
1126   }
1127   free(key);
1128
1129   xbt_test_add1("Remove my %d elements", NB_ELM);
1130   key = xbt_malloc(10);
1131   for (j = 0; j < NB_ELM; j++) {
1132     /* if (!(j%10000)) printf("."); fflush(stdout); */
1133
1134     sprintf(key, "%d", j);
1135     xbt_dict_remove(head, key);
1136   }
1137   free(key);
1138
1139
1140   xbt_test_add0("Free the structure (twice)");
1141   xbt_dict_free(&head);
1142   xbt_dict_free(&head);
1143 }
1144
1145 static void str_free(void *s)
1146 {
1147   char *c = *(char **) s;
1148   free(c);
1149 }
1150
1151 XBT_TEST_UNIT("multicrash", test_dict_multicrash, "Multi-dict crash test")
1152 {
1153
1154 #undef NB_ELM
1155 #define NB_ELM 100              /*00 */
1156 #define DEPTH 5
1157 #define KEY_SIZE 512
1158 #define NB_TEST 20              /*20 */
1159   int verbose = 0;
1160
1161   xbt_dict_t mdict = NULL;
1162   int i, j, k, l;
1163   xbt_dynar_t keys = xbt_dynar_new(sizeof(char *), str_free);
1164   void *data;
1165   char *key;
1166
1167
1168   xbt_test_add0("Generic multicache CRASH test");
1169   xbt_test_log4(" Fill the struct and frees it %d times, using %d elements, "
1170                 "depth of multicache=%d, key size=%d",
1171                 NB_TEST, NB_ELM, DEPTH, KEY_SIZE);
1172
1173   for (l = 0; l < DEPTH; l++) {
1174     key = xbt_malloc(KEY_SIZE);
1175     xbt_dynar_push(keys, &key);
1176   }
1177
1178   for (i = 0; i < NB_TEST; i++) {
1179     mdict = xbt_dict_new();
1180     VERB1("mdict=%p", mdict);
1181     if (verbose > 0)
1182       printf("Test %d\n", i);
1183     /* else if (i%10) printf("."); else printf("%d",i/10); */
1184
1185     for (j = 0; j < NB_ELM; j++) {
1186       if (verbose > 0)
1187         printf("  Add {");
1188
1189       for (l = 0; l < DEPTH; l++) {
1190         key = *(char **) xbt_dynar_get_ptr(keys, l);
1191
1192         for (k = 0; k < KEY_SIZE - 1; k++)
1193           key[k] = rand() % ('z' - 'a') + 'a';
1194
1195         key[k] = '\0';
1196
1197         if (verbose > 0)
1198           printf("%p=%s %s ", key, key, (l < DEPTH - 1 ? ";" : "}"));
1199       }
1200       if (verbose > 0)
1201         printf("in multitree %p.\n", mdict);
1202
1203       xbt_multidict_set(mdict, keys, xbt_strdup(key), free);
1204
1205       data = xbt_multidict_get(mdict, keys);
1206
1207       xbt_test_assert2(data && !strcmp((char *) data, key),
1208                        "Retrieved value (%s) does not match the given one (%s)\n",
1209                        (char *) data, key);
1210     }
1211     xbt_dict_free(&mdict);
1212   }
1213
1214   xbt_dynar_free(&keys);
1215
1216   /*  if (verbose>0)
1217      xbt_dict_dump(mdict,&xbt_dict_print); */
1218
1219   xbt_dict_free(&mdict);
1220   xbt_dynar_free(&keys);
1221
1222 }
1223 #endif /* SIMGRID_TEST */