db_lookup.c

Go to the documentation of this file.
00001 /*                     D B _ L O O K U P . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1988-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** @addtogroup dbio */
00023 
00024 /*@{*/
00025 /** @file db_lookup.c
00026  *
00027  * Functions -
00028  *      db_dirhash      Compute hashing function
00029  *      db_lookup       Convert an object name into directory pointer
00030  *      db_diradd       Add entry to the directory
00031  *      db_dirdelete    Delete entry from directory
00032  *      db_rename       Change name string of a directory entry
00033  *      db_pr_dir       Print contents of database directory
00034  *
00035  *
00036  *  Authors -
00037  *      Michael John Muuss
00038  *
00039  *  Source -
00040  *      SECAD/VLD Computing Consortium, Bldg 394
00041  *      The U. S. Army Ballistic Research Laboratory
00042  *      Aberdeen Proving Ground, Maryland  21005-5066
00043  *
00044  */
00045 
00046 #ifndef lint
00047 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/db_lookup.c,v 14.19 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00048 #endif
00049 
00050 #include "common.h"
00051 
00052 #include <stdio.h>
00053 #ifdef HAVE_STRING_H
00054 #  include <string.h>
00055 #else
00056 #  include <strings.h>
00057 #endif
00058 
00059 #include "machine.h"
00060 #include "vmath.h"
00061 #include "db.h"
00062 #include "raytrace.h"
00063 
00064 #include "./debug.h"
00065 
00066 
00067 /**
00068  *                      D B _ I S _ D I R E C T O R Y _ N O N _ E M P T Y
00069  *
00070  *  Returns -
00071  *      0       if the in-memory directory is empty
00072  *      1       if the in-memory directory has entries,
00073  *              which implies that a db_scan() has already been performed.
00074  */
00075 int
00076 db_is_directory_non_empty(const struct db_i     *dbip)
00077 {
00078     register int        i;
00079 
00080     RT_CK_DBI(dbip);
00081 
00082     for (i = 0; i < RT_DBNHASH; i++)  {
00083         if( dbip->dbi_Head[i] != DIR_NULL )
00084             return 1;
00085     }
00086     return 0;
00087 }
00088 
00089 /**
00090  *                      D B _ G E T _ D I R E C T O R Y _ S I Z E
00091  *
00092  *  Return the number of "struct directory" nodes in the given database.
00093  */
00094 int
00095 db_get_directory_size(const struct db_i *dbip)
00096 {
00097     register struct directory *dp;
00098     register int        count = 0;
00099     int         i;
00100 
00101     RT_CK_DBI(dbip);
00102 
00103     for (i = 0; i < RT_DBNHASH; i++)  {
00104         for (dp = dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw)
00105             count++;
00106     }
00107     return count;
00108 }
00109 
00110 /**
00111  *                      D B _ C K _ D I R E C T O R Y
00112  *
00113  *  For debugging, ensure that all the linked-lists for the directory
00114  *  structure are intact.
00115  */
00116 void
00117 db_ck_directory(const struct db_i *dbip)
00118 {
00119     register struct directory *dp;
00120     int         i;
00121 
00122     RT_CK_DBI(dbip);
00123 
00124     for (i = 0; i < RT_DBNHASH; i++)  {
00125         for (dp = dbip->dbi_Head[i]; dp != DIR_NULL; dp = dp->d_forw)
00126             RT_CK_DIR(dp);
00127     }
00128 }
00129 
00130 /**
00131  *                      D B _ D I R H A S H
00132  *
00133  *  Internal function to return pointer to head of hash chain
00134  *  corresponding to the given string.
00135  */
00136 int
00137 db_dirhash(const char *str)
00138 {
00139     register const unsigned char *s = (unsigned char *)str;
00140     register long sum;
00141     register int i;
00142 
00143     sum = 0;
00144     /* BSD name hashing starts i=0, discarding first char.  why? */
00145     for( i=1; *s; )
00146         sum += *s++ * i++;
00147 
00148     return( RT_DBHASH(sum) );
00149 }
00150 
00151 /**
00152  *  Name -
00153  *      D B _ D I R C H E C K
00154  *
00155  *  Description -
00156  *      This routine ensures that ret_name is not already in the
00157  *      directory. If it is, it tries a fixed number of times to
00158  *      modify ret_name before giving up. Note - most of the time,
00159  *      the hash for ret_name is computed once.
00160  *
00161  *  Inputs -
00162  *      dbip            database instance pointer
00163  *      ret_name        the original name
00164  *      noisy           to blather or not
00165  *
00166  *  Outputs -
00167  *      ret_name        the name to use
00168  *      headp           pointer to the first (struct directory *) in the bucket
00169  *
00170  *  Returns -
00171  *       0      success
00172  *      <0      fail
00173  */
00174 int
00175 db_dircheck(struct db_i         *dbip,
00176             struct bu_vls       *ret_name,
00177             int                 noisy,
00178             struct directory    ***headp)
00179 {
00180     register struct directory   *dp;
00181     register char                       *cp = bu_vls_addr(ret_name);
00182     register char                       n0 = cp[0];
00183     register char                       n1 = cp[1];
00184 
00185     /* Compute hash only once (almost always the case) */
00186     *headp = &(dbip->dbi_Head[db_dirhash(cp)]);
00187 
00188     for (dp = **headp; dp != DIR_NULL; dp=dp->d_forw) {
00189         register char   *this;
00190         if (n0 == *(this=dp->d_namep)  &&       /* speed */
00191             n1 == this[1]  &&                   /* speed */
00192             strcmp(cp, this) == 0) {
00193             /* Name exists in directory already */
00194             register int        c;
00195 
00196             bu_vls_strcpy(ret_name, "A_");
00197             bu_vls_strcat(ret_name, this);
00198 
00199             for (c = 'A'; c <= 'Z'; c++) {
00200                 *cp = c;
00201                 if (db_lookup(dbip, cp, noisy) == DIR_NULL)
00202                     break;
00203             }
00204             if (c > 'Z') {
00205                 bu_log("db_dircheck: Duplicate of name '%s', ignored\n",
00206                        cp);
00207                 return -1;      /* fail */
00208             }
00209             bu_log("db_dircheck: Duplicate of '%s', given temporary name '%s'\n",
00210                    cp+2, cp);
00211 
00212             /* no need to recurse, simply recompute the hash and break */
00213             *headp = &(dbip->dbi_Head[db_dirhash(cp)]);
00214             break;
00215         }
00216     }
00217 
00218     return 0;   /* success */
00219 }
00220 
00221 
00222 /**
00223  *                      D B _ L O O K U P
00224  *
00225  * This routine takes a name and looks it up in the
00226  * directory table.  If the name is present, a pointer to
00227  * the directory struct element is returned, otherwise
00228  * NULL is returned.
00229  *
00230  * If noisy is non-zero, a print occurs, else only
00231  * the return code indicates failure.
00232  *
00233  *  Returns -
00234  *      struct directory        if name is found
00235  *      DIR_NULL                on failure
00236  */
00237 struct directory *
00238 db_lookup(const struct db_i *dbip, register const char *name, int noisy)
00239 {
00240     register struct directory *dp;
00241     register char       n0;
00242     register char       n1;
00243 
00244     if (!name) {
00245         bu_log("db_lookup received NULL name\n");
00246         return (DIR_NULL);
00247     }
00248 
00249     n0 = name[0];
00250     n1 = name[1];
00251 
00252     RT_CK_DBI(dbip);
00253 
00254     dp = dbip->dbi_Head[db_dirhash(name)];
00255     for(; dp != DIR_NULL; dp=dp->d_forw )  {
00256         register char   *this;
00257         if(
00258            n0 == *(this=dp->d_namep)  &&        /* speed */
00259            n1 == this[1]  &&    /* speed */
00260            strcmp( name, this ) == 0
00261            )  {
00262             if(RT_G_DEBUG&DEBUG_DB) bu_log("db_lookup(%s) x%x\n", name, dp);
00263             return(dp);
00264         }
00265     }
00266 
00267     if(noisy || RT_G_DEBUG&DEBUG_DB) bu_log("db_lookup(%s) failed: %s does not exist\n", name, name);
00268     return( DIR_NULL );
00269 }
00270 
00271 /**
00272  *                      D B _ D I R A D D
00273  *
00274  * Add an entry to the directory.
00275  * Try to make the regular path through the code as fast as possible,
00276  * to speed up building the table of contents.
00277  *
00278  * dbip is a pointer to a valid/opened database instance
00279  * name is the string name of the object being added
00280  * laddr is the long offset into the file to the object
00281  * len is the length of the object, number of db granules used
00282  * flags are defined in raytrace.h (DIR_SOLID, DIR_COMB, DIR_REGION, RT_DIR_INMEM, etc)
00283  * for db version 5, ptr is the minor_type (non-null pointer to valid unsigned char code)
00284  *
00285  * an laddr of RT_DIR_PHONY_ADDR (-1L) means that database storage has
00286  * not been allocated yet.
00287  */
00288 struct directory *
00289 db_diradd(register struct db_i *dbip, register const char *name, long int laddr, int len, int flags, genptr_t ptr)
00290 {
00291     struct directory **headp;
00292     register struct directory *dp;
00293     char *tmp_ptr;
00294     struct bu_vls       local;
00295 
00296     RT_CK_DBI(dbip);
00297 
00298     if(RT_G_DEBUG&DEBUG_DB)  {
00299         bu_log("db_diradd(dbip=0x%x, name='%s', addr=0x%x, len=%d, flags=0x%x, ptr=0x%x)\n",
00300                dbip, name, laddr, len, flags, ptr );
00301     }
00302 
00303     if( (tmp_ptr=strchr( name, '/' )) != NULL )  {
00304         /* if this is a version 4 database and the offending char is beyond NAMESIZE
00305          * then it is not really a problem
00306          */
00307         if( dbip->dbi_version < 5 && (tmp_ptr - name) < 16 ) {
00308             bu_log("db_diradd() object named '%s' is illegal, ignored\n", name );
00309             return DIR_NULL;
00310         }
00311     }
00312 
00313     bu_vls_init(&local);
00314     if( dbip->dbi_version < 5 ) {
00315         bu_vls_strncpy(&local, name, NAMESIZE);
00316     } else {
00317         /* must provide a valid minor type */
00318         if (!ptr) {
00319             bu_log("WARNING: db_diradd() called with a null minor type pointer for object %s\nIgnoring %s\n", name, name);
00320             bu_vls_free(&local);
00321             return DIR_NULL;
00322         }
00323         bu_vls_strcpy(&local, name);
00324     }
00325     if (db_dircheck(dbip, &local, 0, &headp) < 0) {
00326         bu_vls_free(&local);
00327         return DIR_NULL;
00328     }
00329 
00330     /* 'name' not found in directory, add it */
00331     RT_GET_DIRECTORY(dp, &rt_uniresource);
00332     RT_CK_DIR(dp);
00333     RT_DIR_SET_NAMEP(dp, bu_vls_addr(&local));  /* sets d_namep */
00334     dp->d_un.file_offset = laddr;
00335     dp->d_flags = flags & ~(RT_DIR_INMEM);
00336     dp->d_len = len;
00337     dp->d_forw = *headp;
00338     BU_LIST_INIT( &dp->d_use_hd );
00339     *headp = dp;
00340     dp->d_animate = NULL;
00341     dp->d_nref = 0;
00342     dp->d_uses = 0;
00343     if( dbip->dbi_version > 4 ) {
00344         dp->d_major_type = DB5_MAJORTYPE_BRLCAD;
00345         dp->d_minor_type = *(unsigned char *)ptr;
00346     }
00347     bu_vls_free(&local);
00348     return( dp );
00349 }
00350 
00351 
00352 /**
00353  *                      D B _ D I R D E L E T E
00354  *
00355  *  Given a pointer to a directory entry, remove it from the
00356  *  linked list, and free the associated memory.
00357  *
00358  *  It is the responsibility of the caller to have released whatever
00359  *  structures have been hung on the d_use_hd bu_list, first.
00360  *
00361  *  Returns -
00362  *       0      on success
00363  *      -1      on failure
00364  */
00365 int
00366 db_dirdelete(register struct db_i *dbip, register struct directory *dp)
00367 {
00368     register struct directory *findp;
00369     register struct directory **headp;
00370 
00371     RT_CK_DBI(dbip);
00372     RT_CK_DIR(dp);
00373 
00374     headp = &(dbip->dbi_Head[db_dirhash(dp->d_namep)]);
00375 
00376     if( dp->d_flags & RT_DIR_INMEM )
00377         {
00378             if( dp->d_un.ptr != NULL )
00379                 bu_free( dp->d_un.ptr, "db_dirdelete() inmem ptr" );
00380         }
00381 
00382     if( *headp == dp )  {
00383         RT_DIR_FREE_NAMEP(dp);  /* frees d_namep */
00384         *headp = dp->d_forw;
00385 
00386         /* Put 'dp' back on the freelist */
00387         dp->d_forw = rt_uniresource.re_directory_hd;
00388         rt_uniresource.re_directory_hd = dp;
00389         return(0);
00390     }
00391     for( findp = *headp; findp != DIR_NULL; findp = findp->d_forw )  {
00392         if( findp->d_forw != dp )
00393             continue;
00394         RT_DIR_FREE_NAMEP(dp);  /* frees d_namep */
00395         findp->d_forw = dp->d_forw;
00396 
00397         /* Put 'dp' back on the freelist */
00398         dp->d_forw = rt_uniresource.re_directory_hd;
00399         rt_uniresource.re_directory_hd = dp;
00400         return(0);
00401     }
00402     return(-1);
00403 }
00404 
00405 /**
00406  *                      D B _ R E N A M E
00407  *
00408  *  Change the name string of a directory entry.
00409  *  Because of the hashing function, this takes some extra work.
00410  *
00411  *  Returns -
00412  *       0      on success
00413  *      -1      on failure
00414  */
00415 int
00416 db_rename(register struct db_i *dbip, register struct directory *dp, const char *newname)
00417 {
00418     register struct directory *findp;
00419     register struct directory **headp;
00420 
00421     RT_CK_DBI(dbip);
00422     RT_CK_DIR(dp);
00423 
00424     /* Remove from linked list */
00425     headp = &(dbip->dbi_Head[db_dirhash(dp->d_namep)]);
00426     if( *headp == dp )  {
00427         /* Was first on list, dequeue */
00428         *headp = dp->d_forw;
00429     } else {
00430         for( findp = *headp; findp != DIR_NULL; findp = findp->d_forw )  {
00431             if( findp->d_forw != dp )
00432                 continue;
00433             /* Dequeue */
00434             findp->d_forw = dp->d_forw;
00435             goto out;
00436         }
00437         return(-1);             /* ERROR: can't find */
00438     }
00439 
00440  out:
00441     /* Effect new name */
00442     RT_DIR_FREE_NAMEP(dp);                      /* frees d_namep */
00443     RT_DIR_SET_NAMEP( dp, newname );    /* sets d_namep */
00444 
00445     /* Add to new linked list */
00446     headp = &(dbip->dbi_Head[db_dirhash(newname)]);
00447     dp->d_forw = *headp;
00448     *headp = dp;
00449     return(0);
00450 }
00451 
00452 /**
00453  *                      D B _ P R _ D I R
00454  *
00455  *  For debugging, print the entire contents of the database directory.
00456  */
00457 void
00458 db_pr_dir(register const struct db_i *dbip)
00459 {
00460     register const struct directory *dp;
00461     register char               *flags;
00462     register int                i;
00463 
00464     RT_CK_DBI(dbip);
00465 
00466     bu_log("db_pr_dir(x%x):  Dump of directory for file %s [%s]\n",
00467            dbip, dbip->dbi_filename,
00468            dbip->dbi_read_only ? "READ-ONLY" : "Read/Write" );
00469 
00470     bu_log("Title = %s\n", dbip->dbi_title);
00471     /* units ? */
00472 
00473     for( i = 0; i < RT_DBNHASH; i++ )  {
00474         for( dp = dbip->dbi_Head[i]; dp != DIR_NULL; dp=dp->d_forw )  {
00475             if( dp->d_flags & DIR_SOLID )
00476                 flags = "SOL";
00477             else if( (dp->d_flags & (DIR_COMB|DIR_REGION)) ==
00478                      (DIR_COMB|DIR_REGION) )
00479                 flags = "REG";
00480             else if( (dp->d_flags & (DIR_COMB|DIR_REGION)) ==
00481                      DIR_COMB )
00482                 flags = "COM";
00483             else
00484                 flags = "Bad";
00485             bu_log("x%.8x %s %s=x%.8x len=%.5d use=%.2d nref=%.2d %s",
00486                    dp,
00487                    flags,
00488                    dp->d_flags & RT_DIR_INMEM ? "  ptr " : "d_addr",
00489                    dp->d_addr,
00490                    dp->d_len,
00491                    dp->d_uses,
00492                    dp->d_nref,
00493                    dp->d_namep );
00494             if( dp->d_animate )
00495                 bu_log(" anim=x%x\n", dp->d_animate );
00496             else
00497                 bu_log("\n");
00498         }
00499     }
00500 }
00501 
00502 
00503 /**
00504  *                      D B _ G E T _ D I R E C T O R Y
00505  *
00506  *  This routine is called by the RT_GET_DIRECTORY macro when the freelist
00507  *  is exhausted.  Rather than simply getting one additional structure,
00508  *  we get a whole batch, saving overhead.
00509  */
00510 void
00511 db_get_directory(register struct resource *resp)
00512 {
00513     register struct directory   *dp;
00514     register int                bytes;
00515 
00516     RT_CK_RESOURCE(resp);
00517     BU_CK_PTBL( &resp->re_directory_blocks );
00518 
00519     BU_ASSERT_PTR( resp->re_directory_hd, ==, NULL );
00520 
00521     /* Get a BIG block */
00522     bytes = bu_malloc_len_roundup(1024*sizeof(struct directory));
00523     dp = (struct directory *)bu_malloc(bytes, "re_directory_blocks from db_get_directory() " BU_FLSTR);
00524 
00525     /* Record storage for later */
00526     bu_ptbl_ins( &resp->re_directory_blocks, (long *)dp );
00527 
00528     while( bytes >= sizeof(struct directory) )  {
00529         dp->d_magic = RT_DIR_MAGIC;
00530         dp->d_forw = resp->re_directory_hd;
00531         resp->re_directory_hd = dp;
00532         dp++;
00533         bytes -= sizeof(struct directory);
00534     }
00535 }
00536 
00537 /**
00538  *                      D B _ L O O K U P _ B Y _ A T T R
00539  *
00540  * lookup directory entries based on directory flags (dp->d_flags) and
00541  * attributes the "dir_flags" arg is a mask for the directory flags
00542  * the *"avs" is an attribute value set used to select from the
00543  * objects that *pass the flags mask. if "op" is 1, then the object
00544  * must have all the *attributes and values that appear in "avs" in
00545  * order to be *selected. If "op" is 2, then the object must have at
00546  * least one of *the attribute/value pairs from "avs".
00547  *
00548  * dir_flags are in the form used in struct directory (d_flags)
00549  *
00550  * for op:
00551  * 1 -> all attribute name/value pairs must be present and match
00552  * 2 -> at least one of the name/value pairs must be present and match
00553  *
00554  * returns a ptbl list of selected directory pointers an empty list
00555  * means nothing met the requirements a NULL return means something
00556  * went wrong.
00557  */
00558 struct bu_ptbl *
00559 db_lookup_by_attr(struct db_i *dbip, int dir_flags, struct bu_attribute_value_set *avs, int op)
00560 {
00561     struct bu_attribute_value_set obj_avs;
00562     struct directory *dp;
00563     struct bu_ptbl *tbl;
00564     int match_count=0;
00565     int attr_count;
00566     int i,j;
00567     int draw;
00568 
00569     RT_CK_DBI(dbip);
00570 
00571     if( avs ) {
00572         BU_CK_AVS( avs );
00573         attr_count = avs->count;
00574     } else {
00575         attr_count = 0;
00576     }
00577     tbl = (struct bu_ptbl *)bu_malloc( sizeof( struct bu_ptbl ), "wdb_get_by_attr ptbl" );
00578     bu_ptbl_init( tbl, 128, "wdb_get_by_attr ptbl_init" );
00579     FOR_ALL_DIRECTORY_START(dp,dbip)
00580         if( (dp->d_flags & dir_flags) == 0 ) continue;
00581     if(attr_count ) {
00582         if( db5_get_attributes( dbip, &obj_avs, dp ) < 0 ) {
00583             bu_log( "ERROR: failed to get attributes for %s\n", dp->d_namep );
00584             return( (struct bu_ptbl *)NULL );
00585         }
00586 
00587         draw = 0;
00588         match_count = 0;
00589         for( i=0 ; i<avs->count ; i++ ) {
00590             for( j=0 ; j<obj_avs.count ; j++ ) {
00591                 if( !strcmp( avs->avp[i].name, obj_avs.avp[j].name ) ) {
00592                     if( !strcmp( avs->avp[i].value, obj_avs.avp[j].value ) ) {
00593                         if( op == 2 ) {
00594                             draw = 1;
00595                             break;
00596                         } else {
00597                             match_count++;
00598                         }
00599                     }
00600                 }
00601             }
00602             if( draw ) break;
00603         }
00604         bu_avs_free( &obj_avs );
00605     } else {
00606         draw = 1;
00607     }
00608     if( draw || match_count == attr_count ) {
00609         bu_ptbl_ins( tbl , (long *)dp );
00610     }
00611     FOR_ALL_DIRECTORY_END
00612 
00613         return( tbl );
00614 }
00615 
00616 /*@}*/
00617 /*
00618  * Local Variables:
00619  * mode: C
00620  * tab-width: 8
00621  * c-basic-offset: 4
00622  * indent-tabs-mode: t
00623  * End:
00624  * ex: shiftwidth=4 tabstop=8
00625  */

Generated on Mon Sep 18 01:24:49 2006 for BRL-CAD by  doxygen 1.4.6