1 /* $Cambridge: exim/src/src/tree.c,v 1.2 2005/01/04 10:00:42 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) University of Cambridge 1995 - 2005 */
8 /* See the file NOTICE for conditions of use and distribution. */
10 /* Functions for maintaining binary balanced trees and some associated
19 /*************************************************
20 * Add entry to non-recipients tree *
21 *************************************************/
23 /* Duplicates are just discarded.
32 tree_add_nonrecipient(uschar *s)
34 tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s));
35 Ustrcpy(node->name, s);
36 node->data.ptr = NULL;
37 if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(node);
42 /*************************************************
43 * Add entry to duplicates tree *
44 *************************************************/
46 /* Duplicates are just discarded.
50 addr the address is is a duplicate of
56 tree_add_duplicate(uschar *s, address_item *addr)
58 tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s));
59 Ustrcpy(node->name, s);
60 node->data.ptr = addr;
61 if (!tree_insertnode(&tree_duplicates, node)) store_reset(node);
66 /*************************************************
67 * Add entry to unusable addresses tree *
68 *************************************************/
70 /* Duplicates are simply discarded.
72 Argument: the host item
77 tree_add_unusable(host_item *h)
81 sprintf(CS s, "T:%.200s:%s", h->name, h->address);
82 node = store_get(sizeof(tree_node) + Ustrlen(s));
83 Ustrcpy(node->name, s);
84 node->data.val = h->why;
85 if (h->status == hstatus_unusable_expired) node->data.val += 256;
86 if (!tree_insertnode(&tree_unusable, node)) store_reset(node);
91 /*************************************************
92 * Write a tree in re-readable form *
93 *************************************************/
95 /* This function writes out a tree in a form in which it can
96 easily be re-read. It is used for writing out the non-recipients
97 tree onto the spool, for retrieval at the next retry time.
99 The format is as follows:
101 . If the tree is empty, write one line containing XX.
103 . Otherwise, each node is written, preceded by two letters
104 (Y/N) indicating whether it has left or right children.
106 . The left subtree (if any) then follows, then the right subtree.
108 First, there's an internal recursive subroutine.
118 write_tree(tree_node *p, FILE *f)
120 fprintf(f, "%c%c %s\n",
121 (p->left == NULL)? 'N':'Y', (p->right == NULL)? 'N':'Y', p->name);
122 if (p->left != NULL) write_tree(p->left, f);
123 if (p->right != NULL) write_tree(p->right, f);
126 /* This is the top-level function, with the same arguments. */
129 tree_write(tree_node *p, FILE *f)
143 /***********************************************************
144 * Binary Balanced Tree Management Routines *
145 ***********************************************************/
147 /* This set of routines maintains a balanced binary tree using
148 the algorithm given in Knuth Vol 3 page 455.
150 The routines make use of uschar * pointers as byte pointers,
151 so as to be able to do arithmetic on them, since ANSI Standard
152 C does not permit additions and subtractions on void pointers. */
155 /*************************************************
156 * Flags and Parameters *
157 *************************************************/
159 #define tree_lbal 1 /* left subtree is longer */
160 #define tree_rbal 2 /* right subtree is longer */
161 #define tree_bmask 3 /* mask for flipping bits */
164 /*************************************************
165 * Insert a new node into a tree *
166 *************************************************/
168 /* The node->name field must (obviously) be set, but the other
169 fields need not be initialized.
172 treebase pointer to the root of the tree
173 node the note to insert, with name field set
175 Returns: TRUE if node inserted; FALSE if not (duplicate)
179 tree_insertnode(tree_node **treebase, tree_node *node)
181 tree_node *p = *treebase;
182 tree_node **q, *r, *s, **t;
185 node->left = node->right = NULL;
188 /* Deal with an empty tree */
196 /* The tree is not empty. While finding the insertion point,
197 q points to the pointer to p, and t points to the pointer to
198 the potential re-balancing point. */
203 /* Loop to search tree for place to insert new node */
207 int c = Ustrcmp(node->name, p->name);
208 if (c == 0) return FALSE; /* Duplicate node encountered */
210 /* Deal with climbing down the tree, exiting from the loop
211 when we reach a leaf. */
213 q = (c > 0)? &(p->right) : &(p->left);
215 if (p == NULL) break;
217 /* Save the address of the pointer to the last node en route
218 which has a non-zero balance factor. */
220 if (p->balance != 0) t = q;
223 /* When the above loop completes, q points to the pointer to NULL;
224 that is the place at which the new node must be inserted. */
228 /* Set up s as the potential re-balancing point, and r as the
229 next node after it along the route. */
232 r = (Ustrcmp(node->name, s->name) > 0)? s->right : s->left;
234 /* Adjust balance factors along the route from s to node. */
240 if (Ustrcmp(node->name, p->name) < 0)
242 p->balance = tree_lbal;
247 p->balance = tree_rbal;
252 /* Now the World-Famous Balancing Act */
254 a = (Ustrcmp(node->name, s->name) < 0)? tree_lbal : tree_rbal;
256 if (s->balance == 0) s->balance = (uschar)a; /* The tree has grown higher */
257 else if (s->balance != (uschar)a) s->balance = 0; /* It's become more balanced */
258 else /* It's got out of balance */
260 /* Perform a single rotation */
262 if (r->balance == (uschar)a)
279 /* Perform a double rotation There was an occasion when the balancing
280 factors were screwed up by a bug in the code that reads a tree from
281 the spool. In case this ever happens again, check for changing p to NULL
282 and don't do it. It is better to have an unbalanced tree than a crash. */
288 if (r->left == NULL) return TRUE; /* Bail out if tree corrupt */
297 if (r->right == NULL) return TRUE; /* Bail out if tree corrupt */
305 s->balance = (p->balance == (uschar)a)? (uschar)(a^tree_bmask) : 0;
306 r->balance = (p->balance == (uschar)(a^tree_bmask))? (uschar)a : 0;
310 /* Finishing touch */
315 return TRUE; /* Successful insertion */
320 /*************************************************
321 * Search tree for node by name *
322 *************************************************/
327 name key to search for
329 Returns: pointer to node, or NULL if not found
333 tree_search(tree_node *p, uschar *name)
337 int c = Ustrcmp(name, p->name);
338 if (c == 0) return p;
339 p = (c < 0)? p->left : p->right;