diff options
Diffstat (limited to '')
-rw-r--r-- | libraries/eina/src/tests/evas_hash.c | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/libraries/eina/src/tests/evas_hash.c b/libraries/eina/src/tests/evas_hash.c new file mode 100644 index 0000000..33615af --- /dev/null +++ b/libraries/eina/src/tests/evas_hash.c | |||
@@ -0,0 +1,536 @@ | |||
1 | #ifdef HAVE_CONFIG_H | ||
2 | # include "config.h" | ||
3 | #endif | ||
4 | |||
5 | #include <stdlib.h> | ||
6 | #include <string.h> | ||
7 | |||
8 | #include "Evas_Data.h" | ||
9 | |||
10 | typedef struct _Evas_Hash_El Evas_Hash_El; | ||
11 | |||
12 | struct _Evas_Hash_El | ||
13 | { | ||
14 | Evas_Object_List _list_data; | ||
15 | const char *key; | ||
16 | void *data; | ||
17 | }; | ||
18 | |||
19 | static inline int _evas_hash_gen(const char *key); | ||
20 | |||
21 | static int _evas_hash_alloc_error = 0; | ||
22 | |||
23 | static inline int | ||
24 | _evas_hash_gen(const char *key) | ||
25 | { | ||
26 | unsigned int hash_num = 5381; | ||
27 | const unsigned char *ptr; | ||
28 | |||
29 | if (!key) | ||
30 | return 0; | ||
31 | |||
32 | for (ptr = (unsigned char *)key; *ptr; ptr++) | ||
33 | hash_num = (hash_num * 33) ^ *ptr; | ||
34 | |||
35 | hash_num &= 0xff; | ||
36 | return (int)hash_num; | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * @defgroup Evas_Hash_Data Hash Data Functions | ||
41 | * | ||
42 | * Functions that add, access or remove data from hashes. | ||
43 | * | ||
44 | * The following example shows how to add and then access data in a | ||
45 | * hash table: | ||
46 | * @code | ||
47 | * Evas_Hash *hash = NULL; | ||
48 | * extern void *my_data; | ||
49 | * | ||
50 | * hash = evas_hash_add(hash, "My Data", my_data); | ||
51 | * if (evas_hash_alloc_error()) | ||
52 | * { | ||
53 | * fprintf(stderr, "ERROR: Memory is low. Hash allocation failed.\n"); | ||
54 | * exit(-1); | ||
55 | * } | ||
56 | * if (evas_hash_find(hash, "My Data") == my_data) | ||
57 | * { | ||
58 | * printf("My Data inserted and successfully found.\n"); | ||
59 | * } | ||
60 | * @endcode | ||
61 | * | ||
62 | * What follows is another example, showing how the @ref evas_hash_del | ||
63 | * function is used: | ||
64 | * @code | ||
65 | * extern Evas_Hash *hash; | ||
66 | * extern void *data; | ||
67 | * | ||
68 | * printf("Insert some data...\n"); | ||
69 | * hash = evas_hash_add(hash, "My Data", my_data); | ||
70 | * printf("Removing by key...\n"); | ||
71 | * hash = evas_hash_del(hash, "My Data", NULL); | ||
72 | * printf("Insert some more data as a NULL key...\n"); | ||
73 | * hash = evas_hash_add(hash, NULL, my_data); | ||
74 | * printf("Removing by data as a NULL key...\n"); | ||
75 | * hash = evas_hash_del(hash, NULL, my_data); | ||
76 | * @endcode | ||
77 | */ | ||
78 | |||
79 | /** | ||
80 | * Adds an entry to the given hash table. | ||
81 | * | ||
82 | * @p key is expected to be a unique string within the hash table. | ||
83 | * Otherwise, you cannot be sure which inserted data pointer will be | ||
84 | * accessed with @ref evas_hash_find , and removed with | ||
85 | * @ref evas_hash_del . | ||
86 | * | ||
87 | * Key strings are case sensitive. | ||
88 | * | ||
89 | * @ref evas_hash_alloc_error should be used to determine if an | ||
90 | * allocation error occurred during this function. | ||
91 | * | ||
92 | * @param hash The given hash table. Can be @c NULL, in which case a | ||
93 | * new hash table is allocated and returned. | ||
94 | * @param key A unique string. Can be @c NULL. | ||
95 | * @param data Data to associate with the string given by @p key. | ||
96 | * @return Either the given hash table, or if the given value for @p | ||
97 | * hash is @c NULL, then a new one. @c NULL will be returned | ||
98 | * if memory could not be allocated for a new table. | ||
99 | * @ingroup Evas_Hash_Data | ||
100 | */ | ||
101 | EAPI Evas_Hash * | ||
102 | evas_hash_add(Evas_Hash *hash, const char *key, const void *data) | ||
103 | { | ||
104 | int hash_num; | ||
105 | Evas_Hash_El *el; | ||
106 | |||
107 | if ((!key) || (!data)) | ||
108 | return hash; | ||
109 | |||
110 | _evas_hash_alloc_error = 0; | ||
111 | if (!hash) | ||
112 | { | ||
113 | hash = calloc(1, sizeof(struct _Evas_Hash)); | ||
114 | if (!hash) | ||
115 | { | ||
116 | _evas_hash_alloc_error = 1; | ||
117 | return NULL; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | if (!(el = malloc(sizeof(struct _Evas_Hash_El) + strlen(key) + 1))) | ||
122 | { | ||
123 | if (hash->population <= 0) | ||
124 | { | ||
125 | free(hash); | ||
126 | hash = NULL; | ||
127 | } | ||
128 | |||
129 | _evas_hash_alloc_error = 1; | ||
130 | return hash; | ||
131 | } | ||
132 | |||
133 | el->key = ((char *)el) + sizeof(struct _Evas_Hash_El); | ||
134 | strcpy((char *)el->key, key); | ||
135 | el->data = (void *)data; | ||
136 | hash_num = _evas_hash_gen(key); | ||
137 | hash->buckets[hash_num] = evas_object_list_prepend(hash->buckets[hash_num], | ||
138 | el); | ||
139 | hash->population++; | ||
140 | return hash; | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Adds an entry to the given hash table and does not duplicate the string key. | ||
145 | * | ||
146 | * @p key is expected to be a unique string within the hash table. | ||
147 | * Otherwise, you cannot be sure which inserted data pointer will be | ||
148 | * accessed with @ref evas_hash_find , and removed with | ||
149 | * @ref evas_hash_del . This call does not make a copy of the key so it must | ||
150 | * be a string constant or stored elsewhere (in the object being added) etc. | ||
151 | * | ||
152 | * Key strings are case sensitive. | ||
153 | * | ||
154 | * @ref evas_hash_alloc_error should be used to determine if an | ||
155 | * allocation error occurred during this function. | ||
156 | * | ||
157 | * @param hash The given hash table. Can be @c NULL, in which case a | ||
158 | * new hash table is allocated and returned. | ||
159 | * @param key A unique string. Can be @c NULL. | ||
160 | * @param data Data to associate with the string given by @p key. | ||
161 | * @return Either the given hash table, or if the given value for @p | ||
162 | * hash is @c NULL, then a new one. @c NULL will be returned | ||
163 | * if memory could not be allocated for a new table. | ||
164 | * @ingroup Evas_Hash_Data | ||
165 | */ | ||
166 | EAPI Evas_Hash * | ||
167 | evas_hash_direct_add(Evas_Hash *hash, const char *key, const void *data) | ||
168 | { | ||
169 | int hash_num; | ||
170 | Evas_Hash_El *el; | ||
171 | |||
172 | if ((!key) || (!data)) | ||
173 | return hash; | ||
174 | |||
175 | _evas_hash_alloc_error = 0; | ||
176 | if (!hash) | ||
177 | { | ||
178 | hash = calloc(1, sizeof(struct _Evas_Hash)); | ||
179 | if (!hash) | ||
180 | { | ||
181 | _evas_hash_alloc_error = 1; | ||
182 | return NULL; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | if (!(el = malloc(sizeof(struct _Evas_Hash_El)))) | ||
187 | { | ||
188 | if (hash->population <= 0) | ||
189 | { | ||
190 | free(hash); | ||
191 | hash = NULL; | ||
192 | } | ||
193 | |||
194 | _evas_hash_alloc_error = 1; | ||
195 | return hash; | ||
196 | } | ||
197 | |||
198 | el->key = key; | ||
199 | el->data = (void *)data; | ||
200 | hash_num = _evas_hash_gen(key); | ||
201 | hash->buckets[hash_num] = evas_object_list_prepend(hash->buckets[hash_num], | ||
202 | el); | ||
203 | hash->population++; | ||
204 | return hash; | ||
205 | } | ||
206 | |||
207 | /** | ||
208 | * Removes the entry identified by @p key or @p data from the given | ||
209 | * hash table. | ||
210 | * | ||
211 | * If @p key is @c NULL, then @p data is used to find a match to | ||
212 | * remove. | ||
213 | * | ||
214 | * @param hash The given hash table. | ||
215 | * @param key The key string. Can be @c NULL. | ||
216 | * @param data The data pointer to remove if @p key is @c NULL. | ||
217 | * Otherwise, not required and can be @c NULL. | ||
218 | * @return The modified hash table. If there are no entries left, the | ||
219 | * hash table will be freed and @c NULL will be returned. | ||
220 | * @ingroup Evas_Hash_Data | ||
221 | */ | ||
222 | EAPI Evas_Hash * | ||
223 | evas_hash_del(Evas_Hash *hash, const char *key, const void *data) | ||
224 | { | ||
225 | int hash_num; | ||
226 | Evas_Hash_El *el; | ||
227 | Evas_Object_List *l; | ||
228 | |||
229 | if (!hash) | ||
230 | return NULL; | ||
231 | |||
232 | if (!key) | ||
233 | for (hash_num = 0; hash_num < 256; hash_num++) | ||
234 | { | ||
235 | for (l = hash->buckets[hash_num]; l; l = l->next) | ||
236 | { | ||
237 | el = (Evas_Hash_El *)l; | ||
238 | if (el->data == data) | ||
239 | { | ||
240 | hash->buckets[hash_num] = evas_object_list_remove( | ||
241 | hash->buckets[hash_num], | ||
242 | el); | ||
243 | free(el); | ||
244 | hash->population--; | ||
245 | if (hash->population <= 0) | ||
246 | { | ||
247 | free(hash); | ||
248 | hash = NULL; | ||
249 | } | ||
250 | |||
251 | return hash; | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | else | ||
256 | { | ||
257 | hash_num = _evas_hash_gen(key); | ||
258 | for (l = hash->buckets[hash_num]; l; l = l->next) | ||
259 | { | ||
260 | el = (Evas_Hash_El *)l; | ||
261 | if (!strcmp(el->key, key)) | ||
262 | if ((!data) || (el->data == data)) | ||
263 | { | ||
264 | hash->buckets[hash_num] = evas_object_list_remove( | ||
265 | hash->buckets[hash_num], | ||
266 | el); | ||
267 | free(el); | ||
268 | hash->population--; | ||
269 | if (hash->population <= 0) | ||
270 | { | ||
271 | free(hash); | ||
272 | hash = NULL; | ||
273 | } | ||
274 | |||
275 | return hash; | ||
276 | } | ||
277 | |||
278 | } | ||
279 | } | ||
280 | |||
281 | return hash; | ||
282 | } | ||
283 | |||
284 | /** | ||
285 | * Retrieves a specific entry in the given hash table. | ||
286 | * @param hash The given hash table. | ||
287 | * @param key The key string of the entry to find. | ||
288 | * @return The data pointer for the stored entry, or @c NULL if not | ||
289 | * found. | ||
290 | * @ingroup Evas_Hash_Data | ||
291 | */ | ||
292 | EAPI void * | ||
293 | evas_hash_find(const Evas_Hash *hash, const char *key) | ||
294 | { | ||
295 | int hash_num; | ||
296 | Evas_Hash_El *el; | ||
297 | Evas_Object_List *l; | ||
298 | |||
299 | _evas_hash_alloc_error = 0; | ||
300 | if ((!hash) || (!key)) | ||
301 | return NULL; | ||
302 | |||
303 | hash_num = _evas_hash_gen(key); | ||
304 | for (l = hash->buckets[hash_num]; l; l = l->next) | ||
305 | { | ||
306 | el = (Evas_Hash_El *)l; | ||
307 | if (!strcmp(el->key, key)) | ||
308 | { | ||
309 | if (l != hash->buckets[hash_num]) | ||
310 | { | ||
311 | Evas_Object_List *bucket; | ||
312 | |||
313 | bucket = hash->buckets[hash_num]; | ||
314 | bucket = evas_object_list_remove(bucket, el); | ||
315 | bucket = evas_object_list_prepend(bucket, el); | ||
316 | ((Evas_Hash *)hash)->buckets[hash_num] = bucket; | ||
317 | } | ||
318 | |||
319 | return el->data; | ||
320 | } | ||
321 | } | ||
322 | return NULL; | ||
323 | } | ||
324 | |||
325 | /** | ||
326 | * Modifies the entry pointer at the specified key and returns the old entry | ||
327 | * @param hash The given hash table. | ||
328 | * @param key The key string of the entry to modify. | ||
329 | * @param data The data to replace the old entry, if it exists. | ||
330 | * @return The data pointer for the old stored entry, or @c NULL if not | ||
331 | * found. If an existing entry is not found, nothing is added to the | ||
332 | * hash. | ||
333 | * @ingroup Evas_Hash_Data | ||
334 | */ | ||
335 | EAPI void * | ||
336 | evas_hash_modify(Evas_Hash *hash, const char *key, const void *data) | ||
337 | { | ||
338 | int hash_num; | ||
339 | Evas_Hash_El *el; | ||
340 | Evas_Object_List *l; | ||
341 | |||
342 | _evas_hash_alloc_error = 0; | ||
343 | if (!hash) | ||
344 | return NULL; | ||
345 | |||
346 | hash_num = _evas_hash_gen(key); | ||
347 | for (l = hash->buckets[hash_num]; l; l = l->next) | ||
348 | { | ||
349 | el = (Evas_Hash_El *)l; | ||
350 | if ((key) && (!strcmp(el->key, key))) | ||
351 | { | ||
352 | void *old_data; | ||
353 | |||
354 | if (l != hash->buckets[hash_num]) | ||
355 | { | ||
356 | hash->buckets[hash_num] = evas_object_list_remove( | ||
357 | hash->buckets[hash_num], | ||
358 | el); | ||
359 | hash->buckets[hash_num] = evas_object_list_prepend( | ||
360 | hash->buckets[hash_num], | ||
361 | el); | ||
362 | } | ||
363 | |||
364 | old_data = el->data; | ||
365 | el->data = (void *)data; | ||
366 | return old_data; | ||
367 | } | ||
368 | } | ||
369 | return NULL; | ||
370 | } | ||
371 | |||
372 | /** | ||
373 | * @defgroup Evas_Hash_General_Group Hash General Functions | ||
374 | * | ||
375 | * Miscellaneous functions that operate on hash objects. | ||
376 | */ | ||
377 | |||
378 | /** | ||
379 | * Retrieves the number of buckets available in the given hash table. | ||
380 | * @param hash The given hash table. | ||
381 | * @return @c 256 if @p hash is not @c NULL. @c 0 otherwise. | ||
382 | * @ingroup Evas_Hash_General_Group | ||
383 | */ | ||
384 | EAPI int | ||
385 | evas_hash_size(const Evas_Hash *hash) | ||
386 | { | ||
387 | if (!hash) | ||
388 | return 0; | ||
389 | |||
390 | return 256; | ||
391 | } | ||
392 | |||
393 | /** | ||
394 | * @todo Complete polishing documentation for evas_hash.c. The | ||
395 | * functions' docs may be grouped, but they need some simplification. | ||
396 | */ | ||
397 | |||
398 | /** | ||
399 | * Free an entire hash table | ||
400 | * @param hash The hash table to be freed | ||
401 | * | ||
402 | * This function frees up all the memory allocated to storing the specified | ||
403 | * hash tale pointed to by @p hash. Any entries in the table that the program | ||
404 | * has no more pointers for elsewhere may now be lost, so this should only be | ||
405 | * called if the program has lready freed any allocated data in the hash table | ||
406 | * or has the pointers for data in the table stored elswehere as well. | ||
407 | * | ||
408 | * Example: | ||
409 | * @code | ||
410 | * extern Evas_Hash *hash; | ||
411 | * | ||
412 | * evas_hash_free(hash); | ||
413 | * hash = NULL; | ||
414 | * @endcode | ||
415 | * @ingroup Evas_Hash_General_Group | ||
416 | */ | ||
417 | EAPI void | ||
418 | evas_hash_free(Evas_Hash *hash) | ||
419 | { | ||
420 | int i, size; | ||
421 | |||
422 | if (!hash) | ||
423 | return; | ||
424 | |||
425 | size = evas_hash_size(hash); | ||
426 | for (i = 0; i < size; i++) | ||
427 | { | ||
428 | while (hash->buckets[i]) | ||
429 | { | ||
430 | Evas_Hash_El *el; | ||
431 | |||
432 | el = (Evas_Hash_El *)hash->buckets[i]; | ||
433 | hash->buckets[i] = evas_object_list_remove(hash->buckets[i], el); | ||
434 | free(el); | ||
435 | } | ||
436 | } | ||
437 | free(hash); | ||
438 | } | ||
439 | |||
440 | /** | ||
441 | * Call a function on every member stored in the hash table | ||
442 | * @param hash The hash table whose members will be walked | ||
443 | * @param func The function to call on each parameter | ||
444 | * @param fdata The data pointer to pass to the function being called | ||
445 | * | ||
446 | * This function goes through every entry in the hash table @p hash and calls | ||
447 | * the function @p func on each member. The function should NOT modify the | ||
448 | * hash table contents if it returns 1. IF the hash table contents are | ||
449 | * modified by this function or the function wishes to stop processing it must | ||
450 | * return 0, otherwise return 1 to keep processing. | ||
451 | * | ||
452 | * Example: | ||
453 | * @code | ||
454 | * extern Evas_Hash *hash; | ||
455 | * | ||
456 | * Evas_Bool hash_fn(Evas_Hash *hash, const char *key, void *data, void *fdata) | ||
457 | * { | ||
458 | * printf("Func data: %s, Hash entry: %s / %p\n", fdata, key, data); | ||
459 | * return 1; | ||
460 | * } | ||
461 | * | ||
462 | * int main(int argc, char **argv) | ||
463 | * { | ||
464 | * char *hash_fn_data; | ||
465 | * | ||
466 | * hash_fn_data = strdup("Hello World"); | ||
467 | * evas_hash_foreach(hash, hash_fn, hash_fn_data); | ||
468 | * free(hash_fn_data); | ||
469 | * } | ||
470 | * @endcode | ||
471 | * @ingroup Evas_Hash_General_Group | ||
472 | */ | ||
473 | EAPI void | ||
474 | evas_hash_foreach(const Evas_Hash *hash, Evas_Bool (*func)( | ||
475 | const Evas_Hash *hash, | ||
476 | const char *key, | ||
477 | void *data, | ||
478 | void *fdata), const void *fdata) | ||
479 | { | ||
480 | int i, size; | ||
481 | |||
482 | if (!hash) | ||
483 | return; | ||
484 | |||
485 | size = evas_hash_size(hash); | ||
486 | for (i = 0; i < size; i++) | ||
487 | { | ||
488 | Evas_Object_List *l, *next_l; | ||
489 | |||
490 | for (l = hash->buckets[i]; l; ) | ||
491 | { | ||
492 | Evas_Hash_El *el; | ||
493 | |||
494 | next_l = l->next; | ||
495 | el = (Evas_Hash_El *)l; | ||
496 | if (!func(hash, el->key, el->data, (void *)fdata)) | ||
497 | return; | ||
498 | |||
499 | l = next_l; | ||
500 | } | ||
501 | } | ||
502 | } | ||
503 | |||
504 | /** | ||
505 | * Return memory allocation failure flag after an function requiring allocation | ||
506 | * @return The state of the allocation flag | ||
507 | * | ||
508 | * This function returns the state of the memory allocation flag. This flag is | ||
509 | * set if memory allocations fail during evas_hash_add() calls. If they do, 1 | ||
510 | * will be returned, otherwise 0 will be returned. The flag will remain in its | ||
511 | * current state until the next call that requires allocation is called, and | ||
512 | * is then reset. | ||
513 | * | ||
514 | * Example: | ||
515 | * @code | ||
516 | * Evas_Hash *hash = NULL; | ||
517 | * extern void *my_data; | ||
518 | * | ||
519 | * hash = evas_hash_add(hash, "My Data", my_data); | ||
520 | * if (evas_hash_alloc_error()) | ||
521 | * { | ||
522 | * fprintf(stderr, "ERROR: Memory is low. Hash allocation failed.\n"); | ||
523 | * exit(-1); | ||
524 | * } | ||
525 | * if (evas_hash_find(hash, "My Data") == my_data) | ||
526 | * { | ||
527 | * printf("My Data inserted and successfully found.\n"); | ||
528 | * } | ||
529 | * @endcode | ||
530 | * @ingroup Evas_Hash_General_Group | ||
531 | */ | ||
532 | EAPI int | ||
533 | evas_hash_alloc_error(void) | ||
534 | { | ||
535 | return _evas_hash_alloc_error; | ||
536 | } | ||