modules/rx/rx_node.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- rx_creat_node
- rx_delete_node
- rx_bin_node
- RX_rt_node
- RX_in_node
1 /***************************************
2 $Revision: 1.32 $
3
4 Radix tree (rx). rx_node.c - functions to operate on nodes of the tree
5 (creation/deletion).
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 <rxroutines.h>
34 #include <memwrap.h>
35 #include <stubs.h>
36 #include <glib.h>
37
38 #include <comparisons.h>
39
40 /***************************************************************************/
41 /*++++++++++++++++
42 rx_creat_node = create a new data node
43 (empty{glue} nodes get created automatically).
44
45 Takes a pointer to the (already allocated) data leaf to be included
46 in the list of data nodes (presumably empty as the node is only now being
47 created).
48
49 Requires a stack of nodes created in CREAT mode (with glue nodes,
50 until deep enough and the last node being non-glue).
51
52 MT notes: requires the tree to be locked.
53
54 Returns: RX_OK or error code.
55
56 +++++++++++++++++*/
57 static
58 er_ret_t
59 rx_creat_node (
/* [<][>][^][v][top][bottom][index][help] */
60 ip_prefix_t *newpref, /*+ prefix of the node to be added +*/
61 rx_tree_t *tree, /*+ tree the new node goes to +*/
62 rx_dataleaf_t *dataleaf, /*+ dataleaf to attach at this node+*/
63 rx_nodcpy_t stack[], /*+ stack==array of node_copies +*/
64 int stackdepth /*+ length of the stack +*/
65 )
66 {
67 rx_node_t *newnode, *curnode, *memnode, *gluenode;
68 unsigned chk_bit, dif_bit, link, curpos;
69 char buf[1024];
70 er_ret_t err;
71
72 /* assume no such node yet. Will die if there is one.*/
73
74 /* calloc, because parent/child keys and child ptrs are not always set.*/
75
76 if( (err=wr_calloc( (void **) & newnode, 1, sizeof(rx_node_t))) != UT_OK) {
77 return err;
78 }
79
80 /* increment the number of nodes in the tree*/
81 tree -> num_nodes ++;
82
83 newnode -> prefix = *newpref;
84
85 /* attach the leaf to a (presumably empty?! hence NULL) list...*/
86 newnode->leaves_ptr = g_list_prepend(NULL, dataleaf);
87 newnode->glue = 0;
88
89 /* OK, so take a look at the tree*/
90
91 if ( tree -> num_nodes == 1 ) {
92 /* The tree was empty. Create a new top node.*/
93
94 tree -> top_ptr = newnode;
95 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "Created as the top node");
96 return RX_OK;
97 }
98
99 /* OK, there is at least one node in the tree. Take a look at the stack.*/
100
101 /* we've got a real node there (not a glue), but we may be too deep.*/
102 /* (it's not a glue, because glues have always two children.*/
103 /* we had to go that deep because from a glue alone one doesn't know */
104 /* what it glues)*/
105 /* GO UP.*/
106 /* take the first differing bit from comparing */
107 /* the new and the found nodes' prefixes. */
108 /* (not deeper than the shorter of the two)*/
109
110 curpos = stackdepth-1;
111 curnode = & stack[curpos].cpy;
112
113 chk_bit = smaller(curnode->prefix.bits, newpref->bits );
114
115 for(dif_bit = 0; dif_bit < chk_bit; dif_bit++) {
116 /* break the loop when the first different bit is found*/
117
118 if( IP_addr_bit_get( & curnode->prefix.ip, dif_bit)
119 != IP_addr_bit_get( & newpref->ip, dif_bit) ) {
120 break;
121 }
122 }
123
124 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET,
125 "cur = %d, new = %d, chk_bit = %d, dif_bit = %d",
126 curnode->prefix.bits, newpref->bits, chk_bit, dif_bit );
127
128 if(dif_bit == IP_sizebits(newpref->ip.space)) die; /* it mustn't happen!!!*/
129
130 /* go up to that level (watch the head of the tree!)*/
131
132 while( curpos > 0 && stack[curpos-1].cpy.prefix.bits >= dif_bit) {
133 curpos--;
134 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET,
135 "up to level %d", curpos );
136 }
137
138 /*
139 if the bit lenghts of the node, new prefix and the diffbit are equal
140 {
141 YOU'VE GOT THE NODE where the new one will be attached.
142 Either it has data (and will be moved accordingly),
143 or is a glue (and will be turned into a regular node).
144 }
145 */
146
147 curnode = & stack[curpos].cpy;
148
149 /* RAM: set a pointer to the real node in memory*/
150 memnode = stack[curpos].srcptr;
151
152 if( dif_bit == newpref->bits
153 && dif_bit == curnode->prefix.bits ) {
154
155 /* such node already exists, nothing to change in the tree!!!*/
156 /* this should be checked before calling this function, so..*/
157
158 die;
159 }
160 /*
161 else ** the branch ends here; we must create a new node... **
162 {
163 OK, how is the new node's prefix length w.r.t the dif_bit ?
164 longer -> make it a child of the node found
165 shorter -> make it the parent of the node found and take its place
166 equal -> make a glue node the parent of both
167 }
168
169 WHEN ATTACHING THE NODE, VALUES FROM THE STACK ARE USED,
170 TO PREVENT EXCESSIVE LOOKUPS AGAIN.
171
172 */
173 else {
174
175 /* **** attach it.*/
176 if( ER_is_traced(FAC_RX, ASP_RX_NODCRE_DET) ) {
177 rx_nod_print(curnode, buf, 1024);
178 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "Looking at node %s", buf);
179 }
180
181 if( curnode -> prefix.bits == dif_bit ) {
182
183 /* attach here as a child of the node found */
184 link = IP_addr_bit_get( &newpref->ip, dif_bit );
185
186 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "attaching as child %d", link);
187
188 if( memnode -> child_ptr[link] != NULL ) {
189 die;
190 }
191
192 memnode -> child_ptr[link] = newnode;
193 newnode -> parent_ptr = memnode;
194 }
195 else if ( newpref->bits == dif_bit ) {
196 /* make it the parent of the node found and take its place,*/
197 /* moving it down.*/
198
199 /* set the link from the NEW node to the OLD one (different than before)*/
200
201 link = IP_addr_bit_get( &curnode->prefix.ip, dif_bit );
202
203 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "shifting down as child %d", link);
204
205 /* PARENT<->NEW LINKS*/
206 /* see if the node was the top_node*/
207 if (curnode -> parent_ptr == NULL) {
208 /* update tree struct */
209 tree -> top_ptr = newnode;
210 } else {
211 /* no - fix the child link at the parent.*/
212 /* at the link where it was attached*/
213 int link = (curnode->parent_ptr->child_ptr[1] == memnode);
214 memnode -> parent_ptr -> child_ptr[link] = newnode;
215 }
216 memnode -> parent_ptr = newnode;
217
218 /* NEW<->CHILD LINKS*/
219 newnode -> parent_ptr = curnode->parent_ptr;
220 newnode -> child_ptr[link] = memnode;
221 }
222 else {
223 /* create a glue and shift the curnode below the glue,*/
224 /* then attach the new node at the glue*/
225
226 /* calloc, because parent/child keys are not set.*/
227
228 if( (err=wr_calloc( (void **)& gluenode, 1, sizeof(rx_node_t))) != UT_OK) {
229 return err; /* die;*/
230 }
231 tree -> num_nodes ++;
232
233 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "created glue node at %p", gluenode);
234
235 gluenode -> prefix.bits = dif_bit;
236
237 /* fill in the address. The glue node should get the prefix*/
238 /* shorter by one than the shorter of the two prefixes that are glued*/
239 /* (difbit)*/
240 /**/
241
242 gluenode -> prefix.ip = newpref->ip;
243 gluenode -> prefix.bits = dif_bit;
244
245 /* the ip in this prefix is probably incorrect. Fix it.*/
246 IP_pref_bit_fix( & gluenode -> prefix );
247
248 gluenode -> leaves_ptr = NULL;
249 gluenode -> glue = 1;
250
251 /* 1. Fix the link to and from the parent to the gluenode.*/
252
253 gluenode -> parent_ptr = curnode->parent_ptr;
254 if (gluenode->parent_ptr == NULL) {
255 tree -> top_ptr = gluenode;
256 }
257 else {
258 /* fix the child link in the parent. */
259 /* if it was at 1, then let fix the link 1, 0 otherwise*/
260
261 link = (curnode->parent_ptr->child_ptr[1] == memnode);
262
263 memnode->parent_ptr->child_ptr[link] = gluenode;
264 }
265
266 /* 2. Fix the links between gluenode and the OLD node*/
267
268 link = IP_addr_bit_get( &newpref->ip, dif_bit );
269
270 gluenode -> child_ptr[ ! link ] = memnode;
271 memnode->parent_ptr = gluenode;
272
273 /* 3. Fix the links between gluenode and the NEW node*/
274
275 gluenode -> child_ptr[ link ] = newnode;
276 newnode -> parent_ptr = gluenode;
277 }
278 return RX_OK;
279 }
280 die;
281 return -1; /*this is just to calm down the compiler*/
282 }
283
284
285 /******************************************************************
286 an auxiliary function to delete data from a node
287 (and delete the node or turn it into a glue afterwards)
288
289 takes
290
291 tree tree
292 curnode pointer to the node
293 dataleaf pointer to a dataleaf with ObjectID (dataleaf->data_key)
294 set; which is used to choose the right dataleaf
295 when browsing data leaves. It is never assumed to be
296 allocated via malloc, can be a local variable as well.
297
298 If the composed flag of the dataleaf in the tree
299 (being the reference count at the same time)
300 is non zero, decrements the count.
301 Deletes the dataleaf when it reaches zero.
302
303 suceeds always or dies when dataleaf with such data cannot be found
304 in the node
305 */
306
307 void
308 rx_delete_node (rx_tree_t *tree, rx_node_t *curnode, rx_dataleaf_t *dataleaf)
/* [<][>][^][v][top][bottom][index][help] */
309 {
310 rx_dataleaf_t *leaffound = NULL;
311 GList *qitem;
312 int leavesum=0;
313
314 /*+ RX_FAM_IP implies there's no dataleaf!!!
315 The structure in place of a dataleaf is payload
316 +*/
317
318 /* go through leaves, comparing the objectID (data_key) */
319 for( qitem = g_list_first(curnode->leaves_ptr);
320 qitem != NULL;
321 qitem = g_list_next(qitem)) {
322 rx_dataleaf_t *leafptr = qitem->data;
323
324 if( tree->family == RX_FAM_IP /* do not look at the pointers */
325 || leafptr->data_key == dataleaf->data_key ) { /* if RX_FAM_IP */
326 leaffound = leafptr;
327 /* no break - we're counting leaves..*/
328 }
329 leavesum++;
330 }
331
332
333 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "%d dataleaves at the node", leavesum);
334
335 /* return error if none of the dataleaves matched */
336 if( leaffound == NULL ) die;
337
338 /* NO error? good. Remove the leaf from the list */
339 curnode->leaves_ptr = g_list_remove ( curnode->leaves_ptr, leaffound );
340
341
342 if(tree->family == RX_FAM_IP ) {
343 /* again: do not look at the leaf if RX_FAM_IP */
344 /* Just free the payload, there must be one and just one. */
345 wr_free(leaffound);
346 }
347 else { /* other families */
348 /* if not >composed< then delete dataleaf */
349 if( leaffound->composed == 0 ) {
350 if( leaffound->data_ptr != NULL /* allow dataleafs without attached */
351 && leaffound->data_len > 0 ) { /* data */
352 wr_free(leaffound->data_ptr);
353 }
354 wr_free(leaffound);
355
356 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "dataleaf refcount = 0, removed");
357
358 }
359 /* else decrement the reference number ( == number of prefixes
360 composing the range minus 1 == the >composed< flag */
361 else {
362 leaffound->composed--;
363
364 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "dataleaf refcount -- to %d ",
365 leaffound->composed );
366 }
367 } /* if family != RX_FAM_IP */
368
369 /* if that was the last leave at this node, then delete node. */
370 if( leavesum == 1 ) {
371 rx_node_t *parent = curnode->parent_ptr;
372
373 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "last dataleaf, removing node");
374
375 assert(curnode->leaves_ptr == NULL);
376 /* To do this, check the number of children: */
377
378 /* 0 - just delete this node and the link to it */
379 if( curnode->child_ptr[0] == NULL && curnode->child_ptr[1] == NULL ) {
380
381 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "no children, just removing");
382
383 if( parent != NULL ) { /* watch the head! */
384 int plink = (parent->child_ptr[1] == curnode);
385 parent->child_ptr[plink] = NULL;
386 }
387 else {
388 assert(tree->top_ptr == curnode);
389 tree->top_ptr = NULL;
390 }
391 tree->num_nodes--;
392 wr_free(curnode);
393
394
395 /* now, if we deleted curnode, let's see if the parent node is a glue.
396 If it is, then hook the remaining child up the grandparent,
397 and delete the parent */
398 if( parent != NULL && parent->glue ) {
399 int slink = (parent->child_ptr[1] != NULL );
400 rx_node_t *schild = parent->child_ptr[slink];
401 rx_node_t *gparent = parent->parent_ptr;
402
403 assert( schild != NULL && parent->child_ptr[ ! slink] == NULL);
404
405 /* upd parent */
406 if( gparent != NULL ) { /* watch the head! */
407 int plink = (gparent->child_ptr[1] == parent);
408 gparent->child_ptr[plink] = parent->child_ptr[slink];
409 } else {
410 assert(tree->top_ptr == parent);
411 tree->top_ptr = parent->child_ptr[slink];
412 }
413
414 /* update the child's parent link too */
415 parent->child_ptr[slink]->parent_ptr = gparent;
416
417 /* del */
418 tree->num_nodes--;
419 wr_free(parent);
420
421 } /* if parent glue */
422 }
423 /* 2 - turn into a glue */
424 else if( curnode->child_ptr[0] != NULL
425 && curnode->child_ptr[1] != NULL ) {
426
427 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "two children, turning into a glue");
428
429 curnode->glue = 1;
430
431 }
432 /* 1 - copy the child's link to parent. then delete */
433 else {
434 int clink = (curnode->child_ptr[1] != NULL );
435
436 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "one child at %d, shifting it up",
437 clink);
438
439 /* upd parent */
440 if( parent != NULL ) { /* watch the head! */
441 int plink = (parent->child_ptr[1] == curnode);
442 parent->child_ptr[plink] = curnode->child_ptr[clink];
443 } else {
444 /* no parent; the child becomes the top node now */
445 tree->top_ptr = curnode->child_ptr[clink];
446 }
447
448 /* update the child's parent link too */
449 curnode->child_ptr[clink]->parent_ptr = parent;
450
451 /* del */
452 tree->num_nodes--;
453 wr_free(curnode);
454 }
455
456
457 } /* leavesum == 1 <=> that was the last data leaf */
458 } /* rx_delete_node */
459
460 /*+++++++++++++++++++
461
462 General function to operate on dataleaves attached to a single node
463 (create / modify / delete).
464
465 searches tree, finds and creates/deletes a node,
466 copies modified nodes to disk using rx_sql_node_set (not yet implemented).
467 Updates memory rollback info.
468
469
470
471
472 creation:
473 Add a dataleaf at the node defined by prefix.
474 Create a new node if it doesn't exist yet.
475
476
477 MT notes: requires the tree to be locked.
478
479 Returns: RX_OK or error code.
480
481 Errors from:
482 rx_bin_search,
483 memory alloc routines.
484
485 - no such node (if not in create mode)
486
487 - too many nodes found (strange).
488
489 +++++++++++++++++*/
490
491 /*static*/
492 er_ret_t
493 rx_bin_node (
/* [<][>][^][v][top][bottom][index][help] */
494 rx_oper_mt mode, /*+ MODE={cre|mod|del} +*/
495 ip_prefix_t *newpref, /*+ prefix of the node +*/
496 rx_tree_t *tree, /*+ pointer to the tree structure +*/
497 rx_dataleaf_t *dataleaf /*+ dataleaf to attach at the node +*/
498 )
499
500 {
501 GList *nodlist = NULL;
502 int nodesfound, stackdepth;
503 int glue;
504 rx_nodcpy_t *curcpy;
505 rx_node_t *curnode;
506 /* rx_nodcpy_t *stack;*/
507 rx_nodcpy_t stack[128];
508 er_ret_t err;
509 char bbf[IP_PREFSTR_MAX];
510
511
512 if( ER_is_traced( FAC_RX, ASP_RX_NODCRE_DET)) {
513 IP_pref_b2a( newpref , bbf, IP_PREFSTR_MAX);
514 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_GEN,
515 "rx_bin_node: %s in spc %d /fam %d operation %d",
516 bbf, tree->space, tree->family, mode);
517 }
518
519 /* first check: are we using the correct tree ???*/
520 if( tree->space != newpref->ip.space ) {
521 /* trying to insert a prefix of space %d into a tree of space %d\n",
522 tree->space,
523 newpref->ip.space);
524 */
525 die;
526 }
527
528 assert( dataleaf );
529 assert( newpref->bits <= IP_sizebits(tree->space) );
530
531 /* fix the prefix, to make sure all insignificant bits are 0*/
532 IP_pref_bit_fix( newpref );
533
534 if( (err=rx_build_stack(stack, &stackdepth, tree, newpref, RX_STK_CREAT))
535 != RX_OK ) {
536 return err; /*die*/
537 }
538
539 /* rx_stk_print(stack, stackdepth);*/
540
541 /* perform a search on the stack. The result is a list, and it must*/
542 /* be properly deleted after use!!*/
543
544 if( (err=rx_nod_search(RX_SRCH_CREAT, 0, 0,
545 tree, newpref, stack, stackdepth,
546 &nodlist, RX_ANS_ALL)) != RX_OK ) {
547 return err; /* die;*/
548 }
549
550
551 /* count number of nodes in the answer */
552 nodesfound = g_list_length (nodlist);
553
554 switch( nodesfound ) {
555 case 0:
556 /* no such node (yet). See what we're up to.
557 if( mode==cre ) create, else - program error, die */
558
559 /* C R E A T I O N */
560 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET,
561 "rx_bin_node: Creating a new node %s in spc %d /fam %d ",
562 bbf, tree->space, tree->family);
563 if( mode != RX_OPER_CRE) {
564 die;
565 }
566
567 rx_creat_node( newpref, tree, dataleaf, stack, stackdepth );
568 break;
569 case 1: /* found */
570 /* set the curnode pointer */
571 curcpy = g_list_nth_data(nodlist, 0);
572 curnode = curcpy->srcptr;
573
574 switch( mode ) {
575 case RX_OPER_CRE:
576 /* attach the data at the node that was found;*/
577
578 /* was it glue ?*/
579 glue = curnode->glue;
580
581 curnode->leaves_ptr = g_list_prepend(curnode->leaves_ptr, dataleaf);
582 /* now it's not a glue anymore */
583 curnode->glue = 0;
584
585 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET, "Appended data to a %s node",
586 glue ? "glue" : "data");
587
588 break;
589 case RX_OPER_DEL:
590
591 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_DET,
592 "rx_bin_node: Deleting node %s in spc %d /fam %d ",
593 bbf, tree->space, tree->family);
594 rx_delete_node( tree, curnode, dataleaf);
595 break;
596 }
597 break;
598 default:
599 /* too many nodes found! from an exact/exact-less-1 search.
600 this cannot happen. Call Ghostbusters now.
601 */
602 die;
603 }
604
605 wr_clear_list( &nodlist );
606
607 return RX_OK;
608 }
609
610
611
612 /***************************************************************************/
613 /* ++++++++++++++++
614 A wrapper around RX_bin_node.
615
616 It's there only to control the freeing of dataleaf copies passed
617 for comparison during deletion.
618
619 +++++++++++++++++*/
620 er_ret_t
621 RX_rt_node (
/* [<][>][^][v][top][bottom][index][help] */
622 rx_oper_mt mode, /*+ MODE={cre|mod|del} +*/
623 ip_prefix_t *newpref, /*+ prefix of the node +*/
624 rx_tree_t *tree, /*+ pointer to the tree structure +*/
625 rx_dataleaf_t *leafptr /*+ dataleaf to attach at the node +*/
626 )
627 {
628 er_ret_t reterr;
629
630 IP_pref_2_rang( & leafptr->iprange, newpref);
631 leafptr->preflen = IP_pref_b2_len(newpref);
632
633 /* store the object's range, used in rp.search */
634
635 reterr = rx_bin_node(mode, newpref, tree, leafptr);
636
637 return reterr;
638 }
639
640 /***************************************************************************/
641 /*+++++++++++++++
642 performs the actual update for inetnums (possibly composed of many prefixes).
643 Decomposes the ranges into prefixes and then falls back to rx_bin_node
644 to perform changes at the nodes.
645
646 Requires/returns - practically the same as rx_bin_node.
647 ++++++++++++++++*/
648
649 er_ret_t
650 RX_in_node( rx_oper_mt mode, /*+ MODE={cre|mod|del} +*/
/* [<][>][^][v][top][bottom][index][help] */
651 ip_range_t *rang, /*+ range of IP addresses +*/
652 rx_tree_t *tree, /*+ pointer to the tree structure +*/
653 rx_dataleaf_t *leafptr /*+ dataleaf to attach at the node +*/
654 )
655 {
656 unsigned i, prefcount;
657 GList *preflist = NULL;
658 char buf[IP_RANGSTR_MAX];
659
660 if( ER_is_traced( FAC_RX, ASP_RX_NODCRE_GEN)) {
661 IP_rang_b2a(rang, buf, IP_RANGSTR_MAX );
662 ER_dbg_va(FAC_RX, ASP_RX_NODCRE_GEN,
663 "rx_inum_node: adding %s", buf);
664 }
665
666 /* decompose, put links to the data leaf into every prefix*/
667 /* that makes up this range.*/
668 IP_rang_decomp(rang, &preflist);
669
670 /* see if there is more than 1 prefix, set the composed flag*/
671 prefcount = g_list_length(preflist);
672 leafptr->composed = (prefcount - 1) ;
673
674 leafptr->iprange = *rang;
675
676 /* XXX */
677 if(prefcount==0) {
678 fprintf(stderr, "**** prefcount=0 range: [%s][%u-%u]\n", buf, (rang->begin).words[0], (rang->end).words[0]);
679 }
680
681 for(i=0; i < prefcount; i++) {
682 ip_prefix_t *mypref = g_list_nth_data(preflist, i);
683
684 rx_bin_node(mode, mypref, tree, leafptr);
685 }
686
687 /* free the storage from decomposition*/
688 wr_clear_list( &preflist );
689
690 return RX_OK;
691 }