/* Copyright (C) 1989-2020 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com) 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 3 of the License, 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 this program. If not, see . */ #include // 'class ITABLE(T)' is the type of a hash table mapping an integer (int >= 0) // to an object of type T. // // 'struct IASSOC(T)' is the type of a association (pair) between an integer // (int >= 0) and an object of type T. // // 'class ITABLE_ITERATOR(T)' is the type of an iterator iterating through a // 'class ITABLE(T)'. // // Nowadays one would use templates for this; this code predates the addition // of templates to C++. #define ITABLE(T) T ## _itable #define IASSOC(T) T ## _iassoc #define ITABLE_ITERATOR(T) T ## _itable_iterator // ptable.h declares this too #ifndef NEXT_PTABLE_SIZE_DEFINED # define NEXT_PTABLE_SIZE_DEFINED extern unsigned next_ptable_size(unsigned); // Return the first suitable // hash table size greater than the given // value. #endif // Declare the types 'class ITABLE(T)', 'struct IASSOC(T)', and 'class // ITABLE_ITERATOR(T)' for the type 'T'. #define declare_itable(T) \ \ struct IASSOC(T) { \ int key; \ T *val; \ IASSOC(T)(); \ }; \ \ class ITABLE(T); \ \ class ITABLE_ITERATOR(T) { \ ITABLE(T) *p; \ unsigned i; \ public: \ ITABLE_ITERATOR(T)(ITABLE(T) *); /* Initialize an iterator running \ through the given table. */ \ int next(int *, T **); /* Fetch the next pair, store the key \ and value in arg1 and arg2, \ respectively, and return 1. If \ there is no more pair in the \ table, return 0. */ \ }; \ \ class ITABLE(T) { \ IASSOC(T) *v; \ unsigned size; \ unsigned used; \ enum { \ FULL_NUM = 2, \ FULL_DEN = 3, \ INITIAL_SIZE = 17 \ }; \ public: \ ITABLE(T)(); /* Create an empty table. */ \ ~ITABLE(T)(); /* Delete a table, including its \ values. */ \ void define(int, T *); /* Define the value (arg2) for a key \ (arg1). */ \ T *lookup(int); /* Return a pointer to the value of \ the given key, if found in the \ table, or NULL otherwise. */ \ friend class ITABLE_ITERATOR(T); \ }; // Values must be allocated by the caller (always using new[], not new) // and are freed by ITABLE. // Define the implementations of the members of the types 'class ITABLE(T)', // 'struct IASSOC(T)', 'class ITABLE_ITERATOR(T)' for the type 'T'. #define implement_itable(T) \ \ IASSOC(T)::IASSOC(T)() \ : key(-1), val(0) \ { \ } \ \ ITABLE(T)::ITABLE(T)() \ { \ v = new IASSOC(T)[size = INITIAL_SIZE]; \ used = 0; \ } \ \ ITABLE(T)::~ITABLE(T)() \ { \ for (unsigned i = 0; i < size; i++) \ delete[] v[i].val; \ delete[] v; \ } \ \ void ITABLE(T)::define(int key, T *val) \ { \ assert(key >= 0); \ unsigned int h = (unsigned int)(key); \ unsigned n; \ for (n = unsigned(h % size); \ v[n].key >= 0; \ n = (n == 0 ? size - 1 : n - 1)) \ if (v[n].key == key) { \ delete[] v[n].val; \ v[n].val = val; \ return; \ } \ if (val == 0) \ return; \ if (used*FULL_DEN >= size*FULL_NUM) { \ IASSOC(T) *oldv = v; \ unsigned old_size = size; \ size = next_ptable_size(size); \ v = new IASSOC(T)[size]; \ for (unsigned i = 0; i < old_size; i++) \ if (oldv[i].key >= 0) { \ if (oldv[i].val != 0) { \ unsigned j; \ for (j = (unsigned int)(oldv[i].key) % size; \ v[j].key >= 0; \ j = (j == 0 ? size - 1 : j - 1)) \ ; \ v[j].key = oldv[i].key; \ v[j].val = oldv[i].val; \ } \ } \ for (n = unsigned(h % size); \ v[n].key >= 0; \ n = (n == 0 ? size - 1 : n - 1)) \ ; \ delete[] oldv; \ } \ v[n].key = key; \ v[n].val = val; \ used++; \ } \ \ T *ITABLE(T)::lookup(int key) \ { \ assert(key >= 0); \ for (unsigned n = (unsigned int)key % size; \ v[n].key >= 0; \ n = (n == 0 ? size - 1 : n - 1)) \ if (v[n].key == key) \ return v[n].val; \ return 0; \ } \ \ ITABLE_ITERATOR(T)::ITABLE_ITERATOR(T)(ITABLE(T) *t) \ : p(t), i(0) \ { \ } \ \ int ITABLE_ITERATOR(T)::next(int *keyp, T **valp) \ { \ unsigned size = p->size; \ IASSOC(T) *v = p->v; \ for (; i < size; i++) \ if (v[i].key >= 0) { \ *keyp = v[i].key; \ *valp = v[i].val; \ i++; \ return 1; \ } \ return 0; \ } // Local Variables: // fill-column: 72 // mode: C++ // End: // vim: set cindent noexpandtab shiftwidth=2 textwidth=72: