1 | /*************************************** 2 | $Revision: 1.35 $ 3 | 4 | Radix tree (rx). rx_search.c - functions to search nodes of the tree 5 | 6 | Status: NOT REVUED, TESTED, COMPLETE 7 | 8 | Design and implementation by: Marek Bukowy 9 | 10 | ******************/ /****************** 11 | Copyright (c) 1999 RIPE NCC 12 | 13 | All Rights Reserved 14 | 15 | Permission to use, copy, modify, and distribute this software and its 16 | documentation for any purpose and without fee is hereby granted, 17 | provided that the above copyright notice appear in all copies and that 18 | both that copyright notice and this permission notice appear in 19 | supporting documentation, and that the name of the author not be 20 | used in advertising or publicity pertaining to distribution of the 21 | software without specific, written prior permission. 22 | 23 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 24 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL 25 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 26 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 27 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 28 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 29 | ***************************************/ 30 | 31 | 32 | #include <erroutines.h> 33 | #include <rxroutines.h> 34 | #include <stubs.h> 35 | 36 | #include "iproutines.h" 37 | 38 | /***************************************************************************/ 39 | 40 | /*++++++++++++++ 41 | Descends the given tree following the last prefix bit to get [past] 42 | the node with the given prefix. 43 | It fills up a stack of COPIES of nodes, including glue nodes. 44 | 45 | Then it also sets the number of elements on the stack: 46 | set maxdepth to the position where a next one would be written 47 | ( = last + 1, or number of nodes pushed) 48 | 49 | The dmodes: 50 | 51 | RX_STK_QUERY_NOGLUE = (search exact/less spec) stop when 52 | * the current prefix length >= newprefix length 53 | * the current prefix does not match anymore 54 | * do not add glue nodes 55 | 56 | RX_STK_QUERY_ALLNOD = as above, except that the glue and data nodes are 57 | treated equally (i.e. glue nodes are not skipped) 58 | 59 | RX_STK_CREAT = descend until the next non-glue node past the one found 60 | in exact mode (for creation) 61 | 62 | ++++++++++++++*/ 63 | 64 | er_ret_t 65 | rx_build_stack(rx_nodcpy_t stack[], 66 | int *maxdepth, 67 | rx_tree_t *tree, 68 | ip_prefix_t *newpref, 69 | rx_stk_mt dmode 70 | ) 71 | { 72 | register rx_node_t *curnode; 73 | register int link, quit_now=0; 74 | register int tracedet = ER_is_traced( FAC_RX, ASP_RX_STKBLD_DET); 75 | char bbf[IP_PREFSTR_MAX]; 76 | 77 | if( ER_is_traced( FAC_RX, ASP_RX_STKBLD_GEN)) { 78 | IP_pref_b2a( newpref , bbf, IP_PREFSTR_MAX); 79 | ER_dbg_va(FAC_RX, ASP_RX_STKBLD_GEN, 80 | "rx_build_stack: searching for %s in mode %d", bbf, dmode); 81 | } 82 | 83 | *maxdepth = 0; 84 | 85 | if ( tree -> num_nodes == 0) { 86 | /* The tree was empty. */ 87 | return RX_OK; 88 | } 89 | 90 | curnode = tree->top_ptr; 91 | /* this works for RAM, for SQL one would have to call a 'getsqlnode' here*/ 92 | 93 | /* OK, there is at least one node. Descend the tree */ 94 | /* as long as the correct bit length is not exceeded*/ 95 | /* or a glue is being found (take the last non-glue node then) */ 96 | /* or you run out of nodes in the direction of descending*/ 97 | 98 | do { 99 | /* check at the current node, where the one we look for would fit*/ 100 | /* (the second argument of IP_addr_bit_get starts with 0,*/ 101 | /* so this effectively looks at the bit next to the last significant bit*/ 102 | /* of the current node*/ 103 | 104 | link = IP_addr_bit_get( & newpref->ip, curnode->prefix.bits ); 105 | 106 | /* check conditions for leaving the loop */ 107 | if(curnode->child_ptr[link] == NULL) { 108 | /* end of branch. quit after adding the current node to the stack*/ 109 | /* (or before - subject to bit test in QUERY mode)*/ 110 | quit_now = 1; 111 | } 112 | else { 113 | /* check the node. 114 | BIG DIFFERENCE between the modes: 115 | in CREAT we don't mind the stack to go too deep, 116 | in QUERY it can lead to false answers 117 | (e.g. a /24 is found for a /23 query). 118 | 119 | So this must be "peeled off the stack" later in the search routine, 120 | if both types of stack are to work properly with query searches. 121 | */ 122 | 123 | 124 | if( curnode->prefix.bits > newpref->bits ) { 125 | /* deep enough.*/ 126 | quit_now = 2; 127 | } 128 | 129 | if(dmode == RX_STK_CREAT && curnode->glue) { 130 | /* mode: creation. */ 131 | /* Cancel quitting if glue -- in CREAT mode the stack building */ 132 | /* should stop at the next real (non-glue) node.*/ 133 | /* ("next" meaning following link #0)*/ 134 | quit_now = 0; 135 | } 136 | } 137 | 138 | /* now that the conditions for leaving the loop after the node is 139 | added on the stack, see if we shouldn't leave the loop BEFOREHAND */ 140 | 141 | /* In query mode, we should quit as soon as we see a mismatch */ 142 | 143 | if(dmode != RX_STK_CREAT 144 | && 0 != IP_addr_cmp(&curnode->prefix.ip, &newpref->ip, 145 | curnode->prefix.bits) ) { 146 | /*QUIT NOW! (but add this node)*/ 147 | quit_now = 4; 148 | } 149 | 150 | /* push the current node on the stack. RAM only.*/ 151 | /* */ 152 | /* (unless quit_now is 64 which means do NOT copy the current node.*/ 153 | /**/ 154 | /* In CREAT and QUERY_ALLNOD modes, push everything. */ 155 | /* In QUERY_NOGLUE mode, only non-glues.*/ 156 | 157 | if( /* quit_now < 64 && disabled as 64 is not in use right now */ 158 | (dmode != RX_STK_QUERY_NOGLUE || curnode->glue == 0 )) { 159 | memcpy( & stack[*maxdepth].cpy, curnode, sizeof(rx_node_t)); 160 | stack[*maxdepth].srcptr = curnode; 161 | stack[*maxdepth].srckey = SQ_NOKEY; 162 | stack[*maxdepth].tree = tree; 163 | (*maxdepth)++; 164 | } 165 | 166 | /* make debug info.*/ 167 | 168 | if( tracedet ) { 169 | IP_pref_b2a( & curnode->prefix , bbf, IP_PREFSTR_MAX ); 170 | ER_dbg_va(FAC_RX, ASP_RX_STKBLD_DET, 171 | "rx_build_stack: %s%d at %s%s (stk len: %d)", 172 | quit_now ? "stop/" : "link ", 173 | quit_now ? quit_now : link, 174 | bbf, ( curnode->glue ) ? " ++glue++" : "", 175 | *maxdepth ); 176 | } 177 | 178 | curnode = curnode -> child_ptr[link]; 179 | 180 | } while( !quit_now ); 181 | 182 | return RX_OK; 183 | } 184 | 185 | /***************************************************************************/ 186 | /*+++++++++ 187 | helper for the nod_search routine: 188 | 189 | allocate a new node copy struct, copy the struct and add to nodlist 190 | ++++++++++*/ 191 | 192 | static 193 | er_ret_t 194 | rx_nod_append( GList **nodlist, rx_nodcpy_t *element) 195 | { 196 | rx_nodcpy_t *newcpy; 197 | er_ret_t err; 198 | 199 | if( (err=wr_calloc( (void **) & newcpy, 1, sizeof(rx_nodcpy_t))) != UT_OK) { 200 | return err; /* die;*/ 201 | } 202 | memcpy(newcpy, element, sizeof(rx_nodcpy_t)); 203 | (*nodlist) = g_list_prepend( *nodlist, newcpy ); 204 | 205 | return RX_OK; 206 | } 207 | 208 | 209 | 210 | 211 | /***************************************************************************/ 212 | 213 | /*+++++++++++ 214 | helper for MORE specific lookup in rx_nod_search 215 | 216 | adds a node to the list of answers. 217 | +++++++++++*/ 218 | 219 | static 220 | er_ret_t 221 | rx_walk_hook_addnode(rx_node_t *node, int level, int nodecounter, 222 | void *userptr) 223 | { 224 | rx_nodcpy_t nodcpy; 225 | hook_addnode_userdat_t *userdat = userptr; 226 | 227 | 228 | /* do not append glue nodes*/ 229 | if( node->glue == 1 ) return RX_OK; 230 | 231 | /* in RAM mode, do not copy the node.*/ 232 | /* memcpy( &nodcpy.cpy, node, sizeof(rx_node_t));*/ 233 | 234 | /* XXX reset to 0 to avoid warnings from workshop: but it 235 | slows things down! */ 236 | memset( &nodcpy.cpy, 0, sizeof(rx_node_t)); 237 | 238 | nodcpy.srcptr = node; 239 | nodcpy.srckey = SQ_NOKEY; 240 | nodcpy.tree = userdat->tree; 241 | 242 | return rx_nod_append( userdat->nodlist, &nodcpy); 243 | } 244 | 245 | 246 | /***************************************************************************/ 247 | 248 | /*+++++++++++ 249 | helper for DBLS lookup in rx_nod_search 250 | 251 | adds a node to the list of answers. 252 | +++++++++++*/ 253 | 254 | static 255 | er_ret_t 256 | rx_walk_hook_adddoubles(rx_node_t *node, int level, int nodecounter, 257 | void *userptr) 258 | { 259 | rx_nodcpy_t nodcpy; 260 | hook_addnode_userdat_t *userdat = userptr; 261 | int leaves = g_list_length(node->leaves_ptr); 262 | char buf[1024]; 263 | 264 | /* do not append glue nodes*/ 265 | if( node->glue == 1 ) return RX_OK; 266 | 267 | 268 | /* add only nodes with more than 1 dataleaf*/ 269 | if( leaves < 2 ) return RX_OK; 270 | 271 | if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) { 272 | rx_nod_print(node, buf, 1024); 273 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 274 | "rx_walk_hook_adddoubles: %30s, %d leaves", buf, leaves); 275 | } 276 | 277 | /* memcpy( &nodcpy.cpy, node, sizeof(rx_node_t));*/ 278 | nodcpy.srcptr = node; 279 | nodcpy.srckey = SQ_NOKEY; 280 | nodcpy.tree = userdat->tree; 281 | 282 | return rx_nod_append( userdat->nodlist, &nodcpy); 283 | } 284 | 285 | 286 | /***************************************************************************/ 287 | er_ret_t 288 | rx_nod_search ( 289 | rx_srch_mt search_mode, 290 | int par_a, 291 | int par_b, 292 | /* see rx_asc_search() for explanation */ 293 | rx_tree_t *tree, /* tree ptr*/ 294 | ip_prefix_t *prefix, /* binary prefix*/ 295 | 296 | rx_nodcpy_t stack[], /* stack==array of node_copies*/ 297 | int stackcount, /* number of element on the stack,*/ 298 | /* can come from a creat stack!*/ 299 | 300 | GList **nodlist, /* answers go here*/ 301 | int max_count /* max # of answers*/ 302 | ) 303 | /* 304 | searches the stack for a given prefix, finds *nodes* in the stack 305 | and appends *copies of the nodes* to the nodlist; 306 | 307 | finds 308 | 0 or 1 nodes for exact search 309 | 0 or 1 nodes for exless (0 if no less specific node found) 310 | any number (incl. 0) for {more|less}^n-m specific 311 | 312 | returns errcode. 313 | 314 | 315 | */ 316 | { 317 | char buf[1024]; 318 | int sps = stackcount-1; /* stack position.*/ 319 | int depthcounter=0; 320 | er_ret_t err=RX_OK; 321 | int i; 322 | hook_addnode_userdat_t datstr; 323 | er_ret_t (*hook_function)(); /* pointer to the walk_hook function*/ 324 | /* (see MORE spec lookup)*/ 325 | 326 | /* structure for carrying data to walk_tree hook functions, used only 327 | in MORE, DBLS and RANG search modes 328 | */ 329 | datstr.nodlist = nodlist; 330 | datstr.tree = tree; 331 | datstr.prefix = prefix; 332 | 333 | 334 | if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) { 335 | IP_pref_b2a( prefix , buf, IP_PREFSTR_MAX); 336 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_GEN, 337 | "rx_nod_search: searching for %s in mode %d (%s)", 338 | buf, search_mode, RX_text_srch_mode(search_mode) ); 339 | } 340 | 341 | /* in non-CREAT modes, glue nodes are skipped anyway. 342 | (they should normally not be there if the stack was created in 343 | the STK_QUERY mode, but it's possible to use a CREAT stack too). 344 | 345 | It's also possible that the stack is too deep. 346 | So, truncate the stack to the last non-glue node 347 | of the length <= search term. 348 | otherwise a /24 would be returned for a /23 query. 349 | 350 | For LESS SPECIFIC searches one has to peel off entries 351 | whose prefixes do not contain the search term, 352 | */ 353 | 354 | if( search_mode != RX_SRCH_CREAT ) { 355 | 356 | while( sps >= 0 ) { 357 | char *reason = NULL; 358 | 359 | if( stack[sps].cpy.prefix.bits > prefix->bits ) { /* too deep*/ 360 | reason = "2deep"; 361 | } 362 | else if( 0 != IP_addr_cmp(& stack[sps].cpy.prefix.ip, &prefix->ip, 363 | stack[sps].cpy.prefix.bits) ) { /* mismatch */ 364 | reason = "mismatch"; 365 | } 366 | else if ( search_mode != RX_SRCH_MORE && search_mode != RX_SRCH_DBLS 367 | && search_mode != RX_SRCH_RANG && stack[sps].cpy.glue == 1 ) { /* is glue*/ 368 | reason = "glue"; 369 | } 370 | #if 0 371 | /* mhm. it can't be limited here, must be done in RP */ 372 | else if ( search_mode == RX_SRCH_LESS && par_a == 1 373 | && stack[sps].cpy.prefix.bits == prefix->bits ) { /* too deep*/ 374 | reason = "2deep4less"; 375 | } 376 | #endif 377 | 378 | else { 379 | 380 | break; /* stop peeling off */ 381 | } 382 | 383 | if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) { 384 | rx_nod_print( & stack[sps].cpy , buf, IP_PREFSTR_MAX); 385 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 386 | "rx_nod_search: peeling off %d: %s (%s)", sps, buf, reason); 387 | } 388 | sps--; 389 | } 390 | } 391 | 392 | /* nothing left on the stack. Sorry.*/ 393 | /* we allow that for more spec search -- this means*/ 394 | /* that the search term is a shorter prefix than the one*/ 395 | /* in the top node. Possibly it's 0/0 which is valid for more spec search.*/ 396 | 397 | if( search_mode != RX_SRCH_MORE && search_mode != RX_SRCH_DBLS 398 | && sps < 0 ) { 399 | return RX_OK; 400 | } 401 | 402 | switch(search_mode) { 403 | case RX_SRCH_EXACT: 404 | case RX_SRCH_CREAT: 405 | /* go up the tree (stack) and exit when the proper prefix is found.*/ 406 | /* For RX_SRCH_EXACT skip glue nodes, for RX_SRCH_CREAT take all.*/ 407 | /* They may contain a valid prefix, so watch out.*/ 408 | 409 | while(sps >= 0) { 410 | 411 | if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) { 412 | rx_nod_print(& stack[sps].cpy, buf, 1024); 413 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 414 | "rx_nod_search: position %d: %s", sps, buf); 415 | } 416 | 417 | if ( search_mode == RX_SRCH_EXACT 418 | && stack[sps].cpy.glue ) { 419 | die; 420 | } 421 | 422 | if ( memcmp( & stack[sps].cpy.prefix, 423 | prefix, 424 | sizeof(ip_prefix_t)) == 0 ) { 425 | /* FOUND!!*/ 426 | /* add to the nodlist.*/ 427 | 428 | if( (err=rx_nod_append( nodlist, & stack[sps])) != RX_OK ) { 429 | return err; 430 | } 431 | 432 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, "rx_nod_search: found!"); 433 | break; 434 | } 435 | sps--; 436 | } 437 | break; 438 | 439 | case RX_SRCH_EXLESS: 440 | /* just fetch the last element off the stack (if any). */ 441 | /* Must be non-glue for EXLESS.*/ 442 | 443 | if( sps >= 0 ) { 444 | rx_nod_append( nodlist, & stack[sps]); 445 | } 446 | 447 | /* else : nothing found.*/ 448 | /* For EXLESS: check if the stack contains only non-glue nodes.*/ 449 | /* If it contains a glue, it means it was created in the CREAT mode,*/ 450 | /* which renders the above algorithm absolutely useless. Then crash,*/ 451 | /* this is a programmer's error.*/ 452 | 453 | while( sps >= 0 ) { 454 | if( stack[sps].cpy.glue ) { 455 | die; 456 | } 457 | sps--; 458 | } 459 | 460 | break; 461 | 462 | case RX_SRCH_LESS: 463 | while( sps >= 0 && depthcounter < par_a ) { 464 | if( stack[sps].cpy.glue == 0 ) { 465 | rx_nod_append( nodlist, & stack[sps]); 466 | depthcounter++; 467 | } 468 | sps--; 469 | } 470 | break; 471 | 472 | case RX_SRCH_MORE: 473 | case RX_SRCH_DBLS: /* special (debug?) mode : find nodes with multiple*/ 474 | /* data leaves. Much like more specific, except that*/ 475 | /* most nodes will be skipped.*/ 476 | /* The difference is in calling another hook function*/ 477 | hook_function = ( search_mode == RX_SRCH_MORE ) 478 | ? rx_walk_hook_addnode 479 | : rx_walk_hook_adddoubles; 480 | 481 | /* the result of a more spec search should NOT contain the object exactly*/ 482 | /* matching the query, even if it exists in the database. So two walks are */ 483 | /* performed, one for each child (if it exists). */ 484 | /* MEMORY IMPLEMENTATION ONLY FOR THE MOMENT*/ 485 | 486 | /* start from the top node if the searched prefix is between the 487 | top node and the first node on the stack (i.e. the first node is 488 | contained within the search term) */ 489 | 490 | /* COVERS THE CASE 0.0.0.0/0 */ 491 | /* or any other prefix that the tree might be set to represent,*/ 492 | /* but there is no actual object for it (not even glue)*/ 493 | 494 | if( sps < 0 ) { 495 | if( tree->num_nodes > 0 /* there is any node in the tree */ 496 | && 0 == IP_addr_cmp( & prefix->ip, 497 | & stack[0].cpy.prefix.ip, 498 | prefix->bits) ) { /* addr match */ 499 | rx_walk_tree( tree->top_ptr, hook_function, 500 | /* RX_WALK_REVERS | */ RX_WALK_SKPGLU, /* skip glue nodes while counting*/ 501 | par_a, /* display this many levels */ 502 | 0, 0, &datstr, &err); 503 | if( err != RX_OK ) { 504 | return err; 505 | } 506 | } 507 | } /* if nothing on stack */ 508 | else { 509 | 510 | /* walk from this node if it matches the query prefix and is 511 | long enough (if it is shorter, then it will harvest too many 512 | results 513 | */ 514 | if( prefix->bits <= stack[sps].srcptr->prefix.bits 515 | && 0 == IP_addr_cmp( & stack[sps].srcptr->prefix.ip, 516 | & prefix->ip, 517 | prefix->bits) ) { 518 | rx_walk_tree( stack[sps].srcptr, hook_function, 519 | /* RX_WALK_REVERS | */ RX_WALK_SKPGLU, /* skip glue nodes while counting*/ 520 | par_a, /* display up to this max length*/ 521 | 0, 0, &datstr, &err); 522 | if( err != RX_OK ) { 523 | return err; 524 | } 525 | } 526 | else { 527 | /* or walk the child nodes otherwise (still check the address) */ 528 | 529 | for( i = 1; i >= 0; i--) { 530 | if( stack[sps].cpy.child_ptr[i] != NULL ) { 531 | if( ER_is_traced( FAC_RX, ASP_RX_SRCH_DET)) { 532 | IP_pref_b2a(& stack[sps].cpy.child_ptr[i]->prefix, buf, 1023); 533 | } 534 | 535 | if( 0 == IP_addr_cmp( & stack[sps].cpy.child_ptr[i]->prefix.ip, 536 | & prefix->ip, 537 | prefix->bits) ) { 538 | 539 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 540 | "rx_nod_search: digging child %d: %s", i, buf); 541 | 542 | rx_walk_tree( stack[sps].cpy.child_ptr[i], hook_function, 543 | /* RX_WALK_REVERS | */ RX_WALK_SKPGLU, /* skip glue nodes while counting*/ 544 | par_a, /* display this many levels */ 545 | 0, 0, &datstr, &err); 546 | if( err != RX_OK ) { 547 | return err; 548 | } 549 | } 550 | else { 551 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, 552 | "rx_nod_search: prefix mismatch with child %d: %s", 553 | i, buf); 554 | } 555 | } 556 | } 557 | } /* if node does not match, dig child nodes */ 558 | 559 | } 560 | break; 561 | 562 | case RX_SRCH_RANG: 563 | /* OK, start from the node at the end of the stack (exless match including 564 | glue nodes) then 565 | 566 | 567 | if its prefix length is 568 | OK -> found! descend from here as long as the prefixes are in range 569 | shorter -> apparently there is even no such glue node. come back down 570 | one step 571 | 572 | */ 573 | 574 | i = sps; /* go up the tree (down the stack) */ 575 | /* until too far (one node too much, after >= )*/ 576 | while( i >= 0 && stack[i].cpy.prefix.bits >= prefix->bits ) { 577 | i--; 578 | } 579 | 580 | /* look where you are:*/ 581 | 582 | if( i < 0 ) /* it was the top object, but its prefix was too long*/ 583 | i=0; /* take the top object as the base*/ 584 | else 585 | i++; /* went one too much, now come back one step*/ 586 | 587 | 588 | rx_walk_tree( stack[i].srcptr, rx_walk_hook_addnode, 589 | RX_WALK_PRFLEN, /* skip glue nodes while counting*/ 590 | par_a, /* display up to this max length*/ 591 | 0, 0, &datstr, &err); 592 | if( err != RX_OK ) { 593 | return err; 594 | } 595 | 596 | break; 597 | 598 | /* return RX_NOYETI;*/ 599 | /*not implemented*/ 600 | /* die; */ 601 | default: 602 | die; /* are you nuts??*/ 603 | } 604 | 605 | return err; 606 | 607 | } 608 | 609 | 610 | 611 | /*****************************************************************************/ 612 | 613 | /*+++++++++++++ 614 | builds a stack for this prefix, finds *nodes* in the stack 615 | and appends *copies of the data leaves* to the LL of answers; 616 | 617 | sorts by SQL object keys and uniq's the data 618 | 619 | finds: 620 | 0 or 1 nodes for exact search 621 | 0 or 1 nodes for exless (0 if no less specific node found) 622 | any number (incl. 0) for {more|less}-n specific 623 | 624 | then copies the nodes/dataleaves to the answer structs and appends them 625 | to the given LL. So, effectively, the number of answers can be 626 | anything from 0 to infinity, because objects may be duplicate 627 | even at the same node. 628 | 629 | returns errcode. 630 | 631 | algorithm: 632 | 633 | builds stack[MAXBIT (==128)]; 634 | 635 | if( more/less-depth && par_a == 0) 636 | 637 | run rx_nod_search, then 638 | 639 | if(more spec) rx_nod_walk(maxdepth=n, append_to_LL() ); 640 | if(less spec) do { append(LL, stack[i]) } while(i-- && n--); 641 | otherwise just set LL 642 | 643 | 644 | The routine provides _at_least_ max_count answers. 645 | It will *try* to stop after max_count as soon as possible 646 | - but it's the higher level routine that should do the final cut. 647 | +++++++++++++++*/ 648 | 649 | er_ret_t 650 | RX_bin_search ( 651 | rx_srch_mt search_mode, 652 | int par_a, 653 | int par_b, 654 | rx_tree_t *tree, /* tree ptr*/ 655 | ip_prefix_t *prefix, /* binary prefix*/ 656 | GList **datleaves, /* data leaves go here*/ 657 | int max_count 658 | ) 659 | 660 | { 661 | rx_nodcpy_t stack[128]; 662 | unsigned k; 663 | int stkcnt, resnum = 0, maxleaves; 664 | GList *nodlist = NULL, *nitem; 665 | rx_node_t *curnode; 666 | rx_nodcpy_t *curcpy; 667 | rx_datref_t *datref; 668 | rx_stk_mt dmode; 669 | 670 | /* more specific node search may start from a glue node, */ 671 | /* for all others the stack should not contain glues.*/ 672 | 673 | dmode = ( search_mode == RX_SRCH_MORE 674 | || search_mode == RX_SRCH_DBLS 675 | || search_mode == RX_SRCH_RANG ) 676 | ? RX_STK_QUERY_ALLNOD 677 | : RX_STK_QUERY_NOGLUE; 678 | 679 | rx_build_stack(stack, &stkcnt, tree, prefix, dmode); 680 | 681 | rx_nod_search( search_mode, par_a, par_b, tree, prefix, 682 | stack, stkcnt, &nodlist, 1000); 683 | 684 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_DET, "RX_bin_search: processing nodes"); 685 | 686 | for( nitem = g_list_first(nodlist); 687 | nitem != NULL; 688 | nitem = g_list_next(nitem)) { 689 | 690 | resnum++; 691 | curcpy = nitem->data; 692 | 693 | /* 694 | if memory mode includes RAM: 695 | * do not expect copies of nodes in the list received from bin_search. 696 | * iterate through data leaves with g_list_nth_data. 697 | */ 698 | 699 | curnode = curcpy->srcptr; 700 | 701 | /* rx_nod_print( curnode, buf, 1024 );*/ 702 | 703 | maxleaves = g_list_length(curnode->leaves_ptr); 704 | /* fprintf(stderr,"###node %d, %d dataleaves attached:", i, maxleaves);*/ 705 | 706 | /* iterate through dataleafs attached to this node*/ 707 | for(k=0; k<maxleaves; k++) { 708 | rx_dataleaf_t *leafptr = g_list_nth_data(curnode->leaves_ptr, k); 709 | 710 | /* 711 | check the conditions to add the leaf: 712 | 713 | XXX never add composed inetnum for exact prefix search 714 | (but do for exact range search...) - must be solved in upper layer. 715 | 716 | */ 717 | 718 | 719 | /* add*/ 720 | 721 | dieif( wr_calloc( (void **) &datref, 722 | sizeof(rx_datref_t), 1) != UT_OK); 723 | datref->leafptr = leafptr; 724 | /* srckey and excluded fields are initialised to 0 by calloc */ 725 | 726 | *datleaves = g_list_prepend(*datleaves, datref); 727 | } 728 | } 729 | 730 | wr_clear_list( &nodlist ); 731 | 732 | ER_dbg_va(FAC_RX, ASP_RX_SRCH_GEN, 733 | "RX_bin_search: found %d nodes", resnum); 734 | 735 | 736 | /* the LL of answers (*datleaves) contains pointers to answer structs, 737 | that SHOULD BE NORMALIZED HERE (==with no redundant entries) 738 | */ 739 | 740 | return RX_OK; 741 | } 742 |