Re: linkedhash-list vs. hash

Eric Blake <[email protected]>
Newsgroups gmane.comp.gnu.m4.patches,gmane.comp.gnu.m4.general
Message-ID <[email protected]>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

According to Bruno Haible on 7/23/2008 5:40 AM:
| Eric Blake wrote:
|> both linkedhash-list and hash appear to fit the bill
|
| The main difference between linkedhash-list and hash is that the former
| implements a list in its own right, i.e. iterating over the elements always
| returns them in the defined order. Whereas in 'hash', you are effectively
| iterating over a set, not a list; i.e. the elements come out in random
order.

m4 doesn't care about the order of the iteration.  So I went with the hash
module.

|
|> Does it really matter whether the set size is prime vs. 2^n-1 in how
|> likely a modulo operation in the hash is to cause collisions?
|
| I would not compromise on this - a modulo operation is fast on all hardware
| since 1995, and the time to search for the next prime around n is
| O(sqrt(n)*log(n)) - much less than the O(n) that you need for resizing the
| table.

Reasonable enough; so the dynamic calculation of a prime is not a strike
against the hash module.

|
| Also, when your hash table size is 2^n-1, you are calling malloc(8*(2^n-1)),
| which - in some malloc implementations - may end up allocating twice as much
| memory, because sizeof(malloc_header_size) + 8*(2^n-1) is just slightly
larger
| than a power of two.

Good point.

|
|> Another difference: linkedlist-hash is hard-coded on its growth
|> parameters, while hash allows the user to specify both fullness threshold
|> and growth factors
|
| Whether you need this or not, depends on the ratio between the number of
| lookups in the table vs. the number of insertions in the table. If this
| ratio is low, you profit from a larger growth factor (maybe around 1.5
or 2.0);
| if it is high, a smaller growth factor (around 1.2 or 1.4) is likely better.
| Anyway, I would first do some profiling to see whether it matters at all.

For 'autoconf -f' on coreutils: pre-patch noticed 2246308 lookups, 209873
collisions, using 509 buckets and 0 resizes.  Overall time was 22.296 seconds.

post-patch, there were still 2246308 lookups, but only 20784 collisions
(an order of magnitude better).  Also, this counted 2326034 hash
callbacks, 3433800 compare callbacks, using 3659 buckets and 5 resizes
from the default size (when m4 -H is not specified, the default is 509,
but since I used NULL for the tuning parameters, the hash module
pre-scales that to 641 buckets to anticipate 80% capacity at 509 entries).
~ Overall time was 22.169 seconds (barely noticeable).

At any rate, here's the series of patches I've pushed to m4 to switch from
hand-rolled to the gnulib hash module.

- --
Don't work too hard, make some time for fun as well!

Eric Blake             [email protected]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkiLg9AACgkQ84KuGfSFAYC3+gCcCOurXGasT0LrzQ21G5AYbYvR
k7wAoNbgzj2ptKMrBFwZk2Tr5kzBa4LK
=KE1d
-----END PGP SIGNATURE-----

_______________________________________________
M4-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/m4-patches
m4.patch364 (text/plain, 36.8 KB)
From 0735b27919f180fcbc03e90681c0c849f40a63c5 Mon Sep 17 00:00:00 2001
From: Eric Blake <[email protected]>
Date: Tue, 22 Jul 2008 16:58:26 -0600
Subject: [PATCH] Backport faster hash lookups of pushdef stack collisions.

* src/m4.h (struct symbol): Replace shadowed bit by stack field.
(SYMBOL_SHADOWED): Delete.
* src/symtab.c (lookup_symbol): Manage pushdef stacks
independently from hash buckets, so that collisions with large
pushdef stacks can be quickly skipped over.
(symtab_print_list): Adjust caller.
* src/builtin.c (dump_symbol): Likewise.
* src/freeze.c (reverse_symbol_list, produce_frozen_state):
Likewise.
* doc/m4.texinfo (Using frozen files): Test the change.

Signed-off-by: Eric Blake <[email protected]>
---
 ChangeLog      |   12 ++++++++
 doc/m4.texinfo |   16 +++++++++++
 src/builtin.c  |    2 +-
 src/freeze.c   |   25 +++++++++--------
 src/m4.h       |    5 +--
 src/symtab.c   |   77 ++++++++++++++++++++++++++++++++++---------------------
 6 files changed, 91 insertions(+), 46 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index c393ee2..dda5fc0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2008-07-22  Eric Blake  <[email protected]>
 
+	Backport faster hash lookups of pushdef stack collisions.
+	* src/m4.h (struct symbol): Replace shadowed bit by stack field.
+	(SYMBOL_SHADOWED): Delete.
+	* src/symtab.c (lookup_symbol): Manage pushdef stacks
+	independently from hash buckets, so that collisions with large
+	pushdef stacks can be quickly skipped over.
+	(symtab_print_list): Adjust caller.
+	* src/builtin.c (dump_symbol): Likewise.
+	* src/freeze.c (reverse_symbol_list, produce_frozen_state):
+	Likewise.
+	* doc/m4.texinfo (Using frozen files): Test the change.
+
 	Mention patch to make earlier autoconf happy.
 	* NEWS: Give URL to autoconf patch to avoid undefined popdef.
 
diff --git a/doc/m4.texinfo b/doc/m4.texinfo
index 79ea41d..e78061c 100644
--- a/doc/m4.texinfo
+++ b/doc/m4.texinfo
@@ -7084,6 +7084,22 @@ traceon(`undefined')dnl
 
 @c Make sure freezing is successful.
 
+@example
+ifdef(`__unix__', ,
+      `errprint(` skipping: syscmd does not have unix semantics
+')m4exit(`77')')dnl
+changequote(`[', `]')dnl
+syscmd([echo 'changequote([,])pushdef([divnum],[hi])dnl' \
+       | ]__program__[ -F in.m4f \
+     && echo 'divnum popdef([divnum])divnum' \
+       | ]__program__[ -R in.m4f \
+     && rm in.m4f])status sysval
+@result{}hi 0
+@result{}status 0
+@end example
+
+@c Detect inability to freeze.
+
 @comment options: -F /none/such
 @comment status: 1
 @example
