ref: 7d02e382d314d5bdde7978ccb7a64ea9201d03db
dir: /hash.c/
#include <ctype.h>
#include "ficl.h"
#define FICL_ASSERT_PHASH(hash, expression) FICL_ASSERT(NULL, expression)
/**************************************************************************
h a s h F o r g e t
** Unlink all words in the hash that have addresses greater than or
** equal to the address supplied. Implementation factor for FORGET
** and MARKER.
**************************************************************************/
void ficlHashForget(ficlHash *hash, void *where)
{
ficlWord *pWord;
unsigned i;
FICL_ASSERT_PHASH(hash, hash);
FICL_ASSERT_PHASH(hash, where);
for (i = 0; i < hash->size; i++)
{
pWord = hash->table[i];
while ((void *)pWord >= where)
{
pWord = pWord->link;
}
hash->table[i] = pWord;
}
return;
}
/**************************************************************************
h a s h H a s h C o d e
**
** Generate a 16 bit hashcode from a character string using a rolling
** shift and add stolen from PJ Weinberger of Bell Labs fame. Case folds
** the name before hashing it...
** N O T E : If string has zero length, returns zero.
**************************************************************************/
ficlUnsigned16 ficlHashCode(ficlString s)
{
/* hashPJW */
ficlUnsigned8 *trace;
ficlUnsigned16 code = (ficlUnsigned16)s.length;
ficlUnsigned16 shift = 0;
if (s.length == 0)
return 0;
/* changed to run without errors under Purify -- lch */
for (trace = (ficlUnsigned8 *)s.text; s.length && *trace; trace++, s.length--)
{
code = (ficlUnsigned16)((code << 4) + tolower(*trace));
shift = (ficlUnsigned16)(code & 0xf000);
if (shift)
{
code ^= (ficlUnsigned16)(shift >> 8);
code ^= (ficlUnsigned16)shift;
}
}
return (ficlUnsigned16)code;
}
/**************************************************************************
h a s h I n s e r t W o r d
** Put a word into the hash table using the word's hashcode as
** an index (modulo the table size).
**************************************************************************/
void ficlHashInsertWord(ficlHash *hash, ficlWord *word)
{
ficlWord **pList;
FICL_ASSERT_PHASH(hash, hash);
FICL_ASSERT_PHASH(hash, word);
if (hash->size == 1)
{
pList = hash->table;
}
else
{
pList = hash->table + (word->hash % hash->size);
}
word->link = *pList;
*pList = word;
return;
}
/**************************************************************************
h a s h L o o k u p
** Find a name in the hash table given the hashcode and text of the name.
** Returns the address of the corresponding ficlWord if found,
** otherwise NULL.
** Note: outer loop on link field supports inheritance in wordlists.
** It's not part of ANS Forth - Ficl only. hashReset creates wordlists
** with NULL link fields.
**************************************************************************/
ficlWord *ficlHashLookup(ficlHash *hash, ficlString name, ficlUnsigned16 hashCode)
{
ficlUnsigned nCmp = name.length;
ficlWord *word;
ficlUnsigned16 hashIdx;
if (nCmp > FICL_NAME_LENGTH)
nCmp = FICL_NAME_LENGTH;
for (; hash != NULL; hash = hash->link)
{
if (hash->size > 1)
hashIdx = (ficlUnsigned16)(hashCode % hash->size);
else /* avoid the modulo op for single threaded lists */
hashIdx = 0;
for (word = hash->table[hashIdx]; word; word = word->link)
{
if ( (word->length == name.length)
&& (!ficlStrincmp(name.text, word->name, nCmp)) )
return word;
#if FICL_ROBUST
FICL_ASSERT_PHASH(hash, word != word->link);
#endif
}
}
return NULL;
}
/**************************************************************************
h a s h R e s e t
** Initialize a ficlHash to empty state.
**************************************************************************/
void ficlHashReset(ficlHash *hash)
{
unsigned i;
FICL_ASSERT_PHASH(hash, hash);
for (i = 0; i < hash->size; i++)
{
hash->table[i] = NULL;
}
hash->link = NULL;
hash->name = NULL;
return;
}