//
// aegis - project change supervisor
// Copyright (C) 1998, 2002-2008, 2012 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
//
// NAME
// itab_alloc
//
// SYNOPSIS
// itab_ty *itabLalloc(int);
//
// DESCRIPTION
// The itab_alloc function is used to allocate a new integer table
// instance in dynamic memory.
//
// RETURNS
// itab_ty *; pointer to table
//
// CAVEAT
// Use itab_free when you are done with it.
//
itab_ty *
itab_alloc()
{
itab_ty *itp;
itab_key_ty j;
trace(("itab_alloc()\n{\n"));
itp = (itab_ty *)mem_alloc(sizeof(itab_ty));
itp->reap = 0;
itp->hash_modulus = 1 << 5; // MUST be a power of 2
itp->hash_mask = itp->hash_modulus - 1;
itp->load = 0;
itp->hash_table =
(itab_row_ty **)mem_alloc(itp->hash_modulus * sizeof(itab_row_ty *));
for (j = 0; j < itp->hash_modulus; ++j)
itp->hash_table[j] = 0;
trace(("return %p;\n", itp));
trace(("}\n"));
return itp;
}
//
// NAME
// itab_free
//
// SYNOPSIS
// void itab_free(itab_ty *);
//
// DESCRIPTION
// The itab_free function is used to release the resources held by
// an integer table.
//
void
itab_free(itab_ty *itp)
{
itab_key_ty j;
trace(("itab_free(itp = %p)\n{\n", itp));
for (j = 0; j < itp->hash_modulus; ++j)
{
itab_row_ty **rpp;
rpp = &itp->hash_table[j];
while (*rpp)
{
itab_row_ty *rp;
rp = *rpp;
*rpp = rp->overflow;
if (itp->reap)
itp->reap(rp->data);
mem_free(rp);
}
}
mem_free(itp->hash_table);
mem_free(itp);
trace(("}\n"));
}
//
// NAME
// split - reduce symbol table load
//
// SYNOPSIS
// void split(itab_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(itab_ty *itp)
{
itab_row_ty **new_hash_table;
itab_key_ty new_hash_modulus;
itab_key_ty new_hash_mask;
itab_key_ty idx;
trace(("split(itp = %p)\n{\n", itp));
//
// double the modulus
//
// This is subtle. If we only increase the modulus by one, the
// load always hovers around 80%, so we have to do a split for
// every insert. I.e. the malloc burden os O(n) for the lifetime of
// the itab. BUT if we double the modulus, the length of time
// until the next split also doubles, making the probablity of a
// split halve, and sigma(2**-n)=1, so the malloc burden becomes O(1)
// for the lifetime of the itab.
//
new_hash_modulus = itp->hash_modulus * 2;
new_hash_mask = new_hash_modulus - 1;
new_hash_table =
(itab_row_ty **)mem_alloc(new_hash_modulus * sizeof(itab_row_ty *));
//
// now redistribute the list elements
//
for (idx = 0; idx < itp->hash_modulus; ++idx)
{
itab_row_ty *p;
new_hash_table[idx] = 0;
new_hash_table[idx + itp->hash_modulus] = 0;
p = itp->hash_table[idx];
while (p)
{
itab_row_ty *p2;
itab_row_ty **ipp;
p2 = p;
p = p2->overflow;
p2->overflow = 0;
//
// 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!
//
assert((p2->key & itp->hash_mask) == idx);
itab_key_ty idx2 = p2->key & new_hash_mask;
for (ipp = &new_hash_table[idx2]; *ipp; ipp = &(*ipp)->overflow)
;
*ipp = p2;
}
}
mem_free(itp->hash_table);
itp->hash_table = new_hash_table;
itp->hash_modulus = new_hash_modulus;
itp->hash_mask = new_hash_mask;
trace(("}\n"));
}
//
// NAME
// itab_query - search for a variable
//
// SYNOPSIS
// int itab_query(itab_ty *, string_ty *key);
//
// DESCRIPTION
// The itab_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 *
itab_query(itab_ty *itp, itab_key_ty key)
{
itab_key_ty idx;
itab_row_ty *p;
void *result;
trace(("itab_query(itp = %p, key = %ld)\n{\n", itp, (long)key));
result = 0;
idx = key & itp->hash_mask;
for (p = itp->hash_table[idx]; p; p = p->overflow)
{
if (key == p->key)
{
result = p->data;
break;
}
}
trace(("return %p;\n", result));
trace(("}\n"));
return result;
}
//
// NAME
// itab_assign - assign a variable
//
// SYNOPSIS
// void itab_assign(itab_ty *, string_ty *key, void *data);
//
// DESCRIPTION
// The itab_assign function is used to assign
// a value to a given variable.
//
// CAVEAT
// The name is copied, the data is not.
//
void
itab_assign(itab_ty *itp, itab_key_ty key, void *data)
{
itab_key_ty idx;
itab_row_ty *p;
trace(("itab_assign(itp = %p, key = %ld, data = %p)\n{\n",
itp, (long)key, data));
idx = key & itp->hash_mask;
for (p = itp->hash_table[idx]; p; p = p->overflow)
{
if (key == p->key)
{
trace(("modify existing entry\n"));
if (itp->reap)
itp->reap(p->data);
p->data = data;
goto done;
}
}
trace(("new entry\n"));
p = (itab_row_ty *)mem_alloc(sizeof(itab_row_ty));
p->key = key;
p->overflow = itp->hash_table[idx];
p->data = data;
itp->hash_table[idx] = p;
itp->load++;
if (itp->load * 10 >= itp->hash_modulus * 8)
split(itp);
done:
trace(("}\n"));
}
//
// NAME
// itab_delete - delete a variable
//
// SYNOPSIS
// void itab_delete(string_ty *name, itab_class_ty class);
//
// DESCRIPTION
// The itab_delete function is used to delete variables.
//
// CAVEAT
// The name is freed, the data is reaped.
// (By default, reap does nothing.)
//
void
itab_delete(itab_ty *itp, itab_key_ty key)
{
itab_key_ty idx;
itab_row_ty **pp;
trace(("itab_delete(itp = %p, key = %ld)\n{\n",
itp, (long)key));
idx = key & itp->hash_mask;
pp = &itp->hash_table[idx];
for (;;)
{
itab_row_ty *p;
p = *pp;
if (!p)
break;
if (key == p->key)
{
if (itp->reap)
itp->reap(p->data);
*pp = p->overflow;
mem_free(p);
itp->load--;
break;
}
pp = &p->overflow;
}
trace(("}\n"));
}
//
// NAME
// itab_walk
//
// SYNOPSIS
// void itab_walk(itab_ty *, void (*)(itab_ty *, itab_key_ty, void *,
// void *), void *);
//
// DESCRIPTION
// The itab_walk function is used to visit each element of an
// integer table, in no particular order.
//
void
itab_walk(itab_ty *itp, void (*func)(itab_ty *, itab_key_ty, void *, void *),
void *arg)
{
long j;
itab_row_ty *rp;
trace(("itab_walk(itp = %p)\n{\n", itp));
for (j = 0; j < itp->hash_modulus; ++j)
for (rp = itp->hash_table[j]; rp; rp = rp->overflow)
func(itp, rp->key, rp->data, arg);
trace(("}\n"));
}
// vim: set ts=8 sw=4 et :