1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2021 - 2022 */
6 /* Copyright (c) University of Cambridge 1995 - 2015 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-only */
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(const uschar *s)
34 rmark rpoint = store_mark();
35 tree_node * node = store_get(sizeof(tree_node) + Ustrlen(s), s);
36 Ustrcpy(node->name, s);
37 node->data.ptr = NULL;
38 if (!tree_insertnode(&tree_nonrecipients, node)) store_reset(rpoint);
43 /*************************************************
44 * Add entry to duplicates tree *
45 *************************************************/
47 /* Duplicates are just discarded.
51 addr the address is is a duplicate of
57 tree_add_duplicate(const uschar *s, address_item *addr)
59 rmark rpoint = store_mark();
60 tree_node * node = store_get(sizeof(tree_node) + Ustrlen(s), s);
61 Ustrcpy(node->name, s);
62 node->data.ptr = addr;
63 if (!tree_insertnode(&tree_duplicates, node)) store_reset(rpoint);
68 /*************************************************
69 * Add entry to unusable addresses tree *
70 *************************************************/
72 /* Duplicates are simply discarded.
74 Argument: the host item
79 tree_add_unusable(const host_item *h)
81 rmark rpoint = store_mark();
84 sprintf(CS s, "T:%.200s:%s", h->name, h->address);
85 node = store_get(sizeof(tree_node) + Ustrlen(s),
86 is_tainted(h->name) || is_tainted(h->address) ? GET_TAINTED : GET_UNTAINTED);
87 Ustrcpy(node->name, s);
88 node->data.val = h->why;
89 if (h->status == hstatus_unusable_expired) node->data.val += 256;
90 if (!tree_insertnode(&tree_unusable, node)) store_reset(rpoint);
95 /*************************************************
96 * Write a tree in re-readable form *
97 *************************************************/
99 /* This function writes out a tree in a form in which it can
100 easily be re-read. It is used for writing out the non-recipients
101 tree onto the spool, for retrieval at the next retry time.
103 The format is as follows:
105 . If the tree is empty, write one line containing XX.
107 . Otherwise, each node is written, preceded by two letters
108 (Y/N) indicating whether it has left or right children.
110 . The left subtree (if any) then follows, then the right subtree.
112 First, there's an internal recursive subroutine.
122 write_tree(tree_node *p, FILE *f)
124 fprintf(f, "%c%c %s\n",
125 (p->left == NULL)? 'N':'Y', (p->right == NULL)? 'N':'Y', p->name);
126 if (p->left != NULL) write_tree(p->left, f);
127 if (p->right != NULL) write_tree(p->right, f);
130 /* This is the top-level function, with the same arguments. */
133 tree_write(tree_node *p, FILE *f)
147 /***********************************************************
148 * Binary Balanced Tree Management Routines *
149 ***********************************************************/
151 /* This set of routines maintains a balanced binary tree using
152 the algorithm given in Knuth Vol 3 page 455.
154 The routines make use of uschar * pointers as byte pointers,
155 so as to be able to do arithmetic on them, since ANSI Standard
156 C does not permit additions and subtractions on void pointers. */
159 /*************************************************
160 * Flags and Parameters *
161 *************************************************/
163 #define tree_lbal 1 /* left subtree is longer */
164 #define tree_rbal 2 /* right subtree is longer */
165 #define tree_bmask 3 /* mask for flipping bits */
168 /*************************************************
169 * Insert a new node into a tree *
170 *************************************************/
172 /* The node->name field must (obviously) be set, but the other
173 fields need not be initialized.
176 treebase pointer to the root of the tree
177 node the note to insert, with name field set
179 Returns: TRUE if node inserted; FALSE if not (duplicate)
183 tree_insertnode(tree_node **treebase, tree_node *node)
185 tree_node *p = *treebase;
186 tree_node **q, *r, *s, **t;
189 node->left = node->right = NULL;
192 /* Deal with an empty tree */
200 /* The tree is not empty. While finding the insertion point,
201 q points to the pointer to p, and t points to the pointer to
202 the potential re-balancing point. */
207 /* Loop to search tree for place to insert new node */
211 int c = Ustrcmp(node->name, p->name);
212 if (c == 0) return FALSE; /* Duplicate node encountered */
214 /* Deal with climbing down the tree, exiting from the loop
215 when we reach a leaf. */
217 q = (c > 0)? &(p->right) : &(p->left);
219 if (p == NULL) break;
221 /* Save the address of the pointer to the last node en route
222 which has a non-zero balance factor. */
224 if (p->balance != 0) t = q;
227 /* When the above loop completes, q points to the pointer to NULL;
228 that is the place at which the new node must be inserted. */
232 /* Set up s as the potential re-balancing point, and r as the
233 next node after it along the route. */
236 r = (Ustrcmp(node->name, s->name) > 0)? s->right : s->left;
238 /* Adjust balance factors along the route from s to node. */
244 if (Ustrcmp(node->name, p->name) < 0)
246 p->balance = tree_lbal;
251 p->balance = tree_rbal;
256 /* Now the World-Famous Balancing Act */
258 a = (Ustrcmp(node->name, s->name) < 0)? tree_lbal : tree_rbal;
260 if (s->balance == 0) s->balance = (uschar)a; /* The tree has grown higher */
261 else if (s->balance != (uschar)a) s->balance = 0; /* It's become more balanced */
262 else /* It's got out of balance */
264 /* Perform a single rotation */
266 if (r->balance == (uschar)a)
283 /* Perform a double rotation There was an occasion when the balancing
284 factors were screwed up by a bug in the code that reads a tree from
285 the spool. In case this ever happens again, check for changing p to NULL
286 and don't do it. It is better to have an unbalanced tree than a crash. */
292 if (r->left == NULL) return TRUE; /* Bail out if tree corrupt */
301 if (r->right == NULL) return TRUE; /* Bail out if tree corrupt */
309 s->balance = (p->balance == (uschar)a)? (uschar)(a^tree_bmask) : 0;
310 r->balance = (p->balance == (uschar)(a^tree_bmask))? (uschar)a : 0;
314 /* Finishing touch */
319 return TRUE; /* Successful insertion */
324 /*************************************************
325 * Search tree for node by name *
326 *************************************************/
331 name key to search for
333 Returns: pointer to node, or NULL if not found
337 tree_search(tree_node *p, const uschar *name)
341 int c = Ustrcmp(name, p->name);
342 if (c == 0) return p;
343 p = c < 0 ? p->left : p->right;
350 /*************************************************
351 * Walk tree recursively and execute function *
352 *************************************************/
357 f function to execute for each name-value-pair
358 ctx context data for f
362 tree_walk(tree_node *p, void (*f)(uschar*, uschar*, void*), void *ctx)
365 f(p->name, p->data.ptr, ctx);
366 tree_walk(p->left, f, ctx);
367 tree_walk(p->right, f, ctx);
372 /* Add a node to a tree, ignoring possibility of duplicates */
375 tree_add_var(uschar * name, uschar * val, void * ctx)
377 tree_node ** root = ctx;
378 tree_node * node = store_get(sizeof(tree_node) + Ustrlen(name), name);
379 Ustrcpy(node->name, name);
380 node->data.ptr = val;
381 (void) tree_insertnode(root, node);
384 /* Duplicate a tree */
387 tree_dup(tree_node ** dstp, tree_node * src)
389 tree_walk(src, tree_add_var, dstp);