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