3 /* dict - a generic dictionnary, variation over the B-tree concept */
5 /* Copyright (c) 2003,2004 Martin Quinson. All rights reserved. */
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. */
14 #include "xbt/mallocator.h"
15 #include "xbt_modinter.h"
16 #include "dict_private.h"
18 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_dict,xbt,
19 "Dictionaries provide the same functionnalities than hash tables");
20 /*####[ Private prototypes ]#################################################*/
22 static xbt_mallocator_t dict_mallocator = NULL;
23 static void* dict_mallocator_new_f(void);
24 static void dict_mallocator_free_f(void* dict);
25 static void dict_mallocator_reset_f(void* dict);
28 /*####[ Code ]###############################################################*/
32 * \return pointer to the destination
33 * \see xbt_dict_new_ext(), xbt_dict_free()
35 * Creates and initialize a new dictionnary with a default hashtable size.
37 xbt_dict_t xbt_dict_new(void) {
40 if (dict_mallocator == NULL) {
42 dict_mallocator = xbt_mallocator_new(256,
43 dict_mallocator_new_f,
44 dict_mallocator_free_f,
45 dict_mallocator_reset_f);
46 dict_elm_mallocator = xbt_mallocator_new(256,
47 dict_elm_mallocator_new_f,
48 dict_elm_mallocator_free_f,
49 dict_elm_mallocator_reset_f);
52 dict = xbt_mallocator_get(dict_mallocator);
53 dict->table_size = 127;
54 dict->table = xbt_new0(xbt_dictelm_t, dict->table_size+1);
63 * \param dict the dictionnary to be freed
65 * Frees a dictionary with all the data
67 void xbt_dict_free(xbt_dict_t *dict) {
69 xbt_dictelm_t current, previous;
73 if (dict != NULL && *dict != NULL) {
74 table_size = (*dict)->table_size;
75 table = (*dict)->table;
76 for (i = 0; i < table_size; i++) {
78 while (current != NULL) {
80 current = current->next;
81 xbt_dictelm_free(previous);
85 xbt_mallocator_release(dict_mallocator, *dict);
91 * Returns the hash code of a string.
93 static XBT_INLINE unsigned int xbt_dict_hash_ext(const char *str, int str_len) {
94 /* fast implementation of djb2 algorithm */
96 register unsigned int hash = 5381;
100 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
106 static XBT_INLINE unsigned int xbt_dict_hash(const char *str) {
107 /* fast implementation of djb2 algorithm */
109 register unsigned int hash = 5381;
111 while ( (c = *str++) ) {
112 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
118 /* Expend the size of the dict */
119 static void xbt_dict_rehash(xbt_dict_t dict) {
120 const int oldsize = dict->table_size + 1;
121 register int newsize = oldsize * 2;
123 register xbt_dictelm_t *currcell;
124 register xbt_dictelm_t *twincell;
125 register xbt_dictelm_t bucklet;
126 register xbt_dictelm_t *pprev;
128 currcell = (xbt_dictelm_t*) xbt_realloc((char*)dict->table, newsize * sizeof(xbt_dictelm_t));
129 memset(&currcell[oldsize], 0, oldsize * sizeof(xbt_dictelm_t)); /* zero second half */
130 dict->table_size = --newsize;
131 dict->table = currcell;
132 DEBUG2("REHASH (%d->%d)",oldsize,newsize);
134 for (i=0; i<oldsize; i++,currcell++) {
135 if (!*currcell) /* empty cell */
137 twincell = currcell+oldsize;
138 for (pprev = currcell, bucklet = *currcell;
141 /* Since we use "& size" instead of "%size" and since the size was doubled,
142 each bucklet of this cell must either :
143 - stay in cell i (ie, currcell)
144 - go to the cell i+oldsize (ie, twincell) */
145 if ((bucklet->hash_code & newsize) != i) { /* Move to b */
146 *pprev = bucklet->next;
147 bucklet->next = *twincell;
153 pprev = &bucklet->next;
158 if (!*currcell) /* everything moved */
166 * \brief Add data to the dict (arbitrary key)
167 * \param dict the container
168 * \param key the key to set the new data
169 * \param key_len the size of the \a key
170 * \param data the data to add in the dict
171 * \param free_ctn function to call with (\a key as argument) when
172 * \a key is removed from the dictionnary
174 * Set the \a data in the structure under the \a key, which can be any kind
175 * of data, as long as its length is provided in \a key_len.
177 XBT_INLINE void xbt_dict_set_ext(xbt_dict_t dict,
178 const char *key, int key_len,
179 void *data, void_f_pvoid_t free_ctn) {
181 unsigned int hash_code = xbt_dict_hash_ext(key,key_len);
183 xbt_dictelm_t current, previous = NULL;
186 // fprintf(stderr,"ADD %.*s hash = %d, size = %d, & = %d\n",key_len,key,hash_code, dict->table_size, hash_code & dict->table_size);
187 current = dict->table[hash_code & dict->table_size];
188 while (current != NULL &&
189 (hash_code != current->hash_code || key_len != current->key_len || memcmp(key, current->key, key_len))) {
191 current = current->next;
194 if (current == NULL) {
195 /* this key doesn't exist yet */
196 current = xbt_dictelm_new(key, key_len, hash_code, data, free_ctn);
198 if (previous == NULL) {
199 dict->table[hash_code & dict->table_size] = current;
201 if ((dict->fill * 100) / (dict->table_size + 1) > MAX_FILL_PERCENT)
202 xbt_dict_rehash(dict);
204 previous->next = current;
208 /* there is already an element with the same key: we overwrite it */
209 if (current->content != NULL && current->free_f != NULL) {
210 current->free_f(current->content);
212 current->content = data;
213 current->free_f = free_ctn;
218 * \brief Add data to the dict (null-terminated key)
220 * \param dict the dict
221 * \param key the key to set the new data
222 * \param data the data to add in the dict
223 * \param free_ctn function to call with (\a key as argument) when
224 * \a key is removed from the dictionnary
226 * set the \a data in the structure under the \a key, which is a
227 * null terminated string.
229 void xbt_dict_set(xbt_dict_t dict,
232 void_f_pvoid_t free_ctn) {
234 xbt_dict_set_ext(dict, key, strlen(key), data, free_ctn);
238 * \brief Retrieve data from the dict (arbitrary key)
240 * \param dict the dealer of data
241 * \param key the key to find data
242 * \param key_len the size of the \a key
243 * \return the data that we are looking for
245 * Search the given \a key. Throws not_found_error when not found.
247 void *xbt_dict_get_ext(xbt_dict_t dict,
248 const char *key, int key_len) {
251 unsigned int hash_code = xbt_dict_hash_ext(key,key_len);
252 xbt_dictelm_t current;
256 current = dict->table[hash_code & dict->table_size];
257 while (current != NULL &&
258 (hash_code != current->hash_code || key_len != current->key_len || memcmp(key, current->key, key_len))) {
259 current = current->next;
263 THROW2(not_found_error, 0, "key %.*s not found", key_len, key);
265 return current->content;
269 * \brief Retrieve data from the dict (null-terminated key)
271 * \param dict the dealer of data
272 * \param key the key to find data
273 * \return the data that we are looking for
275 * Search the given \a key. Throws not_found_error when not found.
276 * Check xbt_dict_get_or_null() for a version returning NULL without exception when
279 void *xbt_dict_get(xbt_dict_t dict,
283 unsigned int hash_code = xbt_dict_hash(key);
284 xbt_dictelm_t current;
288 current = dict->table[hash_code & dict->table_size];
289 while (current != NULL && hash_code != current->hash_code && strcmp(key, current->key))
290 current = current->next;
293 THROW1(not_found_error, 0, "key %s not found", key);
295 return current->content;
299 * \brief like xbt_dict_get(), but returning NULL when not found
301 void *xbt_dict_get_or_null(xbt_dict_t dict,
303 unsigned int hash_code = xbt_dict_hash(key);
304 xbt_dictelm_t current;
308 current = dict->table[hash_code & dict->table_size];
309 while (current != NULL &&
310 hash_code != current->hash_code && strcmp(key, current->key))
311 current = current->next;
316 return current->content;
321 * \brief Remove data from the dict (arbitrary key)
323 * \param dict the trash can
324 * \param key the key of the data to be removed
325 * \param key_len the size of the \a key
327 * Remove the entry associated with the given \a key (throws not_found)
329 void xbt_dict_remove_ext(xbt_dict_t dict,
334 unsigned int hash_code = xbt_dict_hash_ext(key,key_len);
335 xbt_dictelm_t current, previous = NULL;
339 // fprintf(stderr,"RM %.*s hash = %d, size = %d, & = %d\n",key_len,key,hash_code, dict->table_size, hash_code & dict->table_size);
340 current = dict->table[hash_code & dict->table_size];
341 while (current != NULL &&
342 (hash_code != current->hash_code || key_len != current->key_len || strncmp(key, current->key, key_len))) {
343 previous = current; /* save the previous node */
344 current = current->next;
348 THROW2(not_found_error, 0, "key %.*s not found", key_len, key);
350 if (previous != NULL) {
351 previous->next = current->next;
353 dict->table[hash_code & dict->table_size] = current->next;
356 if (!dict->table[hash_code & dict->table_size])
359 xbt_dictelm_free(current);
364 * \brief Remove data from the dict (null-terminated key)
366 * \param dict the dict
367 * \param key the key of the data to be removed
369 * Remove the entry associated with the given \a key
371 void xbt_dict_remove(xbt_dict_t dict, const char *key) {
372 xbt_dict_remove_ext(dict, key, strlen(key));
376 * \brief Remove all data from the dict
377 * \param dict the dict
379 void xbt_dict_reset(xbt_dict_t dict) {
382 xbt_dictelm_t current, previous = NULL;
386 if (dict->count == 0)
389 for (i = 0; i <= dict->table_size; i++) {
390 current = dict->table[i];
391 while (current != NULL) {
393 current = current->next;
394 xbt_dictelm_free(previous);
396 dict->table[i] = NULL;
404 * \brief Return the number of elements in the dict.
405 * \param dict a dictionary
407 int xbt_dict_length(xbt_dict_t dict) {
414 * \brief Outputs the content of the structure (debuging purpose)
416 * \param dict the exibitionist
417 * \param output a function to dump each data in the tree
419 * Ouputs the content of the structure. (for debuging purpose). \a ouput is a
420 * function to output the data. If NULL, data won't be displayed.
423 void xbt_dict_dump(xbt_dict_t dict,
424 void_f_pvoid_t output) {
426 xbt_dictelm_t element;
427 printf("Dict %p:\n", dict);
429 for (i = 0; i < dict->table_size; i++) {
430 element = dict->table[i];
431 while (element != NULL) {
432 printf("%s -> ", element->key);
433 if (output != NULL) {
434 (*output)(element->content);
437 element = element->next;
444 * Destroy the dict mallocators.
445 * This is an internal XBT function called by xbt_exit().
447 void xbt_dict_exit(void) {
448 if (dict_mallocator != NULL) {
449 xbt_mallocator_free(dict_mallocator);
450 dict_mallocator = NULL;
451 xbt_mallocator_free(dict_elm_mallocator);
452 dict_elm_mallocator = NULL;
456 static void* dict_mallocator_new_f(void) {
457 return xbt_new(s_xbt_dict_t, 1);
460 static void dict_mallocator_free_f(void* dict) {
464 static void dict_mallocator_reset_f(void* dict) {
465 /* nothing to do because all fields are
466 * initialized in xbt_dict_new
473 #include "portable.h"
475 XBT_LOG_EXTERNAL_CATEGORY(xbt_dict);
476 XBT_LOG_DEFAULT_CATEGORY(xbt_dict);
478 XBT_TEST_SUITE("dict","Dict data container");
480 static void print_str(void *str) {
481 printf("%s",(char*)PRINTF_STR(str));
484 static void debuged_add_ext(xbt_dict_t head,const char*key,const char*data_to_fill) {
485 char *data=xbt_strdup(data_to_fill);
487 xbt_test_log2("Add %s under %s",PRINTF_STR(data_to_fill),PRINTF_STR(key));
489 xbt_dict_set(head,key,data,&free);
490 if (XBT_LOG_ISENABLED(xbt_dict,xbt_log_priority_debug)) {
491 xbt_dict_dump(head,(void (*)(void*))&printf);
495 static void debuged_add(xbt_dict_t head,const char*key) {
496 debuged_add_ext(head,key,key);
499 static void fill(xbt_dict_t *head) {
500 xbt_test_add0("Fill in the dictionnary");
502 *head = xbt_dict_new();
503 debuged_add(*head,"12");
504 debuged_add(*head,"12a");
505 debuged_add(*head,"12b");
506 debuged_add(*head,"123");
507 debuged_add(*head,"123456");
508 /* Child becomes child of what to add */
509 debuged_add(*head,"1234");
510 /* Need of common ancestor */
511 debuged_add(*head,"123457");
515 static void search_ext(xbt_dict_t head,const char*key, const char *data) {
518 xbt_test_add1("Search %s",key);
519 found=xbt_dict_get(head,key);
520 xbt_test_log1("Found %s",(char *)found);
522 xbt_test_assert1(found,"data do not match expectations: found NULL while searching for %s",data);
524 xbt_test_assert2(!strcmp((char*)data,found),"data do not match expectations: found %s while searching for %s", (char*)found, data);
527 static void search(xbt_dict_t head,const char*key) {
528 search_ext(head,key,key);
531 static void debuged_remove(xbt_dict_t head,const char*key) {
533 xbt_test_add1("Remove '%s'",PRINTF_STR(key));
534 xbt_dict_remove(head,key);
535 /* xbt_dict_dump(head,(void (*)(void*))&printf); */
539 static void traverse(xbt_dict_t head) {
540 xbt_dict_cursor_t cursor=NULL;
545 xbt_dict_foreach(head,cursor,key,data) {
546 if (!key || !data || strcmp(key,data)) {
547 xbt_test_log3("Seen #%d: %s->%s",++i,PRINTF_STR(key),PRINTF_STR(data));
549 xbt_test_log2("Seen #%d: %s",++i,PRINTF_STR(key));
551 xbt_test_assert2(!data || !strcmp(key,data),
552 "Key(%s) != value(%s). Abording\n",key,data);
556 static void search_not_found(xbt_dict_t head, const char *data) {
560 xbt_test_add1("Search %s (expected not to be found)",data);
563 data = xbt_dict_get(head, data);
564 THROW1(unknown_error,0,"Found something which shouldn't be there (%s)",data);
566 if (e.category != not_found_error)
567 xbt_test_exception(e);
571 xbt_test_assert0(ok,"Exception not raised");
574 static void count(xbt_dict_t dict, int length) {
575 xbt_dict_cursor_t cursor;
581 xbt_test_add1("Count elements (expecting %d)", length);
582 xbt_test_assert2(xbt_dict_length(dict) == length, "Announced length(%d) != %d.", xbt_dict_length(dict), length);
584 xbt_dict_foreach(dict,cursor,key,data) {
587 xbt_test_assert2(effective == length, "Effective length(%d) != %d.", effective, length);
591 xbt_dict_t head=NULL;
595 XBT_TEST_UNIT("basic",test_dict_basic,"Basic usage: change, retrieve, traverse"){
596 xbt_test_add0("Traversal the null dictionnary");
599 xbt_test_add0("Traversal and search the empty dictionnary");
600 head = xbt_dict_new();
603 debuged_remove(head,"12346");
605 if (e.category != not_found_error)
606 xbt_test_exception(e);
609 xbt_dict_free(&head);
611 xbt_test_add0("Traverse the full dictionnary");
615 debuged_add_ext(head,"toto","tutu");
616 search_ext(head,"toto","tutu");
617 debuged_remove(head,"toto");
622 xbt_test_add0("Free the dictionnary (twice)");
623 xbt_dict_free(&head);
624 xbt_dict_free(&head);
629 xbt_test_add0("Change 123 to 'Changed 123'");
630 xbt_dict_set(head,"123",strdup("Changed 123"),&free);
633 xbt_test_add0("Change 123 back to '123'");
634 xbt_dict_set(head,"123",strdup("123"),&free);
637 xbt_test_add0("Change 12a to 'Dummy 12a'");
638 xbt_dict_set(head,"12a",strdup("Dummy 12a"),&free);
641 xbt_test_add0("Change 12a to '12a'");
642 xbt_dict_set(head,"12a",strdup("12a"),&free);
645 xbt_test_add0("Traverse the resulting dictionnary");
649 xbt_test_add0("Search 123");
650 data = xbt_dict_get(head,"123");
651 xbt_test_assert(data);
652 xbt_test_assert(!strcmp("123",data));
654 search_not_found(head,"Can't be found");
655 search_not_found(head,"123 Can't be found");
656 search_not_found(head,"12345678 NOT");
661 search(head,"123456");
663 search(head,"123457");
665 xbt_test_add0("Traverse the resulting dictionnary");
668 /* xbt_dict_dump(head,(void (*)(void*))&printf); */
670 xbt_test_add0("Free the dictionnary twice");
671 xbt_dict_free(&head);
672 xbt_dict_free(&head);
674 xbt_test_add0("Traverse the resulting dictionnary");
678 XBT_TEST_UNIT("remove",test_dict_remove,"Removing some values"){
681 xbt_test_add0("Remove non existing data");
683 debuged_remove(head,"Does not exist");
685 if (e.category != not_found_error)
686 xbt_test_exception(e);
691 xbt_dict_free(&head);
693 xbt_test_add0("Remove each data manually (traversing the resulting dictionnary each time)");
695 debuged_remove(head,"12a"); traverse(head);
697 debuged_remove(head,"12b"); traverse(head);
699 debuged_remove(head,"12"); traverse(head);
701 debuged_remove(head,"123456"); traverse(head);
704 debuged_remove(head,"12346");
706 if (e.category != not_found_error)
707 xbt_test_exception(e);
711 debuged_remove(head,"1234"); traverse(head);
712 debuged_remove(head,"123457"); traverse(head);
713 debuged_remove(head,"123"); traverse(head);
715 debuged_remove(head,"12346");
717 if (e.category != not_found_error)
718 xbt_test_exception(e);
722 xbt_test_add0("Free dict, create new fresh one, and then reset the dict");
723 xbt_dict_free(&head);
725 xbt_dict_reset(head);
729 xbt_test_add0("Free the dictionnary twice");
730 xbt_dict_free(&head);
731 xbt_dict_free(&head);
734 XBT_TEST_UNIT("nulldata",test_dict_nulldata,"NULL data management"){
737 xbt_test_add0("Store NULL under 'null'");
738 xbt_dict_set(head,"null",NULL,NULL);
739 search_ext(head,"null",NULL);
741 xbt_test_add0("Check whether I see it while traversing...");
743 xbt_dict_cursor_t cursor=NULL;
747 xbt_dict_foreach(head,cursor,key,data) {
748 if (!key || !data || strcmp(key,data)) {
749 xbt_test_log2("Seen: %s->%s",PRINTF_STR(key),PRINTF_STR(data));
751 xbt_test_log1("Seen: %s",PRINTF_STR(key));
754 if (!strcmp(key,"null"))
757 xbt_test_assert0(found,"the key 'null', associated to NULL is not found");
759 xbt_dict_free(&head);
763 #define SIZEOFKEY 1024
764 static int countelems(xbt_dict_t head) {
765 xbt_dict_cursor_t cursor;
770 xbt_dict_foreach(head,cursor,key,data) {
776 XBT_TEST_UNIT("crash",test_dict_crash,"Crash test"){
777 xbt_dict_t head=NULL;
782 srand((unsigned int)time(NULL));
785 xbt_test_add2("CRASH test number %d (%d to go)",i+1,10-i-1);
786 xbt_test_log0("Fill the struct, count its elems and frees the structure");
787 xbt_test_log1("using 1000 elements with %d chars long randomized keys.",SIZEOFKEY);
789 /* if (i%10) printf("."); else printf("%d",i/10); fflush(stdout); */
791 for (j=0;j<1000;j++) {
793 key=xbt_malloc(SIZEOFKEY);
796 for (k=0;k<SIZEOFKEY-1;k++)
797 key[k]=rand() % ('z' - 'a') + 'a';
799 /* printf("[%d %s]\n",j,key); */
800 data = xbt_dict_get_or_null(head,key);
801 } while (data != NULL);
803 xbt_dict_set(head,key,key,&free);
804 data = xbt_dict_get(head,key);
805 xbt_test_assert2(!strcmp(key,data), "Retrieved value (%s) != Injected value (%s)",key,data);
809 /* xbt_dict_dump(head,(void (*)(void*))&printf); */
811 xbt_dict_free(&head);
812 xbt_dict_free(&head);
817 xbt_test_add1("Fill %d elements, with keys being the number of element",NB_ELM);
818 for (j=0;j<NB_ELM;j++) {
819 /* if (!(j%1000)) { printf("."); fflush(stdout); } */
821 key = xbt_malloc(10);
824 xbt_dict_set(head,key,key,&free);
827 xbt_test_add0("Count the elements (retrieving the key and data for each)");
828 i = countelems(head);
829 xbt_test_log1("There is %d elements",i);
831 xbt_test_add1("Search my %d elements 20 times",NB_ELM);
834 /* if (i%10) printf("."); else printf("%d",i/10); fflush(stdout); */
835 for (j=0;j<NB_ELM;j++) {
838 data = xbt_dict_get(head,key);
839 xbt_test_assert2(!strcmp(key,(char*)data),
840 "key=%s != data=%s\n",key,(char*)data);
845 xbt_test_add1("Remove my %d elements",NB_ELM);
847 for (j=0;j<NB_ELM;j++) {
848 /* if (!(j%10000)) printf("."); fflush(stdout); */
851 xbt_dict_remove(head,key);
856 xbt_test_add0("Free the structure (twice)");
857 xbt_dict_free(&head);
858 xbt_dict_free(&head);
861 static void str_free(void *s) {
866 XBT_TEST_UNIT("multicrash",test_dict_multicrash,"Multi-dict crash test"){
869 #define NB_ELM 100 /*00*/
872 #define NB_TEST 20 /*20*/
875 xbt_dict_t mdict = NULL;
877 xbt_dynar_t keys = xbt_dynar_new(sizeof(char*),str_free);
882 xbt_test_add0("Generic multicache CRASH test");
883 xbt_test_log4(" Fill the struct and frees it %d times, using %d elements, "
884 "depth of multicache=%d, key size=%d",
885 NB_TEST,NB_ELM,DEPTH,KEY_SIZE);
887 for (l=0 ; l<DEPTH ; l++) {
888 key=xbt_malloc(KEY_SIZE);
889 xbt_dynar_push(keys,&key);
892 for (i=0;i<NB_TEST;i++) {
893 mdict = xbt_dict_new();
894 VERB1("mdict=%p",mdict);
896 printf("Test %d\n",i);
897 /* else if (i%10) printf("."); else printf("%d",i/10);*/
899 for (j=0;j<NB_ELM;j++) {
900 if (verbose>0) printf (" Add {");
902 for (l=0 ; l<DEPTH ; l++) {
903 key=*(char**)xbt_dynar_get_ptr(keys,l);
905 for (k=0;k<KEY_SIZE-1;k++)
906 key[k]=rand() % ('z' - 'a') + 'a';
910 if (verbose>0) printf("%p=%s %s ",key, key,(l<DEPTH-1?";":"}"));
912 if (verbose>0) printf("in multitree %p.\n",mdict);
914 xbt_multidict_set(mdict,keys,xbt_strdup(key),free);
916 data = xbt_multidict_get(mdict,keys);
918 xbt_test_assert2(data && !strcmp((char*)data,key),
919 "Retrieved value (%s) does not match the entrered one (%s)\n",
922 xbt_dict_free(&mdict);
925 xbt_dynar_free(&keys);
928 xbt_dict_dump(mdict,&xbt_dict_print);*/
930 xbt_dict_free(&mdict);
931 xbt_dynar_free(&keys);
934 #endif /* SIMGRID_TEST */