diff --git a/src/builtin.c b/src/builtin.c
index 0ea814a..f8a3f3c 100644
--- a/src/builtin.c
+++ b/src/builtin.c
@@ -826,7 +826,7 @@ static void
 dump_symbol (symbol *sym, void *arg)
 {
   struct dump_symbol_data *data = (struct dump_symbol_data *) arg;
-  if (!SYMBOL_SHADOWED (sym) && SYMBOL_TYPE (sym) != TOKEN_VOID)
+  if (SYMBOL_TYPE (sym) != TOKEN_VOID)
     {
       obstack_blank (data->obs, sizeof (symbol *));
       data->base = (symbol **) obstack_base (data->obs);
diff --git a/src/freeze.c b/src/freeze.c
index 5e35c81..4d0eeb4 100644
--- a/src/freeze.c
+++ b/src/freeze.c
@@ -36,8 +36,8 @@ reverse_symbol_list (symbol *sym)
   result = NULL;
   while (sym)
     {
-      next = SYMBOL_NEXT (sym);
-      SYMBOL_NEXT (sym) = result;
+      next = sym->stack;
+      sym->stack = result;
       result = sym;
       sym = next;
     }
@@ -53,6 +53,7 @@ produce_frozen_state (const char *name)
 {
   FILE *file;
   int h;
+  symbol *stack;
   symbol *sym;
   const builtin *bp;
 
@@ -95,15 +96,16 @@ produce_frozen_state (const char *name)
 
   /* Dump all symbols.  */
 
-  for (h = 0; h < hash_table_size; h++)
+  for (stack = symtab[h = 0]; h < hash_table_size;
+       stack = (stack ? SYMBOL_NEXT (stack) : symtab[h++]))
     {
-
-      /* Process all entries in one bucket, from the last to the first.
+      if (!stack)
+	continue;
+      /* Process all entries in each stack from the last to the first.
 	 This order ensures that, at reload time, pushdef's will be
 	 executed with the oldest definitions first.  */
-
-      symtab[h] = reverse_symbol_list (symtab[h]);
-      for (sym = symtab[h]; sym; sym = SYMBOL_NEXT (sym))
+      sym = stack = reverse_symbol_list (stack);
+      while (sym)
 	{
 	  switch (SYMBOL_TYPE (sym))
 	    {
@@ -140,11 +142,10 @@ produce_frozen_state (const char *name)
 	      abort ();
 	      break;
 	    }
+	  sym = sym->stack;
 	}
-
-      /* Reverse the bucket once more, putting it back as it was.  */
-
-      symtab[h] = reverse_symbol_list (symtab[h]);
+      /* Reverse the stack once more, putting it back as it was.  */
+      stack = reverse_symbol_list (stack);
     }
 
   /* Let diversions be issued from output.c module, its cleaner to have this
diff --git a/src/m4.h b/src/m4.h
index 3afe476..dcb4613 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -417,9 +417,9 @@ enum symbol_lookup
 /* Symbol table entry.  */
 struct symbol
 {
-  struct symbol *next;
+  struct symbol *next; /* Next symbol with the same hash.  */
+  struct symbol *stack; /* Stack of shadowed symbols of the same name.  */
   bool_bitfield traced : 1;
-  bool_bitfield shadowed : 1;
   bool_bitfield macro_args : 1;
   bool_bitfield blind_no_args : 1;
   bool_bitfield deleted : 1;
@@ -432,7 +432,6 @@ struct symbol
 
 #define SYMBOL_NEXT(S)		((S)->next)
 #define SYMBOL_TRACED(S)	((S)->traced)
-#define SYMBOL_SHADOWED(S)	((S)->shadowed)
 #define SYMBOL_MACRO_ARGS(S)	((S)->macro_args)
 #define SYMBOL_BLIND_NO_ARGS(S)	((S)->blind_no_args)
 #define SYMBOL_DELETED(S)	((S)->deleted)
diff --git a/src/symtab.c b/src/symtab.c
index 7e76cfc..381d025 100644
--- a/src/symtab.c
+++ b/src/symtab.c
@@ -215,7 +215,6 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 	      SYMBOL_TRACED (sym) = SYMBOL_TRACED (old);
 	      SYMBOL_NAME (sym) = xmemdup0 (name, len);
 	      SYMBOL_NAME_LEN (sym) = len;
-	      SYMBOL_SHADOWED (sym) = false;
 	      SYMBOL_MACRO_ARGS (sym) = false;
 	      SYMBOL_BLIND_NO_ARGS (sym) = false;
 	      SYMBOL_DELETED (sym) = false;
@@ -223,7 +222,9 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 
 	      SYMBOL_NEXT (sym) = SYMBOL_NEXT (old);
 	      SYMBOL_NEXT (old) = NULL;
-	      (*spp) = sym;
+	      sym->stack = old->stack;
+	      old->stack = NULL;
+	      *spp = sym;
 	    }
 	  return sym;
 	}
@@ -240,19 +241,21 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
       SYMBOL_TRACED (sym) = false;
       SYMBOL_NAME (sym) = xmemdup0 (name, len);
       SYMBOL_NAME_LEN (sym) = len;
-      SYMBOL_SHADOWED (sym) = false;
       SYMBOL_MACRO_ARGS (sym) = false;
       SYMBOL_BLIND_NO_ARGS (sym) = false;
       SYMBOL_DELETED (sym) = false;
       SYMBOL_PENDING_EXPANSIONS (sym) = 0;
 
       SYMBOL_NEXT (sym) = *spp;
-      (*spp) = sym;
+      sym->stack = NULL;
+      *spp = sym;
 
       if (mode == SYMBOL_PUSHDEF && cmp == 0)
 	{
-	  SYMBOL_SHADOWED (SYMBOL_NEXT (sym)) = true;
-	  SYMBOL_TRACED (sym) = SYMBOL_TRACED (SYMBOL_NEXT (sym));
+	  sym->stack = sym->next;
+	  sym->next = sym->stack->next;
+	  sym->stack->next = NULL;
+	  SYMBOL_TRACED (sym) = SYMBOL_TRACED (sym->stack);
 	}
       return sym;
 
@@ -270,24 +273,30 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 	return NULL;
       {
 	bool traced = false;
-        symbol *result = sym;
-	if (SYMBOL_NEXT (sym) != NULL
-	    && SYMBOL_SHADOWED (SYMBOL_NEXT (sym))
-	    && mode == SYMBOL_POPDEF)
+	symbol *result = sym;
+	if (sym->stack && mode == SYMBOL_POPDEF)
 	  {
-	    SYMBOL_SHADOWED (SYMBOL_NEXT (sym)) = false;
-	    SYMBOL_TRACED (SYMBOL_NEXT (sym)) = SYMBOL_TRACED (sym);
+	    SYMBOL_TRACED (sym->stack) = SYMBOL_TRACED (sym);
+	    sym->stack->next = sym->next;
+	    *spp = sym->stack;
+	    sym->next = NULL;
+	    sym->stack = NULL;
+	    free_symbol (sym);
 	  }
 	else
-	  traced = SYMBOL_TRACED (sym);
-	do
 	  {
-	    *spp = SYMBOL_NEXT (sym);
-	    free_symbol (sym);
-	    sym = *spp;
+	    traced = SYMBOL_TRACED (sym);
+	    *spp = sym->next;
+	    do
+	      {
+		symbol *old = sym;
+		sym = sym->stack;
+		old->next = NULL;
+		old->stack = NULL;
+		free_symbol (old);
+	      }
+	    while (sym);
 	  }
-	while (*spp != NULL && SYMBOL_SHADOWED (*spp)
-	       && mode == SYMBOL_DELETE);
 	if (traced)
 	  {
 	    sym = (symbol *) xmalloc (sizeof (symbol));
@@ -295,16 +304,16 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 	    SYMBOL_TRACED (sym) = true;
 	    SYMBOL_NAME (sym) = xmemdup0 (name, len);
 	    SYMBOL_NAME_LEN (sym) = len;
-	    SYMBOL_SHADOWED (sym) = false;
 	    SYMBOL_MACRO_ARGS (sym) = false;
 	    SYMBOL_BLIND_NO_ARGS (sym) = false;
 	    SYMBOL_DELETED (sym) = false;
 	    SYMBOL_PENDING_EXPANSIONS (sym) = 0;
 
 	    SYMBOL_NEXT (sym) = *spp;
-	    (*spp) = sym;
+	    sym->stack = NULL;
+	    *spp = sym;
 	  }
-        return result;
+	return result;
       }
 
     default:
@@ -389,19 +398,27 @@ static void
 symtab_print_list (int i)
 {
   symbol *sym;
+  symbol *stack;
   size_t h;
 
   xprintf ("Symbol dump #%d:\n", i);
   for (h = 0; h < hash_table_size; h++)
     for (sym = symtab[h]; sym != NULL; sym = sym->next)
-      xprintf ("\tname %s, len %zu, bucket %lu, addr %p, next %p, "
-	       "flags%s%s%s, pending %d\n",
-	       SYMBOL_NAME (sym), SYMBOL_NAME_LEN (sym),
-	       (unsigned long int) h, sym, SYMBOL_NEXT (sym),
-	       SYMBOL_TRACED (sym) ? " traced" : "",
-	       SYMBOL_SHADOWED (sym) ? " shadowed" : "",
-	       SYMBOL_DELETED (sym) ? " deleted" : "",
-	       SYMBOL_PENDING_EXPANSIONS (sym));
+      {
+	stack = sym;
+	do
+	  {
+	    xprintf ("\tname %s, len %zu, bucket %lu, addr %p, next %p, "
+		     "stack %p, flags%s%s, pending %d\n",
+		     SYMBOL_NAME (stack), SYMBOL_NAME_LEN (stack),
+		     (unsigned long int) h, stack, SYMBOL_NEXT (stack),
+		     stack->stack, SYMBOL_TRACED (stack) ? " traced" : "",
+		     SYMBOL_DELETED (stack) ? " deleted" : "",
+		     SYMBOL_PENDING_EXPANSIONS (stack));
+	    stack = stack->stack;
+	  }
+	while (stack);
+      }
 }
 
 #endif /* DEBUG_SYM */
-- 
1.5.6.4


From 38a274a6e3c1e13e2d9d1c803d64b1a5414ff603 Mon Sep 17 00:00:00 2001
From: Eric Blake <[email protected]>
Date: Tue, 22 Jul 2008 17:18:10 -0600
Subject: [PATCH] Make symbol table opaque.

* src/m4.h (symtab, hash_table_size): No longer export.
(symtab_init): Add parameter.
* src/symtab.c (symtab): Make static, so we can later change
implementations.
(hash_table_size): New private size variable.
(symtab_init): Take size hint, rather than reading a global.
* src/m4.c (hash_table_size): Delete.
(main): Track -H as local variable instead.
* src/freeze.c (dump_symbol_CB): New function, extracted from...
(produce_frozen_state): ...here, to use hack_all_symbols rather
than raw traversal.

Signed-off-by: Eric Blake <[email protected]>
---
 ChangeLog    |   13 +++++++
 src/freeze.c |  115 ++++++++++++++++++++++++++++++----------------------------
 src/m4.c     |    7 ++--
 src/m4.h     |    7 +---
 src/symtab.c |   17 ++++-----
 5 files changed, 86 insertions(+), 73 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index dda5fc0..ed2acce 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2008-07-22  Eric Blake  <[email protected]>
 
+	Make symbol table opaque.
+	* src/m4.h (symtab, hash_table_size): No longer export.
+	(symtab_init): Add parameter.
+	* src/symtab.c (symtab): Make static, so we can later change
+	implementations.
+	(hash_table_size): New private size variable.
+	(symtab_init): Take size hint, rather than reading a global.
+	* src/m4.c (hash_table_size): Delete.
+	(main): Track -H as local variable instead.
+	* src/freeze.c (dump_symbol_CB): New function, extracted from...
+	(produce_frozen_state): ...here, to use hack_all_symbols rather
+	than raw traversal.
+
 	Backport faster hash lookups of pushdef stack collisions.
 	* src/m4.h (struct symbol): Replace shadowed bit by stack field.
 	(SYMBOL_SHADOWED): Delete.
diff --git a/src/freeze.c b/src/freeze.c
index 4d0eeb4..3a48b03 100644
--- a/src/freeze.c
+++ b/src/freeze.c
@@ -44,6 +44,65 @@ reverse_symbol_list (symbol *sym)
   return result;
 }
 
+/*-------------------------------------------------------------------.
+| Dump a stack of pushdef references to the stream F.  Designed as a |
+| callback for hack_all_symbols.                                     |
+`-------------------------------------------------------------------*/
+
+static void
+dump_symbol_CB (symbol *sym, void *f)
+{
+  FILE *file = (FILE *) f;
+  symbol *stack;
+  const builtin *bp;
+
+  /* Process all entries in each stack from the last to the first.
+     This order ensures that, at reload time, pushdef's will be
+     executed with the oldest definitions first.  */
+  sym = stack = reverse_symbol_list (sym);
+  while (sym)
+    {
+      switch (SYMBOL_TYPE (sym))
+	{
+	case TOKEN_TEXT:
+	  xfprintf (file, "T%d,%d\n",
+		    (int) SYMBOL_NAME_LEN (sym),
+		    (int) strlen (SYMBOL_TEXT (sym)));
+	  fwrite (SYMBOL_NAME (sym), 1, SYMBOL_NAME_LEN (sym), file);
+	  fputs (SYMBOL_TEXT (sym), file);
+	  fputc ('\n', file);
+	  break;
+
+	case TOKEN_FUNC:
+	  bp = find_builtin_by_addr (SYMBOL_FUNC (sym));
+	  if (bp == NULL)
+	    {
+	      assert (!"dump_symbol_CB");
+	      abort ();
+	    }
+	  xfprintf (file, "F%d,%d\n",
+		    (int) SYMBOL_NAME_LEN (sym),
+		    (int) strlen (bp->name));
+	  fwrite (SYMBOL_NAME (sym), 1, SYMBOL_NAME_LEN (sym), file);
+	  fputs (bp->name, file);
+	  fputc ('\n', file);
+	  break;
+
+	case TOKEN_VOID:
+	  /* Ignore placeholder tokens that exist due to traceon.  */
+	  break;
+
+	default:
+	  assert (!"dump_symbol_CB");
+	  abort ();
+	  break;
+	}
+      sym = sym->stack;
+    }
+  /* Reverse the stack once more, putting it back as it was.  */
+  reverse_symbol_list (stack);
+}
+
 /*------------------------------------------------.
 | Produce a frozen state to the given file NAME.  |
 `------------------------------------------------*/
@@ -52,10 +111,6 @@ void
 produce_frozen_state (const char *name)
 {
   FILE *file;
-  int h;
-  symbol *stack;
-  symbol *sym;
-  const builtin *bp;
 
   file = fopen (name, O_BINARY ? "wb" : "w");
   if (!file)
@@ -96,57 +151,7 @@ produce_frozen_state (const char *name)
 
   /* Dump all symbols.  */
 
-  for (stack = symtab[h = 0]; h < hash_table_size;
-       stack = (stack ? SYMBOL_NEXT (stack) : symtab[h++]))
-    {
-      if (!stack)
-	continue;
-      /* Process all entries in each stack from the last to the first.
-	 This order ensures that, at reload time, pushdef's will be
-	 executed with the oldest definitions first.  */
-      sym = stack = reverse_symbol_list (stack);
-      while (sym)
-	{
-	  switch (SYMBOL_TYPE (sym))
-	    {
-	    case TOKEN_TEXT:
-	      xfprintf (file, "T%d,%d\n",
-			(int) SYMBOL_NAME_LEN (sym),
-			(int) strlen (SYMBOL_TEXT (sym)));
-	      fwrite (SYMBOL_NAME (sym), 1, SYMBOL_NAME_LEN (sym), file);
-	      fputs (SYMBOL_TEXT (sym), file);
-	      fputc ('\n', file);
-	      break;
-
-	    case TOKEN_FUNC:
-	      bp = find_builtin_by_addr (SYMBOL_FUNC (sym));
-	      if (bp == NULL)
-		{
-		  assert (!"produce_frozen_state");
-		  abort ();
-		}
-	      xfprintf (file, "F%d,%d\n",
-			(int) SYMBOL_NAME_LEN (sym),
-			(int) strlen (bp->name));
-	      fwrite (SYMBOL_NAME (sym), 1, SYMBOL_NAME_LEN (sym), file);
-	      fputs (bp->name, file);
-	      fputc ('\n', file);
-	      break;
-
-	    case TOKEN_VOID:
-	      /* Ignore placeholder tokens that exist due to traceon.  */
-	      break;
-
-	    default:
-	      assert (!"produce_frozen_state");
-	      abort ();
-	      break;
-	    }
-	  sym = sym->stack;
-	}
-      /* Reverse the stack once more, putting it back as it was.  */
-      stack = reverse_symbol_list (stack);
-    }
+  hack_all_symbols (dump_symbol_CB, file);
 
   /* Let diversions be issued from output.c module, its cleaner to have this
      piece of code there.  */
diff --git a/src/m4.c b/src/m4.c
index c73e275..7f0af7e 100644
--- a/src/m4.c
+++ b/src/m4.c
@@ -38,9 +38,6 @@ int sync_output = 0;
 /* Debug (-d[flags]).  */
 int debug_level = 0;
 
-/* Hash table size (should be a prime) (-Hsize).  */
-size_t hash_table_size = HASHMAX;
-
 /* Disable GNU extensions (-G).  */
 int no_gnu_extensions = 0;
 
@@ -412,6 +409,8 @@ main (int argc, char *const *argv, char *const *envp)
   const char *frozen_file_to_read = NULL;
   const char *frozen_file_to_write = NULL;
   const char *macro_sequence = "";
+  /* Hash table size (should be a prime) (-Hsize).  */
+  size_t hash_table_size = HASHMAX;
 
   set_program_name (argv[0]);
   retcode = EXIT_SUCCESS;
@@ -592,7 +591,7 @@ main (int argc, char *const *argv, char *const *envp)
 
   input_init ();
   output_init ();
-  symtab_init ();
+  symtab_init (hash_table_size);
   set_macro_sequence (macro_sequence);
   include_env_init ();
 
diff --git a/src/m4.h b/src/m4.h
index dcb4613..011439e 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -127,7 +127,6 @@ typedef unsigned int bool_bitfield;
 /* Option flags.  */
 extern int sync_output;			/* -s */
 extern int debug_level;			/* -d */
-extern size_t hash_table_size;		/* -H */
 extern int no_gnu_extensions;		/* -G */
 extern int prefix_all_builtins;		/* -P */
 extern size_t max_debug_argument_length;/* -l */
@@ -448,10 +447,8 @@ typedef void hack_symbol (symbol *, void *);
 
 #define HASHMAX 509		/* default, overridden by -Hsize */
 
-extern symbol **symtab;
-
-void free_symbol (symbol *sym);
-void symtab_init (void);
+void free_symbol (symbol *);
+void symtab_init (size_t);
 symbol *lookup_symbol (const char *, size_t, symbol_lookup);
 void hack_all_symbols (hack_symbol *, void *);
 
diff --git a/src/symtab.c b/src/symtab.c
index 381d025..7420e50 100644
--- a/src/symtab.c
+++ b/src/symtab.c
@@ -93,18 +93,17 @@ profile_memcmp (const char *s1, const char *s2, size_t l)
 `----------------------------------------------------------------------*/
 
 /* Pointer to symbol table.  */
-symbol **symtab;
+static symbol **symtab;
+
+/* Number of buckets in symbol table.  */
+static size_t hash_table_size;
 
 void
-symtab_init (void)
+symtab_init (size_t size)
 {
-  size_t i;
-  symbol **s;
-
-  s = symtab = (symbol **) xnmalloc (hash_table_size, sizeof (symbol *));
-
-  for (i = 0; i < hash_table_size; i++)
-    s[i] = NULL;
+  hash_table_size = size;
+  symtab = (symbol **) xnmalloc (hash_table_size, sizeof *symtab);
+  memset (symtab, 0, hash_table_size * sizeof *symtab);
 
 #ifdef DEBUG_SYM
   atexit (show_profile); /* Ignore failure, since this is debug code.  */
-- 
1.5.6.4


From ffaa885fc0efb39bf0ca0df0a592a1c39f92729c Mon Sep 17 00:00:00 2001
From: Eric Blake <[email protected]>
Date: Sat, 26 Jul 2008 13:27:02 -0600
Subject: [PATCH] Use hash module for self-growing symbol table.

* m4/gnulib-cache.m4: Import hash module.
* src/m4.h (struct symbol): Remove next member, change stack to be
circular.
(SYMBOL_NEXT): Delete.
(symtab_free): New prototype.
* src/symtab.c (show_profile) [DEBUG_SYM]: Track more hash
statistics, and dump to /dev/tty rather than stderr.
(symtab): Change type.
(hash_table_size): Delete.
(symtab_hasher, symtab_comparator, symtab_free_entry): New
functions.
(symtab_init, lookup_symbol, hack_all_symbols): Rewrite to wrap
external hash table.
(symtab_free): New function.
(symtab_debug) [DEBUG_SYM]: Adjust client.
* src/m4.c (main): Call symbol table cleanup.
* src/freeze.c (dump_symbol_CB, reverse_symbol_list): Deal with
fact that pushdef stack is now circular.

Signed-off-by: Eric Blake <[email protected]>
---
 ChangeLog          |   22 ++++
 m4/gnulib-cache.m4 |    3 +-
 src/freeze.c       |   25 ++++--
 src/m4.c           |    5 +
 src/m4.h           |    5 +-
 src/symtab.c       |  271 +++++++++++++++++++++++++++++++--------------------
 6 files changed, 213 insertions(+), 118 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index ed2acce..de44226 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2008-07-26  Eric Blake  <[email protected]>
+
+	Use hash module for self-growing symbol table.
+	* m4/gnulib-cache.m4: Import hash module.
+	* src/m4.h (struct symbol): Remove next member, change stack to be
+	circular.
+	(SYMBOL_NEXT): Delete.
+	(symtab_free): New prototype.
+	* src/symtab.c (show_profile) [DEBUG_SYM]: Track more hash
+	statistics, and dump to /dev/tty rather than stderr.
+	(symtab): Change type.
+	(hash_table_size): Delete.
+	(symtab_hasher, symtab_comparator, symtab_free_entry): New
+	functions.
+	(symtab_init, lookup_symbol, hack_all_symbols): Rewrite to wrap
+	external hash table.
+	(symtab_free): New function.
+	(symtab_debug) [DEBUG_SYM]: Adjust client.
+	* src/m4.c (main): Call symbol table cleanup.
+	* src/freeze.c (dump_symbol_CB, reverse_symbol_list): Deal with
+	fact that pushdef stack is now circular.
+
 2008-07-22  Eric Blake  <[email protected]>
 
 	Make symbol table opaque.
diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4
index f716908..a512830 100644
--- a/m4/gnulib-cache.m4
+++ b/m4/gnulib-cache.m4
@@ -15,7 +15,7 @@
 
 
 # Specification in the form of a command-line invocation:
-#   gnulib-tool --import --dir=. --local-dir=local --lib=libm4 --source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=build-aux --with-tests --no-libtool --macro-prefix=M4 announce-gen assert autobuild avltree-oset binary-io clean-temp cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer fseeko gendocs getopt git-version-gen gnumakefile gnupload gpl-3.0 intprops memmem mkstemp obstack obstack-printf-posix progname quote regex stdbool stdint stdlib-safer strtod strtol unlocked-io vasnprintf-posix verror version-etc version-etc-fsf xalloc xmemdup0 xprintf xvasprintf-posix
+#   gnulib-tool --import --dir=. --local-dir=local --lib=libm4 --source-base=lib --m4-base=m4 --doc-base=doc --aux-dir=build-aux --with-tests --no-libtool --macro-prefix=M4 announce-gen assert autobuild avltree-oset binary-io clean-temp cloexec close-stream closein config-h error fdl fflush flexmember fopen-safer fseeko gendocs getopt git-version-gen gnumakefile gnupload gpl-3.0 hash intprops memmem mkstemp obstack obstack-printf-posix progname quote regex stdbool stdint stdlib-safer strtod strtol unlocked-io vasnprintf-posix verror version-etc version-etc-fsf xalloc xmemdup0 xprintf xvasprintf-posix
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
 gl_LOCAL_DIR([local])
@@ -42,6 +42,7 @@ gl_MODULES([
   gnumakefile
   gnupload
   gpl-3.0
+  hash
   intprops
   memmem
   mkstemp
diff --git a/src/freeze.c b/src/freeze.c
index 3a48b03..2a7d9dc 100644
--- a/src/freeze.c
+++ b/src/freeze.c
@@ -30,17 +30,24 @@
 static symbol *
 reverse_symbol_list (symbol *sym)
 {
-  symbol *result;
+  symbol *first = sym;
   symbol *next;
+  symbol *prev = sym;
+  symbol *result;
 
-  result = NULL;
-  while (sym)
+  assert (sym);
+  if (sym->stack == sym)
+    return sym;
+  next = sym->stack;
+  do
     {
-      next = sym->stack;
-      sym->stack = result;
-      result = sym;
+      result = prev;
       sym = next;
+      next = sym->stack;
+      sym->stack = prev;
+      prev = sym;
     }
+  while (prev != first);
   return result;
 }
 
@@ -59,8 +66,9 @@ dump_symbol_CB (symbol *sym, void *f)
   /* Process all entries in each stack from the last to the first.
      This order ensures that, at reload time, pushdef's will be
      executed with the oldest definitions first.  */
-  sym = stack = reverse_symbol_list (sym);
-  while (sym)
+  stack = sym;
+  sym = reverse_symbol_list (sym);
+  do
     {
       switch (SYMBOL_TYPE (sym))
 	{
@@ -99,6 +107,7 @@ dump_symbol_CB (symbol *sym, void *f)
 	}
       sym = sym->stack;
     }
+  while (sym != stack->stack);
   /* Reverse the stack once more, putting it back as it was.  */
   reverse_symbol_list (stack);
 }
diff --git a/src/m4.c b/src/m4.c
index 7f0af7e..551d80c 100644
--- a/src/m4.c
+++ b/src/m4.c
@@ -683,8 +683,13 @@ main (int argc, char *const *argv, char *const *envp)
       undivert_all ();
     }
   output_exit ();
+#ifndef NDEBUG
+  /* Only spend time freeing memory to help isolate leaks; if
+     assertions are disabled, save the time and exit now.  */
   free_regex ();
   quotearg_free ();
+  symtab_free ();
+#endif /* NDEBUG */
 #ifdef DEBUG_REGEX
   if (trace_file)
     fclose (trace_file);
diff --git a/src/m4.h b/src/m4.h
index 011439e..ff0377a 100644
--- a/src/m4.h
+++ b/src/m4.h
@@ -416,8 +416,7 @@ enum symbol_lookup
 /* Symbol table entry.  */
 struct symbol
 {
-  struct symbol *next; /* Next symbol with the same hash.  */
-  struct symbol *stack; /* Stack of shadowed symbols of the same name.  */
+  struct symbol *stack; /* Circular list for pushdef stack of symbol.  */
   bool_bitfield traced : 1;
   bool_bitfield macro_args : 1;
   bool_bitfield blind_no_args : 1;
@@ -429,7 +428,6 @@ struct symbol
   token_data data;  /* Type should be only TOKEN_TEXT or TOKEN_FUNC.  */
 };
 
-#define SYMBOL_NEXT(S)		((S)->next)
 #define SYMBOL_TRACED(S)	((S)->traced)
 #define SYMBOL_MACRO_ARGS(S)	((S)->macro_args)
 #define SYMBOL_BLIND_NO_ARGS(S)	((S)->blind_no_args)
@@ -449,6 +447,7 @@ typedef void hack_symbol (symbol *, void *);
 
 void free_symbol (symbol *);
 void symtab_init (size_t);
+void symtab_free (void);
 symbol *lookup_symbol (const char *, size_t, symbol_lookup);
 void hack_all_symbols (hack_symbol *, void *);
 
diff --git a/src/symtab.c b/src/symtab.c
index 7420e50..a9160c8 100644
--- a/src/symtab.c
+++ b/src/symtab.c
@@ -33,6 +33,8 @@
 
 #include "m4.h"
 
+#include "hash.h"
+
 #ifdef DEBUG_SYM
 /* When evaluating hash table performance, this profiling code shows
    how many collisions were encountered.  */
@@ -47,19 +49,28 @@ struct profile
 
 static struct profile profiles[5];
 static symbol_lookup current_mode;
+static unsigned long long hash_entry;
+static unsigned long long comparator_entry;
+static size_t current_size;
+static unsigned int resizes;
 
 /* On exit, show a profile of symbol table performance.  */
 static void
 show_profile (void)
 {
   int i;
+  FILE *f = fopen ("/dev/tty", "w");
   for (i = 0; i < 5; i++)
     {
-      xfprintf(stderr, "m4: lookup mode %d called %d times, %d compares, "
+      xfprintf(f, "m4: lookup mode %d called %d times, %d compares, "
 	       "%d misses, %lld bytes\n",
 	       i, profiles[i].entry, profiles[i].comparisons,
 	       profiles[i].misses, profiles[i].bytes);
     }
+  xfprintf(f, "m4: %llu hash callbacks, %llu compare callbacks, "
+	   "%zu buckets, %u resizes\n",
+	   hash_entry, comparator_entry, current_size, resizes - 1);
+  fclose (f);
 }
 
 /* Like memcmp (S1, S2, L), but also track profiling statistics.  */
@@ -87,33 +98,12 @@ profile_memcmp (const char *s1, const char *s2, size_t l)
 #endif /* DEBUG_SYM */
 
 
-/*----------------------------------------------------------------------.
-| Initialise the symbol table, by allocating the necessary storage, and |
-| zeroing all the entries.					        |
-`----------------------------------------------------------------------*/
-
 /* Pointer to symbol table.  */
-static symbol **symtab;
-
-/* Number of buckets in symbol table.  */
-static size_t hash_table_size;
-
-void
-symtab_init (size_t size)
-{
-  hash_table_size = size;
-  symtab = (symbol **) xnmalloc (hash_table_size, sizeof *symtab);
-  memset (symtab, 0, hash_table_size * sizeof *symtab);
-
-#ifdef DEBUG_SYM
-  atexit (show_profile); /* Ignore failure, since this is debug code.  */
-#endif /* DEBUG_SYM */
-}
+static Hash_table *symtab;
 
 /*--------------------------------------------------.
 | Return a hashvalue for a string S of length LEN.  |
 `--------------------------------------------------*/
-
 static size_t
 hash (const char *s, size_t len)
 {
@@ -126,6 +116,82 @@ hash (const char *s, size_t len)
   return val;
 }
 
+/*----------------------------------------------------.
+| Wrap our hash inside signature expected by hash.h.  |
+`----------------------------------------------------*/
+static size_t
+symtab_hasher (const void *entry, size_t buckets)
+{
+#ifdef DEBUG_SYM
+  hash_entry++;
+  if (buckets != current_size)
+    {
+      resizes++;
+      current_size = buckets;
+    }
+#endif /* DEBUG_SYM */
+  const symbol *sym = (const symbol *) entry;
+  return hash (SYMBOL_NAME (sym), SYMBOL_NAME_LEN (sym)) % buckets;
+}
+
+/*----------------------------------------------.
+| Compare two hash table entries for equality.  |
+`----------------------------------------------*/
+static bool
+symtab_comparator (const void *entry_a, const void *entry_b)
+{
+#ifdef DEBUG_SYM
+  comparator_entry++;
+#endif /* DEBUG_SYM */
+  const symbol *sym_a = (const symbol *) entry_a;
+  const symbol *sym_b = (const symbol *) entry_b;
+  return (SYMBOL_NAME_LEN (sym_a) == SYMBOL_NAME_LEN (sym_b)
+	  && memcmp (SYMBOL_NAME (sym_a), SYMBOL_NAME (sym_b),
+		     SYMBOL_NAME_LEN (sym_a)) == 0);
+}
+
+/*---------------------------.
+| Reclaim an entry on exit.  |
+`---------------------------*/
+static void
+symtab_free_entry (void *entry)
+{
+  symbol *sym = (symbol *) entry;
+  while (sym->stack != sym)
+    {
+      symbol *old = sym->stack;
+      sym->stack = old->stack;
+      assert (!SYMBOL_PENDING_EXPANSIONS (old));
+      free_symbol (old);
+    }
+  assert (!SYMBOL_PENDING_EXPANSIONS (sym));
+  free_symbol (sym);
+}
+
+/*--------------------------------------------------------------.
+| Initialize the symbol table, with SIZE as a hint for expected |
+| number of entries.					        |
+`--------------------------------------------------------------*/
+void
+symtab_init (size_t size)
+{
+  symtab = hash_initialize (size, NULL, symtab_hasher, symtab_comparator,
+			    symtab_free_entry);
+
+#ifdef DEBUG_SYM
+  atexit (show_profile); /* Ignore failure, since this is debug code.  */
+#endif /* DEBUG_SYM */
+}
+
+/*------------------------.
+| Clean up entire table.  |
+`------------------------*/
+void
+symtab_free (void)
+{
+  hash_free (symtab);
+}
+
 /*--------------------------------------------.
 | Free all storage associated with a symbol.  |
 `--------------------------------------------*/
@@ -162,38 +228,23 @@ free_symbol (symbol *sym)
 symbol *
 lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 {
-  size_t h;
-  int cmp = 1;
-  symbol *sym, *prev;
-  symbol **spp;
+  symbol *sym;
+  symbol *entry;
+  symbol tmp;
 
 #if DEBUG_SYM
   current_mode = mode;
   profiles[mode].entry++;
 #endif /* DEBUG_SYM */
 
-  h = hash (name, len);
-  sym = symtab[h % hash_table_size];
-
-  for (prev = NULL; sym != NULL; prev = sym, sym = sym->next)
-    {
-      cmp = (len < SYMBOL_NAME_LEN (sym) ? -1 : len > SYMBOL_NAME_LEN (sym) ? 1
-	     : memcmp (SYMBOL_NAME (sym), name, len));
-      if (cmp >= 0)
-	break;
-    }
-
-  /* If just searching, return status of search.  */
-
-  if (mode == SYMBOL_LOOKUP)
-    return cmp == 0 ? sym : NULL;
-
-  /* Symbol not found.  */
-
-  spp = (prev != NULL) ?  &prev->next : &symtab[h % hash_table_size];
+  tmp.name = (char *) name;
+  tmp.len = len;
+  entry = (symbol *) hash_lookup (symtab, &tmp);
 
   switch (mode)
     {
+    case SYMBOL_LOOKUP:
+      return entry ? entry->stack : NULL;
 
     case SYMBOL_INSERT:
 
@@ -202,14 +253,15 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 	 a new one; if not, just return the symbol.  If not found, just
 	 insert the name, and return the new symbol.  */
 
-      if (cmp == 0 && sym != NULL)
+      if (entry)
 	{
+	  sym = entry->stack;
 	  if (SYMBOL_PENDING_EXPANSIONS (sym) > 0)
 	    {
 	      symbol *old = sym;
 	      SYMBOL_DELETED (old) = true;
 
-	      sym = (symbol *) xmalloc (sizeof (symbol));
+	      sym = (symbol *) xmalloc (sizeof *sym);
 	      SYMBOL_TYPE (sym) = TOKEN_VOID;
 	      SYMBOL_TRACED (sym) = SYMBOL_TRACED (old);
 	      SYMBOL_NAME (sym) = xmemdup0 (name, len);
@@ -219,11 +271,20 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 	      SYMBOL_DELETED (sym) = false;
 	      SYMBOL_PENDING_EXPANSIONS (sym) = 0;
 
-	      SYMBOL_NEXT (sym) = SYMBOL_NEXT (old);
-	      SYMBOL_NEXT (old) = NULL;
-	      sym->stack = old->stack;
+	      if (old == entry)
+		{
+		  old = (symbol *) hash_delete (symtab, entry);
+		  assert (entry == old);
+		  sym->stack = sym;
+		  entry = (symbol *) hash_insert (symtab, sym);
+		  assert (sym == entry);
+		}
+	      else
+		{
+		  entry->stack = sym;
+		  sym->stack = old->stack;
+		}
 	      old->stack = NULL;
-	      *spp = sym;
 	    }
 	  return sym;
 	}
@@ -231,11 +292,13 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 
     case SYMBOL_PUSHDEF:
 
-      /* Insert a name in the symbol table.  If there is already a symbol
-	 with the name, insert this in front of it, and mark the old
-	 symbol as "shadowed".  */
-
-      sym = (symbol *) xmalloc (sizeof (symbol));
+      /* Insert a name in the symbol table.  If there is already a
+	 symbol with the name, add it to the pushdef stack.  Since the
+	 hash table does not allow the insertion of duplicates, the
+	 pushdef stack is a circular chain; the hash entry is the
+	 oldest entry, which points to the newest entry; all other
+	 entries point to the next older entry.  */
+      sym = (symbol *) xmalloc (sizeof *sym);
       SYMBOL_TYPE (sym) = TOKEN_VOID;
       SYMBOL_TRACED (sym) = false;
       SYMBOL_NAME (sym) = xmemdup0 (name, len);
@@ -245,17 +308,19 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
       SYMBOL_DELETED (sym) = false;
       SYMBOL_PENDING_EXPANSIONS (sym) = 0;
 
-      SYMBOL_NEXT (sym) = *spp;
-      sym->stack = NULL;
-      *spp = sym;
-
-      if (mode == SYMBOL_PUSHDEF && cmp == 0)
+      if (entry)
 	{
-	  sym->stack = sym->next;
-	  sym->next = sym->stack->next;
-	  sym->stack->next = NULL;
+	  assert (mode == SYMBOL_PUSHDEF);
+	  sym->stack = entry->stack;
+	  entry->stack = sym;
 	  SYMBOL_TRACED (sym) = SYMBOL_TRACED (sym->stack);
 	}
+      else
+	{
+	  sym->stack = sym;
+	  entry = (symbol *) hash_insert (symtab, sym);
+	  assert (sym == entry);
+	}
       return sym;
 
     case SYMBOL_DELETE:
@@ -268,37 +333,36 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 	 definition is still in use, let the caller free the memory
 	 after it is done with the symbol.  */
 
-      if (cmp != 0 || sym == NULL)
+      if (!entry)
 	return NULL;
       {
 	bool traced = false;
-	symbol *result = sym;
-	if (sym->stack && mode == SYMBOL_POPDEF)
+	symbol *result = sym = entry->stack;
+	if (sym != entry && mode == SYMBOL_POPDEF)
 	  {
 	    SYMBOL_TRACED (sym->stack) = SYMBOL_TRACED (sym);
-	    sym->stack->next = sym->next;
-	    *spp = sym->stack;
-	    sym->next = NULL;
+	    entry->stack = sym->stack;
 	    sym->stack = NULL;
 	    free_symbol (sym);
 	  }
 	else
 	  {
 	    traced = SYMBOL_TRACED (sym);
-	    *spp = sym->next;
-	    do
+	    while (sym != entry)
 	      {
 		symbol *old = sym;
 		sym = sym->stack;
-		old->next = NULL;
 		old->stack = NULL;
 		free_symbol (old);
 	      }
-	    while (sym);
+	    sym = (symbol *) hash_delete (symtab, entry);
+	    assert (sym == entry);
+	    sym->stack = NULL;
+	    free_symbol (sym);
 	  }
 	if (traced)
 	  {
-	    sym = (symbol *) xmalloc (sizeof (symbol));
+	    sym = (symbol *) xmalloc (sizeof *sym);
 	    SYMBOL_TYPE (sym) = TOKEN_VOID;
 	    SYMBOL_TRACED (sym) = true;
 	    SYMBOL_NAME (sym) = xmemdup0 (name, len);
@@ -308,9 +372,9 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 	    SYMBOL_DELETED (sym) = false;
 	    SYMBOL_PENDING_EXPANSIONS (sym) = 0;
 
-	    SYMBOL_NEXT (sym) = *spp;
-	    sym->stack = NULL;
-	    *spp = sym;
+	    sym->stack = sym;
+	    entry = (symbol *) hash_insert (symtab, sym);
+	    assert (sym == entry);
 	  }
 	return result;
       }
@@ -335,20 +399,17 @@ lookup_symbol (const char *name, size_t len, symbol_lookup mode)
 void
 hack_all_symbols (hack_symbol *func, void *data)
 {
-  size_t h;
-  symbol *sym;
+  symbol *sym = (symbol *) hash_get_first (symtab);
   symbol *next;
 
-  for (h = 0; h < hash_table_size; h++)
+  while (sym)
     {
       /* We allow func to call SYMBOL_POPDEF, which can invalidate
 	 sym, so we must grab the next element to traverse before
 	 calling func.  */
-      for (sym = symtab[h]; sym != NULL; sym = next)
-	{
-	  next = SYMBOL_NEXT (sym);
-	  func (sym, data);
-	}
+      next = (symbol *) hash_get_next (symtab, sym);
+      func (sym->stack, data);
+      sym = next;
     }
 }
 
@@ -396,28 +457,26 @@ symtab_debug (void)
 static void
 symtab_print_list (int i)
 {
-  symbol *sym;
+  symbol *sym = (symbol *) hash_get_first (symtab);
   symbol *stack;
-  size_t h;
 
   xprintf ("Symbol dump #%d:\n", i);
-  for (h = 0; h < hash_table_size; h++)
-    for (sym = symtab[h]; sym != NULL; sym = sym->next)
-      {
-	stack = sym;
-	do
-	  {
-	    xprintf ("\tname %s, len %zu, bucket %lu, addr %p, next %p, "
-		     "stack %p, flags%s%s, pending %d\n",
-		     SYMBOL_NAME (stack), SYMBOL_NAME_LEN (stack),
-		     (unsigned long int) h, stack, SYMBOL_NEXT (stack),
-		     stack->stack, SYMBOL_TRACED (stack) ? " traced" : "",
-		     SYMBOL_DELETED (stack) ? " deleted" : "",
-		     SYMBOL_PENDING_EXPANSIONS (stack));
-	    stack = stack->stack;
-	  }
-	while (stack);
-      }
+  while (sym)
+    {
+      stack = sym->stack;
+      do
+	{
+	  xprintf ("\tname %s, len %zu, addr %p, "
+		   "stack %p, flags%s%s, pending %d\n",
+		   SYMBOL_NAME (stack), SYMBOL_NAME_LEN (stack),
+		   stack, stack->stack, SYMBOL_TRACED (stack) ? " traced" : "",
+		   SYMBOL_DELETED (stack) ? " deleted" : "",
+		   SYMBOL_PENDING_EXPANSIONS (stack));
+	  stack = stack->stack;
+	}
+      while (stack != sym);
+      sym = (symbol *) hash_get_next (symtab, sym);
+    }
 }
 
 #endif /* DEBUG_SYM */
-- 
1.5.6.4
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.