mirror of
https://github.com/ThomasDickey/mawk-snapshots.git
synced 2026-01-26 19:09:15 +00:00
329 lines
5.8 KiB
C
329 lines
5.8 KiB
C
/********************************************
|
|
hash.c
|
|
copyright 2008-2012,2021, Thomas E. Dickey
|
|
copyright 1991-1993,1994, Michael D. Brennan
|
|
|
|
This is a source file for mawk, an implementation of
|
|
the AWK programming language.
|
|
|
|
Mawk is distributed without warranty under the terms of
|
|
the GNU General Public License, version 2, 1991.
|
|
********************************************/
|
|
|
|
/*
|
|
* $MawkId: hash.c,v 1.20 2021/05/28 23:59:20 tom Exp $
|
|
*/
|
|
|
|
/* hash.c */
|
|
|
|
#include "mawk.h"
|
|
#include "memory.h"
|
|
#include "symtype.h"
|
|
|
|
#ifdef NO_LEAKS
|
|
#include "bi_vars.h"
|
|
#endif
|
|
|
|
/*
|
|
* FNV-1 hash function
|
|
* http://www.isthe.com/chongo/tech/comp/fnv/index.html
|
|
*/
|
|
unsigned
|
|
hash(const char *s)
|
|
{
|
|
/* FNV-1 */
|
|
register unsigned h = 2166136261U;
|
|
|
|
while (*s) {
|
|
h ^= (UChar) (*s++);
|
|
h *= 16777619U;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
unsigned
|
|
hash2(const char *s, size_t len)
|
|
{
|
|
/* FNV-1 */
|
|
register unsigned h = 2166136261U;
|
|
|
|
while (len-- != 0) {
|
|
h ^= (UChar) (*s++);
|
|
h *= 16777619U;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
typedef struct hash {
|
|
struct hash *link;
|
|
SYMTAB symtab;
|
|
} HASHNODE;
|
|
|
|
static HASHNODE *hash_table[HASH_PRIME];
|
|
|
|
#ifdef NO_LEAKS
|
|
static void free_hashnode(HASHNODE *);
|
|
#else
|
|
#define free_hashnode(p) zfree(delete(p->symtab.name), sizeof(HASHNODE))
|
|
#endif
|
|
|
|
/*
|
|
insert a string in the symbol table.
|
|
Caller knows the symbol is not there
|
|
-- used for initialization
|
|
*/
|
|
|
|
SYMTAB *
|
|
insert(const char *s)
|
|
{
|
|
register HASHNODE *p = ZMALLOC(HASHNODE);
|
|
register unsigned h;
|
|
|
|
p->link = hash_table[h = hash(s) % HASH_PRIME];
|
|
p->symtab.name = s;
|
|
#ifdef NO_LEAKS
|
|
p->symtab.free_name = 0;
|
|
#endif
|
|
hash_table[h] = p;
|
|
return &p->symtab;
|
|
}
|
|
|
|
/* Find s in the symbol table,
|
|
if not there insert it, s must be dup'ed */
|
|
|
|
SYMTAB *
|
|
find(const char *s)
|
|
{
|
|
register HASHNODE *p;
|
|
HASHNODE *q;
|
|
unsigned h;
|
|
|
|
p = hash_table[h = hash(s) % HASH_PRIME];
|
|
q = (HASHNODE *) 0;
|
|
while (1) {
|
|
if (!p) {
|
|
p = ZMALLOC(HASHNODE);
|
|
p->symtab.type = ST_NONE;
|
|
p->symtab.name = strcpy(zmalloc(strlen(s) + 1), s);
|
|
#ifdef NO_LEAKS
|
|
p->symtab.free_name = 1;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
if (strcmp(p->symtab.name, s) == 0) /* found */
|
|
{
|
|
if (!q) /* already at the front */
|
|
return &p->symtab;
|
|
else { /* delete from the list */
|
|
q->link = p->link;
|
|
break;
|
|
}
|
|
}
|
|
|
|
q = p;
|
|
p = p->link;
|
|
}
|
|
/* put p on front of the list */
|
|
p->link = hash_table[h];
|
|
hash_table[h] = p;
|
|
return &p->symtab;
|
|
}
|
|
|
|
/* remove a node from the hash table
|
|
return a ptr to the node */
|
|
|
|
static unsigned last_hash;
|
|
|
|
static HASHNODE *
|
|
delete(const char *s)
|
|
{
|
|
register HASHNODE *p;
|
|
HASHNODE *q = (HASHNODE *) 0;
|
|
unsigned h;
|
|
|
|
p = hash_table[last_hash = h = hash(s) % HASH_PRIME];
|
|
while (p) {
|
|
if (strcmp(p->symtab.name, s) == 0) /* found */
|
|
{
|
|
if (q)
|
|
q->link = p->link;
|
|
else
|
|
hash_table[h] = p->link;
|
|
return p;
|
|
} else {
|
|
q = p;
|
|
p = p->link;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG /* we should not ever get here */
|
|
bozo("delete");
|
|
#endif
|
|
return (HASHNODE *) 0;
|
|
}
|
|
|
|
/* when processing user functions, global ids which are
|
|
replaced by local ids are saved on this list */
|
|
|
|
static HASHNODE *save_list;
|
|
|
|
/* store a global id on the save list,
|
|
return a ptr to the local symtab */
|
|
SYMTAB *
|
|
save_id(const char *s)
|
|
{
|
|
HASHNODE *p, *q;
|
|
unsigned h;
|
|
|
|
p = delete(s);
|
|
q = ZMALLOC(HASHNODE);
|
|
q->symtab.type = ST_LOCAL_NONE;
|
|
q->symtab.name = p->symtab.name;
|
|
/* put q in the hash table */
|
|
q->link = hash_table[h = last_hash];
|
|
hash_table[h] = q;
|
|
|
|
/* save p */
|
|
p->link = save_list;
|
|
save_list = p;
|
|
|
|
return &q->symtab;
|
|
}
|
|
|
|
/* restore all global identifiers */
|
|
void
|
|
restore_ids(void)
|
|
{
|
|
register HASHNODE *p, *q;
|
|
|
|
q = save_list;
|
|
save_list = (HASHNODE *) 0;
|
|
while (q) {
|
|
register unsigned h;
|
|
p = q;
|
|
q = q->link;
|
|
free_hashnode(p);
|
|
p->link = hash_table[h = last_hash];
|
|
hash_table[h] = p;
|
|
}
|
|
}
|
|
|
|
/* search the symbol table backwards for the
|
|
disassembler. This is slow -- so what
|
|
*/
|
|
|
|
const char *
|
|
reverse_find(int type, PTR ptr)
|
|
{
|
|
CELL *cp = 0;
|
|
ARRAY array = 0;
|
|
static char uk[] = "unknown";
|
|
|
|
int i;
|
|
HASHNODE *p;
|
|
|
|
switch (type) {
|
|
case ST_VAR:
|
|
case ST_FIELD:
|
|
cp = *(CELL **) ptr;
|
|
break;
|
|
|
|
case ST_ARRAY:
|
|
array = *(ARRAY *) ptr;
|
|
break;
|
|
|
|
default:
|
|
return uk;
|
|
}
|
|
|
|
for (i = 0; i < HASH_PRIME; i++) {
|
|
p = hash_table[i];
|
|
while (p) {
|
|
if (p->symtab.type == type) {
|
|
switch (type) {
|
|
case ST_VAR:
|
|
case ST_FIELD:
|
|
if (cp == p->symtab.stval.cp)
|
|
return p->symtab.name;
|
|
break;
|
|
|
|
case ST_ARRAY:
|
|
if (array == p->symtab.stval.array)
|
|
return p->symtab.name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
p = p->link;
|
|
}
|
|
}
|
|
return uk;
|
|
}
|
|
|
|
#ifdef NO_LEAKS
|
|
static void
|
|
free_symtab_name(HASHNODE * p)
|
|
{
|
|
if (p->symtab.free_name) {
|
|
zfree((PTR) (p->symtab.name), strlen(p->symtab.name) + 1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_hashnode(HASHNODE * p)
|
|
{
|
|
CELL *cp;
|
|
|
|
TRACE(("...deleting hash %p (%p) %s %d\n",
|
|
(void *) p,
|
|
(void *) &(p->symtab),
|
|
p->symtab.name, p->symtab.type));
|
|
p = delete(p->symtab.name);
|
|
switch (p->symtab.type) {
|
|
case ST_FUNCT:
|
|
free_codes(p->symtab.name,
|
|
p->symtab.stval.fbp->code,
|
|
p->symtab.stval.fbp->size);
|
|
if (p->symtab.stval.fbp->nargs)
|
|
zfree(p->symtab.stval.fbp->typev, p->symtab.stval.fbp->nargs);
|
|
zfree(p->symtab.stval.fbp, sizeof(FBLOCK));
|
|
break;
|
|
case ST_NONE:
|
|
break;
|
|
case ST_VAR:
|
|
cp = p->symtab.stval.cp;
|
|
if (cp != 0
|
|
&& (cp < bi_vars || cp > bi_vars + NUM_BI_VAR)) {
|
|
switch (cp->type) {
|
|
case C_STRING:
|
|
case C_STRNUM:
|
|
case C_MBSTRN:
|
|
free_STRING(string(cp));
|
|
break;
|
|
}
|
|
zfree(cp, sizeof(CELL));
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
free_symtab_name(p);
|
|
zfree(p, sizeof(HASHNODE));
|
|
}
|
|
|
|
void
|
|
hash_leaks(void)
|
|
{
|
|
int i;
|
|
HASHNODE *p;
|
|
|
|
TRACE(("hash_leaks\n"));
|
|
for (i = 0; i < HASH_PRIME; i++) {
|
|
while ((p = hash_table[i]) != 0) {
|
|
free_hashnode(p);
|
|
}
|
|
}
|
|
}
|
|
#endif
|