diff src/elhash.c @ 223:2c611d1463a6 r20-4b10

Import from CVS: tag r20-4b10
author cvs
date Mon, 13 Aug 2007 10:10:54 +0200
parents 78478c60bfcd
children 557eaa0339bf
line wrap: on
line diff
--- a/src/elhash.c	Mon Aug 13 10:10:03 2007 +0200
+++ b/src/elhash.c	Mon Aug 13 10:10:54 2007 +0200
@@ -1,6 +1,7 @@
 /* Lisp interface to hash tables.
    Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
    Copyright (C) 1995, 1996 Ben Wing.
+   Copyright (C) 1997 Free Software Foundation, Inc.
 
 This file is part of XEmacs.
 
@@ -27,7 +28,8 @@
 #include "elhash.h"
 #include "bytecode.h"
 
-Lisp_Object Qhashtablep;
+Lisp_Object Qhashtablep, Qhashtable;
+Lisp_Object Qweak, Qkey_weak, Qvalue_weak, Qnon_weak;
 
 #define LISP_OBJECTS_PER_HENTRY (sizeof (hentry) / sizeof (Lisp_Object))/* 2 */
 
@@ -69,28 +71,287 @@
   ((markobj) (table->zero_entry));
   return table->harray;
 }
+
+/* Printing hashtables.
+
+   This is non-trivial, because we use a readable structure-style
+   syntax for hashtables.  This means that a typical hashtable will be
+   readably printed in the form of:
+
+   #s(hashtable size 2 data (key1 value1 key2 value2))
+
+   The supported keywords are `type' (non-weak (or nil), weak,
+   key-weak and value-weak), `test' (eql (or nil), eq or equal),
+   `size' (a natnum or nil) and `data' (a list).
+
+   If `print-readably' is non-nil, then a simpler syntax is used; for
+   instance:
+
+   #<hashtable size 2/13 data (key1 value1 key2 value2) 0x874d>
+
+   The data is truncated to four pairs, and the rest is shown with
+   `...'.  The actual printer is non-consing.  */
+
+struct print_mapper_arg {
+  EMACS_INT count;		/* Used to implement the truncation
+				   for non-readable printing, as well
+				   as to avoid the unnecessary space
+				   at the beginning.  */
+  Lisp_Object printcharfun;
+};
+
+static void
+print_hashtable_data_mapper (void *key, void *contents, void *arg)
+{
+  Lisp_Object keytem, valuetem;
+  struct print_mapper_arg *closure = (struct print_mapper_arg *)arg;
+
+  if (closure->count < 4 || print_readably)
+    {
+      CVOID_TO_LISP (keytem, key);
+      CVOID_TO_LISP (valuetem, contents);
+
+      if (closure->count)
+	write_c_string (" ", closure->printcharfun);
+
+      print_internal (keytem, closure->printcharfun, 1);
+      write_c_string (" ", closure->printcharfun);
+      print_internal (valuetem, closure->printcharfun, 1);
+    }
+  ++closure->count;
+}
+
+/* Print the data of the hashtable.  This maps through a Lisp
+   hashtable and prints key/value pairs using PRINTCHARFUN.  */
+static void
+print_hashtable_data (Lisp_Object hashtable, Lisp_Object printcharfun)
+{
+  struct print_mapper_arg closure;
+  closure.count = 0;
+  closure.printcharfun = printcharfun;
+
+  write_c_string (" data (", printcharfun);
+  elisp_maphash (print_hashtable_data_mapper, hashtable, &closure);
+  write_c_string ((!print_readably && closure.count > 4) ? " ...)" : ")",
+		  printcharfun);
+}
+
+/* Needed for tests.  */
+static int lisp_object_eql_equal (CONST void *x1, CONST void *x2);
+static int lisp_object_equal_equal (CONST void *x1, CONST void *x2);
 
 static void
 print_hashtable (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
 {
   struct hashtable *table = XHASHTABLE (obj);
-  char buf[200];
+  char buf[128];
+
+  write_c_string (print_readably ? "#s(hashtable" : "#<hashtable",
+		  printcharfun);
+  if (table->type != HASHTABLE_NONWEAK)
+    {
+      sprintf (buf, " type %s",
+	       (table->type == HASHTABLE_WEAK ? "weak" :
+		table->type == HASHTABLE_KEY_WEAK ? "key-weak" :
+		table->type == HASHTABLE_VALUE_WEAK ? "value-weak" :
+		"you-d-better-not-see-this"));
+      write_c_string (buf, printcharfun);
+    }
+  /* These checks are way kludgy... */
+  if (table->test_function == NULL)
+    write_c_string (" test eq", printcharfun);
+  else if (table->test_function == lisp_object_equal_equal)
+    write_c_string (" test equal", printcharfun);
+  else if (table->test_function == lisp_object_eql_equal)
+      ;
+  else
+    abort ();
+  if (table->fullness || !print_readably)
+    {
+      if (print_readably)
+	sprintf (buf, " size %d", table->fullness);
+      else
+	sprintf (buf, " size %u/%ld", table->fullness,
+		 XVECTOR_LENGTH (table->harray) / LISP_OBJECTS_PER_HENTRY);
+      write_c_string (buf, printcharfun);
+    }
+  if (table->fullness)
+    print_hashtable_data (obj, printcharfun);
   if (print_readably)
-    error ("printing unreadable object #<hashtable 0x%x>",
-	   table->header.uid);
-  sprintf (buf, GETTEXT ("#<%shashtable %d/%ld 0x%x>"),
-	   (table->type == HASHTABLE_WEAK ? "weak " :
-	    table->type == HASHTABLE_KEY_WEAK ? "key-weak " :
-	    table->type == HASHTABLE_VALUE_WEAK ? "value-weak " :
-	    table->type == HASHTABLE_KEY_CAR_WEAK ? "key-car-weak " :
-	    table->type == HASHTABLE_VALUE_CAR_WEAK ? "value-car-weak " :
-	    ""),
-           table->fullness,
-           XVECTOR_LENGTH (table->harray) / LISP_OBJECTS_PER_HENTRY,
-           table->header.uid);
-  write_c_string (buf, printcharfun);
+    write_c_string (")", printcharfun);
+  else
+    {
+      sprintf (buf, " 0x%x>", table->header.uid);
+      write_c_string (buf, printcharfun);
+    }
+}
+
+
+/* Pretty reading of hashtables.
+
+   Here we use the existing structures mechanism (which is,
+   unfortunately, pretty cumbersome) for validating and instantiating
+   the hashtables.  The idea is that the side-effect of reading a
+   #s(hashtable PLIST) object is creation of a hashtable with desired
+   properties, and that the hashtable is returned.  */
+
+/* Validation functions: each keyword provides its own validation
+   function.  The errors should maybe be continuable, but it is
+   unclear how this would cope with ERRB.  */
+static int
+hashtable_type_validate (Lisp_Object keyword, Lisp_Object value,
+			 Error_behavior errb)
+{
+  if (!(NILP (value)
+	|| EQ (value, Qnon_weak)
+	|| EQ (value, Qweak)
+	|| EQ (value, Qkey_weak)
+	|| EQ (value, Qvalue_weak)))
+    {
+      maybe_signal_simple_error ("Invalid hashtable type", value,
+				 Qhashtable, errb);
+      return 0;
+    }
+  return 1;
+}
+
+static int
+hashtable_test_validate (Lisp_Object keyword, Lisp_Object value,
+			 Error_behavior errb)
+{
+  if (!(NILP (value)
+	|| EQ (value, Qeq)
+	|| EQ (value, Qeql)
+	|| EQ (value, Qequal)))
+    {
+      maybe_signal_simple_error ("Invalid hashtable test", value,
+				 Qhashtable, errb);
+      return 0;
+    }
+  return 1;
+}
+
+static int
+hashtable_size_validate (Lisp_Object keyword, Lisp_Object value,
+			 Error_behavior errb)
+{
+  if (!NATNUMP (value))
+    {
+      maybe_signal_error (Qwrong_type_argument, list2 (Qnatnump, value),
+			  Qhashtable, errb);
+      return 0;
+    }
+  return 1;
 }
 
+static int
+hashtable_data_validate (Lisp_Object keyword, Lisp_Object value,
+			 Error_behavior errb)
+{
+  int num = 0;
+  Lisp_Object tail;
+
+  /* #### Doesn't respect ERRB!  */
+  EXTERNAL_LIST_LOOP (tail, value)
+    {
+      ++num;
+      QUIT;
+    }
+  if (num & 1)
+    {
+      maybe_signal_simple_error
+	("Hashtable data must have alternating keyword/value pairs", value,
+	 Qhashtable, errb);
+      return 0;
+    }
+  return 1;
+}
+
+/* The actual instantiation of hashtable.  This does practically no
+   error checking, because it relies on the fact that the paranoid
+   functions above have error-checked everything to the last details.
+   If this assumption is wrong, we will get a crash immediately (with
+   error-checking compiled in), and we'll know if there is a bug in
+   the structure mechanism.  So there.  */
+static Lisp_Object
+hashtable_instantiate (Lisp_Object plist)
+{
+  /* I'm not sure whether this can GC, but better safe than sorry.  */
+  Lisp_Object hashtab = Qnil;
+  Lisp_Object type = Qnil, test = Qnil, size = Qnil, data = Qnil;
+  Lisp_Object key, value;
+  struct gcpro gcpro1;
+  GCPRO1 (hashtab);
+
+  while (!NILP (plist))
+    {
+      key = XCAR (plist);
+      plist = XCDR (plist);
+      value = XCAR (plist);
+      plist = XCDR (plist);
+      if (EQ (key, Qtype))
+	type = value;
+      else if (EQ (key, Qtest))
+	test = value;
+      else if (EQ (key, Qsize))
+	size = value;
+      else if (EQ (key, Qdata))
+	data = value;
+      else
+	abort ();
+    }
+  if (NILP (type))
+    type = Qnon_weak;
+  if (NILP (size))
+    {
+      /* Divide by two, because data is a plist. */
+      XSETINT (size, XINT (Flength (data)) / 2);
+    }
+
+  /* Create the hashtable.  */
+  if (EQ (type, Qnon_weak))
+    hashtab = Fmake_hashtable (size, test);
+  else if (EQ (type, Qweak))
+    hashtab = Fmake_weak_hashtable (size, test);
+  else if (EQ (type, Qkey_weak))
+    hashtab = Fmake_key_weak_hashtable (size, test);
+  else if (EQ (type, Qvalue_weak))
+    hashtab = Fmake_value_weak_hashtable (size, test);
+  else
+    abort ();
+
+  /* And fill it with data.  */
+  while (!NILP (data))
+    {
+      key = XCAR (data);
+      data = XCDR (data);
+      value = XCAR (data);
+      data = XCDR (data);
+      Fputhash (key, value, hashtab);
+    }
+
+  UNGCPRO;
+  return hashtab;
+}
+
+/* Initialize the hashtable as a structure type.  This is called from
+   emacs.c.  */
+void
+structure_type_create_hashtable (void)
+{
+  struct structure_type *st;
+
+  st = define_structure_type (Qhashtable, 0, hashtable_instantiate);
+  define_structure_type_keyword (st, Qtype, hashtable_type_validate);
+  define_structure_type_keyword (st, Qtest, hashtable_test_validate);
+  define_structure_type_keyword (st, Qsize, hashtable_size_validate);
+  define_structure_type_keyword (st, Qdata, hashtable_data_validate);
+}
+
+/* Basic conversion and allocation functions. */
+
+/* Create a C hashtable from the data in the Lisp hashtable.  The
+   actual vector is not copied, nor are the keys or values copied.  */
 static void
 ht_copy_to_c (struct hashtable *ht, c_hashtable c_table)
 {
@@ -453,6 +714,7 @@
 static void
 verify_function (Lisp_Object function, CONST char *description)
 {
+  /* #### Unused DESCRIPTION?  */
   if (SYMBOLP (function))
   {
     if (NILP (function))
@@ -464,7 +726,7 @@
     return;
   else if (CONSP (function))
   {
-    Lisp_Object funcar = Fcar (function);
+    Lisp_Object funcar = XCAR (function);
     if ((SYMBOLP (funcar)) && (EQ (funcar, Qlambda) ||
 			       EQ (funcar, Qautoload)))
       return;
@@ -717,7 +979,6 @@
 	   (The remhash above has taken care of zero_entry.)
 	   */
 	struct Lisp_Vector *ptr = XVECTOR (XHASHTABLE (rest)->harray);
-	int len = vector_length (ptr);
 #ifdef LRECORD_VECTOR
 	if (! MARKED_RECORD_P(XHASHTABLE(rest)->harray))
 	  {
@@ -725,6 +986,7 @@
 	    did_mark = 1;
 	  }
 #else
+	int len = vector_length (ptr);
 	if (len >= 0)
 	  {
 	    ptr->size = -1 - len;
@@ -879,6 +1141,11 @@
   DEFSUBR (Fmake_key_weak_hashtable);
   DEFSUBR (Fmake_value_weak_hashtable);
   defsymbol (&Qhashtablep, "hashtablep");
+  defsymbol (&Qhashtable, "hashtable");
+  defsymbol (&Qweak, "weak");
+  defsymbol (&Qkey_weak, "key-weak");
+  defsymbol (&Qvalue_weak, "value-weak");
+  defsymbol (&Qnon_weak, "non-weak");
 }
 
 void