BSD 4_3_Net_2 development
[unix-history] / usr / src / usr.bin / groff / troff / dictionary.cc
// -*- C++ -*-
/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
Written by James Clark (jjc@jclark.uucp)
This file is part of groff.
groff 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 1, or (at your option) any later
version.
groff 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 groff; see the file LICENSE. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "groff.h"
#include "symbol.h"
#include "dictionary.h"
// is `p' a good size for a hash table
static int is_good_size(int p)
{
const int SMALL = 10;
for (unsigned i = 2; i <= p/2; i++)
if (p % i == 0)
return 0;
for (i = 0x100; i != 0; i <<= 8)
if (i % p <= SMALL || i % p > p - SMALL)
return 0;
return 1;
}
dictionary::dictionary(int n) : threshold(0.5), factor(1.5), used(0), size(n)
{
table = new association[n];
for (int i = 0; i < n; i++)
table[i].v = 0;
}
// see Knuth, Sorting and Searching, p518, Algorithm L
// we can't use double-hashing because we want a remove function
void *dictionary::lookup(symbol s, void *v)
{
for (int i = s.hash() % size;
table[i].v != 0;
i == 0 ? i = size - 1: --i)
if (s == table[i].s) {
if (v != 0) {
void *temp = table[i].v;
table[i].v = v;
return temp;
}
else
return table[i].v;
}
if (v == 0)
return 0;
++used;
table[i].v = v;
table[i].s = s;
if ((double)used/(double)size >= threshold || used + 1 >= size) {
int old_size = size;
size = int(size*factor);
while (!is_good_size(size))
++size;
association *old_table = table;
table = new association[size];
used = 0;
for (i = 0; i < old_size; i++)
if (old_table[i].v != 0)
(void)lookup(old_table[i].s, old_table[i].v);
}
return 0;
}
void *dictionary::lookup(const char *p)
{
symbol s(p, MUST_ALREADY_EXIST);
if (s.is_null())
return 0;
else
return lookup(s);
}
// see Knuth, Sorting and Searching, p527, Algorithm R
void *dictionary::remove(symbol s)
{
// this relies on the fact that we are using linear probing
for (int i = s.hash() % size;
table[i].v != 0 && s != table[i].s;
i == 0 ? i = size - 1: --i)
;
void *p = table[i].v;
while (table[i].v != 0) {
table[i].v = 0;
int j = i;
int r;
do {
--i;
if (i < 0)
i = size - 1;
if (table[i].v == 0)
break;
r = table[i].s.hash() % size;
} while ((i <= r && r < j) || (j < i && i <= r));
table[j] = table[i];
}
if (p != 0)
--used;
return p;
}
dictionary_iterator::dictionary_iterator(dictionary &d) : dict(&d), i(0)
{
}
int dictionary_iterator::get(symbol *sp, void **vp)
{
for (; i < dict->size; i++)
if (dict->table[i].v) {
*sp = dict->table[i].s;
*vp = dict->table[i].v;
i++;
return 1;
}
return 0;
}
object_dictionary_iterator::object_dictionary_iterator(object_dictionary &od)
: di(od.d)
{
}
object::object() : rcount(0)
{
}
object::~object()
{
}
void object::add_reference()
{
rcount += 1;
}
void object::remove_reference()
{
if (--rcount == 0)
delete this;
}
object_dictionary::object_dictionary(int n) : d(n)
{
}
object *object_dictionary::lookup(symbol nm)
{
return (object *)d.lookup(nm);
}
void object_dictionary::define(symbol nm, object *obj)
{
obj->add_reference();
obj = (object *)d.lookup(nm, obj);
if (obj)
obj->remove_reference();
}
void object_dictionary::rename(symbol oldnm, symbol newnm)
{
object *obj = (object *)d.remove(oldnm);
if (obj) {
obj = (object *)d.lookup(newnm, obj);
if (obj)
obj->remove_reference();
}
}
void object_dictionary::remove(symbol nm)
{
object *obj = (object *)d.remove(nm);
if (obj)
obj->remove_reference();
}
// Return non-zero if oldnm was defined.
int object_dictionary::alias(symbol newnm, symbol oldnm)
{
object *obj = (object *)d.lookup(oldnm);
if (obj) {
obj->add_reference();
obj = (object *)d.lookup(newnm, obj);
if (obj)
obj->remove_reference();
return 1;
}
return 0;
}