1    | /***************************************
2    |   $Revision: 1.19 $
3    | 
4    |   Radix tree (rx).  rx_tree.c - functions to operate on trees
5    |   (creation/deletion/finding).
6    | 
7    |   Status: NOT REVUED, TESTED, INCOMPLETE
8    | 
9    |   Design and implementation by: Marek Bukowy
10   | 
11   |   ******************/ /******************
12   |   Copyright (c) 1999                              RIPE NCC
13   |  
14   |   All Rights Reserved
15   |   
16   |   Permission to use, copy, modify, and distribute this software and its
17   |   documentation for any purpose and without fee is hereby granted,
18   |   provided that the above copyright notice appear in all copies and that
19   |   both that copyright notice and this permission notice appear in
20   |   supporting documentation, and that the name of the author not be
21   |   used in advertising or publicity pertaining to distribution of the
22   |   software without specific, written prior permission.
23   |   
24   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
26   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
27   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
28   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
29   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30   |   ***************************************/
31   | 
32   | #include <erroutines.h>
33   | #include <iproutines.h>
34   | #include <memwrap.h>
35   | #include <stubs.h>
36   | 
37   | /***************************************************************************/
38   | 
39   | #define RX_IMPL
40   | 
41   | #include <rxroutines.h>
42   | /***************************************************************************/
43   | 
44   | 
45   | /*+++++++++  
46   |   go down the tree calling func on every node.
47   |   (func takes the node pointer and the current level)
48   | 
49   |   the function is called recursively with level increased
50   |   it stops recursing when no child nodes are found or maxlevel is reached.
51   |   
52   |   therefore the initial call must set level to 0.
53   |   
54   |   the nodecounter increments at every node, and is the return value
55   |   of the function. So start with 0 to get the number of nodes traversed.
56   |   
57   |   ERROR HANDLING IS DIFFERENT HERE!
58   |   Unlike other functions it is not the return value:
59   |   The error code from the func function IF DEFINED (== not NULL ) goes 
60   |   to the variable pointed to by the last parameter.
61   | 
62   |   XXX: nodecounter is ALWAYS passed as 0, so should probably be removed from
63   |        calling parameter list, shane 2001-02-01
64   | ++++++++++++*/
65   | int
66   | rx_walk_tree(rx_node_t *node, 
67   | 	     er_ret_t (*func)(rx_node_t *node, int level, int nodecounter, 
68   | 			  void *userptr), 
69   | 	     rx_walk_mt walk_mode, 
70   | 	                     /* controls if glue nodes are counted*/
71   | 	                     /* and if levels or prefix lenghts are checked*/
72   | 	     int maxlevel, 
73   | 	     int level, 
74   | 	     int nodecounter,
75   | 	     void *userptr,
76   | 	     er_ret_t *err)
77   | {
78   | int i, link;
79   | 
80   |  if( node == NULL ) die; /* program error. we expect a valid, checked, node.*/
81   | 
82   |  /* check the limits and maybe quit here: prefix length for RX_WALK_PRFLEN, */
83   |  /* level otherwise */
84   |  
85   |  if(walk_mode & RX_WALK_PRFLEN) {
86   |    if(node->prefix.bits > maxlevel) {
87   |      return nodecounter; 
88   |    }
89   |  }
90   |  else if( level > maxlevel ) {
91   |    return nodecounter; 
92   |  }
93   | 
94   | 
95   |  /* process the node appropriately: */
96   |  /* if (not node glue) or (process glue nodes) */
97   | 
98   |  if( node->glue == 0 || (walk_mode & RX_WALK_SKPGLU) == 0 ) {
99   | 
100  |    /* increase our depth counter */
101  |    level++;
102  | 
103  |    /* increase the count of visited nodes */
104  |    nodecounter++;
105  | 
106  |    /* call supplied function, if any */
107  |    if( func != NULL ) {
108  |      *err = func(node, level, nodecounter, userptr);
109  | 
110  |      /* abort the walk on error*/
111  |      if( *err != RX_OK ) {
112  |        ER_dbg_va(FAC_RX, ASP_RX_TREE_WALK, 
113  |   	         "walk_tree: func returned error %d, aborting", *err);
114  |        return nodecounter;
115  |      }
116  |    }
117  |  }
118  | 
119  |  /* process left and right children */
120  |  for(i=0; i<=1; i++) {
121  |    
122  |    /* reverse the sense of the walk*/
123  |    link = ( walk_mode & RX_WALK_REVERS ) ? ! i : i;
124  |      
125  |    if( node->child_ptr[link] != NULL ) {
126  |      nodecounter += rx_walk_tree(node->child_ptr[link], func, walk_mode,
127  | 				 maxlevel, level, 0, userptr, err);
128  |      /* abort the walk on error*/
129  |      if( func != NULL && *err != RX_OK ) {
130  |        break;
131  |      }
132  |    }
133  |  }
134  |  
135  |  /* return count of nodes visited */
136  |  return nodecounter;
137  | }
138  | 
139  | /*+++++++++  
140  |   go down the tree and delete all nodes of the tree.
141  | 
142  |   the function is called recursively with level increased
143  |   it stops recursing when no child nodes are found or maxlevel is reached.
144  |   
145  |   therefore the initial call must set level to 0.
146  |   
147  |   the nodecounter increments at every node, and is the return value
148  |   of the function. So start with 0 to get the number of nodes traversed.
149  |   
150  |   ERROR HANDLING IS DIFFERENT HERE!
151  |   Unlike other functions it is not the return value:
152  |   The error code from the func function IF DEFINED (== not NULL ) goes 
153  |   to the variable pointed to by the last parameter.
154  | 
155  |   XXX: nodecounter is ALWAYS passed as 0, so should probably be removed from
156  |        calling parameter list, shane 2001-02-01
157  | ++++++++++++*/
158  | void rx_delete_dataleaves(void *element_data, void *result_ptr)
159  | {
160  | 	rx_dataleaf_t *leafptr = element_data;
161  | 	rx_tree_t *tree = result_ptr;
162  | 	
163  | 	if(tree->family == RX_FAM_IP ) {
164  |         /* do not look at the leaf if  RX_FAM_IP */
165  |         /* Just free the payload, there must be one and just one. */
166  |            wr_free(leafptr);
167  |         }  
168  |         else { /* other families */
169  |           /* if not >composed< then delete dataleaf */
170  |           if( leafptr->composed == 0 ) {
171  |              if( leafptr->data_ptr )
172  | 	         wr_free(leafptr->data_ptr);
173  |              wr_free(leafptr);
174  |       
175  |              ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "dataleaf refcount = 0, removed");
176  |       
177  |           }
178  |           /* else decrement the reference number ( == number of prefixes 
179  |              composing the range minus 1 == the >composed< flag */
180  |           else {
181  |              leafptr->composed--;
182  |       
183  |              ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "dataleaf refcount -- to %d ",
184  | 		           leafptr->composed );
185  |           }  
186  |         } /* if family != RX_FAM_IP */
187  | }
188  | 
189  | void rx_delete_treenode(rx_tree_t *tree, rx_node_t *curnode)
190  | {
191  |      if(curnode->leaves_ptr) {
192  | 	 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "deleting dataleaves of node at %08x", curnode);    
193  |          g_list_foreach( curnode->leaves_ptr, rx_delete_dataleaves, tree);
194  | 	 /* delete the GList */
195  | 	 g_list_free(curnode->leaves_ptr);
196  |      }
197  |      ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "node at %08x removed", curnode);
198  |      wr_free(curnode);
199  | 	
200  | }
201  | 
202  | /* The fuction itself */
203  | int
204  | rx_delete_tree(rx_tree_t *tree, rx_node_t *node,
205  |              int maxlevel, 
206  | 	     int level, 
207  | 	     int nodecounter,
208  | 	     void *userptr)
209  | {
210  | int i, link;
211  | 
212  |  if( node == NULL ) die; /* program error. we expect a valid, checked, node.*/
213  | 
214  |  /* check if the level is reached */
215  |  
216  |  if( level > maxlevel ) {
217  |    return nodecounter; 
218  |  }
219  | 
220  | 
221  |  /* process the node appropriately: */
222  |  /* increase our depth counter */
223  |  level++;
224  | 
225  |  /* increase the count of visited nodes */
226  |  nodecounter++;
227  | 
228  | 
229  |  /* process left and right children */
230  |  for(i=0; i<=1; i++) {
231  |    
232  |    link = i;
233  |      
234  |    if( node->child_ptr[link] != NULL ) {
235  |      nodecounter += rx_delete_tree(tree, node->child_ptr[link], maxlevel, level, 0, userptr);
236  |      /* delete the processed child node */
237  |      rx_delete_treenode(tree, node->child_ptr[link]);
238  |    }
239  |  }
240  |  
241  |  /* if this is the top level - delete the top node and the tree*/
242  |  if(node == tree->top_ptr){
243  | 	 rx_delete_treenode(tree, node); 
244  | 	 tree->top_ptr=NULL;
245  |  }
246  | 	 
247  |  /* return count of nodes deleted */
248  |  return nodecounter;
249  | }
250  | 
251  | 
252  | /***************************************************************************/
253  | /*++++++
254  |   creates a (top) tree for the space, fills out sql table of trees
255  |   generates a tablename for a tree (if NONE)
256  |   updates LL of trees
257  | 
258  |   MT-note: locks/unlocks the forest (still to be done)
259  |   
260  | ++++++++*/
261  | er_ret_t 
262  | RX_tree_cre (
263  | 	      char      *prefixstr, /*+ prefix the tree will cover (string) +*/
264  | 	      rx_fam_t   fam_id,
265  | 	      rx_mem_mt   mem_mode, /* memory only, memory+sql, sql only +*/
266  | 	      rx_subtree_mt subtrees,	/*+ one of NONE, AUTO, HAND +*/
267  | 	      rx_tree_t **treestore /* store the tree pointer here */
268  | 	     )
269  | 
270  | {
271  |   er_ret_t     err;
272  |   rx_tree_t    *newtree;
273  |   ip_prefix_t  newpref;
274  |   ip_space_t   spc_id;
275  | 
276  |   if( IP_pref_e2b(&newpref, prefixstr) != IP_OK ) {
277  |     die;
278  |   }
279  | 
280  |   spc_id = IP_pref_b2_space( &newpref );
281  |   
282  |   if ( (err=wr_malloc( (void **) & newtree, sizeof(rx_tree_t))) != UT_OK ) {
283  |     return err;  /* die*/
284  |   }
285  |   
286  |   ER_dbg_va(FAC_RX, ASP_RX_TREE_GEN, "creating a tree at %08x", newtree);
287  | 
288  |   /* copy tree settings */ 
289  |   newtree -> space  = spc_id;
290  |   newtree -> family = fam_id;
291  | 
292  |   newtree -> subtrees = subtrees;
293  |   newtree -> mem_mode = mem_mode;
294  | 
295  |   /* set other tree values */
296  | 
297  |   /* parent set to NULL because it's not a subtree */
298  |   newtree -> parent_tree = NULL;
299  |   /* PR_zeroprefix(& newtree -> prefix);*/
300  |   newtree -> maxbits = IP_sizebits(spc_id);
301  | 
302  |   strcpy(newtree->data_table.val,"");
303  |   strcpy(newtree->radix_table.val,"");
304  |   strcpy(newtree->leaves_table.val,"");
305  | 
306  |   newtree->num_nodes = 0;
307  | 
308  |   newtree->top_ptr = NULL;
309  |   newtree->top_key = SQ_NOKEY;
310  |   
311  |   newtree->prefix = newpref;
312  | 
313  |   TH_init_read_write_lockw( &(newtree->rwlock));
314  | 
315  |   *treestore = newtree;
316  |   
317  |   return RX_OK;
318  | }
319  | 
320  | 
321  | /* ************************************
322  |    special walk function for use in consistency checks - it checks the parent
323  |    pointer too.
324  | ************************************/
325  | int rx_check_walk_tree( rx_node_t *node, 
326  | 			rx_node_t *parent_node, 
327  | 			int nodecounter,
328  | 			rx_treecheck_t *checkstruct )
329  | {
330  | int i;
331  | 
332  |  /* checks*/
333  |  if( node == NULL ) {    
334  |    checkstruct->code |= 1;
335  |  }
336  |  if( node->parent_ptr != parent_node ) {
337  |    checkstruct->code |= 2;
338  |  }
339  |  if( node->glue && node->leaves_ptr ) {
340  |    checkstruct->code |= 4;
341  |  }
342  |  if( node->glue && (node->child_ptr[0] == NULL || node->child_ptr[1] == NULL ) ) {
343  |    checkstruct->code |= 8;
344  |  }
345  |  
346  |  
347  |  if( node->leaves_ptr && checkstruct->datatoo ) {
348  |    switch( checkstruct->tree->family ) {
349  |    case  RX_FAM_IP:
350  |      /* the simplest (?) case: only one leaf attached to any node 
351  | 	(except for glues) */
352  |      if( g_list_length(node->leaves_ptr) != 1 ) {
353  |        checkstruct->code |= 16;
354  |      }
355  |      break;
356  |    case RX_FAM_RT:
357  |      /* many dataleaves attached to nodes. */
358  |      break;
359  |    case RX_FAM_IN:
360  |      /* many dataleaves attached to nodes. 
361  | 	Some leaves pointed to from many nodes => from as many as the number
362  | 	of composing prefixes 
363  |      */
364  |      break;
365  |    default: 
366  |      /* ignore */
367  |      break;
368  |    }
369  |  }
370  |  
371  |   
372  |  if( checkstruct->code != 0 ) {
373  |    checkstruct->node = node;
374  |  
375  |    return nodecounter;          /* abort the walk on error*/
376  |  }
377  | 
378  | 
379  |   nodecounter++;
380  |   
381  |   for(i=0; i<=1; i++) {
382  |     if( node->child_ptr[i] != NULL ) {
383  |       nodecounter += rx_check_walk_tree( node->child_ptr[i], 
384  | 					 node,
385  | 					 0, checkstruct );
386  |       /* abort the walk on error*/
387  |       if ( checkstruct->code != 0 ) {
388  | 	die; break;
389  |       }
390  |     }
391  |   }
392  |   return nodecounter;
393  | }
394  | 
395  | /* **************************************************************************
396  | tree consistency check.
397  | 
398  | if datatoo = 0, then only parent/child links are checked.
399  | 
400  | if datatoo = 1, then a check on the contents of the nodes is done too.
401  | 
402  | **************************************************************************/
403  | 
404  | er_ret_t
405  | RX_treecheck( rx_tree_t *tree, int datatoo, rx_treecheck_t *errorfound)
406  | {
407  |   er_ret_t err = RX_OK;
408  |   int nodnum;
409  |   
410  |   errorfound->tree = tree;
411  |   errorfound->datatoo = datatoo;
412  | 
413  |   /* errorfound.node will be set by hook if it finds an error*/
414  |   errorfound->code = 0;
415  |   
416  |   nodnum = rx_check_walk_tree( tree->top_ptr, 
417  | 			       NULL,
418  | 			       0,
419  | 			       errorfound );
420  |   
421  |   if( nodnum != tree->num_nodes ) { 
422  |     errorfound->code |= 1024;
423  |   }
424  |   if( tree->num_nodes == 0 && tree->top_ptr != NULL ) { 
425  |     errorfound->code |= 2048;
426  |   }
427  |   if( tree->num_nodes != 0 && tree->top_ptr == NULL ) { 
428  |     errorfound->code |= 4096;
429  |   }
430  |   
431  |   if( errorfound->code != 0) {
432  |     err = RX_DATNOF;
433  |   }
434  |   return err;
435  | }