Annotation of GNUtools/cctools/as/hash.c, revision 1.1.1.1

1.1       root        1: /* hash.c - hash table lookup strings -
                      2:    Copyright (C) 1987 Free Software Foundation, Inc.
                      3: 
                      4: This file is part of GAS, the GNU Assembler.
                      5: 
                      6: GAS is free software; you can redistribute it and/or modify
                      7: it under the terms of the GNU General Public License as published by
                      8: the Free Software Foundation; either version 1, or (at your option)
                      9: any later version.
                     10: 
                     11: GAS is distributed in the hope that it will be useful,
                     12: but WITHOUT ANY WARRANTY; without even the implied warranty of
                     13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     14: GNU General Public License for more details.
                     15: 
                     16: You should have received a copy of the GNU General Public License
                     17: along with GAS; see the file COPYING.  If not, write to
                     18: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
                     19: 
                     20: /*
                     21:  * BUGS, GRIPES, APOLOGIA etc.
                     22:  *
                     23:  * A typical user doesn't need ALL this: I intend to make a library out
                     24:  * of it one day - Dean Elsner.
                     25:  * Also, I want to change the definition of a symbol to (address,length)
                     26:  * so I can put arbitrary binary in the names stored. [see hsh.c for that]
                     27:  *
                     28:  * This slime is common coupled inside the module. Com-coupling (and other
                     29:  * vandalism) was done to speed running time. The interfaces at the
                     30:  * module's edges are adequately clean.
                     31:  *
                     32:  * There is no way to (a) run a test script through this heap and (b)
                     33:  * compare results with previous scripts, to see if we have broken any
                     34:  * code. Use GNU (f)utilities to do this. A few commands assist test.
                     35:  * The testing is awkward: it tries to be both batch & interactive.
                     36:  * For now, interactive rules!
                     37:  */
                     38: 
                     39: /*
                     40:  *  The idea is to implement a symbol table. A test jig is here.
                     41:  *  Symbols are arbitrary strings; they can't contain '\0'.
                     42:  *     [See hsh.c for a more general symbol flavour.]
                     43:  *  Each symbol is associated with a char*, which can point to anything
                     44:  *  you want, allowing an arbitrary property list for each symbol.
                     45:  *
                     46:  *  The basic operations are:
                     47:  *
                     48:  *    new                     creates symbol table, returns handle
                     49:  *    find (symbol)           returns char*
                     50:  *    insert (symbol,char*)   error if symbol already in table
                     51:  *    delete (symbol)         returns char* if symbol was in table
                     52:  *    apply                   so you can delete all symbols before die()
                     53:  *    die                     destroy symbol table (free up memory)
                     54:  *
                     55:  *  Supplementary functions include:
                     56:  *
                     57:  *    say                     how big? what % full?
                     58:  *    replace (symbol,newval) report previous value
                     59:  *    jam (symbol,value)      assert symbol:=value
                     60:  *
                     61:  *  You, the caller, have control over errors: this just reports them.
                     62:  *
                     63:  *  This package requires malloc(), free().
                     64:  *  Malloc(size) returns NULL or address of char[size].
                     65:  *  Free(address) frees same.
                     66:  */
                     67: 
                     68: /*
                     69:  *  The code and its structures are re-enterent.
                     70:  *  Before you do anything else, you must call hash_new() which will
                     71:  *  return the address of a hash-table-control-block (or NULL if there
                     72:  *  is not enough memory). You then use this address as a handle of the
                     73:  *  symbol table by passing it to all the other hash_...() functions.
                     74:  *  The only approved way to recover the memory used by the symbol table
                     75:  *  is to call hash_die() with the handle of the symbol table.
                     76:  *
                     77:  *  Before you call hash_die() you normally delete anything pointed to
                     78:  *  by individual symbols. After hash_die() you can't use that symbol
                     79:  *  table again.
                     80:  *
                     81:  *  The char* you associate with a symbol may not be NULL (0) because
                     82:  *  NULL is returned whenever a symbol is not in the table. Any other
                     83:  *  value is OK, except DELETED, #defined below.
                     84:  *
                     85:  *  When you supply a symbol string for insertion, YOU MUST PRESERVE THE
                     86:  *  STRING until that symbol is deleted from the table. The reason is that
                     87:  *  only the address you supply, NOT the symbol string itself, is stored
                     88:  *  in the symbol table.
                     89:  *
                     90:  *  You may delete and add symbols arbitrarily.
                     91:  *  Any or all symbols may have the same 'value' (char *). In fact, these
                     92:  *  routines don't do anything with your symbol values.
                     93:  *
                     94:  *  You have no right to know where the symbol:char* mapping is stored,
                     95:  *  because it moves around in memory; also because we may change how it
                     96:  *  works and we don't want to break your code do we? However the handle
                     97:  *  (address of struct hash_control) is never changed in
                     98:  *  the life of the symbol table.
                     99:  *
                    100:  *  What you CAN find out about a symbol table is:
                    101:  *    how many slots are in the hash table?
                    102:  *    how many slots are filled with symbols?
                    103:  *    (total hashes,collisions) for (reads,writes) (*)
                    104:  *  All of the above values vary in time.
                    105:  *  (*) some of these numbers will not be meaningful if we change the
                    106:  *  internals.
                    107:  */
                    108: 
                    109: /*
                    110:  *  I N T E R N A L
                    111:  *
                    112:  *  Hash table is an array of hash_entries; each entry is a pointer to a
                    113:  *  a string and a user-supplied value 1 char* wide.
                    114:  *
                    115:  *  The array always has 2 ** n elements, n>0, n integer.
                    116:  *  There is also a 'wall' entry after the array, which is always empty
                    117:  *  and acts as a sentinel to stop running off the end of the array.
                    118:  *  When the array gets too full, we create a new array twice as large
                    119:  *  and re-hash the symbols into the new array, then forget the old array.
                    120:  *  (Of course, we copy the values into the new array before we junk the
                    121:  *  old array!)
                    122:  *
                    123:  */
                    124: 
                    125: #include <stdio.h>
                    126: #include <stdlib.h>
                    127: #include <ctype.h>
                    128: #include "hash.h"
                    129: #include "xmalloc.h"
                    130: #include "messages.h"
                    131: 
                    132: #define TRUE           (1)
                    133: #define FALSE          (0)
                    134: #define min(a, b)      ((a) < (b) ? (a) : (b))
                    135: 
                    136: 
                    137: #define DELETED     ((char *)1)        /* guarenteed invalid address */
                    138: #define START_POWER    (11)    /* power of two: size of new hash table *//* JF was 6 */
                    139: /* JF These next two aren't used any more. */
                    140: /* #define START_SIZE    (64)  / * 2 ** START_POWER */
                    141: /* #define START_FULL    (32)      / * number of entries before table expands */
                    142: #define islive(ptr) (ptr->hash_string && ptr->hash_string!=DELETED)
                    143:                                /* above TRUE if a symbol is in entry @ ptr */
                    144: 
                    145: #define STAT_SIZE      (0)      /* number of slots in hash table */
                    146:                                /* the wall does not count here */
                    147:                                /* we expect this is always a power of 2 */
                    148: #define STAT_ACCESS    (1)     /* number of hash_ask()s */
                    149: #define STAT__READ     (0)      /* reading */
                    150: #define STAT__WRITE    (1)      /* writing */
                    151: #define STAT_COLLIDE   (3)     /* number of collisions (total) */
                    152:                                /* this may exceed STAT_ACCESS if we have */
                    153:                                /* lots of collisions/access */
                    154: #define STAT_USED      (5)     /* slots used right now */
                    155: #define STATLENGTH     (6)     /* size of statistics block */
                    156: #if STATLENGTH != HASH_STATLENGTH
                    157: Panic! Please make #include "stat.h" agree with previous definitions!
                    158: #endif
                    159: 
                    160: /* #define SUSPECT to do runtime checks */
                    161: /* #define TEST to be a test jig for hash...() */
                    162: 
                    163: #ifdef TEST                    /* TEST: use smaller hash table */
                    164: #undef  START_POWER
                    165: #define START_POWER (3)
                    166: #undef  START_SIZE
                    167: #define START_SIZE  (8)
                    168: #undef  START_FULL
                    169: #define START_FULL  (4)
                    170: static void hash_die(
                    171:     struct hash_control *handle);
                    172: static void hash_say(
                    173:     struct hash_control *handle,
                    174:     int buffer[/*bufsiz*/],
                    175:     int bufsiz);
                    176: static char *hash_delete(              /* previous value         */
                    177:     struct hash_control *handle,
                    178:     char *string);
                    179: static char *hash_replace(             /* previous value         */
                    180:     struct hash_control *handle,
                    181:     char *string,
                    182:     char *value);
                    183: #endif /* TEST */
                    184: 
                    185: /*------------------ plan ---------------------------------- i = internal
                    186: 
                    187: struct hash_control * c;
                    188: struct hash_entry   * e;                                                    i
                    189: int                   b[z];     buffer for statistics
                    190:                       z         size of b
                    191: char                * s;        symbol string (address) [ key ]
                    192: char                * v;        value string (address)  [datum]
                    193: boolean               f;        TRUE if we found s in hash table            i
                    194: char                * t;        error string; "" means OK
                    195: int                   a;        access type [0...n)                         i
                    196: 
                    197: c=hash_new       ()             create new hash_control
                    198: 
                    199: hash_die         (c)            destroy hash_control (and hash table)
                    200:                                 table should be empty.
                    201:                                 doesn't check if table is empty.
                    202:                                 c has no meaning after this.
                    203: 
                    204: hash_say         (c,b,z)        report statistics of hash_control.
                    205:                                 also report number of available statistics.
                    206: 
                    207: v=hash_delete    (c,s)          delete symbol, return old value if any.
                    208:     ask()                       NULL means no old value.
                    209:     f
                    210: 
                    211: v=hash_replace   (c,s,v)        replace old value of s with v.
                    212:     ask()                       NULL means no old value: no table change.
                    213:     f
                    214: 
                    215: t=hash_insert    (c,s,v)        insert (s,v) in c.
                    216:     ask()                       return error string.
                    217:     f                           it is an error to insert if s is already
                    218:                                 in table.
                    219:                                 if any error, c is unchanged.
                    220: 
                    221: t=hash_jam       (c,s,v)        assert that new value of s will be v.       i
                    222:     ask()                       it may decide to GROW the table.            i
                    223:     f                                                                       i
                    224:     grow()                                                                  i
                    225: t=hash_grow      (c)            grow the hash table.                        i
                    226:     jam()                       will invoke JAM.                            i
                    227: 
                    228: ?=hash_apply     (c,y)          apply y() to every symbol in c.
                    229:     y                           evtries visited in 'unspecified' order.
                    230: 
                    231: v=hash_find      (c,s)          return value of s, or NULL if s not in c.
                    232:     ask()
                    233:     f
                    234: 
                    235: f,e=hash_ask()   (c,s,a)        return slot where s SHOULD live.            i
                    236:     code()                      maintain collision stats in c.              i
                    237: 
                    238: .=hash_code      (c,s)          compute hash-code for s,                    i
                    239:                                 from parameters of c.                       i
                    240: 
                    241: */
                    242: 
                    243: static char hash_found;                /* returned by hash_ask() to stop extra */
                    244:                                /* testing. hash_ask() wants to return both */
                    245:                                /* a slot and a status. This is the status. */
                    246:                                /* TRUE: found symbol */
                    247:                                /* FALSE: absent: empty or deleted slot */
                    248:                                /* Also returned by hash_jam(). */
                    249:                                /* TRUE: we replaced a value */
                    250:                                /* FALSE: we inserted a value */
                    251: 
                    252: static struct hash_entry *hash_ask(
                    253:     struct hash_control *handle,
                    254:     char *string,
                    255:     int access);
                    256: static int hash_code(
                    257:     struct hash_control *handle,
                    258:     char *string);
                    259: static char *hash_grow(
                    260:     struct hash_control *handle);
                    261: 
                    262: /*
                    263:  *             h a s h _ n e w ( )
                    264:  *
                    265:  */
                    266: struct hash_control *
                    267: hash_new(void)                 /* create a new hash table */
                    268:                                /* return NULL if failed */
                    269:                                /* return handle (address of struct hash) */
                    270: {
                    271:   register struct hash_control * retval;
                    272:   register struct hash_entry *   room; /* points to hash table */
                    273:   register struct hash_entry *   wall;
                    274:   register struct hash_entry *   entry;
                    275:   register int *                 ip;   /* scan stats block of struct hash_control */
                    276:   register int *                 nd;   /* limit of stats block */
                    277: 
                    278:   if ((room = (struct hash_entry *) malloc( sizeof(struct hash_entry)*((1<<START_POWER) + 1) ) ))
                    279:                                /* +1 for the wall entry */
                    280:     {
                    281:       if ((retval = (struct hash_control *) malloc(sizeof(struct hash_control)) ))
                    282:        {
                    283:          nd = retval->hash_stat + STATLENGTH;
                    284:          for (ip=retval->hash_stat; ip<nd; ip++)
                    285:            {
                    286:              *ip = 0;
                    287:            }
                    288: 
                    289:          retval -> hash_stat[STAT_SIZE]  = 1<<START_POWER;
                    290:          retval -> hash_mask             = (1<<START_POWER) - 1;
                    291:          retval -> hash_sizelog          = START_POWER;
                    292:                                /* works for 1's compl ok */
                    293:          retval -> hash_where            = room;
                    294:          retval -> hash_wall             =
                    295:            wall                          = room + (1<<START_POWER);
                    296:          retval -> hash_full             = (1<<START_POWER)/2;
                    297:          for (entry=room; entry<=wall; entry++)
                    298:            {
                    299:              entry->hash_string = NULL;
                    300:            }
                    301:        }
                    302:     }
                    303:   else
                    304:     {
                    305:       retval = NULL;           /* no room for table: fake a failure */
                    306:     }
                    307:   return(retval);              /* return NULL or set-up structs */
                    308: }
                    309: 
                    310: #ifdef TEST
                    311: /*
                    312:  *           h a s h _ d i e ( )
                    313:  *
                    314:  * Table should be empty, but this is not checked.
                    315:  * To empty the table, try hash_apply()ing a symbol deleter.
                    316:  * Return to free memory both the hash table and it's control
                    317:  * block.
                    318:  * 'handle' has no meaning after this function.
                    319:  * No errors are recoverable.
                    320:  */
                    321: static
                    322: void
                    323: hash_die(
                    324: struct hash_control *handle)
                    325: {
                    326:   free((char *)handle->hash_where);
                    327:   free((char *)handle);
                    328: }
                    329: 
                    330: /*
                    331:  *           h a s h _ s a y ( )
                    332:  *
                    333:  * Return the size of the statistics table, and as many statistics as
                    334:  * we can until either (a) we have run out of statistics or (b) caller
                    335:  * has run out of buffer.
                    336:  * NOTE: hash_say treats all statistics alike.
                    337:  * These numbers may change with time, due to insertions, deletions
                    338:  * and expansions of the table.
                    339:  * The first "statistic" returned is the length of hash_stat[].
                    340:  * Then contents of hash_stat[] are read out (in ascending order)
                    341:  * until your buffer or hash_stat[] is exausted.
                    342:  */
                    343: static
                    344: void
                    345: hash_say(
                    346: struct hash_control *handle,
                    347: int buffer[/*bufsiz*/],
                    348: int bufsiz)
                    349: {
                    350:   register int * nd;                   /* limit of statistics block */
                    351:   register int * ip;                   /* scan statistics */
                    352: 
                    353:   ip = handle -> hash_stat;
                    354:   nd = ip + min(bufsiz-1,STATLENGTH);
                    355:   if (bufsiz>0)                        /* trust nothing! bufsiz<=0 is dangerous */
                    356:     {
                    357:       *buffer++ = STATLENGTH;
                    358:       for (; ip<nd; ip++,buffer++)
                    359:        {
                    360:          *buffer = *ip;
                    361:        }
                    362:     }
                    363: }
                    364: 
                    365: /*
                    366:  *           h a s h _ d e l e t e ( )
                    367:  *
                    368:  * Try to delete a symbol from the table.
                    369:  * If it was there, return its value (and adjust STAT_USED).
                    370:  * Otherwise, return NULL.
                    371:  * Anyway, the symbol is not present after this function.
                    372:  *
                    373:  */
                    374: static
                    375: char *                         /* NULL if string not in table, else */
                    376: hash_delete(                   /* returns value of deleted symbol */
                    377: struct hash_control *handle,
                    378: char *string)
                    379: {
                    380:   register char *                   retval; /* NULL if string not in table */
                    381:   register struct hash_entry *      entry; /* NULL or entry of this symbol */
                    382: 
                    383:   entry = hash_ask(handle,string,STAT__WRITE);
                    384:   if (hash_found)
                    385:     {
                    386:          retval = entry -> hash_value;
                    387:          entry -> hash_string = DELETED; /* mark as deleted */
                    388:          handle -> hash_stat[STAT_USED] -= 1; /* slots-in-use count */
                    389: #ifdef SUSPECT
                    390:          if (handle->hash_stat[STAT_USED]<0)
                    391:            {
                    392: #ifdef NeXT
                    393:              as_fatal("hash_delete");
                    394: #else
                    395:              error("hash_delete");
                    396: #endif
                    397:            }
                    398: #endif /* def SUSPECT */
                    399:     }
                    400:   else
                    401:     {
                    402:       retval = NULL;
                    403:     }
                    404:   return(retval);
                    405: }
                    406: 
                    407: /*
                    408:  *                   h a s h _ r e p l a c e ( )
                    409:  *
                    410:  * Try to replace the old value of a symbol with a new value.
                    411:  * Normally return the old value.
                    412:  * Return NULL and don't change the table if the symbol is not already
                    413:  * in the table.
                    414:  */
                    415: static
                    416: char *
                    417: hash_replace(
                    418: struct hash_control *handle,
                    419: char *string,
                    420: char *value)
                    421: {
                    422:   register struct hash_entry *      entry;
                    423:   register char *                   retval;
                    424: 
                    425:   entry = hash_ask(handle,string,STAT__WRITE);
                    426:   if (hash_found)
                    427:     {
                    428:       retval = entry -> hash_value;
                    429:       entry -> hash_value = value;
                    430:     }
                    431:   else
                    432:     {
                    433:       retval = NULL;
                    434:     }
                    435:   ;
                    436:   return (retval);
                    437: }
                    438: #endif /* TEST */
                    439: 
                    440: /*
                    441:  *                   h a s h _ i n s e r t ( )
                    442:  *
                    443:  * Insert a (symbol-string, value) into the hash table.
                    444:  * Return an error string, "" means OK.
                    445:  * It is an 'error' to insert an existing symbol.
                    446:  */
                    447: char *                         /* return error string */
                    448: hash_insert(
                    449: struct hash_control *handle,
                    450: char *string,
                    451: char *value)
                    452: {
                    453:   register struct hash_entry * entry;
                    454:   register char *              retval;
                    455: 
                    456:   retval = "";
                    457:   if (handle->hash_stat[STAT_USED] > handle->hash_full)
                    458:     {
                    459:       retval = hash_grow(handle);
                    460:     }
                    461:   if ( ! * retval)
                    462:     {
                    463:       entry = hash_ask(handle,string,STAT__WRITE);
                    464:       if (hash_found)
                    465:        {
                    466:          retval = "exists";
                    467:        }
                    468:       else
                    469:        {
                    470:          entry -> hash_value  = value;
                    471:          entry -> hash_string = string;
                    472:          handle-> hash_stat[STAT_USED]  += 1;
                    473:        }
                    474:     }
                    475:   return(retval);
                    476: }
                    477: 
                    478: /*
                    479:  *               h a s h _ j a m ( )
                    480:  *
                    481:  * Regardless of what was in the symbol table before, after hash_jam()
                    482:  * the named symbol has the given value. The symbol is either inserted or
                    483:  * (its value is) relpaced.
                    484:  * An error message string is returned, "" means OK.
                    485:  *
                    486:  * WARNING: this may decide to grow the hashed symbol table.
                    487:  * To do this, we call hash_grow(), WHICH WILL recursively CALL US.
                    488:  *
                    489:  * We report status internally: hash_found is TRUE if we replaced, but
                    490:  * false if we inserted.
                    491:  */
                    492: char *
                    493: hash_jam(
                    494: struct hash_control *handle,
                    495: char *string,
                    496: char *value)
                    497: {
                    498:   register char *                   retval;
                    499:   register struct hash_entry *      entry;
                    500: 
                    501:   retval = "";
                    502:   if (handle->hash_stat[STAT_USED] > handle->hash_full)
                    503:     {
                    504:       retval = hash_grow(handle);
                    505:     }
                    506:   if (! * retval)
                    507:     {
                    508:       entry = hash_ask(handle,string,STAT__WRITE);
                    509:       if ( ! hash_found)
                    510:        {
                    511:          entry -> hash_string = string;
                    512:          handle->hash_stat[STAT_USED] += 1;
                    513:        }
                    514:       entry -> hash_value = value;
                    515:     }
                    516:   return(retval);
                    517: }
                    518: 
                    519: /*
                    520:  *               h a s h _ g r o w ( )
                    521:  *
                    522:  * Grow a new (bigger) hash table from the old one.
                    523:  * We choose to double the hash table's size.
                    524:  * Return a human-scrutible error string: "" if OK.
                    525:  * Warning! This uses hash_jam(), which had better not recurse
                    526:  * back here! Hash_jam() conditionally calls us, but we ALWAYS
                    527:  * call hash_jam()!
                    528:  * Internal.
                    529:  */
                    530: static
                    531: char *
                    532: hash_grow(             /* make a hash table grow */
                    533: struct hash_control *handle)
                    534: {
                    535:   register struct hash_entry *      newwall;
                    536:   register struct hash_entry *      newwhere;
                    537:   struct hash_entry *      newtrack;
                    538:   register struct hash_entry *      oldtrack;
                    539:   register struct hash_entry *      oldwhere;
                    540:   register struct hash_entry *      oldwall;
                    541:   register int                      temp;
                    542:   int                      newsize;
                    543:   char *                   string;
                    544:   char *                   retval;
                    545: #ifdef SUSPECT
                    546:   int                      oldused;
                    547: #endif
                    548: 
                    549:   /*
                    550:    * capture info about old hash table
                    551:    */
                    552:   oldwhere = handle -> hash_where;
                    553:   oldwall  = handle -> hash_wall;
                    554: #ifdef SUSPECT
                    555:   oldused  = handle -> hash_stat[STAT_USED];
                    556: #endif
                    557:   /*
                    558:    * attempt to get enough room for a hash table twice as big
                    559:    */
                    560:   temp = handle->hash_stat[STAT_SIZE];
                    561:   if ((newwhere = (struct hash_entry *) xmalloc((long)((temp+temp+1)*sizeof(struct hash_entry)))))
                    562:                                /* +1 for wall slot */
                    563:     {
                    564:       retval = "";             /* assume success until proven otherwise */
                    565:       /*
                    566:        * have enough room: now we do all the work.
                    567:        * double the size of everything in handle,
                    568:        * note: hash_mask frob works for 1's & for 2's complement machines
                    569:        */
                    570:       handle->hash_mask              = handle->hash_mask + handle->hash_mask + 1;
                    571:       handle->hash_stat[STAT_SIZE] <<= 1;
                    572:       newsize                        = handle->hash_stat[STAT_SIZE];
                    573:       handle->hash_where             = newwhere;
                    574:       handle->hash_full            <<= 1;
                    575:       handle->hash_sizelog         += 1;
                    576:       handle->hash_stat[STAT_USED]   = 0;
                    577:       handle->hash_wall              =
                    578:       newwall                        = newwhere + newsize;
                    579:       /*
                    580:        * set all those pesky new slots to vacant.
                    581:        */
                    582:       for (newtrack=newwhere; newtrack < newwall; newtrack++)
                    583:        {
                    584:          newtrack -> hash_string = NULL;
                    585:        }
                    586:       /*
                    587:        * we will do a scan of the old table, the hard way, using the
                    588:        * new control block to re-insert the data into new hash table.
                    589:        */
                    590:       handle -> hash_stat[STAT_USED] = 0;      /* inserts will bump it up to correct */
                    591:       for (oldtrack=oldwhere; oldtrack < oldwall; oldtrack++)
                    592:        {
                    593:          if ( (string=oldtrack->hash_string) && string!=DELETED )
                    594:            {
                    595:              if ( * (retval = hash_jam(handle,string,oldtrack->hash_value) ) )
                    596:                {
                    597:                  break;
                    598:                }
                    599:            }
                    600:        }
                    601: #ifdef SUSPECT
                    602:       if ( !*retval && handle->hash_stat[STAT_USED] != oldused)
                    603:        {
                    604:          retval = "hash_used";
                    605:        }
                    606: #endif
                    607:       if (!*retval)
                    608:        {
                    609:          /*
                    610:           * we have a completely faked up control block.
                    611:           * return the old hash table.
                    612:           */
                    613:          free((char *)oldwhere);
                    614:          /*
                    615:           * Here with success. retval is already "".
                    616:           */
                    617:        }
                    618:     }
                    619:   else
                    620:     {
                    621:       retval = "no room";
                    622:     }
                    623:   return(retval);
                    624: }
                    625: 
                    626: /*
                    627:  *          h a s h _ a p p l y ( )
                    628:  *
                    629:  * Use this to scan each entry in symbol table.
                    630:  * For each symbol, this calls (applys) a nominated function supplying the
                    631:  * symbol's value (and the symbol's name).
                    632:  * The idea is you use this to destroy whatever is associted with
                    633:  * any values in the table BEFORE you destroy the table with hash_die.
                    634:  * Of course, you can use it for other jobs; whenever you need to
                    635:  * visit all extant symbols in the table.
                    636:  *
                    637:  * We choose to have a call-you-back idea for two reasons:
                    638:  *  asthetic: it is a neater idea to use apply than an explicit loop
                    639:  *  sensible: if we ever had to grow the symbol table (due to insertions)
                    640:  *            then we would lose our place in the table when we re-hashed
                    641:  *            symbols into the new table in a different order.
                    642:  *
                    643:  * The order symbols are visited depends entirely on the hashing function.
                    644:  * Whenever you insert a (symbol, value) you risk expanding the table. If
                    645:  * you do expand the table, then the hashing function WILL change, so you
                    646:  * MIGHT get a different order of symbols visited. In other words, if you
                    647:  * want the same order of visiting symbols as the last time you used
                    648:  * hash_apply() then you better not have done any hash_insert()s or
                    649:  * hash_jam()s since the last time you used hash_apply().
                    650:  *
                    651:  * In future we may use the value returned by your nominated function.
                    652:  * One idea is to abort the scan if, after applying the function to a
                    653:  * certain node, the function returns a certain code.
                    654:  * To be safe, please make your functions of type char *. If you always
                    655:  * return NULL, then the scan will complete, visiting every symbol in
                    656:  * the table exactly once. ALL OTHER RETURNED VALUES have no meaning yet!
                    657:  * Caveat Actor!
                    658:  *
                    659:  * The function you supply should be of the form:
                    660:  *      char * myfunct(string,value)
                    661:  *              char * string;        |* the symbol's name *|
                    662:  *              char * value;         |* the symbol's value *|
                    663:  *      {
                    664:  *        |* ... *|
                    665:  *        return(NULL);
                    666:  *      }
                    667:  *
                    668:  * The returned value of hash_apply() is (char*)NULL. In future it may return
                    669:  * other values. NULL means "completed scan OK". Other values have no meaning
                    670:  * yet. (The function has no graceful failures.)
                    671:  */
                    672: char *
                    673: hash_apply(
                    674: struct hash_control *handle,
                    675: char *(*function)(char *hash_string, char *hash_value))
                    676: {
                    677:   register struct hash_entry *      entry;
                    678:   register struct hash_entry *      wall;
                    679: 
                    680:   wall = handle->hash_wall;
                    681:   for (entry = handle->hash_where; entry < wall; entry++)
                    682:     {
                    683:       if (islive(entry))       /* silly code: tests entry->string twice! */
                    684:        {
                    685:          (*function)(entry->hash_string,entry->hash_value);
                    686:        }
                    687:     }
                    688:   return (NULL);
                    689: }
                    690: 
                    691: /*
                    692:  *          h a s h _ f i n d ( )
                    693:  *
                    694:  * Given symbol string, find value (if any).
                    695:  * Return found value or NULL.
                    696:  */
                    697: char *
                    698: hash_find(
                    699: struct hash_control *handle,
                    700: char *string)
                    701: {
                    702:   register struct hash_entry *      entry;
                    703:   register char *                   retval;
                    704: 
                    705:   entry = hash_ask(handle,string,STAT__READ);
                    706:   if (hash_found)
                    707:     {
                    708:       retval = entry->hash_value;
                    709:     }
                    710:   else
                    711:     {
                    712:       retval = NULL;
                    713:     }
                    714:   return(retval);
                    715: }
                    716: 
                    717: /*
                    718:  *          h a s h _ a s k ( )
                    719:  *
                    720:  * Searches for given symbol string.
                    721:  * Return the slot where it OUGHT to live. It may be there.
                    722:  * Return hash_found: TRUE only if symbol is in that slot.
                    723:  * Access argument is to help keep statistics in control block.
                    724:  * Internal.
                    725:  */
                    726: static
                    727: struct hash_entry *    /* string slot, may be empty or deleted */
                    728: hash_ask(
                    729: struct hash_control * handle,
                    730: char *                string,
                    731: int                   access) /* access type */
                    732: {
                    733:   register char        *string1;       /* JF avoid strcmp calls */
                    734:   register char *                   s;
                    735:   register int                      c;
                    736:   register struct hash_entry *      slot;
                    737:   register int                      collision; /* count collisions */
                    738: 
                    739:   slot = handle->hash_where + hash_code(handle,string); /* start looking here */
                    740:   handle->hash_stat[STAT_ACCESS+access] += 1;
                    741:   collision = 0;
                    742:   hash_found = FALSE;
                    743:   while ( (s = slot->hash_string) && s!=DELETED )
                    744:     {
                    745:        for(string1=string;;) {
                    746:                if(!(c= *s++)) {
                    747:                        if(!*string1)
                    748:                                hash_found = TRUE;
                    749:                        break;
                    750:                }
                    751:                if(*string1++!=c)
                    752:                        break;
                    753:        }
                    754:        if(hash_found)
                    755:                break;
                    756:       collision++;
                    757:       slot++;
                    758:     }
                    759:   /*
                    760:    * slot:                                                      return:
                    761:    *       in use:     we found string                           slot
                    762:    *       at empty:
                    763:    *                   at wall:        we fell off: wrap round   ????
                    764:    *                   in table:       dig here                  slot
                    765:    *       at DELETED: dig here                                  slot
                    766:    */
                    767:   if (slot==handle->hash_wall)
                    768:     {
                    769:       slot = handle->hash_where; /* now look again */
                    770:       while( (s = slot->hash_string) && s!=DELETED )
                    771:        {
                    772:          for(string1=string;*s;string1++,s++) {
                    773:            if(*string1!=*s)
                    774:                break;
                    775:          }
                    776:          if(*s==*string1) {
                    777:              hash_found = TRUE;
                    778:              break;
                    779:            }
                    780:          collision++;
                    781:          slot++;
                    782:        }
                    783:       /*
                    784:        * slot:                                                   return:
                    785:        *       in use: we found it                                slot
                    786:        *       empty:  wall:         ERROR IMPOSSIBLE             !!!!
                    787:        *               in table:     dig here                     slot
                    788:        *       DELETED:dig here                                   slot
                    789:        */
                    790:     }
                    791: /*   fprintf(stderr,"hash_ask(%s)->%d(%d)\n",string,hash_code(handle,string),collision); */
                    792:   handle -> hash_stat[STAT_COLLIDE+access] += collision;
                    793:   return(slot);                        /* also return hash_found */
                    794: }
                    795: 
                    796: /*
                    797:  *           h a s h _ c o d e
                    798:  *
                    799:  * Does hashing of symbol string to hash number.
                    800:  * Internal.
                    801:  */
                    802: static
                    803: int
                    804: hash_code(
                    805: struct hash_control *handle,
                    806: char *string)
                    807: {
                    808:   register long int                 h;      /* hash code built here */
                    809:   register long int                 c;      /* each character lands here */
                    810:   register int                    n;      /* Amount to shift h by */
                    811: 
                    812:   n = (handle->hash_sizelog - 3);
                    813:   h = 0;
                    814:   while((c = *string++))
                    815:     {
                    816:       h += c;
                    817:       h = (h<<3) + (h>>n) + c;
                    818:     }
                    819:   return (h & handle->hash_mask);
                    820: }
                    821: 
                    822: /*
                    823:  * Here is a test program to exercise above.
                    824:  */
                    825: #ifdef TEST
                    826: 
                    827: #define TABLES (6)             /* number of hash tables to maintain */
                    828:                                /* (at once) in any testing */
                    829: #define STATBUFSIZE (12)       /* we can have 12 statistics */
                    830: 
                    831: int statbuf[STATBUFSIZE];      /* display statistics here */
                    832: char answer[100];              /* human farts here */
                    833: struct hash_control *
                    834:        hashtable[TABLES];      /* we test many hash tables at once */
                    835: struct hash_control * h;       /* points to curent hash_control */
                    836: struct hash_control ** pp;
                    837: struct hash_control *  p;
                    838: char *  name;
                    839: char *  value;
                    840: int     size;
                    841: int     used;
                    842: char    command;
                    843: int     number;                        /* number 0:TABLES-1 of current hashed */
                    844:                                /* symbol table */
                    845: static char *what(
                    846:     char * description);
                    847: static char *destroy(
                    848:     char * string,
                    849:     char * value);
                    850: static char *applicatee(
                    851:     char * string,
                    852:     char * value);
                    853: static void whattable(
                    854:     void);
                    855: 
                    856: main()
                    857: {
                    858:   int *  ip;
                    859:   char * error;
                    860:   char * thing;
                    861: 
                    862:   number = 0;
                    863:   h = NULL;
                    864:   printf("type h <RETURN> for help\n");
                    865:   for(;;)
                    866:     {
                    867:       printf("hash_test command: ");
                    868:       gets(answer);
                    869:       command = answer[0];
                    870:       if (isupper(command)) command = tolower(command);        /* ecch! */
                    871:       switch (command)
                    872:        {
                    873:        case '#':
                    874:          printf("old hash table #=%d.\n",number);
                    875:          whattable();
                    876:          break;
                    877:        case '?':
                    878:          for (pp=hashtable; pp<hashtable+TABLES; pp++)
                    879:            {
                    880:              printf("address of hash table #%d control block is %xx\n"
                    881:                     ,pp-hashtable,*pp);
                    882:            }
                    883:          break;
                    884:        case 'a':
                    885:          hash_apply(h,applicatee);
                    886:          break;
                    887:        case 'd':
                    888:          hash_apply(h,destroy);
                    889:          hash_die(h);
                    890:          break;
                    891:        case 'f':
                    892:          thing = hash_find(h,name=what("symbol"));
                    893:          printf("value of \"%s\" is \"%s\"\n",name,thing?thing:"NOT-PRESENT");
                    894:          break;
                    895:        case 'h':
                    896:          printf("# show old, select new default hash table number\n");
                    897:          printf("? display all hashtable control block addresses\n");
                    898:          printf("a apply a simple display-er to each symbol in table\n");
                    899:          printf("d die: destroy hashtable\n");
                    900:          printf("f find value of nominated symbol\n");
                    901:          printf("h this help\n");
                    902:          printf("i insert value into symbol\n");
                    903:          printf("j jam value into symbol\n");
                    904:          printf("n new hashtable\n");
                    905:          printf("r replace a value with another\n");
                    906:          printf("s say what %% of table is used\n");
                    907:          printf("q exit this program\n");
                    908:          printf("x delete a symbol from table, report its value\n");
                    909:          break;
                    910:        case 'i':
                    911:          error = hash_insert(h,name=what("symbol"),value=what("value"));
                    912:          if(*error)
                    913:            {
                    914:              printf("symbol=\"%s\"  value=\"%s\"  error=%s\n",name,value,error);
                    915:            }
                    916:          break;
                    917:        case 'j':
                    918:          error = hash_jam(h,name=what("symbol"),value=what("value"));
                    919:          if (*error)
                    920:            {
                    921:              printf("symbol=\"%s\"  value=\"%s\"  error=%s\n",name,value,error);
                    922:            }
                    923:          break;
                    924:        case 'n':
                    925:          h = hashtable[number] = hash_new();
                    926:          break;
                    927:        case 'q':
                    928:          exit(0);
                    929:        case 'r':
                    930:          thing = hash_replace(h,name=what("symbol"),value=what("value"));
                    931:          printf("old value was \"%s\"\n",thing?thing:"{}");
                    932:          break;
                    933:        case 's':
                    934:          hash_say(h,statbuf,STATBUFSIZE);
                    935:          for (ip=statbuf; ip<statbuf+STATBUFSIZE; ip++)
                    936:            {
                    937:              printf("%d ",*ip);
                    938:            }
                    939:          printf("\n");
                    940:          break;
                    941:        case 'x':
                    942:          thing = hash_delete(h,name=what("symbol"));
                    943:          printf("old value was \"%s\"\n",thing?thing:"{}");
                    944:          break;
                    945:        default:
                    946:          printf("I can't understand command \"%c\"\n",command);
                    947:          break;
                    948:        }
                    949:     }
                    950: }
                    951: 
                    952: static
                    953: char *
                    954: what(
                    955: char * description)
                    956: {
                    957:   char * retval;
                    958: 
                    959:   printf("   %s : ",description);
                    960:   gets(answer);
                    961:   /* will one day clean up answer here */
                    962:   retval = malloc(strlen(answer)+1);
                    963:   if (!retval)
                    964:     {
                    965: #ifdef NeXT
                    966:       as_fatal("room");
                    967: #else
                    968:       error("room");
                    969: #endif
                    970:     }
                    971:   (void)strcpy(retval,answer);
                    972:   return(retval);
                    973: }
                    974: 
                    975: static
                    976: char *
                    977: destroy(
                    978: char * string,
                    979: char * value)
                    980: {
                    981:   free(string);
                    982:   free(value);
                    983:   return(NULL);
                    984: }
                    985: 
                    986: static
                    987: char *
                    988: applicatee(
                    989: char * string,
                    990: char * value)
                    991: {
                    992:   printf("%.20s-%.20s\n",string,value);
                    993:   return(NULL);
                    994: }
                    995: 
                    996: static
                    997: void
                    998: whattable(                     /* determine number: what hash table to use */
                    999: void)                          /* also determine h: points to hash_control */
                   1000: {
                   1001: 
                   1002:   for (;;)
                   1003:     {
                   1004:       printf("   what hash table (%d:%d) ?  ",0,TABLES-1);
                   1005:       gets(answer);
                   1006:       sscanf(answer,"%d",&number);
                   1007:       if (number>=0 && number<TABLES)
                   1008:        {
                   1009:          h = hashtable[number];
                   1010:          if (!h)
                   1011:            {
                   1012:              printf("warning: current hash-table-#%d. has no hash-control\n",number);
                   1013:            }
                   1014:          return;
                   1015:        }
                   1016:       else
                   1017:        {
                   1018:          printf("invalid hash table number: %d\n",number);
                   1019:        }
                   1020:     }
                   1021: }
                   1022: 
                   1023: void *
                   1024: xmalloc(
                   1025: long n)
                   1026: {
                   1027:     void *retval;
                   1028: 
                   1029:        if(!(retval = malloc((unsigned)n))){
                   1030:            printf("virtual memory exceeded\n");
                   1031:            exit(1);
                   1032:        }
                   1033:        return(retval);
                   1034: }
                   1035: 
                   1036: void *
                   1037: xrealloc(
                   1038: void *ptr,
                   1039: long n)
                   1040: {
                   1041:     if((ptr = realloc(ptr, (unsigned)n)) == 0){
                   1042:        printf("virtual memory exceeded\n");
                   1043:        exit(1);
                   1044:     }
                   1045:     return(ptr);
                   1046: }
                   1047: 
                   1048: void
                   1049: as_fatal(
                   1050: const char *format,
                   1051: ...)
                   1052: {
                   1053:     va_list ap;
                   1054: 
                   1055:        va_start(ap, format);
                   1056:        fprintf (stderr, "FATAL:");
                   1057:        vfprintf(stderr, format, ap);
                   1058:        fprintf(stderr, "\n");
                   1059:        va_end(ap);
                   1060:        exit(1);
                   1061: }
                   1062: #endif /* #ifdef TEST */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.