XEmacs 21.2.47 (Zephir).
[chise/xemacs-chise.git.1] / src / elhash.c
index 52ba848..b034860 100644 (file)
@@ -29,13 +29,14 @@ Boston, MA 02111-1307, USA.  */
 
 Lisp_Object Qhash_tablep;
 static Lisp_Object Qhashtable, Qhash_table;
-static Lisp_Object Qweakness, Qvalue;
+static Lisp_Object Qweakness, Qvalue, Qkey_or_value, Qkey_and_value;
 static Lisp_Object Vall_weak_hash_tables;
 static Lisp_Object Qrehash_size, Qrehash_threshold;
 static Lisp_Object Q_size, Q_test, Q_weakness, Q_rehash_size, Q_rehash_threshold;
 
 /* obsolete as of 19990901 in xemacs-21.2 */
-static Lisp_Object Qweak, Qkey_weak, Qvalue_weak, Qnon_weak, Q_type;
+static Lisp_Object Qweak, Qkey_weak, Qvalue_weak, Qkey_or_value_weak;
+static Lisp_Object Qnon_weak, Q_type;
 
 typedef struct hentry
 {
@@ -70,9 +71,9 @@ struct Lisp_Hash_Table
 #define HASH_TABLE_MIN_SIZE 10
 
 #define HASH_CODE(key, ht)                                             \
-((((ht)->hash_function ? (ht)->hash_function (key) : LISP_HASH (key))  \
-  * (ht)->golden_ratio)                                                        \
- % (ht)->size)
+  ((((ht)->hash_function ? (ht)->hash_function (key) : LISP_HASH (key))        \
+    * (ht)->golden_ratio)                                              \
+   % (ht)->size)
 
 #define KEYS_EQUAL_P(key1, key2, testfun) \
   (EQ (key1, key2) || ((testfun) && (testfun) (key1, key2)))
@@ -276,7 +277,7 @@ hash_table_hash (Lisp_Object hash_table, int depth)
    `size'             (a natnum or nil)
    `rehash-size'      (a float)
    `rehash-threshold' (a float)
-   `weakness'         (nil, t, key or value)
+   `weakness'         (nil, key, value, key-and-value, or key-or-value)
    `data'             (a list)
 
    If `print-readably' is nil, then a simpler syntax is used, for example
@@ -351,9 +352,10 @@ print_hash_table (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
   if (ht->weakness != HASH_TABLE_NON_WEAK)
     {
       sprintf (buf, " weakness %s",
-              (ht->weakness == HASH_TABLE_WEAK       ? "t"     :
-               ht->weakness == HASH_TABLE_KEY_WEAK   ? "key"   :
-               ht->weakness == HASH_TABLE_VALUE_WEAK ? "value" :
+              (ht->weakness == HASH_TABLE_WEAK           ? "key-and-value" :
+               ht->weakness == HASH_TABLE_KEY_WEAK       ? "key" :
+               ht->weakness == HASH_TABLE_VALUE_WEAK     ? "value" :
+               ht->weakness == HASH_TABLE_KEY_VALUE_WEAK ? "key-or-value" :
                "you-d-better-not-see-this"));
       write_c_string (buf, printcharfun);
     }
@@ -432,36 +434,54 @@ compute_hash_table_derived_values (Lisp_Hash_Table *ht)
 }
 
 Lisp_Object
-make_general_lisp_hash_table (enum hash_table_test test,
-                             size_t size,
-                             double rehash_size,
-                             double rehash_threshold,
-                             enum hash_table_weakness weakness)
+make_standard_lisp_hash_table (enum hash_table_test test,
+                              size_t size,
+                              double rehash_size,
+                              double rehash_threshold,
+                              enum hash_table_weakness weakness)
 {
-  Lisp_Object hash_table;
-  Lisp_Hash_Table *ht = alloc_lcrecord_type (Lisp_Hash_Table, &lrecord_hash_table);
+  hash_table_hash_function_t hash_function =  0;
+  hash_table_test_function_t test_function = 0;
 
   switch (test)
     {
     case HASH_TABLE_EQ:
-      ht->test_function = 0;
-      ht->hash_function = 0;
+      test_function = 0;
+      hash_function = 0;
       break;
 
     case HASH_TABLE_EQL:
-      ht->test_function = lisp_object_eql_equal;
-      ht->hash_function = lisp_object_eql_hash;
+      test_function = lisp_object_eql_equal;
+      hash_function = lisp_object_eql_hash;
       break;
 
     case HASH_TABLE_EQUAL:
-      ht->test_function = lisp_object_equal_equal;
-      ht->hash_function = lisp_object_equal_hash;
+      test_function = lisp_object_equal_equal;
+      hash_function = lisp_object_equal_hash;
       break;
 
     default:
       abort ();
     }
 
+  return make_general_lisp_hash_table (hash_function, test_function,
+                                      size, rehash_size, rehash_threshold,
+                                      weakness);
+}
+
+Lisp_Object
+make_general_lisp_hash_table (hash_table_hash_function_t hash_function,
+                             hash_table_test_function_t test_function,
+                             size_t size,
+                             double rehash_size,
+                             double rehash_threshold,
+                             enum hash_table_weakness weakness)
+{
+  Lisp_Object hash_table;
+  Lisp_Hash_Table *ht = alloc_lcrecord_type (Lisp_Hash_Table, &lrecord_hash_table);
+
+  ht->test_function = test_function;
+  ht->hash_function = hash_function;
   ht->weakness = weakness;
 
   ht->rehash_size =
@@ -480,13 +500,7 @@ make_general_lisp_hash_table (enum hash_table_test test,
   compute_hash_table_derived_values (ht);
 
   /* We leave room for one never-occupied sentinel hentry at the end.  */
-  ht->hentries = xnew_array (hentry, ht->size + 1);
-
-  {
-    hentry *e, *sentinel;
-    for (e = ht->hentries, sentinel = e + ht->size; e <= sentinel; e++)
-      CLEAR_HENTRY (e);
-  }
+  ht->hentries = xnew_array_and_zero (hentry, ht->size + 1);
 
   XSETHASH_TABLE (hash_table, ht);
 
@@ -503,7 +517,7 @@ make_lisp_hash_table (size_t size,
                      enum hash_table_weakness weakness,
                      enum hash_table_test test)
 {
-  return make_general_lisp_hash_table (test, size, -1.0, -1.0, weakness);
+  return make_standard_lisp_hash_table (test, size, -1.0, -1.0, weakness);
 }
 
 /* Pretty reading of hash tables.
@@ -539,16 +553,19 @@ static int
 hash_table_weakness_validate (Lisp_Object keyword, Lisp_Object value,
                              Error_behavior errb)
 {
-  if (EQ (value, Qnil))                return 1;
-  if (EQ (value, Qt))          return 1;
-  if (EQ (value, Qkey))                return 1;
-  if (EQ (value, Qvalue))      return 1;
+  if (EQ (value, Qnil))                        return 1;
+  if (EQ (value, Qt))                  return 1;
+  if (EQ (value, Qkey))                        return 1;
+  if (EQ (value, Qkey_and_value))      return 1;
+  if (EQ (value, Qkey_or_value))       return 1;
+  if (EQ (value, Qvalue))              return 1;
 
   /* Following values are obsolete as of 19990901 in xemacs-21.2 */
-  if (EQ (value, Qnon_weak))   return 1;
-  if (EQ (value, Qweak))       return 1;
-  if (EQ (value, Qkey_weak))   return 1;
-  if (EQ (value, Qvalue_weak)) return 1;
+  if (EQ (value, Qnon_weak))           return 1;
+  if (EQ (value, Qweak))               return 1;
+  if (EQ (value, Qkey_weak))           return 1;
+  if (EQ (value, Qkey_or_value_weak))  return 1;
+  if (EQ (value, Qvalue_weak))         return 1;
 
   maybe_signal_simple_error ("Invalid hash table weakness",
                             value, Qhash_table, errb);
@@ -558,16 +575,19 @@ hash_table_weakness_validate (Lisp_Object keyword, Lisp_Object value,
 static enum hash_table_weakness
 decode_hash_table_weakness (Lisp_Object obj)
 {
-  if (EQ (obj, Qnil))       return HASH_TABLE_NON_WEAK;
-  if (EQ (obj, Qt))         return HASH_TABLE_WEAK;
-  if (EQ (obj, Qkey))        return HASH_TABLE_KEY_WEAK;
-  if (EQ (obj, Qvalue))      return HASH_TABLE_VALUE_WEAK;
+  if (EQ (obj, Qnil))                  return HASH_TABLE_NON_WEAK;
+  if (EQ (obj, Qt))                    return HASH_TABLE_WEAK;
+  if (EQ (obj, Qkey_and_value))                return HASH_TABLE_WEAK;
+  if (EQ (obj, Qkey))                  return HASH_TABLE_KEY_WEAK;
+  if (EQ (obj, Qkey_or_value))         return HASH_TABLE_KEY_VALUE_WEAK;
+  if (EQ (obj, Qvalue))                        return HASH_TABLE_VALUE_WEAK;
 
   /* Following values are obsolete as of 19990901 in xemacs-21.2 */
-  if (EQ (obj, Qnon_weak))   return HASH_TABLE_NON_WEAK;
-  if (EQ (obj, Qweak))      return HASH_TABLE_WEAK;
-  if (EQ (obj, Qkey_weak))   return HASH_TABLE_KEY_WEAK;
-  if (EQ (obj, Qvalue_weak)) return HASH_TABLE_VALUE_WEAK;
+  if (EQ (obj, Qnon_weak))             return HASH_TABLE_NON_WEAK;
+  if (EQ (obj, Qweak))                 return HASH_TABLE_WEAK;
+  if (EQ (obj, Qkey_weak))             return HASH_TABLE_KEY_WEAK;
+  if (EQ (obj, Qkey_or_value_weak))    return HASH_TABLE_KEY_VALUE_WEAK;
+  if (EQ (obj, Qvalue_weak))           return HASH_TABLE_VALUE_WEAK;
 
   signal_simple_error ("Invalid hash table weakness", obj);
   return HASH_TABLE_NON_WEAK; /* not reached */
@@ -714,7 +734,7 @@ hash_table_instantiate (Lisp_Object plist)
     }
 
   /* Create the hash table.  */
-  hash_table = make_general_lisp_hash_table
+  hash_table = make_standard_lisp_hash_table
     (decode_hash_table_test (test),
      decode_hash_table_size (size),
      decode_hash_table_rehash_size (rehash_size),
@@ -800,14 +820,16 @@ the factor by which to increase the size of the hash table when enlarging.
 Keyword :rehash-threshold must be a float between 0.0 and 1.0,
 and specifies the load factor of the hash table which triggers enlarging.
 
-Non-standard keyword :weakness can be `nil' (default), `t', `key' or `value'.
+Non-standard keyword :weakness can be `nil' (default), `t', `key-and-value',
+`key', `value' or `key-or-value'. `t' is an alias for `key-and-value'.
 
-A weak hash table is one whose pointers do not count as GC referents:
-for any key-value pair in the hash table, if the only remaining pointer
-to either the key or the value is in a weak hash table, then the pair
-will be removed from the hash table, and the key and value collected.
-A non-weak hash table (or any other pointer) would prevent the object
-from being collected.
+A key-and-value-weak hash table, also known as a fully-weak or simply
+as a weak hash table, is one whose pointers do not count as GC
+referents: for any key-value pair in the hash table, if the only
+remaining pointer to either the key or the value is in a weak hash
+table, then the pair will be removed from the hash table, and the key
+and value collected.  A non-weak hash table (or any other pointer)
+would prevent the object from being collected.
 
 A key-weak hash table is similar to a fully-weak hash table except that
 a key-value pair will be removed only if the key remains unmarked
@@ -820,6 +842,12 @@ that a key-value pair will be removed only if the value remains
 unmarked outside of weak hash tables.  The pair will remain in the
 hash table if the value is pointed to by something other than a weak
 hash table, even if the key is not.
+
+A key-or-value-weak hash table is similar to a fully-weak hash table except
+that a key-value pair will be removed only if the value and the key remain
+unmarked outside of weak hash tables.  The pair will remain in the
+hash table if the value or key are pointed to by something other than a weak
+hash table, even if the other is not.
 */
        (int nargs, Lisp_Object *args))
 {
@@ -856,7 +884,7 @@ if (!NILP (var)) hash_table_##var##_validate (Q##var, var, ERROR_ME);
   VALIDATE_VAR (rehash_threshold);
   VALIDATE_VAR (weakness);
 
-  return make_general_lisp_hash_table
+  return make_standard_lisp_hash_table
     (decode_hash_table_test (test),
      decode_hash_table_size (size),
      decode_hash_table_rehash_size (rehash_size),
@@ -1109,16 +1137,17 @@ beyond which the HASH-TABLE is enlarged by rehashing.
 
 DEFUN ("hash-table-weakness", Fhash_table_weakness, 1, 1, 0, /*
 Return the weakness of HASH-TABLE.
-This can be one of `nil', `t', `key' or `value'.
+This can be one of `nil', `key-and-value', `key-or-value', `key' or `value'.
 */
        (hash_table))
 {
   switch (xhash_table (hash_table)->weakness)
     {
-    case HASH_TABLE_WEAK:      return Qt;
-    case HASH_TABLE_KEY_WEAK:  return Qkey;
-    case HASH_TABLE_VALUE_WEAK:        return Qvalue;
-    default:                   return Qnil;
+    case HASH_TABLE_WEAK:              return Qkey_and_value;
+    case HASH_TABLE_KEY_WEAK:          return Qkey;
+    case HASH_TABLE_KEY_VALUE_WEAK:    return Qkey_or_value;
+    case HASH_TABLE_VALUE_WEAK:                return Qvalue;
+    default:                           return Qnil;
     }
 }
 
@@ -1131,10 +1160,11 @@ This can be one of `non-weak', `weak', `key-weak' or `value-weak'.
 {
   switch (xhash_table (hash_table)->weakness)
     {
-    case HASH_TABLE_WEAK:      return Qweak;
-    case HASH_TABLE_KEY_WEAK:  return Qkey_weak;
-    case HASH_TABLE_VALUE_WEAK:        return Qvalue_weak;
-    default:                   return Qnon_weak;
+    case HASH_TABLE_WEAK:              return Qweak;
+    case HASH_TABLE_KEY_WEAK:          return Qkey_weak;
+    case HASH_TABLE_KEY_VALUE_WEAK:    return Qkey_or_value_weak;
+    case HASH_TABLE_VALUE_WEAK:                return Qvalue_weak;
+    default:                           return Qnon_weak;
     }
 }
 
@@ -1171,6 +1201,19 @@ may remhash or puthash the entry currently being processed by FUNCTION.
   return Qnil;
 }
 
+/* #### If the Lisp function being called does a puthash and this
+   #### causes the hash table to be resized, the results will be quite
+   #### random and we will likely crash.  To fix this, either set a
+   #### flag in the hash table while we're mapping and signal an error
+   #### when new entries are added, or fix things to make this
+   #### operation work properly, like this: Store two hash tables in
+   #### each hash table object -- the second one is written to when
+   #### you do a puthash inside of a mapping operation, and the
+   #### various operations need to check both hash tables for entries.
+   #### As soon as the last maphash over a particular hash table
+   #### object terminates, the entries in the second table are added
+   #### to the first (using an unwind-protect). --ben */
+
 /* Map *C* function FUNCTION over the elements of a lisp hash table. */
 void
 elisp_maphash (maphash_function_t function,
@@ -1218,6 +1261,15 @@ elisp_map_remhash (maphash_function_t predicate,
 /************************************************************************/
 /*                garbage collecting weak hash tables                  */
 /************************************************************************/
+#define MARK_OBJ(obj) do {             \
+  Lisp_Object mo_obj = (obj);          \
+  if (!marked_p (mo_obj))              \
+    {                                  \
+      mark_object (mo_obj);            \
+      did_mark = 1;                    \
+    }                                  \
+} while (0)
+
 
 /* Complete the marking for semi-weak hash tables. */
 int
@@ -1241,9 +1293,6 @@ finish_marking_weak_hash_tables (void)
       /* Now, scan over all the pairs.  For all pairs that are
         half-marked, we may need to mark the other half if we're
         keeping this pair. */
-#define MARK_OBJ(obj) \
-do { if (!marked_p (obj)) mark_object (obj), did_mark = 1; } while (0)
-
       switch (ht->weakness)
        {
        case HASH_TABLE_KEY_WEAK:
@@ -1260,6 +1309,17 @@ do { if (!marked_p (obj)) mark_object (obj), did_mark = 1; } while (0)
                MARK_OBJ (e->key);
          break;
 
+       case HASH_TABLE_KEY_VALUE_WEAK:
+         for (; e < sentinel; e++)
+           if (!HENTRY_CLEAR_P (e))
+             {
+               if (marked_p (e->value))
+                 MARK_OBJ (e->key);
+               else if (marked_p (e->key))
+                 MARK_OBJ (e->value);
+             }
+         break;
+
        case HASH_TABLE_KEY_CAR_WEAK:
          for (; e < sentinel; e++)
            if (!HENTRY_CLEAR_P (e))
@@ -1270,6 +1330,24 @@ do { if (!marked_p (obj)) mark_object (obj), did_mark = 1; } while (0)
                }
          break;
 
+         /* We seem to be sprouting new weakness types at an alarming
+            rate. At least this is not externally visible - and in
+            fact all of these KEY_CAR_* types are only used by the
+            glyph code. */
+       case HASH_TABLE_KEY_CAR_VALUE_WEAK:
+         for (; e < sentinel; e++)
+           if (!HENTRY_CLEAR_P (e))
+             {
+               if (!CONSP (e->key) || marked_p (XCAR (e->key)))
+                 {
+                   MARK_OBJ (e->key);
+                   MARK_OBJ (e->value);
+                 }
+               else if (marked_p (e->value))
+                 MARK_OBJ (e->key);
+             }
+         break;
+
        case HASH_TABLE_VALUE_CAR_WEAK:
          for (; e < sentinel; e++)
            if (!HENTRY_CLEAR_P (e))
@@ -1397,7 +1475,7 @@ internal_hash (Lisp_Object obj, int depth)
 
 DEFUN ("sxhash", Fsxhash, 1, 1, 0, /*
 Return a hash value for OBJECT.
-(equal obj1 obj2) implies (= (sxhash obj1) (sxhash obj2)).
+\(equal obj1 obj2) implies (= (sxhash obj1) (sxhash obj2)).
 */
        (object))
 {
@@ -1452,11 +1530,14 @@ syms_of_elhash (void)
   defsymbol (&Qhashtable, "hashtable");
   defsymbol (&Qweakness, "weakness");
   defsymbol (&Qvalue, "value");
+  defsymbol (&Qkey_or_value, "key-or-value");
+  defsymbol (&Qkey_and_value, "key-and-value");
   defsymbol (&Qrehash_size, "rehash-size");
   defsymbol (&Qrehash_threshold, "rehash-threshold");
 
   defsymbol (&Qweak, "weak");             /* obsolete */
   defsymbol (&Qkey_weak, "key-weak");     /* obsolete */
+  defsymbol (&Qkey_or_value_weak, "key-or-value-weak");    /* obsolete */
   defsymbol (&Qvalue_weak, "value-weak"); /* obsolete */
   defsymbol (&Qnon_weak, "non-weak");     /* obsolete */
 
@@ -1473,5 +1554,5 @@ vars_of_elhash (void)
 {
   /* This must NOT be staticpro'd */
   Vall_weak_hash_tables = Qnil;
-  pdump_wire_list (&Vall_weak_hash_tables);
+  dump_add_weak_object_chain (&Vall_weak_hash_tables);
 }