changeset 25005:95eace73d3ef

(toplevel): Add hash tables. (init_fns): New. (Fmessage): Use message3. (Fcurrent_message): If echo_area_message is set, return a substring of that string. (Fformat): Add text properties to the result string from properties of the format string and properties of string arguments.
author Gerd Moellmann <gerd@gnu.org>
date Wed, 21 Jul 1999 21:43:52 +0000
parents bfd115279703
children c79dc141ef5a
files src/fns.c
diffstat 1 files changed, 1328 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/fns.c	Wed Jul 21 21:43:52 1999 +0000
+++ b/src/fns.c	Wed Jul 21 21:43:52 1999 +0000
@@ -48,6 +48,11 @@
 #define NULL (void *)0
 #endif
 
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
 /* Nonzero enables use of dialog boxes for questions
    asked by mouse commands.  */
 int use_dialog_box;
@@ -3174,10 +3179,1326 @@
       *e++ = (unsigned char) (0xff & value);
     }
 }
+
+
+
+/***********************************************************************
+ *****                                                             *****
+ *****			     Hash Tables                           *****
+ *****                                                             *****
+ ***********************************************************************/
+
+/* Implemented by gerd@gnu.org.  This hash table implementation was
+   inspired by CMUCL hash tables.  */
+
+/* Ideas:
+
+   1. For small tables, association lists are probably faster than
+   hash tables because they have lower overhead.
+
+   For uses of hash tables where the O(1) behavior of table
+   operations is not a requirement, it might therefore be a good idea
+   not to hash.  Instead, we could just do a linear search in the
+   key_and_value vector of the hash table.  This could be done
+   if a `:linear-search t' argument is given to make-hash-table.  */
+
+
+/* Return the contents of vector V at index IDX.  */
+
+#define AREF(V, IDX)       XVECTOR (V)->contents[IDX]
+
+/* Value is the key part of entry IDX in hash table H.  */
+
+#define HASH_KEY(H, IDX)   AREF ((H)->key_and_value, 2 * (IDX))
+
+/* Value is the value part of entry IDX in hash table H.  */
+
+#define HASH_VALUE(H, IDX) AREF ((H)->key_and_value, 2 * (IDX) + 1)
+
+/* Value is the index of the next entry following the one at IDX
+   in hash table H.  */
+
+#define HASH_NEXT(H, IDX)  AREF ((H)->next, (IDX))
+
+/* Value is the hash code computed for entry IDX in hash table H.  */
+
+#define HASH_HASH(H, IDX)  AREF ((H)->hash, (IDX))
+
+/* Value is the index of the element in hash table H that is the
+   start of the collision list at index IDX in the index vector of H.  */
+
+#define HASH_INDEX(H, IDX)  AREF ((H)->index, (IDX))
+
+/* Value is the size of hash table H.  */
+
+#define HASH_TABLE_SIZE(H) XVECTOR ((H)->next)->size
+
+/* The list of all weak hash tables.  Don't staticpro this one.  */
+
+Lisp_Object Vweak_hash_tables;
+
+/* Various symbols.  */
+
+Lisp_Object Qhash_table_p, Qeq, Qeql, Qequal, Qkey_weak, Qvalue_weak;
+Lisp_Object Qkey_value_weak;
+Lisp_Object QCtest, QCsize, QCrehash_size, QCrehash_threshold, QCweak;
+Lisp_Object Qhash_table_test;
+
+/* Function prototypes.  */
+
+static struct Lisp_Hash_Table *check_hash_table P_ ((Lisp_Object));
+static int next_almost_prime P_ ((int));
+static int get_key_arg P_ ((Lisp_Object, int, Lisp_Object *, char *));
+static Lisp_Object larger_vector P_ ((Lisp_Object, int, Lisp_Object));
+static void maybe_resize_hash_table P_ ((struct Lisp_Hash_Table *));
+static int cmpfn_eq P_ ((struct Lisp_Hash_Table *, Lisp_Object, unsigned,
+			 Lisp_Object, unsigned));
+static int cmpfn_eql P_ ((struct Lisp_Hash_Table *, Lisp_Object, unsigned,
+			  Lisp_Object, unsigned));
+static int cmpfn_equal P_ ((struct Lisp_Hash_Table *, Lisp_Object, unsigned,
+			    Lisp_Object, unsigned));
+static int cmpfn_user_defined P_ ((struct Lisp_Hash_Table *, Lisp_Object,
+				   unsigned, Lisp_Object, unsigned));
+static unsigned hashfn_eq P_ ((struct Lisp_Hash_Table *, Lisp_Object));
+static unsigned hashfn_eql P_ ((struct Lisp_Hash_Table *, Lisp_Object));
+static unsigned hashfn_equal P_ ((struct Lisp_Hash_Table *, Lisp_Object));
+static unsigned hashfn_user_defined P_ ((struct Lisp_Hash_Table *,
+					 Lisp_Object));
+static unsigned sxhash_string P_ ((unsigned char *, int));
+static unsigned sxhash_list P_ ((Lisp_Object, int));
+static unsigned sxhash_vector P_ ((Lisp_Object, int));
+static unsigned sxhash_bool_vector P_ ((Lisp_Object));
+
+
+
+/***********************************************************************
+			       Utilities
+ ***********************************************************************/
+
+/* If OBJ is a Lisp hash table, return a pointer to its struct
+   Lisp_Hash_Table.  Otherwise, signal an error.  */
+
+static struct Lisp_Hash_Table *
+check_hash_table (obj)
+     Lisp_Object obj;
+{
+  CHECK_HASH_TABLE (obj, 0);
+  return XHASH_TABLE (obj);
+}
+
+
+/* Value is the next integer I >= N, N >= 0 which is "almost" a prime
+   number.  */
+
+static int
+next_almost_prime (n)
+     int n;
+{
+  if (n % 2 == 0)
+    n += 1;
+  if (n % 3 == 0)
+    n += 2;
+  if (n % 7 == 0)
+    n += 4;
+  return n;
+}
+
+
+/* Find KEY in ARGS which has size NARGS.  Don't consider indices for
+   which USED[I] is non-zero.  If found at index I in ARGS, set
+   USED[I] and USED[I + 1] to 1, and return I + 1.  Otherwise return
+   -1.  This function is used to extract a keyword/argument pair from
+   a DEFUN parameter list.  */
+
+static int
+get_key_arg (key, nargs, args, used)
+     Lisp_Object key;
+     int nargs;
+     Lisp_Object *args;
+     char *used;
+{
+  int i;
+  
+  for (i = 0; i < nargs - 1; ++i)
+    if (!used[i] && EQ (args[i], key))
+      break;
+  
+  if (i >= nargs - 1)
+    i = -1;
+  else
+    {
+      used[i++] = 1;
+      used[i] = 1;
+    }
+  
+  return i;
+}
+
+
+/* Return a Lisp vector which has the same contents as VEC but has
+   size NEW_SIZE, NEW_SIZE >= VEC->size.  Entries in the resulting
+   vector that are not copied from VEC are set to INIT.  */
+
+static Lisp_Object
+larger_vector (vec, new_size, init)
+     Lisp_Object vec;
+     int new_size;
+     Lisp_Object init;
+{
+  struct Lisp_Vector *v;
+  int i, old_size;
+
+  xassert (VECTORP (vec));
+  old_size = XVECTOR (vec)->size;
+  xassert (new_size >= old_size);
+
+  v = allocate_vectorlike (new_size);
+  v->size = new_size;
+  bcopy (XVECTOR (vec)->contents, v->contents,
+	 old_size * sizeof *v->contents);
+  for (i = old_size; i < new_size; ++i)
+    v->contents[i] = init;
+  XSETVECTOR (vec, v);
+  return vec;
+}
+
+
+/***********************************************************************
+			 Low-level Functions
+ ***********************************************************************/
+
+/* Compare KEY1 which has hash code HASH1 and KEY2 with hash code
+   HASH2 in hash table H using `eq'.  Value is non-zero if KEY1 and
+   KEY2 are the same.  */
+
+static int
+cmpfn_eq (h, key1, hash1, key2, hash2)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key1, key2;
+     unsigned hash1, hash2;
+{
+  return EQ (key1, key2);
+}
+
+
+/* Compare KEY1 which has hash code HASH1 and KEY2 with hash code
+   HASH2 in hash table H using `eql'.  Value is non-zero if KEY1 and
+   KEY2 are the same.  */
+
+static int
+cmpfn_eql (h, key1, hash1, key2, hash2)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key1, key2;
+     unsigned hash1, hash2;
+{
+  return (EQ (key1, key2)
+	  || (FLOATP (key1)
+	      && FLOATP (key2)
+	      && XFLOAT (key1)->data == XFLOAT (key2)->data));
+}
+
+
+/* Compare KEY1 which has hash code HASH1 and KEY2 with hash code
+   HASH2 in hash table H using `equal'.  Value is non-zero if KEY1 and
+   KEY2 are the same.  */
+
+static int
+cmpfn_equal (h, key1, hash1, key2, hash2)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key1, key2;
+     unsigned hash1, hash2;
+{
+  return (EQ (key1, key2)
+	  || (hash1 == hash2
+	      && !NILP (Fequal (key1, key2))));
+}
+
+  
+/* Compare KEY1 which has hash code HASH1, and KEY2 with hash code
+   HASH2 in hash table H using H->user_cmp_function.  Value is non-zero
+   if KEY1 and KEY2 are the same.  */
+
+static int
+cmpfn_user_defined (h, key1, hash1, key2, hash2)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key1, key2;
+     unsigned hash1, hash2;
+{
+  if (hash1 == hash2)
+    {
+      Lisp_Object args[3];
+  
+      args[0] = h->user_cmp_function;
+      args[1] = key1;
+      args[2] = key2;
+      return !NILP (Ffuncall (3, args));
+    }
+  else
+    return 0;
+}
+
+
+/* Value is a hash code for KEY for use in hash table H which uses
+   `eq' to compare keys.  The hash code returned is guaranteed to fit
+   in a Lisp integer.  */
+
+static unsigned
+hashfn_eq (h, key)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key;
+{
+  /* Lisp strings can change their address.  Don't try to compute a
+     hash code for a string from its address.  */
+  if (STRINGP (key))
+    return sxhash_string (XSTRING (key)->data, XSTRING (key)->size);
+  else
+    return XUINT (key) ^ XGCTYPE (key);
+}
+
+
+/* Value is a hash code for KEY for use in hash table H which uses
+   `eql' to compare keys.  The hash code returned is guaranteed to fit
+   in a Lisp integer.  */
+
+static unsigned
+hashfn_eql (h, key)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key;
+{
+  /* Lisp strings can change their address.  Don't try to compute a
+     hash code for a string from its address.  */
+  if (STRINGP (key))
+    return sxhash_string (XSTRING (key)->data, XSTRING (key)->size);
+  else if (FLOATP (key))
+    return sxhash (key, 0);
+  else
+    return XUINT (key) ^ XGCTYPE (key);
+}
+
+
+/* Value is a hash code for KEY for use in hash table H which uses
+   `equal' to compare keys.  The hash code returned is guaranteed to fit
+   in a Lisp integer.  */
+
+static unsigned
+hashfn_equal (h, key)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key;
+{
+  return sxhash (key, 0);
+}
+
+
+/* Value is a hash code for KEY for use in hash table H which uses as
+   user-defined function to compare keys.  The hash code returned is
+   guaranteed to fit in a Lisp integer.  */
+
+static unsigned
+hashfn_user_defined (h, key)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key;
+{
+  Lisp_Object args[2], hash;
+  
+  args[0] = h->user_hash_function;
+  args[1] = key;
+  hash = Ffuncall (2, args);
+  if (!INTEGERP (hash))
+    Fsignal (Qerror,
+	     list2 (build_string ("Illegal hash code returned from \
+user-supplied hash function"),
+		    hash));
+  return XUINT (hash);
+}
+
+
+/* Create and initialize a new hash table.
+
+   TEST specifies the test the hash table will use to compare keys.
+   It must be either one of the predefined tests `eq', `eql' or
+   `equal' or a symbol denoting a user-defined test named TEST with
+   test and hash functions USER_TEST and USER_HASH.
+   
+   Give the table initial capacity SIZE, SIZE > 0, an integer.
+
+   If REHASH_SIZE is an integer, it must be > 0, and this hash table's
+   new size when it becomes full is computed by adding REHASH_SIZE to
+   its old size.  If REHASH_SIZE is a float, it must be > 1.0, and the
+   table's new size is computed by multiplying its old size with
+   REHASH_SIZE.
+
+   REHASH_THRESHOLD must be a float <= 1.0, and > 0.  The table will
+   be resized when the ratio of (number of entries in the table) /
+   (table size) is >= REHASH_THRESHOLD.
+
+   WEAK specifies the weakness of the table.  If non-nil, it must be
+   one of the symbols `key-weak', `value-weak' or `key-value-weak'.  */
+
+Lisp_Object
+make_hash_table (test, size, rehash_size, rehash_threshold, weak,
+		 user_test, user_hash)
+     Lisp_Object test, size, rehash_size, rehash_threshold, weak;
+     Lisp_Object user_test, user_hash;
+{
+  struct Lisp_Hash_Table *h;
+  struct Lisp_Vector *v;
+  Lisp_Object table;
+  int index_size, i, len, sz;
+
+  /* Preconditions.  */
+  xassert (SYMBOLP (test));
+  xassert (INTEGERP (size) && XINT (size) > 0);
+  xassert ((INTEGERP (rehash_size) && XINT (rehash_size) > 0)
+	   || (FLOATP (rehash_size) && XFLOATINT (rehash_size) > 1.0));
+  xassert (FLOATP (rehash_threshold)
+	   && XFLOATINT (rehash_threshold) > 0
+	   && XFLOATINT (rehash_threshold) <= 1.0);
+
+  /* Allocate a vector, and initialize it.  */
+  len = VECSIZE (struct Lisp_Hash_Table);
+  v = allocate_vectorlike (len);
+  v->size = len;
+  for (i = 0; i < len; ++i)
+    v->contents[i] = Qnil;
+
+  /* Initialize hash table slots.  */
+  sz = XFASTINT (size);
+  h = (struct Lisp_Hash_Table *) v;
+  
+  h->test = test;
+  if (EQ (test, Qeql))
+    {
+      h->cmpfn = cmpfn_eql;
+      h->hashfn = hashfn_eql;
+    }
+  else if (EQ (test, Qeq))
+    {
+      h->cmpfn = cmpfn_eq;
+      h->hashfn = hashfn_eq;
+    }
+  else if (EQ (test, Qequal))
+    {
+      h->cmpfn = cmpfn_equal;
+      h->hashfn = hashfn_equal;
+    }
+  else
+    {
+      h->user_cmp_function = user_test;
+      h->user_hash_function = user_hash;
+      h->cmpfn = cmpfn_user_defined;
+      h->hashfn = hashfn_user_defined;
+    }
+  
+  h->weak = weak;
+  h->rehash_threshold = rehash_threshold;
+  h->rehash_size = rehash_size;
+  h->count = make_number (0);
+  h->key_and_value = Fmake_vector (make_number (2 * sz), Qnil);
+  h->hash = Fmake_vector (size, Qnil);
+  h->next = Fmake_vector (size, Qnil);
+  index_size = next_almost_prime (sz / XFLOATINT (rehash_threshold));
+  h->index = Fmake_vector (make_number (index_size), Qnil);
+
+  /* Set up the free list.  */
+  for (i = 0; i < sz - 1; ++i)
+    HASH_NEXT (h, i) = make_number (i + 1);
+  h->next_free = make_number (0);
+
+  XSET_HASH_TABLE (table, h);
+  xassert (HASH_TABLE_P (table));
+  xassert (XHASH_TABLE (table) == h);
+
+  /* Maybe add this hash table to the list of all weak hash tables.  */
+  if (NILP (h->weak))
+    h->next_weak = Qnil;
+  else
+    {
+      h->next_weak = Vweak_hash_tables;
+      Vweak_hash_tables = table;
+    }
+
+  return table;
+}
+
+
+/* Resize hash table H if it's too full.  If H cannot be resized
+   because it's already too large, throw an error.  */
+
+static INLINE void
+maybe_resize_hash_table (h)
+     struct Lisp_Hash_Table *h;
+{
+  if (NILP (h->next_free))
+    {
+      int old_size = HASH_TABLE_SIZE (h);
+      int i, new_size, index_size;
+ 
+      if (INTEGERP (h->rehash_size))
+	new_size = old_size + XFASTINT (h->rehash_size);
+      else
+	new_size = old_size * XFLOATINT (h->rehash_size);
+      index_size = next_almost_prime (new_size
+				      / XFLOATINT (h->rehash_threshold));
+      if (max (index_size, 2 * new_size) & ~VALMASK)
+	error ("Hash table too large to resize");
+
+      h->key_and_value = larger_vector (h->key_and_value, 2 * new_size, Qnil);
+      h->next = larger_vector (h->next, new_size, Qnil);
+      h->hash = larger_vector (h->hash, new_size, Qnil);
+      h->index = Fmake_vector (make_number (index_size), Qnil);
+
+      /* Update the free list.  Do it so that new entries are added at
+         the end of the free list.  This makes some operations like
+         maphash faster.  */
+      for (i = old_size; i < new_size - 1; ++i)
+	HASH_NEXT (h, i) = make_number (i + 1);
+      
+      if (!NILP (h->next_free))
+	{
+	  Lisp_Object last, next;
+	  
+	  last = h->next_free;
+	  while (next = HASH_NEXT (h, XFASTINT (last)),
+		 !NILP (next))
+	    last = next;
+	  
+	  HASH_NEXT (h, XFASTINT (last)) = make_number (old_size);
+	}
+      else
+	XSETFASTINT (h->next_free, old_size);
+
+      /* Rehash.  */
+      for (i = 0; i < old_size; ++i)
+	if (!NILP (HASH_HASH (h, i)))
+	  {
+	    unsigned hash_code = XUINT (HASH_HASH (h, i));
+	    int start_of_bucket = hash_code % XVECTOR (h->index)->size;
+	    HASH_NEXT (h, i) = HASH_INDEX (h, start_of_bucket);
+	    HASH_INDEX (h, start_of_bucket) = make_number (i);
+	  }
+    }	
+}
+
+
+/* Lookup KEY in hash table H.  If HASH is non-null, return in *HASH
+   the hash code of KEY.  Value is the index of the entry in H
+   matching KEY, or -1 if not found.  */
+
+int
+hash_lookup (h, key, hash)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key;
+     unsigned *hash;
+{
+  unsigned hash_code;
+  int start_of_bucket;
+  Lisp_Object idx;
+
+  hash_code = h->hashfn (h, key);
+  if (hash)
+    *hash = hash_code;
+  
+  start_of_bucket = hash_code % XVECTOR (h->index)->size;
+  idx = HASH_INDEX (h, start_of_bucket);
+
+  while (!NILP (idx))
+    {
+      int i = XFASTINT (idx);
+      if (h->cmpfn (h, key, hash_code, HASH_KEY (h, i), HASH_HASH (h, i)))
+	break;
+      idx = HASH_NEXT (h, i);
+    }
+
+  return NILP (idx) ? -1 : XFASTINT (idx);
+}
+
+
+/* Put an entry into hash table H that associates KEY with VALUE.
+   HASH is a previously computed hash code of KEY.  */
+
+void
+hash_put (h, key, value, hash)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key, value;
+     unsigned hash;
+{
+  int start_of_bucket, i;
+
+  xassert ((hash & ~VALMASK) == 0);
+
+  /* Increment count after resizing because resizing may fail.  */
+  maybe_resize_hash_table (h);
+  h->count = make_number (XFASTINT (h->count) + 1);
+  
+  /* Store key/value in the key_and_value vector.  */
+  i = XFASTINT (h->next_free);
+  h->next_free = HASH_NEXT (h, i);
+  HASH_KEY (h, i) = key;
+  HASH_VALUE (h, i) = value;
+
+  /* Remember its hash code.  */
+  HASH_HASH (h, i) = make_number (hash);
+
+  /* Add new entry to its collision chain.  */
+  start_of_bucket = hash % XVECTOR (h->index)->size;
+  HASH_NEXT (h, i) = HASH_INDEX (h, start_of_bucket);
+  HASH_INDEX (h, start_of_bucket) = make_number (i);
+}
+
+
+/* Remove the entry matching KEY from hash table H, if there is one.  */
+
+void
+hash_remove (h, key)
+     struct Lisp_Hash_Table *h;
+     Lisp_Object key;
+{
+  unsigned hash_code;
+  int start_of_bucket;
+  Lisp_Object idx, prev;
+
+  hash_code = h->hashfn (h, key);
+  start_of_bucket = hash_code % XVECTOR (h->index)->size;
+  idx = HASH_INDEX (h, start_of_bucket);
+  prev = Qnil;
+
+  while (!NILP (idx))
+    {
+      int i = XFASTINT (idx);
+
+      if (h->cmpfn (h, key, hash_code, HASH_KEY (h, i), HASH_HASH (h, i)))
+	{
+	  /* Take entry out of collision chain.  */
+	  if (NILP (prev))
+	    HASH_INDEX (h, start_of_bucket) = HASH_NEXT (h, i);
+	  else
+	    HASH_NEXT (h, XFASTINT (prev)) = HASH_NEXT (h, i);
+
+	  /* Clear slots in key_and_value and add the slots to
+	     the free list.  */
+	  HASH_KEY (h, i) = HASH_VALUE (h, i) = HASH_HASH (h, i) = Qnil;
+	  HASH_NEXT (h, i) = h->next_free;
+	  h->next_free = make_number (i);
+	  h->count = make_number (XFASTINT (h->count) - 1);
+	  xassert (XINT (h->count) >= 0);
+	  break;
+	}
+      else
+	{
+	  prev = idx;
+	  idx = HASH_NEXT (h, i);
+	}
+    }
+}
+
+
+/* Clear hash table H.  */
+
+void
+hash_clear (h)
+     struct Lisp_Hash_Table *h;
+{
+  if (XFASTINT (h->count) > 0)
+    {
+      int i, size = HASH_TABLE_SIZE (h);
+
+      for (i = 0; i < size; ++i)
+	{
+	  HASH_NEXT (h, i) = i < size - 1 ? make_number (i + 1) : Qnil;
+	  HASH_KEY (h, i) = Qnil;
+	  HASH_VALUE (h, i) = Qnil;
+	  HASH_HASH (h, i) = Qnil;
+	}
+
+      for (i = 0; i < XVECTOR (h->index)->size; ++i)
+	XVECTOR (h->index)->contents[i] = Qnil;
+
+      h->next_free = make_number (0);
+      h->count = make_number (0);
+    }
+}
+
+
+
+/************************************************************************
+			   Weak Hash Tables
+ ************************************************************************/
+
+/* Remove elements from weak hash tables that don't survive the
+   current garbage collection.  Remove weak tables that don't survive
+   from Vweak_hash_tables.  Called from gc_sweep.  */
+
+void
+sweep_weak_hash_tables ()
+{
+  Lisp_Object table;
+  struct Lisp_Hash_Table *h = 0, *prev;
+
+  for (table = Vweak_hash_tables; !GC_NILP (table); table = h->next_weak)
+    {
+      prev = h;
+      h = XHASH_TABLE (table);
+	
+      if (h->size & ARRAY_MARK_FLAG)
+	{
+	  if (XFASTINT (h->count) > 0)
+	    {
+	      int bucket, n;
+
+	      n = XVECTOR (h->index)->size & ~ARRAY_MARK_FLAG;
+	      for (bucket = 0; bucket < n; ++bucket)
+		{
+		  Lisp_Object idx, key, value, prev, next;
+
+		  /* Follow collision chain, removing entries that
+		     don't survive this garbage collection.  */
+		  idx = HASH_INDEX (h, bucket);
+		  prev = Qnil;
+		  while (!GC_NILP (idx))
+		    {
+		      int remove_p;
+		      int i = XFASTINT (idx);
+		      Lisp_Object next;
+
+		      if (EQ (h->weak, Qkey_weak))
+			remove_p = !survives_gc_p (HASH_KEY (h, i));
+		      else if (EQ (h->weak, Qvalue_weak))
+			remove_p = !survives_gc_p (HASH_VALUE (h, i));
+		      else if (EQ (h->weak, Qkey_value_weak))
+			remove_p = (!survives_gc_p (HASH_KEY (h, i))
+				    || !survives_gc_p (HASH_VALUE (h, i)));
+		      else
+			abort ();
+		      
+		      next = HASH_NEXT (h, i);
+		      if (remove_p)
+			{
+			  /* Take out of collision chain.  */
+			  if (GC_NILP (prev))
+			    HASH_INDEX (h, i) = next;
+			  else
+			    HASH_NEXT (h, XFASTINT (prev)) = next;
+
+			  /* Add to free list.  */
+			  HASH_NEXT (h, i) = h->next_free;
+			  h->next_free = idx;
+
+			  /* Clear key, value, and hash.  */
+			  HASH_KEY (h, i) = HASH_VALUE (h, i) = Qnil;
+			  HASH_HASH (h, i) = Qnil;
+
+			  h->count = make_number (XFASTINT (h->count) - 1);
+			}
+		      else
+			{
+			  /* Make sure key and value survive.  */
+			  mark_object (&HASH_KEY (h, i));
+			  mark_object (&HASH_VALUE (h, i));
+			}
+
+		      idx = next;
+		    }
+		}
+	    }
+	}
+      else
+	{
+	  /* Table is not marked, and will thus be freed.
+	     Take it out of the list of weak hash tables.  */
+	  if (prev)
+	    prev->next_weak = h->next_weak;
+	  else
+	    Vweak_hash_tables = h->next_weak;
+	}
+    }
+}
+
+
+
+/***********************************************************************
+			Hash Code Computation
+ ***********************************************************************/
+
+/* Maximum depth up to which to dive into Lisp structures.  */
+
+#define SXHASH_MAX_DEPTH 3
+
+/* Maximum length up to which to take list and vector elements into
+   account.  */
+
+#define SXHASH_MAX_LEN   7
+
+/* Combine two integers X and Y for hashing.  */
+
+#define SXHASH_COMBINE(X, Y)						\
+     ((((unsigned)(X) << 4) + ((unsigned)(X) >> 24) & 0x0fffffff)	\
+      + (unsigned)(Y))
+
+
+/* Return a hash for string PTR which has length LEN.  */
+
+static unsigned
+sxhash_string (ptr, len)
+     unsigned char *ptr;
+     int len;
+{
+  unsigned char *p = ptr;
+  unsigned char *end = p + len;
+  unsigned char c;
+  unsigned hash = 0;
+
+  while (p != end)
+    {
+      c = *p++;
+      if (c >= 0140)
+	c -= 40;
+      hash = ((hash << 3) + (hash >> 28) + c);
+    }
+  
+  return hash & 07777777777;
+}
+
+
+/* Return a hash for list LIST.  DEPTH is the current depth in the
+   list.  We don't recurse deeper than SXHASH_MAX_DEPTH in it.  */
+
+static unsigned
+sxhash_list (list, depth)
+     Lisp_Object list;
+     int depth;
+{
+  unsigned hash = 0;
+  int i;
+  
+  if (depth < SXHASH_MAX_DEPTH)
+    for (i = 0;
+	 CONSP (list) && i < SXHASH_MAX_LEN;
+	 list = XCDR (list), ++i)
+      {
+	unsigned hash2 = sxhash (XCAR (list), depth + 1);
+	hash = SXHASH_COMBINE (hash, hash2);
+      }
+
+  return hash;
+}
+
+
+/* Return a hash for vector VECTOR.  DEPTH is the current depth in
+   the Lisp structure.  */
+
+static unsigned
+sxhash_vector (vec, depth)
+     Lisp_Object vec;
+     int depth;
+{
+  unsigned hash = XVECTOR (vec)->size;
+  int i, n;
+
+  n = min (SXHASH_MAX_LEN, XVECTOR (vec)->size);
+  for (i = 0; i < n; ++i)
+    {
+      unsigned hash2 = sxhash (XVECTOR (vec)->contents[i], depth + 1);
+      hash = SXHASH_COMBINE (hash, hash2);
+    }
+
+  return hash;
+}
+
+
+/* Return a hash for bool-vector VECTOR.  */
+
+static unsigned
+sxhash_bool_vector (vec)
+     Lisp_Object vec;
+{
+  unsigned hash = XBOOL_VECTOR (vec)->size;
+  int i, n;
+
+  n = min (SXHASH_MAX_LEN, XBOOL_VECTOR (vec)->vector_size);
+  for (i = 0; i < n; ++i)
+    hash = SXHASH_COMBINE (hash, XBOOL_VECTOR (vec)->data[i]);
+
+  return hash;
+}
+
+
+/* Return a hash code for OBJ.  DEPTH is the current depth in the Lisp
+   structure.  Value is an unsigned integer clipped to VALMASK.  */
+
+unsigned
+sxhash (obj, depth)
+     Lisp_Object obj;
+     int depth;
+{
+  unsigned hash;
+
+  if (depth > SXHASH_MAX_DEPTH)
+    return 0;
+  
+  switch (XTYPE (obj))
+    {
+    case Lisp_Int:
+      hash = XUINT (obj);
+      break;
+
+    case Lisp_Symbol:
+      hash = sxhash_string (XSYMBOL (obj)->name->data,
+			    XSYMBOL (obj)->name->size);
+      break;
+
+    case Lisp_Misc:
+      hash = XUINT (obj);
+      break;
+
+    case Lisp_String:
+      hash = sxhash_string (XSTRING (obj)->data, XSTRING (obj)->size);
+      break;
+
+      /* This can be everything from a vector to an overlay.  */
+    case Lisp_Vectorlike:
+      if (VECTORP (obj))
+	/* According to the CL HyperSpec, two arrays are equal only if
+	   they are `eq', except for strings and bit-vectors.  In
+	   Emacs, this works differently.  We have to compare element
+	   by element.  */
+	hash = sxhash_vector (obj, depth);
+      else if (BOOL_VECTOR_P (obj))
+	hash = sxhash_bool_vector (obj);
+      else
+	/* Others are `equal' if they are `eq', so let's take their
+	   address as hash.  */
+	hash = XUINT (obj);
+      break;
+
+    case Lisp_Cons:
+      hash = sxhash_list (obj, depth);
+      break;
+
+    case Lisp_Float:
+      {
+	unsigned char *p = (unsigned char *) &XFLOAT (obj)->data;
+	unsigned char *e = p + sizeof XFLOAT (obj)->data;
+	for (hash = 0; p < e; ++p)
+	  hash = SXHASH_COMBINE (hash, *p);
+	break;
+      }
+
+    default:
+      abort ();
+    }
+
+  return hash & VALMASK;
+}
+
+
+
+/***********************************************************************
+			    Lisp Interface
+ ***********************************************************************/
+
+
+DEFUN ("sxhash", Fsxhash, Ssxhash, 1, 1, 0,
+  "Compute a hash code for OBJ and return it as integer.")
+  (obj)
+     Lisp_Object obj;
+{
+  unsigned hash = sxhash (obj, 0);;
+  return make_number (hash);
+}
+
+
+DEFUN ("make-hash-table", Fmake_hash_table, Smake_hash_table, 0, MANY, 0,
+  "Create and return a new hash table.\n\
+Arguments are specified as keyword/argument pairs.  The following\n\
+arguments are defined:\n\
+\n\
+:TEST TEST -- TEST must be a symbol that specifies how to compare keys.
+Default is `eql'.  Predefined are the tests `eq', `eql', and `equal'.\n\
+User-supplied test and hash functions can be specified via\n\
+`define-hash-table-test'.\n\
+\n\
+:SIZE SIZE -- A hint as to how many elements will be put in the table.
+Default is 65.\n\
+\n\
+:REHASH-SIZE REHASH-SIZE - Indicates how to expand the table when\n\
+it fills up.  If REHASH-SIZE is an integer, add that many space.\n\
+If it is a float, it must be > 1.0, and the new size is computed by\n\
+multiplying the old size with that factor.  Default is 1.5.\n\
+\n\
+:REHASH-THRESHOLD THRESHOLD -- THRESHOLD must a float > 0, and <= 1.0.\n\
+Resize the hash table when ratio of the number of entries in the table.\n\
+Default is 0.8.\n\
+\n\
+:WEAK WEAK -- WEAK must be one of nil, t, `key-weak', `value-weak' or\n\
+`key-value-weak'.  WEAK t means the same as `key-value-weak'.  Elements\n\
+ are removed from a weak hash table when their key, value or both \n\
+according to WEAKNESS are otherwise unreferenced.  Default is nil.")
+  (nargs, args)
+     int nargs;
+     Lisp_Object *args;
+{
+  Lisp_Object test, size, rehash_size, rehash_threshold, weak;
+  Lisp_Object user_test, user_hash;
+  char *used;
+  int i;
+
+  /* The vector `used' is used to keep track of arguments that
+     have been consumed.  */
+  used = (char *) alloca (nargs * sizeof *used);
+  bzero (used, nargs * sizeof *used);
+
+  /* See if there's a `:test TEST' among the arguments.  */
+  i = get_key_arg (QCtest, nargs, args, used);
+  test = i < 0 ? Qeql : args[i];
+  if (!EQ (test, Qeq) && !EQ (test, Qeql) && !EQ (test, Qequal))
+    {
+      /* See if it is a user-defined test.  */
+      Lisp_Object prop;
+      
+      prop = Fget (test, Qhash_table_test);
+      if (!CONSP (prop) || XFASTINT (Flength (prop)) < 2)
+	Fsignal (Qerror, list2 (build_string ("Illegal hash table test"),
+				test));
+      user_test = Fnth (make_number (0), prop);
+      user_hash = Fnth (make_number (1), prop);
+    }
+  else
+    user_test = user_hash = Qnil;
+
+  /* See if there's a `:size SIZE' argument.  */
+  i = get_key_arg (QCsize, nargs, args, used);
+  size = i < 0 ? make_number (DEFAULT_HASH_SIZE) : args[i];
+  if (!INTEGERP (size) || XINT (size) <= 0)
+    Fsignal (Qerror,
+	     list2 (build_string ("Illegal hash table size"),
+		    size));
+
+  /* Look for `:rehash-size SIZE'.  */
+  i = get_key_arg (QCrehash_size, nargs, args, used);
+  rehash_size = i < 0 ? make_float (DEFAULT_REHASH_SIZE) : args[i];
+  if (!NUMBERP (rehash_size)
+      || (INTEGERP (rehash_size) && XINT (rehash_size) <= 0)
+      || XFLOATINT (rehash_size) <= 1.0)
+    Fsignal (Qerror,
+	     list2 (build_string ("Illegal hash table rehash size"),
+		    rehash_size));
+  
+  /* Look for `:rehash-threshold THRESHOLD'.  */
+  i = get_key_arg (QCrehash_threshold, nargs, args, used);
+  rehash_threshold = i < 0 ? make_float (DEFAULT_REHASH_THRESHOLD) : args[i];
+  if (!FLOATP (rehash_threshold)
+      || XFLOATINT (rehash_threshold) <= 0.0
+      || XFLOATINT (rehash_threshold) > 1.0)
+    Fsignal (Qerror,
+	     list2 (build_string ("Illegal hash table rehash threshold"),
+		    rehash_threshold));
+  
+  /* Look for `:weak WEAK'.  */
+  i = get_key_arg (QCweak, nargs, args, used);
+  weak = i < 0 ? Qnil : args[i];
+  if (EQ (weak, Qt))
+    weak = Qkey_value_weak;
+  if (!NILP (weak)
+      && !EQ (weak, Qkey_weak)
+      && !EQ (weak, Qvalue_weak)
+      && !EQ (weak, Qkey_value_weak))
+    Fsignal (Qerror, list2 (build_string ("Illegal hash table weakness"), 
+			    weak));
+  
+  /* Now, all args should have been used up, or there's a problem.  */
+  for (i = 0; i < nargs; ++i)
+    if (!used[i])
+      Fsignal (Qerror,
+	       list2 (build_string ("Invalid argument list"), args[i]));
+
+  return make_hash_table (test, size, rehash_size, rehash_threshold, weak,
+			  user_test, user_hash);
+}
+
+
+DEFUN ("makehash", Fmakehash, Smakehash, 0, MANY, 0,
+       "Create a new hash table.\n\
+Optional first argument SIZE is a hint to the implementation as\n\
+to how many elements will be put in the table.  Default is 65.\n\
+\n\
+Optional second argument TEST specifies how to compare keys in\n\
+the table.  Predefined tests are `eq', `eql', and `equal'.  Default\n\
+is `eql'.  New tests can be defined with `define-hash-table-test'.\n\
+\n\
+Optional third argument WEAK must be one of nil, t, `key-weak',\n\
+ `value-weak' or `key-value-weak'.  WEAK t means the same as\n\
+ `key-value-weak'.  Default is nil.  Elements of weak hash tables\n\
+are removed when their key, value or both are otherwise unreferenced.\n\
+\n\
+The rest of the optional arguments are keyword/value pairs.  The\n\
+following are recognized:\n\
+\n\
+:REHASH-SIZE REHASH-SIZE - Indicates how to expand the table when\n\
+it fills up.  If REHASH-SIZE is an integer, add that many space.\n\
+If it is a float, it must be > 1.0, and the new size is computed by\n\
+multiplying the old size with that factor.  Default is 1.5.\n\
+\n\
+:REHASH-THRESHOLD THRESHOLD -- THRESHOLD must a float > 0, and <= 1.0.\n\
+Resize the hash table when ratio of the number of entries in the table.\n\
+Default is 0.8.")
+  (nargs, args)
+     int nargs;
+     Lisp_Object *args;
+{
+  Lisp_Object args2[nargs + 6];
+  int i, j;
+
+  /* Recognize size argument.  */
+  i = j = 0;
+  if (INTEGERP (args[i]))
+    {
+      args2[j++] = QCsize;
+      args2[j++] = args[i++];
+    }
+
+  /* Recognize test argument.  */
+  if (SYMBOLP (args[i])
+      && !EQ (args[i], QCrehash_size)
+      && !EQ (args[i], QCrehash_threshold)
+      && !EQ (args[i], QCweak))
+    {
+      args2[j++] = QCtest;
+      args2[j++] = args[i++];
+    }
+
+  /* Recognize weakness argument.  */
+  if (EQ (args[i], Qt)
+      || NILP (args[i])
+      || EQ (args[i], Qkey_weak)
+      || EQ (args[i], Qvalue_weak)
+      || EQ (args[i], Qkey_value_weak))
+    {
+      args2[j++] = QCweak;
+      args2[j++] = args[i++];
+    }
+
+  /* Copy remaining arguments.  */
+  while (i < nargs)
+    args2[j++] = args[i++];
+
+  return Fmake_hash_table (j, args2);
+}
+
+
+DEFUN ("hash-table-count", Fhash_table_count, Shash_table_count, 1, 1, 0,
+  "Return the number of elements in TABLE.")
+  (table)
+       Lisp_Object table;
+{
+  return check_hash_table (table)->count;
+}
+
+  
+DEFUN ("hash-table-rehash-size", Fhash_table_rehash_size,
+       Shash_table_rehash_size, 1, 1, 0,
+  "Return the current rehash size of TABLE.")
+  (table)
+       Lisp_Object table;
+{
+  return check_hash_table (table)->rehash_size;
+}
+  
+
+DEFUN ("hash-table-rehash-threshold", Fhash_table_rehash_threshold,
+       Shash_table_rehash_threshold, 1, 1, 0,
+  "Return the current rehash threshold of TABLE.")
+  (table)
+       Lisp_Object table;
+{
+  return check_hash_table (table)->rehash_threshold;
+}
+  
+
+DEFUN ("hash-table-size", Fhash_table_size, Shash_table_size, 1, 1, 0,
+  "Return the size of TABLE.\n\
+The size can be used as an argument to `make-hash-table' to create\n\
+a hash table than can hold as many elements of TABLE holds\n\
+without need for resizing.")
+  (table)
+       Lisp_Object table;
+{
+  struct Lisp_Hash_Table *h = check_hash_table (table);
+  return make_number (HASH_TABLE_SIZE (h));
+}
+  
+
+DEFUN ("hash-table-test", Fhash_table_test, Shash_table_test, 1, 1, 0,
+  "Return the test TABLE uses.")
+  (table)
+       Lisp_Object table;
+{
+  return check_hash_table (table)->test;
+}
+
+  
+DEFUN ("hash-table-weak", Fhash_table_weak, Shash_table_weak, 1, 1, 0,
+  "Return the weakness of TABLE.")
+  (table)
+       Lisp_Object table;
+{
+  return check_hash_table (table)->weak;
+}
+
+  
+DEFUN ("hash-table-p", Fhash_table_p, Shash_table_p, 1, 1, 0,
+  "Return t if OBJ is a Lisp hash table object.")
+  (obj)
+     Lisp_Object obj;
+{
+  return HASH_TABLE_P (obj) ? Qt : Qnil;
+}
+
+
+DEFUN ("clrhash", Fclrhash, Sclrhash, 1, 1, 0,
+  "Clear hash table TABLE.")
+  (table)
+     Lisp_Object table;
+{
+  hash_clear (check_hash_table (table));
+  return Qnil;
+}
+
+
+DEFUN ("gethash", Fgethash, Sgethash, 2, 3, 0,
+  "Look up KEY in TABLE and return its associated value.\n\
+If KEY is not found, return DFLT which defaults to nil.")
+  (table, key, dflt)
+     Lisp_Object table, key;
+{
+  struct Lisp_Hash_Table *h = check_hash_table (table);
+  int i = hash_lookup (h, key, NULL);
+  return i >= 0 ? HASH_VALUE (h, i) : dflt;
+}
+
+
+DEFUN ("puthash", Fputhash, Sputhash, 3, 3, 0,
+  "Associate KEY with VALUE is hash table TABLE.\n\
+If KEY is already present in table, replace its current value with\n\
+VALUE.")
+  (table, key, value)
+     Lisp_Object table, key, value;
+{
+  struct Lisp_Hash_Table *h = check_hash_table (table);
+  int i;
+  unsigned hash;
+
+  i = hash_lookup (h, key, &hash);
+  if (i >= 0)
+    HASH_VALUE (h, i) = value;
+  else
+    hash_put (h, key, value, hash);
+  
+  return Qnil;
+}
+
+
+DEFUN ("remhash", Fremhash, Sremhash, 2, 2, 0,
+  "Remove KEY from TABLE.")
+  (table, key)
+     Lisp_Object table, key;
+{
+  struct Lisp_Hash_Table *h = check_hash_table (table);
+  hash_remove (h, key);
+  return Qnil;
+}
+
+
+DEFUN ("maphash", Fmaphash, Smaphash, 2, 2, 0,
+  "Call FUNCTION for all entries in hash table TABLE.\n\
+FUNCTION is called with 2 arguments KEY and VALUE.")
+  (function, table)
+     Lisp_Object function, table;
+{
+  struct Lisp_Hash_Table *h = check_hash_table (table);
+  Lisp_Object args[3];
+  int i;
+
+  for (i = 0; i < HASH_TABLE_SIZE (h); ++i)
+    if (!NILP (HASH_HASH (h, i)))
+      {
+	args[0] = function;
+	args[1] = HASH_KEY (h, i);
+	args[2] = HASH_VALUE (h, i);
+	Ffuncall (3, args);
+      }
+  
+  return Qnil;
+}
+
+
+DEFUN ("define-hash-table-test", Fdefine_hash_table_test,
+       Sdefine_hash_table_test, 3, 3, 0,
+  "Define a new hash table test with name NAME, a symbol.\n\
+In hash tables create with NAME specified as test, use TEST to compare\n\
+keys, and HASH for computing hash codes of keys.\n\
+\n\
+TEST must be a function taking two arguments and returning non-nil\n\
+if both arguments are the same.   HASH must be a function taking\n\
+one argument and return an integer that is the hash code of the\n\
+argument.  Hash code computation should use the whole value range of\n\
+integers, including negative integers.")
+  (name, test, hash)
+     Lisp_Object name, test, hash;
+{
+  return Fput (name, Qhash_table_test, list2 (test, hash));
+}
+
+
+
 
 void
 syms_of_fns ()
 {
+  /* Hash table stuff.  */
+  Qhash_table_p = intern ("hash-table-p");
+  staticpro (&Qhash_table_p);
+  Qeq = intern ("eq");
+  staticpro (&Qeq);
+  Qeql = intern ("eql");
+  staticpro (&Qeql);
+  Qequal = intern ("equal");
+  staticpro (&Qequal);
+  QCtest = intern (":test");
+  staticpro (&QCtest);
+  QCsize = intern (":size");
+  staticpro (&QCsize);
+  QCrehash_size = intern (":rehash-size");
+  staticpro (&QCrehash_size);
+  QCrehash_threshold = intern (":rehash-threshold");
+  staticpro (&QCrehash_threshold);
+  QCweak = intern (":weak");
+  staticpro (&QCweak);
+  Qkey_weak = intern ("key-weak");
+  staticpro (&Qkey_weak);
+  Qvalue_weak = intern ("value-weak");
+  staticpro (&Qvalue_weak);
+  Qkey_value_weak = intern ("key-value-weak");
+  staticpro (&Qkey_value_weak);
+  Qhash_table_test = intern ("hash-table-test");
+  staticpro (&Qhash_table_test);
+
+  defsubr (&Ssxhash);
+  defsubr (&Smake_hash_table);
+  defsubr (&Smakehash);
+  defsubr (&Shash_table_count);
+  defsubr (&Shash_table_rehash_size);
+  defsubr (&Shash_table_rehash_threshold);
+  defsubr (&Shash_table_size);
+  defsubr (&Shash_table_test);
+  defsubr (&Shash_table_weak);
+  defsubr (&Shash_table_p);
+  defsubr (&Sclrhash);
+  defsubr (&Sgethash);
+  defsubr (&Sputhash);
+  defsubr (&Sremhash);
+  defsubr (&Smaphash);
+  defsubr (&Sdefine_hash_table_test);
+  
   Qstring_lessp = intern ("string-lessp");
   staticpro (&Qstring_lessp);
   Qprovide = intern ("provide");
@@ -3272,3 +4593,10 @@
   defsubr (&Sbase64_encode_string);
   defsubr (&Sbase64_decode_string);
 }
+
+
+void
+init_fns ()
+{
+  Vweak_hash_tables = Qnil;
+}