#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;
}
}
/*
* 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;
}
/*
* 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;
}