1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2015 */
6 /* See the file NOTICE for conditions of use and distribution. */
8 /* Functions for maintaining binary balanced trees and some associated
17 /*************************************************
18 * Add entry to non-recipients tree *
19 *************************************************/
21 /* Duplicates are just discarded.
30 tree_add_nonrecipient(uschar *s)
32 tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s));
33 Ustrcpy(node->name, s);
34 node->data.ptr = NULL;
35 if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(node);
40 /*************************************************
41 * Add entry to duplicates tree *
42 *************************************************/
44 /* Duplicates are just discarded.
48 addr the address is is a duplicate of
54 tree_add_duplicate(uschar *s, address_item *addr)
56 tree_node *node = store_get(sizeof(tree_node) + Ustrlen(s));
57 Ustrcpy(node->name, s);
58 node->data.ptr = addr;
59 if (!tree_insertnode(&tree_duplicates, node)) store_reset(node);
64 /*************************************************
65 * Add entry to unusable addresses tree *
66 *************************************************/
68 /* Duplicates are simply discarded.
70 Argument: the host item
75 tree_add_unusable(host_item *h)
79 sprintf(CS s, "T:%.200s:%s", h->name, h->address);
80 node = store_get(sizeof(tree_node) + Ustrlen(s));
81 Ustrcpy(node->name, s);
82 node->data.val = h->why;
83 if (h->status == hstatus_unusable_expired) node->data.val += 256;
84 if (!tree_insertnode(&tree_unusable, node)) store_reset(node);
89 /*************************************************
90 * Write a tree in re-readable form *
91 *************************************************/
93 /* This function writes out a tree in a form in which it can
94 easily be re-read. It is used for writing out the non-recipients
95 tree onto the spool, for retrieval at the next retry time.
97 The format is as follows:
99 . If the tree is empty, write one line containing XX.
101 . Otherwise, each node is written, preceded by two letters
102 (Y/N) indicating whether it has left or right children.
104 . The left subtree (if any) then follows, then the right subtree.
106 First, there's an internal recursive subroutine.
116 write_tree(tree_node *p, FILE *f)
118 fprintf(f, "%c%c %s\n",
119 (p->left == NULL)? 'N':'Y', (p->right == NULL)? 'N':'Y', p->name);
120 if (p->left != NULL) write_tree(p->left, f);
121 if (p->right != NULL) write_tree(p->right, f);
124 /* This is the top-level function, with the same arguments. */
127 tree_write(tree_node *p, FILE *f)
141 /***********************************************************
142 * Binary Balanced Tree Management Routines *
143 ***********************************************************/
145 /* This set of routines maintains a balanced binary tree using
146 the algorithm given in Knuth Vol 3 page 455.
148 The routines make use of uschar * pointers as byte pointers,
149 so as to be able to do arithmetic on them, since ANSI Standard
150 C does not permit additions and subtractions on void pointers. */
153 /*************************************************
154 * Flags and Parameters *
155 *************************************************/
157 #define tree_lbal 1 /* left subtree is longer */
158 #define tree_rbal 2 /* right subtree is longer */
159 #define tree_bmask 3 /* mask for flipping bits */
162 /*************************************************
163 * Insert a new node into a tree *
164 *************************************************/
166 /* The node->name field must (obviously) be set, but the other
167 fields need not be initialized.
170 treebase pointer to the root of the tree
171 node the note to insert, with name field set
173 Returns: TRUE if node inserted; FALSE if not (duplicate)
177 tree_insertnode(tree_node **treebase, tree_node *node)
179 tree_node *p = *treebase;
180 tree_node **q, *r, *s, **t;
183 node->left = node->right = NULL;
186 /* Deal with an empty tree */
194 /* The tree is not empty. While finding the insertion point,
195 q points to the pointer to p, and t points to the pointer to
196 the potential re-balancing point. */
201 /* Loop to search tree for place to insert new node */
205 int c = Ustrcmp(node->name, p->name);
206 if (c == 0) return FALSE; /* Duplicate node encountered */
208 /* Deal with climbing down the tree, exiting from the loop
209 when we reach a leaf. */
211 q = (c > 0)? &(p->right) : &(p->left);
213 if (p == NULL) break;
215 /* Save the address of the pointer to the last node en route
216 which has a non-zero balance factor. */
218 if (p->balance != 0) t = q;
221 /* When the above loop completes, q points to the pointer to NULL;
222 that is the place at which the new node must be inserted. */
226 /* Set up s as the potential re-balancing point, and r as the
227 next node after it along the route. */
230 r = (Ustrcmp(node->name, s->name) > 0)? s->right : s->left;
232 /* Adjust balance factors along the route from s to node. */
238 if (Ustrcmp(node->name, p->name) < 0)
240 p->balance = tree_lbal;
245 p->balance = tree_rbal;
250 /* Now the World-Famous Balancing Act */
252 a = (Ustrcmp(node->name, s->name) < 0)? tree_lbal : tree_rbal;
254 if (s->balance == 0) s->balance = (uschar)a; /* The tree has grown higher */
255 else if (s->balance != (uschar)a) s->balance = 0; /* It's become more balanced */
256 else /* It's got out of balance */
258 /* Perform a single rotation */
260 if (r->balance == (uschar)a)
277 /* Perform a double rotation There was an occasion when the balancing
278 factors were screwed up by a bug in the code that reads a tree from
279 the spool. In case this ever happens again, check for changing p to NULL
280 and don't do it. It is better to have an unbalanced tree than a crash. */
286 if (r->left == NULL) return TRUE; /* Bail out if tree corrupt */
295 if (r->right == NULL) return TRUE; /* Bail out if tree corrupt */
303 s->balance = (p->balance == (uschar)a)? (uschar)(a^tree_bmask) : 0;
304 r->balance = (p->balance == (uschar)(a^tree_bmask))? (uschar)a : 0;
308 /* Finishing touch */
313 return TRUE; /* Successful insertion */
318 /*************************************************
319 * Search tree for node by name *
320 *************************************************/
325 name key to search for
327 Returns: pointer to node, or NULL if not found
331 tree_search(tree_node *p, const uschar *name)
335 int c = Ustrcmp(name, p->name);
336 if (c == 0) return p;
337 p = (c < 0)? p->left : p->right;
344 /*************************************************
345 * Walk tree recursively and execute function *
346 *************************************************/
351 f function to execute for each name-value-pair
352 ctx context data for f
356 tree_walk(tree_node *p, void (*f)(uschar*, uschar*, void*), void *ctx)
358 if (p == NULL) return;
359 f(p->name, p->data.ptr, ctx);
360 if (p->left != NULL) tree_walk(p->left, f, ctx);
361 if (p->right != NULL) tree_walk(p->right, f, ctx);