/*
* dnsutl - utilities to make DNS easier to configure
* Copyright (C) 1990-1994, 1996, 1999, 2000, 2006, 2007 Peter Miller
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
symtab_ty *
symtab_alloc(str_hash_ty size)
{
symtab_ty *stp;
str_hash_ty j;
stp = mem_alloc(sizeof(symtab_ty));
stp->chain = 0;
stp->reap = 0;
stp->hash_modulus = 1 << 2; /* MUST be a power of 2 */
while (stp->hash_modulus < size)
stp->hash_modulus <<= 1;
stp->hash_cutover = stp->hash_modulus;
stp->hash_split = stp->hash_modulus - stp->hash_cutover;
stp->hash_cutover_mask = stp->hash_cutover - 1;
stp->hash_cutover_split_mask = (stp->hash_cutover * 2) - 1;
stp->hash_load = 0;
stp->hash_table = mem_alloc(stp->hash_modulus * sizeof(symtab_row_ty *));
for (j = 0; j < stp->hash_modulus; ++j)
stp->hash_table[j] = 0;
return stp;
}
void
symtab_free(symtab_ty *stp)
{
str_hash_ty j;
for (j = 0; j < stp->hash_modulus; ++j)
{
symtab_row_ty **rpp;
rpp = &stp->hash_table[j];
while (*rpp)
{
symtab_row_ty *rp;
rp = *rpp;
*rpp = rp->overflow;
if (stp->reap)
stp->reap(rp->data);
str_free(rp->key);
mem_free(rp);
}
}
mem_free(stp->hash_table);
mem_free(stp);
}
/*
* NAME
* split - reduce symbol table load
*
* SYNOPSIS
* void split(symtab_ty);
*
* DESCRIPTION
* The split function is used to split symbols in the bucket indicated by
* the split point. The symbols are split between that bucket and the one
* after the current end of the table.
*
* CAVEAT
* It is only sensable to do this when the symbol table load exceeds some
* reasonable threshold. A threshold of 80% is suggested.
*/
static void
split(symtab_ty *stp)
{
symtab_row_ty *p;
symtab_row_ty **ipp;
symtab_row_ty *p2;
str_hash_ty index;
/*
* get the list to be split across buckets
*/
p = stp->hash_table[stp->hash_split];
stp->hash_table[stp->hash_split] = 0;
/*
* increase the modulus by one
*/
stp->hash_modulus++;
stp->hash_table =
mem_change_size
(
stp->hash_table,
stp->hash_modulus * sizeof(symtab_row_ty *)
);
stp->hash_table[stp->hash_modulus - 1] = 0;
stp->hash_split = stp->hash_modulus - stp->hash_cutover;
if (stp->hash_split >= stp->hash_cutover)
{
stp->hash_cutover = stp->hash_modulus;
stp->hash_split = 0;
stp->hash_cutover_mask = stp->hash_cutover - 1;
stp->hash_cutover_split_mask = (stp->hash_cutover * 2) - 1;
}
/*
* now redistribute the list elements
*
* It is important to preserve the order of the links because
* they can be push-down stacks, and to simply add them to the
* head of the list will reverse the order of the stack!
*/
while (p)
{
p2 = p;
p = p2->overflow;
p2->overflow = 0;
index = p2->key->str_hash & stp->hash_cutover_mask;
if (index < stp->hash_split)
{
index =
(
p2->key->str_hash
&
stp->hash_cutover_split_mask
);
}
for
(
ipp = &stp->hash_table[index];
*ipp;
ipp = &(*ipp)->overflow
)
;
*ipp = p2;
}
}
/*
* NAME
* symtab_query - search for a variable
*
* SYNOPSIS
* int symtab_query(symtab_ty *, string_ty *key);
*
* DESCRIPTION
* The symtab_query function is used to reference a variable.
*
* RETURNS
* If the variable has been defined, the function returns a non-zero value
* and the value is returned through the 'value' pointer.
* If the variable has not been defined, it returns zero,
* and 'value' is unaltered.
*/
void *
symtab_query(symtab_ty *stp, string_ty *key)
{
str_hash_ty index;
symtab_row_ty *p;
void *result;
result = 0;
while (stp)
{
index = key->str_hash & stp->hash_cutover_mask;
if (index < stp->hash_split)
index = key->str_hash & stp->hash_cutover_split_mask;
for (p = stp->hash_table[index]; p; p = p->overflow)
{
if (str_equal(key, p->key))
{
result = p->data;
goto done;
}
}
stp = stp->chain;
}
done:
return result;
}
/*
* NAME
* symtab_assign - assign a variable
*
* SYNOPSIS
* void symtab_assign(symtab_ty *, string_ty *key, void *data);
*
* DESCRIPTION
* The symtab_assign function is used to assign
* a value to a given variable.
*
* CAVEAT
* The name is copied, the data is not.
*/
void
symtab_assign(symtab_ty *stp, string_ty *key, void *data)
{
str_hash_ty index;
symtab_row_ty *p;
index = key->str_hash & stp->hash_cutover_mask;
if (index < stp->hash_split)
index = key->str_hash & stp->hash_cutover_split_mask;
for (p = stp->hash_table[index]; p; p = p->overflow)
{
if (str_equal(key, p->key))
{
if (stp->reap)
stp->reap(p->data);
p->data = data;
goto done;
}
}
p = mem_alloc(sizeof(symtab_row_ty));
p->key = str_copy(key);
p->overflow = stp->hash_table[index];
p->data = data;
stp->hash_table[index] = p;
stp->hash_load++;
while (stp->hash_load * 10 >= stp->hash_modulus * 8)
split(stp);
done:;
}
/*
* NAME
* symtab_assign_push - assign a variable
*
* SYNOPSIS
* void symtab_assign_push(symtab_ty *, string_ty *key, void *data);
*
* DESCRIPTION
* The symtab_assign function is used to assign
* a value to a given variable.
* Any previous value will be obscured until this one
* is deleted with symtab_delete.
*
* CAVEAT
* The name is copied, the data is not.
*/
void
symtab_assign_push(symtab_ty *stp, string_ty *key, void *data)
{
str_hash_ty index;
symtab_row_ty *p;
index = key->str_hash & stp->hash_cutover_mask;
if (index < stp->hash_split)
index = key->str_hash & stp->hash_cutover_split_mask;
p = mem_alloc(sizeof(symtab_row_ty));
p->key = str_copy(key);
p->overflow = stp->hash_table[index];
p->data = data;
stp->hash_table[index] = p;
stp->hash_load++;
while (stp->hash_load * 10 >= stp->hash_modulus * 8)
split(stp);
}
/*
* NAME
* symtab_delete - delete a variable
*
* SYNOPSIS
* void symtab_delete(string_ty *name, symtab_class_ty class);
*
* DESCRIPTION
* The symtab_delete function is used to delete variables.
*
* CAVEAT
* The name is freed, the data is reaped.
* (By default, reap does nothing.)
*/
void
symtab_delete(symtab_ty *stp, string_ty *key)
{
str_hash_ty index;
symtab_row_ty **pp;
index = key->str_hash & stp->hash_cutover_mask;
if (index < stp->hash_split)
index = key->str_hash & stp->hash_cutover_split_mask;
pp = &stp->hash_table[index];
for (;;)
{
symtab_row_ty *p;
p = *pp;
if (!p)
break;
if (str_equal(key, p->key))
{
if (stp->reap)
stp->reap(p->data);
str_free(p->key);
*pp = p->overflow;
mem_free(p);
stp->hash_load--;
break;
}
pp = &p->overflow;
}
}
/*
* NAME
* symtab_dump - dump symbol table
*
* SYNOPSIS
* void symtab_dump(symtab_ty *stp, char *caption);
*
* DESCRIPTION
* The symtab_dump function is used to dump the contents of the
* symbol table. The caption will be used to indicate why the
* symbol table was dumped.
*
* CAVEAT
* This function is only available when symbol DEBUG is defined.
*/
#ifdef DEBUG
void
symtab_dump(symtab_ty *stp, char *caption)
{
str_hash_ty j;
symtab_row_ty *p;
error("symbol table %s = {", caption);
for (j = 0; j < stp->hash_modulus; ++j)
{
for (p = stp->hash_table[j]; p; p = p->overflow)
{
error
(
"key = \"%s\", data = %08lX",
p->key->str_text,
(long)p->data
);
}
}
error("}");
}
#endif
void
symtab_walk(symtab_ty *stp, void (*func)(symtab_ty *, string_ty *, void *,
void *), void *arg)
{
str_hash_ty j;
symtab_row_ty *rp;
for (j = 0; j < stp->hash_modulus; ++j)
for (rp = stp->hash_table[j]; rp; rp = rp->overflow)
func(stp, rp->key, rp->data, arg);
}
int
symtab_keys(symtab_ty *stp, strlist_ty *slp)
{
int n;
size_t j;
symtab_row_ty *rp;
strlist_zero(slp);
n = 0;
for (j = 0; j < stp->hash_modulus; ++j)
{
for (rp = stp->hash_table[j]; rp; rp = rp->overflow)
{
++n;
if (slp)
strlist_append_unique(slp, rp->key);
}
}
return n;
}
void
symtab_assign_downcase(symtab_ty *stp, string_ty *key, void *data)
{
string_ty *key2;
key2 = str_downcase(key);
symtab_assign(stp, key2, data);
str_free(key2);
}
void *
symtab_query_downcase(symtab_ty *stp, string_ty *key)
{
string_ty *key2;
void *data;
key2 = str_downcase(key);
data = symtab_query(stp, key2);
str_free(key2);
return data;
}