]> jfr.im git - solanum.git/blame - librb/src/dictionary.c
Clean up the provider status logic.
[solanum.git] / librb / src / dictionary.c
CommitLineData
d6bda36d
AC
1/*
2 * charybdis: an advanced ircd
a4bf26dd 3 * rb_dictionary.c: Dictionary-based information storage.
d6bda36d
AC
4 *
5 * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
6 * Copyright (c) 2007 Jilles Tjoelker <jilles -at- stack.nl>
7 *
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice is present in all copies.
11 *
12 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
13 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
16 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
20 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
21 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22 * POSSIBILITY OF SUCH DAMAGE.
23 */
24
a4bf26dd
EM
25#include <librb_config.h>
26#include <rb_lib.h>
27#include <rb_dictionary.h>
d6bda36d 28
4177311e 29struct rb_dictionary
d6bda36d
AC
30{
31 DCF compare_cb;
4177311e 32 rb_dictionary_element *root, *head, *tail;
d6bda36d
AC
33 unsigned int count;
34 char *id;
35 unsigned int dirty:1;
21d5a11c
AC
36
37 rb_dlink_node node;
d6bda36d
AC
38};
39
21d5a11c
AC
40static rb_dlink_list dictionary_list = {NULL, NULL, 0};
41
d6bda36d 42/*
a4bf26dd 43 * rb_dictionary_create(const char *name, DCF compare_cb)
d6bda36d
AC
44 *
45 * Dictionary object factory.
46 *
47 * Inputs:
48 * - dictionary name
49 * - function to use for comparing two entries in the dtree
50 *
51 * Outputs:
52 * - on success, a new dictionary object.
53 *
54 * Side Effects:
55 * - if services runs out of memory and cannot allocate the object,
56 * the program will abort.
57 */
4177311e 58rb_dictionary *rb_dictionary_create(const char *name,
d6bda36d
AC
59 DCF compare_cb)
60{
4177311e 61 rb_dictionary *dtree = (rb_dictionary *) rb_malloc(sizeof(rb_dictionary));
d6bda36d
AC
62
63 dtree->compare_cb = compare_cb;
47a03750 64 dtree->id = rb_strdup(name);
d6bda36d 65
21d5a11c
AC
66 rb_dlinkAdd(dtree, &dtree->node, &dictionary_list);
67
d6bda36d
AC
68 return dtree;
69}
70
71/*
4177311e 72 * rb_dictionary_set_comparator_func(rb_dictionary *dict,
d6bda36d
AC
73 * DCF compare_cb)
74 *
75 * Resets the comparator function used by the dictionary code for
76 * updating the DTree structure.
77 *
78 * Inputs:
79 * - dictionary object
80 * - new comparator function (passed as functor)
81 *
82 * Outputs:
83 * - nothing
84 *
85 * Side Effects:
86 * - the dictionary comparator function is reset.
87 */
4177311e 88void rb_dictionary_set_comparator_func(rb_dictionary *dict,
d6bda36d
AC
89 DCF compare_cb)
90{
a4bf26dd
EM
91 lrb_assert(dict != NULL);
92 lrb_assert(compare_cb != NULL);
d6bda36d
AC
93
94 dict->compare_cb = compare_cb;
95}
96
97/*
4177311e 98 * rb_dictionary_get_comparator_func(rb_dictionary *dict)
d6bda36d
AC
99 *
100 * Returns the current comparator function used by the dictionary.
101 *
102 * Inputs:
103 * - dictionary object
104 *
105 * Outputs:
106 * - comparator function (returned as functor)
107 *
108 * Side Effects:
109 * - none
110 */
111DCF
4177311e 112rb_dictionary_get_comparator_func(rb_dictionary *dict)
d6bda36d 113{
a4bf26dd 114 lrb_assert(dict != NULL);
d6bda36d
AC
115
116 return dict->compare_cb;
117}
118
119/*
4177311e 120 * rb_dictionary_get_linear_index(rb_dictionary *dict,
45dfdf46 121 * const void *key)
d6bda36d
AC
122 *
123 * Gets a linear index number for key.
124 *
125 * Inputs:
126 * - dictionary tree object
127 * - pointer to data
128 *
129 * Outputs:
130 * - position, from zero.
131 *
132 * Side Effects:
133 * - rebuilds the linear index if the tree is marked as dirty.
134 */
135int
4177311e 136rb_dictionary_get_linear_index(rb_dictionary *dict, const void *key)
d6bda36d 137{
4177311e 138 rb_dictionary_element *elem;
d6bda36d 139
a4bf26dd
EM
140 lrb_assert(dict != NULL);
141 lrb_assert(key != NULL);
d6bda36d 142
a4bf26dd 143 elem = rb_dictionary_find(dict, key);
d6bda36d
AC
144 if (elem == NULL)
145 return -1;
146
147 if (!dict->dirty)
148 return elem->position;
149 else
150 {
4177311e 151 rb_dictionary_element *delem;
d6bda36d
AC
152 int i;
153
154 for (delem = dict->head, i = 0; delem != NULL; delem = delem->next, i++)
155 delem->position = i;
156
157 dict->dirty = FALSE;
158 }
159
160 return elem->position;
161}
162
163/*
4177311e 164 * rb_dictionary_retune(rb_dictionary *dict, const void *key)
d6bda36d
AC
165 *
166 * Retunes the tree, self-optimizing for the element which belongs to key.
167 *
d6bda36d
AC
168 * Inputs:
169 * - node to begin search from
170 *
171 * Outputs:
172 * - none
173 *
174 * Side Effects:
175 * - a new root node is nominated.
176 */
2e819b6b 177static void
4177311e 178rb_dictionary_retune(rb_dictionary *dict, const void *key)
d6bda36d 179{
4177311e 180 rb_dictionary_element n, *tn, *left, *right, *node;
d6bda36d
AC
181 int ret;
182
a4bf26dd 183 lrb_assert(dict != NULL);
d6bda36d
AC
184
185 if (dict->root == NULL)
186 return;
187
188 /*
189 * we initialize n with known values, since it's on stack
190 * memory. otherwise the dict would become corrupted.
191 *
192 * n is used for temporary storage while the tree is retuned.
193 * -nenolod
194 */
195 n.left = n.right = NULL;
196 left = right = &n;
197
198 /* this for(;;) loop is the main workhorse of the rebalancing */
199 for (node = dict->root; ; )
200 {
201 if ((ret = dict->compare_cb(key, node->key)) == 0)
202 break;
203
204 if (ret < 0)
205 {
206 if (node->left == NULL)
207 break;
208
209 if ((ret = dict->compare_cb(key, node->left->key)) < 0)
210 {
211 tn = node->left;
212 node->left = tn->right;
213 tn->right = node;
214 node = tn;
215
216 if (node->left == NULL)
217 break;
218 }
219
220 right->left = node;
221 right = node;
222 node = node->left;
223 }
224 else
225 {
226 if (node->right == NULL)
227 break;
228
229 if ((ret = dict->compare_cb(key, node->right->key)) > 0)
230 {
231 tn = node->right;
232 node->right = tn->left;
233 tn->left = node;
234 node = tn;
235
236 if (node->right == NULL)
237 break;
238 }
239
240 left->right = node;
241 left = node;
242 node = node->right;
243 }
244 }
245
246 left->right = node->left;
247 right->left = node->right;
248
249 node->left = n.right;
250 node->right = n.left;
251
252 dict->root = node;
253}
254
255/*
4177311e
EM
256 * rb_dictionary_link(rb_dictionary *dict,
257 * rb_dictionary_element *delem)
d6bda36d
AC
258 *
259 * Links a dictionary tree element to the dictionary.
260 *
261 * When we add new nodes to the tree, it becomes the
262 * next nominated root. This is perhaps not a wise
263 * optimization because of automatic retuning, but
264 * it keeps the code simple.
265 *
266 * Inputs:
267 * - dictionary tree
268 * - dictionary tree element
269 *
270 * Outputs:
271 * - nothing
272 *
273 * Side Effects:
274 * - a node is linked to the dictionary tree
275 */
2e819b6b 276static void
4177311e
EM
277rb_dictionary_link(rb_dictionary *dict,
278 rb_dictionary_element *delem)
d6bda36d 279{
a4bf26dd
EM
280 lrb_assert(dict != NULL);
281 lrb_assert(delem != NULL);
d6bda36d
AC
282
283 dict->dirty = TRUE;
284
285 dict->count++;
286
287 if (dict->root == NULL)
288 {
289 delem->left = delem->right = NULL;
290 delem->next = delem->prev = NULL;
291 dict->head = dict->tail = dict->root = delem;
292 }
293 else
294 {
295 int ret;
296
a4bf26dd 297 rb_dictionary_retune(dict, delem->key);
d6bda36d
AC
298
299 if ((ret = dict->compare_cb(delem->key, dict->root->key)) < 0)
300 {
301 delem->left = dict->root->left;
302 delem->right = dict->root;
303 dict->root->left = NULL;
304
305 if (dict->root->prev)
306 dict->root->prev->next = delem;
307 else
308 dict->head = delem;
309
310 delem->prev = dict->root->prev;
311 delem->next = dict->root;
312 dict->root->prev = delem;
313 dict->root = delem;
314 }
315 else if (ret > 0)
316 {
317 delem->right = dict->root->right;
318 delem->left = dict->root;
319 dict->root->right = NULL;
320
321 if (dict->root->next)
322 dict->root->next->prev = delem;
323 else
324 dict->tail = delem;
325
326 delem->next = dict->root->next;
327 delem->prev = dict->root;
328 dict->root->next = delem;
329 dict->root = delem;
330 }
331 else
332 {
333 dict->root->key = delem->key;
334 dict->root->data = delem->data;
335 dict->count--;
336
99b461bb 337 rb_free(delem);
d6bda36d
AC
338 }
339 }
340}
341
342/*
4177311e 343 * rb_dictionary_unlink_root(rb_dictionary *dict)
d6bda36d
AC
344 *
345 * Unlinks the root dictionary tree element from the dictionary.
346 *
347 * Inputs:
348 * - dictionary tree
349 *
350 * Outputs:
351 * - nothing
352 *
353 * Side Effects:
354 * - the root node is unlinked from the dictionary tree
355 */
2e819b6b 356static void
4177311e 357rb_dictionary_unlink_root(rb_dictionary *dict)
d6bda36d 358{
4177311e 359 rb_dictionary_element *delem, *nextnode, *parentofnext;
d6bda36d
AC
360
361 dict->dirty = TRUE;
362
363 delem = dict->root;
364 if (delem == NULL)
365 return;
366
367 if (dict->root->left == NULL)
368 dict->root = dict->root->right;
369 else if (dict->root->right == NULL)
370 dict->root = dict->root->left;
371 else
372 {
373 /* Make the node with the next highest key the new root.
374 * This node has a NULL left pointer. */
375 nextnode = delem->next;
a4bf26dd 376 lrb_assert(nextnode->left == NULL);
d6bda36d
AC
377 if (nextnode == delem->right)
378 {
379 dict->root = nextnode;
380 dict->root->left = delem->left;
381 }
382 else
383 {
384 parentofnext = delem->right;
385 while (parentofnext->left != NULL && parentofnext->left != nextnode)
386 parentofnext = parentofnext->left;
a4bf26dd 387 lrb_assert(parentofnext->left == nextnode);
d6bda36d
AC
388 parentofnext->left = nextnode->right;
389 dict->root = nextnode;
390 dict->root->left = delem->left;
391 dict->root->right = delem->right;
392 }
393 }
394
395 /* linked list */
396 if (delem->prev != NULL)
397 delem->prev->next = delem->next;
398
399 if (dict->head == delem)
400 dict->head = delem->next;
401
402 if (delem->next)
403 delem->next->prev = delem->prev;
404
405 if (dict->tail == delem)
406 dict->tail = delem->prev;
407
408 dict->count--;
409}
410
411/*
4177311e 412 * rb_dictionary_destroy(rb_dictionary *dtree,
d6bda36d
AC
413 * void (*destroy_cb)(dictionary_elem_t *delem, void *privdata),
414 * void *privdata);
415 *
416 * Recursively destroys all nodes in a dictionary tree.
417 *
418 * Inputs:
419 * - dictionary tree object
420 * - optional iteration callback
421 * - optional opaque/private data to pass to callback
422 *
423 * Outputs:
424 * - nothing
425 *
426 * Side Effects:
427 * - on success, a dtree and optionally it's children are destroyed.
428 *
429 * Notes:
430 * - if this is called without a callback, the objects bound to the
431 * DTree will not be destroyed.
432 */
4177311e
EM
433void rb_dictionary_destroy(rb_dictionary *dtree,
434 void (*destroy_cb)(rb_dictionary_element *delem, void *privdata),
d6bda36d
AC
435 void *privdata)
436{
4177311e 437 rb_dictionary_element *n, *tn;
d6bda36d 438
a4bf26dd 439 lrb_assert(dtree != NULL);
d6bda36d 440
5cefa1d6 441 RB_DLINK_FOREACH_SAFE(n, tn, dtree->head)
d6bda36d
AC
442 {
443 if (destroy_cb != NULL)
444 (*destroy_cb)(n, privdata);
445
99b461bb 446 rb_free(n);
d6bda36d
AC
447 }
448
21d5a11c 449 rb_dlinkDelete(&dtree->node, &dictionary_list);
55d5f797 450 rb_free(dtree->id);
637c4932 451 rb_free(dtree);
d6bda36d
AC
452}
453
454/*
4177311e 455 * rb_dictionary_foreach(rb_dictionary *dtree,
d6bda36d
AC
456 * void (*destroy_cb)(dictionary_elem_t *delem, void *privdata),
457 * void *privdata);
458 *
459 * Iterates over all entries in a DTree.
460 *
461 * Inputs:
462 * - dictionary tree object
463 * - optional iteration callback
464 * - optional opaque/private data to pass to callback
465 *
466 * Outputs:
467 * - nothing
468 *
469 * Side Effects:
470 * - on success, a dtree is iterated
471 */
4177311e
EM
472void rb_dictionary_foreach(rb_dictionary *dtree,
473 int (*foreach_cb)(rb_dictionary_element *delem, void *privdata),
d6bda36d
AC
474 void *privdata)
475{
4177311e 476 rb_dictionary_element *n, *tn;
d6bda36d 477
a4bf26dd 478 lrb_assert(dtree != NULL);
d6bda36d 479
5cefa1d6 480 RB_DLINK_FOREACH_SAFE(n, tn, dtree->head)
d6bda36d
AC
481 {
482 /* delem_t is a subclass of node_t. */
4177311e 483 rb_dictionary_element *delem = (rb_dictionary_element *) n;
d6bda36d
AC
484
485 if (foreach_cb != NULL)
486 (*foreach_cb)(delem, privdata);
487 }
488}
489
490/*
4177311e
EM
491 * rb_dictionary_search(rb_dictionary *dtree,
492 * void (*destroy_cb)(rb_dictionary_element *delem, void *privdata),
d6bda36d
AC
493 * void *privdata);
494 *
495 * Searches all entries in a DTree using a custom callback.
496 *
497 * Inputs:
498 * - dictionary tree object
499 * - optional iteration callback
500 * - optional opaque/private data to pass to callback
501 *
502 * Outputs:
503 * - on success, the requested object
504 * - on failure, NULL.
505 *
506 * Side Effects:
507 * - a dtree is iterated until the requested conditions are met
508 */
4177311e
EM
509void *rb_dictionary_search(rb_dictionary *dtree,
510 void *(*foreach_cb)(rb_dictionary_element *delem, void *privdata),
d6bda36d
AC
511 void *privdata)
512{
4177311e 513 rb_dictionary_element *n, *tn;
d6bda36d
AC
514 void *ret = NULL;
515
a4bf26dd 516 lrb_assert(dtree != NULL);
d6bda36d 517
5cefa1d6 518 RB_DLINK_FOREACH_SAFE(n, tn, dtree->head)
d6bda36d
AC
519 {
520 /* delem_t is a subclass of node_t. */
4177311e 521 rb_dictionary_element *delem = (rb_dictionary_element *) n;
d6bda36d
AC
522
523 if (foreach_cb != NULL)
524 ret = (*foreach_cb)(delem, privdata);
525
526 if (ret)
527 break;
528 }
529
530 return ret;
531}
532
533/*
4177311e
EM
534 * rb_dictionary_foreach_start(rb_dictionary *dtree,
535 * rb_dictionary_iter *state);
d6bda36d
AC
536 *
537 * Initializes a static DTree iterator.
538 *
539 * Inputs:
540 * - dictionary tree object
541 * - static DTree iterator
542 *
543 * Outputs:
544 * - nothing
545 *
546 * Side Effects:
547 * - the static iterator, &state, is initialized.
548 */
4177311e
EM
549void rb_dictionary_foreach_start(rb_dictionary *dtree,
550 rb_dictionary_iter *state)
d6bda36d 551{
a4bf26dd
EM
552 lrb_assert(dtree != NULL);
553 lrb_assert(state != NULL);
d6bda36d
AC
554
555 state->cur = NULL;
556 state->next = NULL;
557
558 /* find first item */
559 state->cur = dtree->head;
560
561 if (state->cur == NULL)
562 return;
563
564 /* make state->cur point to first item and state->next point to
565 * second item */
566 state->next = state->cur;
a4bf26dd 567 rb_dictionary_foreach_next(dtree, state);
d6bda36d
AC
568}
569
570/*
4177311e
EM
571 * rb_dictionary_foreach_cur(rb_dictionary *dtree,
572 * rb_dictionary_iter *state);
d6bda36d
AC
573 *
574 * Returns the data from the current node being iterated by the
575 * static iterator.
576 *
577 * Inputs:
578 * - dictionary tree object
579 * - static DTree iterator
580 *
581 * Outputs:
582 * - reference to data in the current dtree node being iterated
583 *
584 * Side Effects:
585 * - none
586 */
4177311e
EM
587void *rb_dictionary_foreach_cur(rb_dictionary *dtree,
588 rb_dictionary_iter *state)
d6bda36d 589{
a4bf26dd
EM
590 lrb_assert(dtree != NULL);
591 lrb_assert(state != NULL);
d6bda36d
AC
592
593 return state->cur != NULL ? state->cur->data : NULL;
594}
595
596/*
4177311e
EM
597 * rb_dictionary_foreach_next(rb_dictionary *dtree,
598 * rb_dictionary_iter *state);
d6bda36d
AC
599 *
600 * Advances a static DTree iterator.
601 *
602 * Inputs:
603 * - dictionary tree object
604 * - static DTree iterator
605 *
606 * Outputs:
607 * - nothing
608 *
609 * Side Effects:
610 * - the static iterator, &state, is advanced to a new DTree node.
611 */
4177311e
EM
612void rb_dictionary_foreach_next(rb_dictionary *dtree,
613 rb_dictionary_iter *state)
d6bda36d 614{
a4bf26dd
EM
615 lrb_assert(dtree != NULL);
616 lrb_assert(state != NULL);
d6bda36d
AC
617
618 if (state->cur == NULL)
619 {
a4bf26dd 620 rb_lib_log("rb_dictionary_foreach_next(): called again after iteration finished on dtree<%p>", (void *)dtree);
d6bda36d
AC
621 return;
622 }
623
624 state->cur = state->next;
625
626 if (state->next == NULL)
627 return;
628
629 state->next = state->next->next;
630}
631
632/*
4177311e 633 * rb_dictionary_find(rb_dictionary *dtree, const void *key)
d6bda36d
AC
634 *
635 * Looks up a DTree node by name.
636 *
637 * Inputs:
638 * - dictionary tree object
639 * - name of node to lookup
640 *
641 * Outputs:
642 * - on success, the dtree node requested
643 * - on failure, NULL
644 *
645 * Side Effects:
646 * - none
647 */
4177311e 648rb_dictionary_element *rb_dictionary_find(rb_dictionary *dict, const void *key)
d6bda36d 649{
a4bf26dd
EM
650 lrb_assert(dict != NULL);
651 lrb_assert(key != NULL);
d6bda36d
AC
652
653 /* retune for key, key will be the tree's root if it's available */
a4bf26dd 654 rb_dictionary_retune(dict, key);
d6bda36d
AC
655
656 if (dict->root && !dict->compare_cb(key, dict->root->key))
657 return dict->root;
658
659 return NULL;
660}
661
662/*
4177311e 663 * rb_dictionary_add(rb_dictionary *dtree, const void *key, void *data)
d6bda36d
AC
664 *
665 * Creates a new DTree node and binds data to it.
666 *
667 * Inputs:
668 * - dictionary tree object
669 * - name for new DTree node
670 * - data to bind to the new DTree node
671 *
672 * Outputs:
673 * - on success, a new DTree node
674 * - on failure, NULL
675 *
676 * Side Effects:
677 * - data is inserted into the DTree.
678 */
4177311e 679rb_dictionary_element *rb_dictionary_add(rb_dictionary *dict, const void *key, void *data)
d6bda36d 680{
4177311e 681 rb_dictionary_element *delem;
d6bda36d 682
a4bf26dd
EM
683 lrb_assert(dict != NULL);
684 lrb_assert(key != NULL);
685 lrb_assert(data != NULL);
686 lrb_assert(rb_dictionary_find(dict, key) == NULL);
d6bda36d 687
99b461bb 688 delem = rb_malloc(sizeof(*delem));
d6bda36d
AC
689 delem->key = key;
690 delem->data = data;
691
a4bf26dd 692 rb_dictionary_link(dict, delem);
d6bda36d
AC
693
694 return delem;
695}
696
697/*
4177311e 698 * rb_dictionary_delete(rb_dictionary *dtree, const void *key)
d6bda36d
AC
699 *
700 * Deletes data from a dictionary tree.
701 *
702 * Inputs:
703 * - dictionary tree object
704 * - name of DTree node to delete
705 *
706 * Outputs:
707 * - on success, the remaining data that needs to be mowgli_freed
708 * - on failure, NULL
709 *
710 * Side Effects:
711 * - data is removed from the DTree.
712 *
713 * Notes:
714 * - the returned data needs to be mowgli_freed/released manually!
715 */
4177311e 716void *rb_dictionary_delete(rb_dictionary *dtree, const void *key)
d6bda36d 717{
4177311e 718 rb_dictionary_element *delem = rb_dictionary_find(dtree, key);
d6bda36d
AC
719 void *data;
720
721 if (delem == NULL)
722 return NULL;
723
724 data = delem->data;
725
a4bf26dd 726 rb_dictionary_unlink_root(dtree);
99b461bb 727 rb_free(delem);
d6bda36d
AC
728
729 return data;
730}
731
732/*
4177311e 733 * rb_dictionary_retrieve(rb_dictionary *dtree, const void *key)
d6bda36d
AC
734 *
735 * Retrieves data from a dictionary.
736 *
737 * Inputs:
738 * - dictionary tree object
739 * - name of node to lookup
740 *
741 * Outputs:
742 * - on success, the data bound to the DTree node.
743 * - on failure, NULL
744 *
745 * Side Effects:
746 * - none
747 */
4177311e 748void *rb_dictionary_retrieve(rb_dictionary *dtree, const void *key)
d6bda36d 749{
4177311e 750 rb_dictionary_element *delem = rb_dictionary_find(dtree, key);
d6bda36d
AC
751
752 if (delem != NULL)
753 return delem->data;
754
755 return NULL;
756}
757
758/*
4177311e 759 * rb_dictionary_size(rb_dictionary *dict)
d6bda36d
AC
760 *
761 * Returns the size of a dictionary.
762 *
763 * Inputs:
764 * - dictionary tree object
765 *
766 * Outputs:
767 * - size of dictionary
768 *
769 * Side Effects:
770 * - none
771 */
4177311e 772unsigned int rb_dictionary_size(rb_dictionary *dict)
d6bda36d 773{
a4bf26dd 774 lrb_assert(dict != NULL);
d6bda36d
AC
775
776 return dict->count;
777}
778
779/* returns the sum of the depths of the subtree rooted in delem at depth depth */
780static int
4177311e 781stats_recurse(rb_dictionary_element *delem, int depth, int *pmaxdepth)
d6bda36d
AC
782{
783 int result;
784
785 if (depth > *pmaxdepth)
786 *pmaxdepth = depth;
787 result = depth;
d99ff029 788 if (delem && delem->left)
d6bda36d 789 result += stats_recurse(delem->left, depth + 1, pmaxdepth);
d99ff029 790 if (delem && delem->right)
d6bda36d
AC
791 result += stats_recurse(delem->right, depth + 1, pmaxdepth);
792 return result;
793}
794
795/*
4177311e 796 * rb_dictionary_stats(rb_dictionary *dict, void (*cb)(const char *line, void *privdata), void *privdata)
d6bda36d
AC
797 *
798 * Returns the size of a dictionary.
799 *
800 * Inputs:
801 * - dictionary tree object
802 * - callback
803 * - data for callback
804 *
805 * Outputs:
806 * - none
807 *
808 * Side Effects:
809 * - callback called with stats text
810 */
4177311e 811void rb_dictionary_stats(rb_dictionary *dict, void (*cb)(const char *line, void *privdata), void *privdata)
d6bda36d
AC
812{
813 char str[256];
814 int sum, maxdepth;
815
a4bf26dd 816 lrb_assert(dict != NULL);
d6bda36d 817
d99ff029
AC
818 if (dict->count)
819 {
820 maxdepth = 0;
821 sum = stats_recurse(dict->root, 0, &maxdepth);
8dacf9e9 822 snprintf(str, sizeof str, "%-30s %-15s %-10d %-10d %-10d %-10d", dict->id, "DICT", dict->count, sum, sum / dict->count, maxdepth);
d99ff029
AC
823 }
824 else
825 {
8dacf9e9 826 snprintf(str, sizeof str, "%-30s %-15s %-10s %-10s %-10s %-10s", dict->id, "DICT", "0", "0", "0", "0");
d99ff029
AC
827 }
828
d6bda36d 829 cb(str, privdata);
21d5a11c
AC
830}
831
a4bf26dd 832void rb_dictionary_stats_walk(void (*cb)(const char *line, void *privdata), void *privdata)
21d5a11c
AC
833{
834 rb_dlink_node *ptr;
835
836 RB_DLINK_FOREACH(ptr, dictionary_list.head)
837 {
a4bf26dd 838 rb_dictionary_stats(ptr->data, cb, privdata);
21d5a11c 839 }
d6bda36d 840}