Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
cleaning the actor twice seems somewhat overplayed
[simgrid.git] / src / xbt / dict.cpp
1 /* dict - a generic dictionary, variation over hash table                   */
2
3 /* Copyright (c) 2004-2019. The SimGrid Team. 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 #include "xbt/dict.h"
9 #include "dict_private.h"
10 #include "simgrid/Exception.hpp"
11 #include "src/xbt_modinter.h"
12 #include "xbt/ex.h"
13 #include "xbt/log.h"
14 #include "xbt/mallocator.h"
15 #include "xbt/str.h"
16
17 #include <cstdio>
18 #include <cstring>
19
20 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_dict, xbt, "Dictionaries provide the same functionalities as hash tables");
21
22 constexpr int MAX_FILL_PERCENT = 80;
23
24 /**
25  * @brief Constructor
26  * @param free_ctn function to call with (@a data as argument) when @a data is removed from the dictionary
27  * @return pointer to the destination
28  * @see xbt_dict_free()
29  *
30  * Creates and initialize a new dictionary with a default hashtable size.
31  * The dictionary is homogeneous: each element share the same free function.
32  */
33 xbt_dict_t xbt_dict_new_homogeneous(void_f_pvoid_t free_ctn)
34 {
35   if (dict_elm_mallocator == nullptr)
36     xbt_dict_preinit();
37
38   xbt_dict_t dict;
39
40   dict = xbt_new(s_xbt_dict_t, 1);
41   dict->free_f = free_ctn;
42   dict->table_size = 127;
43   dict->table = xbt_new0(xbt_dictelm_t, dict->table_size + 1);
44   dict->count = 0;
45   dict->fill = 0;
46
47   return dict;
48 }
49
50 /**
51  * @brief Destructor
52  * @param dict the dictionary to be freed
53  *
54  * Frees a dictionary with all the data
55  */
56 void xbt_dict_free(xbt_dict_t * dict)
57 {
58   if (dict != nullptr && *dict != nullptr) {
59     int table_size       = (*dict)->table_size;
60     xbt_dictelm_t* table = (*dict)->table;
61     /* Warning: the size of the table is 'table_size+1'...
62      * This is because table_size is used as a binary mask in xbt_dict_rehash */
63     for (int i = 0; (*dict)->count && i <= table_size; i++) {
64       xbt_dictelm_t current = table[i];
65       xbt_dictelm_t previous;
66
67       while (current != nullptr) {
68         previous = current;
69         current = current->next;
70         xbt_dictelm_free(*dict, previous);
71         (*dict)->count--;
72       }
73     }
74     xbt_free(table);
75     xbt_free(*dict);
76     *dict = nullptr;
77   }
78 }
79
80 /** Returns the amount of elements in the dict */
81 unsigned int xbt_dict_size(xbt_dict_t dict)
82 {
83   return (dict != nullptr ? static_cast<unsigned int>(dict->count) : static_cast<unsigned int>(0));
84 }
85
86 /* Expend the size of the dict */
87 static void xbt_dict_rehash(xbt_dict_t dict)
88 {
89   const unsigned oldsize = dict->table_size + 1;
90   unsigned newsize = oldsize * 2;
91
92   xbt_dictelm_t *currcell = (xbt_dictelm_t *) xbt_realloc((char *) dict->table, newsize * sizeof(xbt_dictelm_t));
93   memset(&currcell[oldsize], 0, oldsize * sizeof(xbt_dictelm_t));       /* zero second half */
94   newsize--;
95   dict->table_size = newsize;
96   dict->table = currcell;
97   XBT_DEBUG("REHASH (%u->%u)", oldsize, newsize);
98
99   for (unsigned i = 0; i < oldsize; i++, currcell++) {
100     if (*currcell == nullptr) /* empty cell */
101       continue;
102
103     xbt_dictelm_t *twincell = currcell + oldsize;
104     xbt_dictelm_t *pprev = currcell;
105     xbt_dictelm_t bucklet = *currcell;
106     for (; bucklet != nullptr; bucklet = *pprev) {
107       /* Since we use "& size" instead of "%size" and since the size was doubled, each bucklet of this cell must either:
108          - stay  in  cell i (ie, currcell)
109          - go to the cell i+oldsize (ie, twincell) */
110       if ((bucklet->hash_code & newsize) != i) {        /* Move to b */
111         *pprev = bucklet->next;
112         bucklet->next = *twincell;
113         if (*twincell == nullptr)
114           dict->fill++;
115         *twincell = bucklet;
116       } else {
117         pprev = &bucklet->next;
118       }
119     }
120
121     if (*currcell == nullptr) /* everything moved */
122       dict->fill--;
123   }
124 }
125
126 /**
127  * @brief Add data to the dict (arbitrary key)
128  * @param dict the container
129  * @param key the key to set the new data
130  * @param key_len the size of the @a key
131  * @param data the data to add in the dict
132  * @param free_ctn unused parameter (kept for compatibility)
133  *
134  * Set the @a data in the structure under the @a key, which can be any kind of data, as long as its length is provided
135  * in @a key_len.
136  */
137 void xbt_dict_set_ext(xbt_dict_t dict, const char* key, int key_len, void* data,
138                       XBT_ATTRIB_UNUSED void_f_pvoid_t free_ctn)
139 {
140   unsigned int hash_code = xbt_str_hash_ext(key, key_len);
141
142   xbt_dictelm_t current;
143   xbt_dictelm_t previous = nullptr;
144
145   XBT_CDEBUG(xbt_dict, "ADD %.*s hash = %u, size = %d, & = %u", key_len, key, hash_code,
146              dict->table_size, hash_code & dict->table_size);
147   current = dict->table[hash_code & dict->table_size];
148   while (current != nullptr && (hash_code != current->hash_code || key_len != current->key_len
149           || memcmp(key, current->key, key_len))) {
150     previous = current;
151     current = current->next;
152   }
153
154   if (current == nullptr) {
155     /* this key doesn't exist yet */
156     current = xbt_dictelm_new(key, key_len, hash_code, data);
157     dict->count++;
158     if (previous == nullptr) {
159       dict->table[hash_code & dict->table_size] = current;
160       dict->fill++;
161       if ((dict->fill * 100) / (dict->table_size + 1) > MAX_FILL_PERCENT)
162         xbt_dict_rehash(dict);
163     } else {
164       previous->next = current;
165     }
166   } else {
167     XBT_CDEBUG(xbt_dict, "Replace %.*s by %.*s under key %.*s",
168                key_len, (char *) current->content, key_len, (char *) data, key_len, (char *) key);
169     /* there is already an element with the same key: overwrite it */
170     xbt_dictelm_set_data(dict, current, data);
171   }
172 }
173
174 /**
175  * @brief Add data to the dict (null-terminated key)
176  *
177  * @param dict the dict
178  * @param key the key to set the new data
179  * @param data the data to add in the dict
180  * @param free_ctn unused parameter (kept for compatibility)
181  *
182  * set the @a data in the structure under the @a key, which is anull terminated string.
183  */
184 void xbt_dict_set(xbt_dict_t dict, const char *key, void *data, void_f_pvoid_t free_ctn)
185 {
186   xbt_dict_set_ext(dict, key, strlen(key), data, free_ctn);
187 }
188
189 /**
190  * @brief Retrieve data from the dict (arbitrary key)
191  *
192  * @param dict the dealer of data
193  * @param key the key to find data
194  * @param key_len the size of the @a key
195  * @return the data that we are looking for
196  *
197  * Search the given @a key. Throws not_found_error when not found.
198  */
199 void *xbt_dict_get_ext(xbt_dict_t dict, const char *key, int key_len)
200 {
201   unsigned int hash_code = xbt_str_hash_ext(key, key_len);
202   xbt_dictelm_t current = dict->table[hash_code & dict->table_size];
203
204   while (current != nullptr && (hash_code != current->hash_code || key_len != current->key_len
205           || memcmp(key, current->key, key_len))) {
206     current = current->next;
207   }
208
209   if (current == nullptr)
210     THROWF(not_found_error, 0, "key %.*s not found", key_len, key);
211
212   return current->content;
213 }
214
215 /** @brief like xbt_dict_get_ext(), but returning nullptr when not found */
216 void *xbt_dict_get_or_null_ext(xbt_dict_t dict, const char *key, int key_len)
217 {
218   unsigned int hash_code = xbt_str_hash_ext(key, key_len);
219   xbt_dictelm_t current = dict->table[hash_code & dict->table_size];
220
221   while (current != nullptr && (hash_code != current->hash_code || key_len != current->key_len
222           || memcmp(key, current->key, key_len))) {
223     current = current->next;
224   }
225
226   if (current == nullptr)
227     return nullptr;
228
229   return current->content;
230 }
231
232 /**
233  * @brief retrieve the key associated to that object. Warning, that's a linear search
234  *
235  * Returns nullptr if the object cannot be found
236  */
237 char *xbt_dict_get_key(xbt_dict_t dict, const void *data)
238 {
239   for (int i = 0; i <= dict->table_size; i++) {
240     xbt_dictelm_t current = dict->table[i];
241     while (current != nullptr) {
242       if (current->content == data)
243         return current->key;
244       current = current->next;
245     }
246   }
247   return nullptr;
248 }
249
250 /**
251  * @brief Retrieve data from the dict (null-terminated key)
252  *
253  * @param dict the dealer of data
254  * @param key the key to find data
255  * @return the data that we are looking for
256  *
257  * Search the given @a key. Throws not_found_error when not found.
258  * Check xbt_dict_get_or_null() for a version returning nullptr without exception when not found.
259  */
260 void *xbt_dict_get(xbt_dict_t dict, const char *key)
261 {
262   return xbt_dict_get_elm(dict, key)->content;
263 }
264
265 /**
266  * @brief Retrieve element from the dict (null-terminated key)
267  *
268  * @param dict the dealer of data
269  * @param key the key to find data
270  * @return the s_xbt_dictelm_t that we are looking for
271  *
272  * Search the given @a key. Throws not_found_error when not found.
273  * Check xbt_dict_get_or_null() for a version returning nullptr without exception when not found.
274  */
275 xbt_dictelm_t xbt_dict_get_elm(xbt_dict_t dict, const char *key)
276 {
277   xbt_dictelm_t current = xbt_dict_get_elm_or_null(dict, key);
278
279   if (current == nullptr)
280     THROWF(not_found_error, 0, "key %s not found", key);
281
282   return current;
283 }
284
285 /**
286  * @brief like xbt_dict_get(), but returning nullptr when not found
287  */
288 void *xbt_dict_get_or_null(xbt_dict_t dict, const char *key)
289 {
290   xbt_dictelm_t current = xbt_dict_get_elm_or_null(dict, key);
291
292   if (current == nullptr)
293     return nullptr;
294
295   return current->content;
296 }
297
298 /**
299  * @brief like xbt_dict_get_elm(), but returning nullptr when not found
300  */
301 xbt_dictelm_t xbt_dict_get_elm_or_null(xbt_dict_t dict, const char *key)
302 {
303   unsigned int hash_code = xbt_str_hash(key);
304   xbt_dictelm_t current = dict->table[hash_code & dict->table_size];
305
306   while (current != nullptr && (hash_code != current->hash_code || strcmp(key, current->key)))
307     current = current->next;
308   return current;
309 }
310
311 /**
312  * @brief Remove data from the dict (arbitrary key)
313  *
314  * @param dict the trash can
315  * @param key the key of the data to be removed
316  * @param key_len the size of the @a key
317  *
318  * Remove the entry associated with the given @a key (throws not_found)
319  */
320 void xbt_dict_remove_ext(xbt_dict_t dict, const char *key, int key_len)
321 {
322   unsigned int hash_code = xbt_str_hash_ext(key, key_len);
323   xbt_dictelm_t previous = nullptr;
324   xbt_dictelm_t current = dict->table[hash_code & dict->table_size];
325
326   while (current != nullptr && (hash_code != current->hash_code || key_len != current->key_len
327           || strncmp(key, current->key, key_len))) {
328     previous = current;         /* save the previous node */
329     current = current->next;
330   }
331
332   if (current == nullptr)
333     THROWF(not_found_error, 0, "key %.*s not found", key_len, key);
334   else {
335     if (previous != nullptr) {
336       previous->next = current->next;
337     } else {
338       dict->table[hash_code & dict->table_size] = current->next;
339     }
340   }
341
342   if (not dict->table[hash_code & dict->table_size])
343     dict->fill--;
344
345   xbt_dictelm_free(dict, current);
346   dict->count--;
347 }
348
349 /**
350  * @brief Remove data from the dict (null-terminated key)
351  *
352  * @param dict the dict
353  * @param key the key of the data to be removed
354  *
355  * Remove the entry associated with the given @a key
356  */
357 void xbt_dict_remove(xbt_dict_t dict, const char *key)
358 {
359   xbt_dict_remove_ext(dict, key, strlen(key));
360 }
361
362 /** @brief Remove all data from the dict */
363 void xbt_dict_reset(xbt_dict_t dict)
364 {
365   if (dict->count == 0)
366     return;
367
368   for (int i = 0; i <= dict->table_size; i++) {
369     xbt_dictelm_t previous = nullptr;
370     xbt_dictelm_t current = dict->table[i];
371     while (current != nullptr) {
372       previous = current;
373       current = current->next;
374       xbt_dictelm_free(dict, previous);
375     }
376     dict->table[i] = nullptr;
377   }
378
379   dict->count = 0;
380   dict->fill = 0;
381 }
382
383 /**
384  * @brief Return the number of elements in the dict.
385  * @param dict a dictionary
386  */
387 int xbt_dict_length(xbt_dict_t dict)
388 {
389   return dict->count;
390 }
391
392 /**
393  * @brief test if the dict is empty or not
394  */
395 int xbt_dict_is_empty(xbt_dict_t dict)
396 {
397   return not dict || (xbt_dict_length(dict) == 0);
398 }
399
400 /**
401  * @brief Outputs the content of the structure (debugging purpose)
402  *
403  * @param dict the exibitionist
404  * @param output a function to dump each data in the tree
405  *
406  * Outputs the content of the structure. (for debugging purpose).
407  * @a output is a function to output the data. If nullptr, data won't be displayed.
408  */
409 void xbt_dict_dump(xbt_dict_t dict, void_f_pvoid_t output)
410 {
411   xbt_dictelm_t element;
412   printf("Dict %p:\n", dict);
413   if (dict != nullptr) {
414     for (int i = 0; i < dict->table_size; i++) {
415       element = dict->table[i];
416       if (element) {
417         printf("[\n");
418         while (element != nullptr) {
419           printf(" %s -> '", element->key);
420           if (output != nullptr) {
421             output(element->content);
422           }
423           printf("'\n");
424           element = element->next;
425         }
426         printf("]\n");
427       } else {
428         printf("[]\n");
429       }
430     }
431   }
432 }
433
434 /**
435  * Create the dict mallocators.
436  * This is an internal XBT function called during the lib initialization.
437  * It can be used several times to recreate the mallocator, for example when you switch to MC mode
438  */
439 void xbt_dict_preinit()
440 {
441   if (dict_elm_mallocator == nullptr)
442     dict_elm_mallocator = xbt_mallocator_new(256, dict_elm_mallocator_new_f, dict_elm_mallocator_free_f,
443       dict_elm_mallocator_reset_f);
444 }
445
446 /**
447  * Destroy the dict mallocators.
448  * This is an internal XBT function during the lib initialization
449  */
450 void xbt_dict_postexit()
451 {
452   if (dict_elm_mallocator != nullptr) {
453     xbt_mallocator_free(dict_elm_mallocator);
454     dict_elm_mallocator = nullptr;
455   }
456 }