modules/rx/rx_search.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. rx_build_stack
  2. rx_nod_append
  3. rx_walk_hook_addnode
  4. rx_walk_hook_adddoubles
  5. rx_nod_search
  6. RX_bin_search

   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[], 
     /* [<][>][^][v][top][bottom][index][help] */
  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) 
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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 (
     /* [<][>][^][v][top][bottom][index][help] */
 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 (
     /* [<][>][^][v][top][bottom][index][help] */
 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 

/* [<][>][^][v][top][bottom][index][help] */