summaryrefslogtreecommitdiffstats
path: root/src/include/ptable.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/include/ptable.h')
-rw-r--r--src/include/ptable.h233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/include/ptable.h b/src/include/ptable.h
new file mode 100644
index 0000000..fa79293
--- /dev/null
+++ b/src/include/ptable.h
@@ -0,0 +1,233 @@
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+#include <string.h>
+
+// 'class PTABLE(T)' is the type of a hash table mapping a string
+// (const char *) to an object of type T.
+//
+// 'struct PASSOC(T)' is the type of a association (pair) between a
+// string (const char *) and an object of type T.
+//
+// 'class PTABLE_ITERATOR(T)' is the type of an iterator iterating through a
+// 'class PTABLE(T)'.
+//
+// Nowadays one would use templates for this; this code predates the addition
+// of templates to C++.
+#define PTABLE(T) T ## _ptable
+#define PASSOC(T) T ## _passoc
+#define PTABLE_ITERATOR(T) T ## _ptable_iterator
+
+// itable.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
+
+extern unsigned long hash_string(const char *); // Return a hash code of the
+ // given string. The hash function is
+ // platform dependent. */
+
+// Declare the types 'class PTABLE(T)', 'struct PASSOC(T)', and 'class
+// PTABLE_ITERATOR(T)' for the type 'T'.
+#define declare_ptable(T) \
+ \
+struct PASSOC(T) { \
+ char *key; \
+ T *val; \
+ PASSOC(T)(); \
+}; \
+ \
+class PTABLE(T); \
+ \
+class PTABLE_ITERATOR(T) { \
+ PTABLE(T) *p; \
+ unsigned i; \
+public: \
+ PTABLE_ITERATOR(T)(PTABLE(T) *); /* Initialize an iterator running \
+ through the given table. */ \
+ int next(const char **, 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 PTABLE(T) { \
+ PASSOC(T) *v; \
+ unsigned size; \
+ unsigned used; \
+ enum { \
+ FULL_NUM = 1, \
+ FULL_DEN = 4, \
+ INITIAL_SIZE = 17 \
+ }; \
+public: \
+ PTABLE(T)(); /* Create an empty table. */ \
+ ~PTABLE(T)(); /* Delete a table, including its \
+ values. */ \
+ const char *define(const char *, T *);/* Define the value (arg2) for a key \
+ (arg1). Return the copy in the \
+ table of the key (arg1), or \
+ possibly NULL if the value (arg2) \
+ is NULL. */ \
+ T *lookup(const char *); /* Return a pointer to the value of \
+ the given key, if found in the \
+ table, or NULL otherwise. */ \
+ T *lookupassoc(const char **); /* Return a pointer to the value of \
+ the given key, passed by reference,\
+ and replace the key argument with \
+ the copy found in the table, if \
+ the key is found in the table. \
+ Return NULL otherwise. */ \
+ friend class PTABLE_ITERATOR(T); \
+};
+
+
+// Keys (which are strings) are allocated and freed by PTABLE.
+// Values must be allocated by the caller (always using new[], not new)
+// and are freed by PTABLE.
+
+// Define the implementations of the members of the types 'class PTABLE(T)',
+// 'struct PASSOC(T)', 'class PTABLE_ITERATOR(T)' for the type 'T'.
+#define implement_ptable(T) \
+ \
+PASSOC(T)::PASSOC(T)() \
+: key(0), val(0) \
+{ \
+} \
+ \
+PTABLE(T)::PTABLE(T)() \
+{ \
+ v = new PASSOC(T)[size = INITIAL_SIZE]; \
+ used = 0; \
+} \
+ \
+PTABLE(T)::~PTABLE(T)() \
+{ \
+ for (unsigned i = 0; i < size; i++) { \
+ free(v[i].key); \
+ /* XXX leak, because we don't know whether */ \
+ /* 'free', 'delete', or 'delete[]' should be used */ \
+ /* delete[] v[i].val; */ \
+ } \
+ delete[] v; \
+} \
+ \
+const char *PTABLE(T)::define(const char *key, T *val) \
+{ \
+ assert(key != 0); \
+ unsigned long h = hash_string(key); \
+ unsigned n; \
+ for (n = unsigned(h % size); \
+ v[n].key != 0; \
+ n = (n == 0 ? size - 1 : n - 1)) \
+ if (strcmp(v[n].key, key) == 0) { \
+ /* XXX leak, because we don't know whether */ \
+ /* 'free', 'delete', or 'delete[]' should be used */ \
+ /* delete[] v[n].val; */ \
+ v[n].val = val; \
+ return v[n].key; \
+ } \
+ if (val == 0) \
+ return 0; \
+ if (used*FULL_DEN >= size*FULL_NUM) { \
+ PASSOC(T) *oldv = v; \
+ unsigned old_size = size; \
+ size = next_ptable_size(size); \
+ v = new PASSOC(T)[size]; \
+ for (unsigned i = 0; i < old_size; i++) \
+ if (oldv[i].key != 0) { \
+ if (oldv[i].val == 0) \
+ free(oldv[i].key); \
+ else { \
+ unsigned j; \
+ for (j = unsigned(hash_string(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; \
+ } \
+ char *temp = (char*)malloc(strlen(key)+1); \
+ strcpy(temp, key); \
+ v[n].key = temp; \
+ v[n].val = val; \
+ used++; \
+ return temp; \
+} \
+ \
+T *PTABLE(T)::lookup(const char *key) \
+{ \
+ assert(key != 0); \
+ for (unsigned n = unsigned(hash_string(key) % size); \
+ v[n].key != 0; \
+ n = (n == 0 ? size - 1 : n - 1)) \
+ if (strcmp(v[n].key, key) == 0) \
+ return v[n].val; \
+ return 0; \
+} \
+ \
+T *PTABLE(T)::lookupassoc(const char **keyptr) \
+{ \
+ const char *key = *keyptr; \
+ assert(key != 0); \
+ for (unsigned n = unsigned(hash_string(key) % size); \
+ v[n].key != 0; \
+ n = (n == 0 ? size - 1 : n - 1)) \
+ if (strcmp(v[n].key, key) == 0) { \
+ *keyptr = v[n].key; \
+ return v[n].val; \
+ } \
+ return 0; \
+} \
+ \
+PTABLE_ITERATOR(T)::PTABLE_ITERATOR(T)(PTABLE(T) *t) \
+: p(t), i(0) \
+{ \
+} \
+ \
+int PTABLE_ITERATOR(T)::next(const char **keyp, T **valp) \
+{ \
+ unsigned size = p->size; \
+ PASSOC(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: