Mercurial > hg > xemacs-beta
diff src/elhash.c @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/elhash.c Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,916 @@ +/* Lisp interface to hash tables. + Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. + Copyright (C) 1995, 1996 Ben Wing. + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +#include <config.h> +#include "lisp.h" +#include "hash.h" +#include "elhash.h" +#include "bytecode.h" + +Lisp_Object Qhashtablep; + +#define LISP_OBJECTS_PER_HENTRY (sizeof (hentry) / sizeof (Lisp_Object))/* 2 */ + +struct hashtable_struct +{ + struct lcrecord_header header; + unsigned int fullness; + unsigned long (*hash_function) (CONST void *); + int (*test_function) (CONST void *, CONST void *); + Lisp_Object zero_entry; + Lisp_Object harray; + enum hashtable_type type; /* whether and how this hashtable is weak */ + Lisp_Object next_weak; /* Used to chain together all of the weak + hashtables. Don't mark through this. */ +}; + +static Lisp_Object Vall_weak_hashtables; + +static Lisp_Object mark_hashtable (Lisp_Object, void (*) (Lisp_Object)); +static void print_hashtable (Lisp_Object, Lisp_Object, int); +DEFINE_LRECORD_IMPLEMENTATION ("hashtable", hashtable, + mark_hashtable, print_hashtable, 0, 0, 0, + struct hashtable_struct); + +static Lisp_Object +mark_hashtable (Lisp_Object obj, void (*markobj) (Lisp_Object)) +{ + struct hashtable_struct *table = XHASHTABLE (obj); + + if (table->type != HASHTABLE_NONWEAK) + { + /* If the table is weak, we don't want to mark the keys and values + (we scan over them after everything else has been marked, + and mark or remove them as necessary). Note that we will mark + the table->harray itself at the same time; it's hard to mark + that here without also marking its contents. */ + return Qnil; + } + ((markobj) (table->zero_entry)); + return (table->harray); +} + +static void +print_hashtable (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag) +{ + struct hashtable_struct *table = XHASHTABLE (obj); + char buf[200]; + 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, + (vector_length (XVECTOR (table->harray)) / LISP_OBJECTS_PER_HENTRY), + table->header.uid); + write_c_string (buf, printcharfun); +} + +static void +ht_copy_to_c (struct hashtable_struct *ht, + c_hashtable c_table) +{ + int len; + + c_table->harray = (void *) vector_data (XVECTOR (ht->harray)); + c_table->zero_set = (!GC_UNBOUNDP (ht->zero_entry)); + c_table->zero_entry = LISP_TO_VOID (ht->zero_entry); + len = vector_length (XVECTOR (ht->harray)); + if (len < 0) + { + /* #### if alloc.c mark_object() changes, this must change too. */ + /* barf gag retch. When a vector is marked, its len is + made less than 0. In the prune_weak_hashtables() stage, + we are called on vectors that are like this, and we must + be able to deal. */ + assert (gc_in_progress); + len = -1 - len; + } + c_table->size = len/LISP_OBJECTS_PER_HENTRY; + c_table->fullness = ht->fullness; + c_table->hash_function = ht->hash_function; + c_table->test_function = ht->test_function; + XSETHASHTABLE (c_table->elisp_table, ht); +} + +static void +ht_copy_from_c (c_hashtable c_table, + struct hashtable_struct *ht) +{ + struct Lisp_Vector dummy; + /* C is truly hateful */ + void *vec_addr + = ((char *) c_table->harray + - ((char *) &(dummy.contents) - (char *) &dummy)); + + XSETVECTOR (ht->harray, vec_addr); + if (c_table->zero_set) + VOID_TO_LISP (ht->zero_entry, c_table->zero_entry); + else + ht->zero_entry = Qunbound; + ht->fullness = c_table->fullness; +} + + +static struct hashtable_struct * +allocate_hashtable (void) +{ + struct hashtable_struct *table + = alloc_lcrecord (sizeof (struct hashtable_struct), lrecord_hashtable); + table->harray = Qnil; + table->zero_entry = Qunbound; + table->fullness = 0; + table->hash_function = 0; + table->test_function = 0; + return (table); +} + +char * +elisp_hvector_malloc (unsigned int bytes, Lisp_Object table) +{ + Lisp_Object new_vector; + struct hashtable_struct *ht; + + ht = XHASHTABLE (table); + assert (bytes > vector_length (XVECTOR (ht->harray)) * sizeof (Lisp_Object)); + new_vector = make_vector ((bytes / sizeof (Lisp_Object)), Qzero); + return ((char *) (vector_data (XVECTOR (new_vector)))); +} + +void +elisp_hvector_free (void *ptr, Lisp_Object table) +{ + struct hashtable_struct *ht = XHASHTABLE (table); +#if defined (USE_ASSERTIONS) || defined (DEBUG_XEMACS) + Lisp_Object current_vector = ht->harray; +#endif + + assert (((void *) vector_data (XVECTOR (current_vector))) == ptr); + ht->harray = Qnil; /* Let GC do its job */ + return; +} + + +DEFUN ("hashtablep", Fhashtablep, Shashtablep, 1, 1, 0 /* +Return t if OBJ is a hashtable, else nil. +*/ ) + (obj) + Lisp_Object obj; +{ + return ((HASHTABLEP (obj)) ? Qt : Qnil); +} + + + + +#if 0 /* I don't think these are needed any more. + If using the general lisp_object_equal_*() functions + causes efficiency problems, these can be resurrected. --ben */ +/* equality and hash functions for Lisp strings */ +int +lisp_string_equal (CONST void *x1, CONST void *x2) +{ + Lisp_Object str1, str2; + CVOID_TO_LISP (str1, x1); + CVOID_TO_LISP (str2, x2); + return !strcmp ((char *) string_data (XSTRING (str1)), + (char *) string_data (XSTRING (str2))); +} + +unsigned long +lisp_string_hash (CONST void *x) +{ + Lisp_Object str; + CVOID_TO_LISP (str, x); + return hash_string (string_data (XSTRING (str)), + string_length (XSTRING (str))); +} + +#endif /* 0 */ + +static int +lisp_object_eql_equal (CONST void *x1, CONST void *x2) +{ + Lisp_Object obj1, obj2; + CVOID_TO_LISP (obj1, x1); + CVOID_TO_LISP (obj2, x2); + return + (FLOATP (obj1) ? !NILP (Fequal (obj1, obj2)) : EQ (obj1, obj2)); +} + +static unsigned long +lisp_object_eql_hash (CONST void *x) +{ + Lisp_Object obj; + CVOID_TO_LISP (obj, x); + if (FLOATP (obj)) + return internal_hash (obj, 0); + else + return LISP_HASH (obj); +} + +static int +lisp_object_equal_equal (CONST void *x1, CONST void *x2) +{ + Lisp_Object obj1, obj2; + CVOID_TO_LISP (obj1, x1); + CVOID_TO_LISP (obj2, x2); + return !NILP (Fequal (obj1, obj2)); +} + +static unsigned long +lisp_object_equal_hash (CONST void *x) +{ + Lisp_Object obj; + CVOID_TO_LISP (obj, x); + return internal_hash (obj, 0); +} + +Lisp_Object +make_lisp_hashtable (int size, + enum hashtable_type type, + enum hashtable_test_fun test) +{ + Lisp_Object result; + struct hashtable_struct *table = allocate_hashtable (); + + table->harray = make_vector ((compute_harray_size (size) + * LISP_OBJECTS_PER_HENTRY), + Qzero); + switch (test) + { + case HASHTABLE_EQ: + table->test_function = 0; + table->hash_function = 0; + break; + + case HASHTABLE_EQL: + table->test_function = lisp_object_eql_equal; + table->hash_function = lisp_object_eql_hash; + break; + + case HASHTABLE_EQUAL: + table->test_function = lisp_object_equal_equal; + table->hash_function = lisp_object_equal_hash; + break; + + default: + abort (); + } + + table->type = type; + XSETHASHTABLE (result, table); + + if (table->type != HASHTABLE_NONWEAK) + { + table->next_weak = Vall_weak_hashtables; + Vall_weak_hashtables = result; + } + else + table->next_weak = Qunbound; + + return (result); +} + +static enum hashtable_test_fun +decode_hashtable_test_fun (Lisp_Object sym) +{ + if (NILP (sym)) + return HASHTABLE_EQL; + + CHECK_SYMBOL (sym); + + if (EQ (sym, Qeq)) + return HASHTABLE_EQ; + if (EQ (sym, Qequal)) + return HASHTABLE_EQUAL; + if (EQ (sym, Qeql)) + return HASHTABLE_EQL; + signal_simple_error ("Invalid hashtable test fun", sym); + return 0; /* not reached */ +} + +DEFUN ("make-hashtable", Fmake_hashtable, Smake_hashtable, 1, 2, 0 /* +Make a hashtable of initial size SIZE. +Comparison between keys is done with TEST-FUN, which must be one of +`eq', `eql', or `equal'. The default is `eql'; i.e. two keys must +be the same object (or have the same floating-point value, for floats) +to be considered equivalent. + +See also `make-weak-hashtable', `make-key-weak-hashtable', and +`make-value-weak-hashtable'. +*/ ) + (size, test_fun) + Lisp_Object size, test_fun; +{ + CHECK_NATNUM (size); + return make_lisp_hashtable (XINT (size), HASHTABLE_NONWEAK, + decode_hashtable_test_fun (test_fun)); +} + +DEFUN ("copy-hashtable", Fcopy_hashtable, Scopy_hashtable, 1, 1, 0 /* +Make a new hashtable which contains the same keys and values +as the given table. The keys and values will not themselves be copied. +*/ ) + (old_table) + Lisp_Object old_table; +{ + struct _C_hashtable old_htbl; + struct _C_hashtable new_htbl; + struct hashtable_struct *old_ht; + struct hashtable_struct *new_ht; + Lisp_Object result; + + CHECK_HASHTABLE (old_table); + old_ht = XHASHTABLE (old_table); + ht_copy_to_c (old_ht, &old_htbl); + + /* we can't just call Fmake_hashtable() here because that will make a + table that is slightly larger than the one we're trying to copy, + which will make copy_hash() blow up. */ + new_ht = allocate_hashtable (); + new_ht->fullness = 0; + new_ht->zero_entry = Qunbound; + new_ht->hash_function = old_ht->hash_function; + new_ht->test_function = old_ht->test_function; + new_ht->harray = Fmake_vector (Flength (old_ht->harray), Qzero); + ht_copy_to_c (new_ht, &new_htbl); + copy_hash (&new_htbl, &old_htbl); + ht_copy_from_c (&new_htbl, new_ht); + new_ht->type = old_ht->type; + XSETHASHTABLE (result, new_ht); + + if (UNBOUNDP (old_ht->next_weak)) + new_ht->next_weak = Qunbound; + else + { + new_ht->next_weak = Vall_weak_hashtables; + Vall_weak_hashtables = result; + } + + return (result); +} + + +DEFUN ("gethash", Fgethash, Sgethash, 2, 3, 0 /* +Find hash value for KEY in TABLE. +If there is no corresponding value, return DEFAULT (defaults to nil). +*/ ) + (key, table, defalt) + Lisp_Object key, table, defalt; /* One can't even spell correctly in C */ +{ + CONST void *vval; + struct _C_hashtable htbl; + if (!gc_in_progress) + CHECK_HASHTABLE (table); + ht_copy_to_c (XHASHTABLE (table), &htbl); + if (gethash (LISP_TO_VOID (key), &htbl, &vval)) + { + Lisp_Object val; + CVOID_TO_LISP (val, vval); + return val; + } + else + return defalt; +} + + +DEFUN ("remhash", Fremhash, Sremhash, 2, 2, 0 /* +Remove hash value for KEY in TABLE. +*/ ) + (key, table) + Lisp_Object key, table; +{ + struct _C_hashtable htbl; + CHECK_HASHTABLE (table); + + ht_copy_to_c (XHASHTABLE (table), &htbl); + remhash (LISP_TO_VOID (key), &htbl); + ht_copy_from_c (&htbl, XHASHTABLE (table)); + return Qnil; +} + + +DEFUN ("puthash", Fputhash, Sputhash, 3, 3, 0 /* +Hash KEY to VAL in TABLE. +*/ ) + (key, val, table) + Lisp_Object key, val, table; +{ + struct hashtable_struct *ht; + void *vkey = LISP_TO_VOID (key); + + CHECK_HASHTABLE (table); + ht = XHASHTABLE (table); + if (!vkey) + ht->zero_entry = val; + else + { + struct gcpro gcpro1, gcpro2, gcpro3; + struct _C_hashtable htbl; + + ht_copy_to_c (XHASHTABLE (table), &htbl); + GCPRO3 (key, val, table); + puthash (vkey, LISP_TO_VOID (val), &htbl); + ht_copy_from_c (&htbl, XHASHTABLE (table)); + UNGCPRO; + } + return (val); +} + +DEFUN ("clrhash", Fclrhash, Sclrhash, 1, 1, 0 /* +Flush TABLE. +*/ ) + (table) + Lisp_Object table; +{ + struct _C_hashtable htbl; + CHECK_HASHTABLE (table); + ht_copy_to_c (XHASHTABLE (table), &htbl); + clrhash (&htbl); + ht_copy_from_c (&htbl, XHASHTABLE (table)); + return Qnil; +} + +DEFUN ("hashtable-fullness", Fhashtable_fullness, Shashtable_fullness, 1, 1, 0 /* +Return number of entries in TABLE. +*/ ) + (table) + Lisp_Object table; +{ + struct _C_hashtable htbl; + CHECK_HASHTABLE (table); + ht_copy_to_c (XHASHTABLE (table), &htbl); + return (make_int (htbl.fullness)); +} + + +static void +verify_function (Lisp_Object function, CONST char *description) +{ + if (SYMBOLP (function)) + { + if (NILP (function)) + return; + else + function = indirect_function (function, 1); + } + if (SUBRP (function) || COMPILED_FUNCTIONP (function)) + return; + else if (CONSP (function)) + { + Lisp_Object funcar = Fcar (function); + if ((SYMBOLP (funcar)) + && (EQ (funcar, Qlambda) +#ifdef MOCKLISP_SUPPORT + || EQ (funcar, Qmocklisp) +#endif + || EQ (funcar, Qautoload))) + return; + } + signal_error (Qinvalid_function, list1 (function)); +} + +static void +lisp_maphash_function (CONST void *void_key, + void *void_val, + void *void_fn) +{ + /* This function can GC */ + Lisp_Object key, val, fn; + CVOID_TO_LISP (key, void_key); + VOID_TO_LISP (val, void_val); + VOID_TO_LISP (fn, void_fn); + call2 (fn, key, val); +} + + +DEFUN ("maphash", Fmaphash, Smaphash, 2, 2, 0 /* +Map FUNCTION over entries in TABLE, calling it with two args, +each key and value in the table. +*/ ) + (function, table) + Lisp_Object function, table; +{ + struct _C_hashtable htbl; + struct gcpro gcpro1, gcpro2; + + verify_function (function, GETTEXT ("hashtable mapping function")); + CHECK_HASHTABLE (table); + ht_copy_to_c (XHASHTABLE (table), &htbl); + GCPRO2 (table, function); + maphash (lisp_maphash_function, &htbl, LISP_TO_VOID (function)); + UNGCPRO; + return Qnil; +} + + +/* This function is for mapping a *C* function over the elements of a + lisp hashtable. + */ +void +elisp_maphash (maphash_function function, Lisp_Object table, void *closure) +{ + struct _C_hashtable htbl; + + if (!gc_in_progress) CHECK_HASHTABLE (table); + ht_copy_to_c (XHASHTABLE (table), &htbl); + maphash (function, &htbl, closure); +} + +void +elisp_map_remhash (remhash_predicate function, + Lisp_Object table, + void *closure) +{ + struct _C_hashtable htbl; + + if (!gc_in_progress) CHECK_HASHTABLE (table); + ht_copy_to_c (XHASHTABLE (table), &htbl); + map_remhash (function, &htbl, closure); + ht_copy_from_c (&htbl, XHASHTABLE (table)); +} + +#if 0 +void +elisp_table_op (Lisp_Object table, generic_hashtable_op op, void *arg1, + void *arg2, void *arg3) +{ + struct _C_hashtable htbl; + CHECK_HASHTABLE (table); + ht_copy_to_c (XHASHTABLE (table), &htbl); + (*op) (&htbl, arg1, arg2, arg3); + ht_copy_from_c (&htbl, XHASHTABLE (table)); +} +#endif /* 0 */ + + + +DEFUN ("make-weak-hashtable", Fmake_weak_hashtable, Smake_weak_hashtable, + 1, 2, 0 /* +Make a fully weak hashtable of initial size SIZE. +A weak hashtable is one whose pointers do not count as GC referents: +for any key-value pair in the hashtable, 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 table, and the key and value collected. A +non-weak hash table (or any other pointer) would prevent the object +from being collected. + +You can also create semi-weak hashtables; see `make-key-weak-hashtable' +and `make-value-weak-hashtable'. +*/ ) + (size, test_fun) + Lisp_Object size, test_fun; +{ + CHECK_NATNUM (size); + return make_lisp_hashtable (XINT (size), HASHTABLE_WEAK, + decode_hashtable_test_fun (test_fun)); +} + +DEFUN ("make-key-weak-hashtable", Fmake_key_weak_hashtable, + Smake_key_weak_hashtable, 1, 2, 0 /* +Make a key-weak hashtable of initial size SIZE. +A key-weak hashtable is similar to a fully-weak hashtable (see +`make-weak-hashtable') except that a key-value pair will be removed +only if the key remains unmarked outside of weak hashtables. The pair +will remain in the hashtable if the key is pointed to by something other +than a weak hashtable, even if the value is not. +*/ ) + (size, test_fun) + Lisp_Object size, test_fun; +{ + CHECK_NATNUM (size); + return make_lisp_hashtable (XINT (size), HASHTABLE_KEY_WEAK, + decode_hashtable_test_fun (test_fun)); +} + +DEFUN ("make-value-weak-hashtable", Fmake_value_weak_hashtable, + Smake_value_weak_hashtable, 1, 2, 0 /* +Make a value-weak hashtable of initial size SIZE. +A value-weak hashtable is similar to a fully-weak hashtable (see +`make-weak-hashtable') except that a key-value pair will be removed only +if the value remains unmarked outside of weak hashtables. The pair will +remain in the hashtable if the value is pointed to by something other +than a weak hashtable, even if the key is not. +*/ ) + (size, test_fun) + Lisp_Object size, test_fun; +{ + CHECK_NATNUM (size); + return make_lisp_hashtable (XINT (size), HASHTABLE_VALUE_WEAK, + decode_hashtable_test_fun (test_fun)); +} + +struct marking_closure +{ + int (*obj_marked_p) (Lisp_Object); + void (*markobj) (Lisp_Object); + enum hashtable_type type; + int did_mark; +}; + +static void +marking_mapper (CONST void *key, void *contents, void *closure) +{ + Lisp_Object keytem, valuetem; + struct marking_closure *fmh = + (struct marking_closure *) closure; + + /* This function is called over each pair in the hashtable. + We complete the marking for semi-weak hashtables. */ + CVOID_TO_LISP (keytem, key); + CVOID_TO_LISP (valuetem, contents); + + switch (fmh->type) + { + case HASHTABLE_KEY_WEAK: + if ((fmh->obj_marked_p) (keytem) && + !(fmh->obj_marked_p) (valuetem)) + { + (fmh->markobj) (valuetem); + fmh->did_mark = 1; + } + break; + + case HASHTABLE_VALUE_WEAK: + if ((fmh->obj_marked_p) (valuetem) && + !(fmh->obj_marked_p) (keytem)) + { + (fmh->markobj) (keytem); + fmh->did_mark = 1; + } + break; + + case HASHTABLE_KEY_CAR_WEAK: + if (!CONSP (keytem) || (fmh->obj_marked_p) (XCAR (keytem))) + { + if (!(fmh->obj_marked_p) (keytem)) + { + (fmh->markobj) (keytem); + fmh->did_mark = 1; + } + if (!(fmh->obj_marked_p) (valuetem)) + { + (fmh->markobj) (valuetem); + fmh->did_mark = 1; + } + } + break; + + case HASHTABLE_VALUE_CAR_WEAK: + if (!CONSP (valuetem) || (fmh->obj_marked_p) (XCAR (valuetem))) + { + if (!(fmh->obj_marked_p) (keytem)) + { + (fmh->markobj) (keytem); + fmh->did_mark = 1; + } + if (!(fmh->obj_marked_p) (valuetem)) + { + (fmh->markobj) (valuetem); + fmh->did_mark = 1; + } + } + break; + + default: + abort (); /* Huh? */ + } + + return; +} + +int +finish_marking_weak_hashtables (int (*obj_marked_p) (Lisp_Object), + void (*markobj) (Lisp_Object)) +{ + Lisp_Object rest; + int did_mark = 0; + + for (rest = Vall_weak_hashtables; + !GC_NILP (rest); + rest = XHASHTABLE (rest)->next_weak) + { + enum hashtable_type type; + + if (! ((*obj_marked_p) (rest))) + /* The hashtable is probably garbage. Ignore it. */ + continue; + type = XHASHTABLE (rest)->type; + if (type == HASHTABLE_KEY_WEAK || type == HASHTABLE_VALUE_WEAK + || type == HASHTABLE_KEY_CAR_WEAK + || type == HASHTABLE_VALUE_CAR_WEAK) + { + struct marking_closure fmh; + + fmh.obj_marked_p = obj_marked_p; + fmh.markobj = markobj; + fmh.type = type; + fmh.did_mark = 0; + /* 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. */ + elisp_maphash (marking_mapper, rest, &fmh); + if (fmh.did_mark) + did_mark = 1; + } + + /* #### If alloc.c mark_object changes, this must change also... */ + { + /* Now mark the vector itself. (We don't need to call markobj + here because we know that everything *in* it is already marked, + we just need to prevent the vector itself from disappearing.) + (The remhash above has taken care of zero_entry.) + */ + struct Lisp_Vector *ptr = XVECTOR (XHASHTABLE (rest)->harray); + int len = vector_length (ptr); + if (len >= 0) + { + ptr->size = -1 - len; + did_mark = 1; + } + /* else it's already marked (remember, this function is iterated + until marking stops) */ + } + } + + return did_mark; +} + +struct pruning_closure +{ + int (*obj_marked_p) (Lisp_Object); +}; + +static int +pruning_mapper (CONST void *key, CONST void *contents, void *closure) +{ + Lisp_Object keytem, valuetem; + struct pruning_closure *fmh = + (struct pruning_closure *) closure; + + /* This function is called over each pair in the hashtable. + We remove the pairs that aren't completely marked (everything + that is going to stay ought to have been marked already + by the finish_marking stage). */ + CVOID_TO_LISP (keytem, key); + CVOID_TO_LISP (valuetem, contents); + + return (! ((*fmh->obj_marked_p) (keytem) && + (*fmh->obj_marked_p) (valuetem))); +} + +void +prune_weak_hashtables (int (*obj_marked_p) (Lisp_Object)) +{ + Lisp_Object rest, prev = Qnil; + for (rest = Vall_weak_hashtables; + !GC_NILP (rest); + rest = XHASHTABLE (rest)->next_weak) + { + if (! ((*obj_marked_p) (rest))) + { + /* This table itself is garbage. Remove it from the list. */ + if (GC_NILP (prev)) + Vall_weak_hashtables = XHASHTABLE (rest)->next_weak; + else + XHASHTABLE (prev)->next_weak = XHASHTABLE (rest)->next_weak; + } + else + { + struct pruning_closure fmh; + fmh.obj_marked_p = obj_marked_p; + /* Now, scan over all the pairs. Remove all of the pairs + in which the key or value, or both, is unmarked + (depending on the type of weak hashtable). */ + elisp_map_remhash (pruning_mapper, rest, &fmh); + prev = rest; + } + } +} + +/* Return a hash value for an array of Lisp_Objects of size SIZE. */ + +unsigned long +internal_array_hash (Lisp_Object *arr, int size, int depth) +{ + int i; + unsigned long hash = 0; + + if (size <= 5) + { + for (i = 0; i < size; i++) + hash = HASH2 (hash, internal_hash (arr[i], depth + 1)); + return hash; + } + + /* just pick five elements scattered throughout the array. + A slightly better approach would be to offset by some + noise factor from the points chosen below. */ + for (i = 0; i < 5; i++) + hash = HASH2 (hash, internal_hash (arr[i*size/5], depth + 1)); + + return hash; +} + +/* Return a hash value for a Lisp_Object. This is for use when hashing + objects with the comparison being `equal' (for `eq', you can just + use the Lisp_Object itself as the hash value). You need to make a + tradeoff between the speed of the hash function and how good the + hashing is. In particular, the hash function needs to be FAST, + so you can't just traipse down the whole tree hashing everything + together. Most of the time, objects will differ in the first + few elements you hash. Thus, we only go to a short depth (5) + and only hash at most 5 elements out of a vector. Theoretically + we could still take 5^5 time (a big big number) to compute a + hash, but practically this won't ever happen. */ + +unsigned long +internal_hash (Lisp_Object obj, int depth) +{ + if (depth > 5) + return 0; + if (CONSP (obj)) + { + /* no point in worrying about tail recursion, since we're not + going very deep */ + return HASH2 (internal_hash (XCAR (obj), depth + 1), + internal_hash (XCDR (obj), depth + 1)); + } + else if (STRINGP (obj)) + return hash_string (string_data (XSTRING (obj)), + string_length (XSTRING (obj))); +#ifndef LRECORD_VECTOR + else if (VECTORP (obj)) + { + struct Lisp_Vector *v = XVECTOR (obj); + return HASH2 (vector_length (v), + internal_array_hash (v->contents, vector_length (v), + depth + 1)); + } +#endif /* !LRECORD_VECTOR */ + else if (LRECORDP (obj)) + { + CONST struct lrecord_implementation + *imp = XRECORD_LHEADER (obj)->implementation; + if (imp->hash) + return ((imp->hash) (obj, depth)); + } + + return LISP_HASH (obj); +} + + +/************************************************************************/ +/* initialization */ +/************************************************************************/ + +void +syms_of_elhash (void) +{ + defsubr (&Smake_hashtable); + defsubr (&Scopy_hashtable); + defsubr (&Shashtablep); + defsubr (&Sgethash); + defsubr (&Sputhash); + defsubr (&Sremhash); + defsubr (&Sclrhash); + defsubr (&Smaphash); + defsubr (&Shashtable_fullness); + defsubr (&Smake_weak_hashtable); + defsubr (&Smake_key_weak_hashtable); + defsubr (&Smake_value_weak_hashtable); + defsymbol (&Qhashtablep, "hashtablep"); +} + +void +vars_of_elhash (void) +{ + /* This must not be staticpro'd */ + Vall_weak_hashtables = Qnil; +}