Merge branch 'master' into boehm-demers-weiser-gc
[bpt/guile.git] / libguile / hashtab.c
1 /* Copyright (C) 1995,1996,1998,1999,2000,2001, 2003, 2004, 2006, 2008 Free Software Foundation, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
17 */
18
19
20 \f
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <stdio.h>
26
27 #include "libguile/_scm.h"
28 #include "libguile/alist.h"
29 #include "libguile/hash.h"
30 #include "libguile/eval.h"
31 #include "libguile/root.h"
32 #include "libguile/vectors.h"
33 #include "libguile/ports.h"
34
35 #include "libguile/validate.h"
36 #include "libguile/hashtab.h"
37
38
39 \f
40
41 /* NOTES
42 *
43 * 1. The current hash table implementation uses weak alist vectors
44 * (implementation in weaks.c) internally, but we do the scanning
45 * ourselves (in scan_weak_hashtables) because we need to update the
46 * hash table structure when items are dropped during GC.
47 *
48 * 2. All hash table operations still work on alist vectors.
49 *
50 */
51
52 /* Hash tables are either vectors of association lists or smobs
53 * containing such vectors. Currently, the vector version represents
54 * constant size tables while those wrapped in a smob represents
55 * resizing tables.
56 *
57 * Growing or shrinking, with following rehashing, is triggered when
58 * the load factor
59 *
60 * L = N / S (N: number of items in table, S: bucket vector length)
61 *
62 * passes an upper limit of 0.9 or a lower limit of 0.25.
63 *
64 * The implementation stores the upper and lower number of items which
65 * trigger a resize in the hashtable object.
66 *
67 * Possible hash table sizes (primes) are stored in the array
68 * hashtable_size.
69 */
70
71 scm_t_bits scm_tc16_hashtable;
72
73 static unsigned long hashtable_size[] = {
74 31, 61, 113, 223, 443, 883, 1759, 3517, 7027, 14051, 28099, 56197, 112363,
75 224717, 449419, 898823, 1797641, 3595271, 7190537, 14381041
76 #if 0
77 /* vectors are currently restricted to 2^24-1 = 16777215 elements. */
78 28762081, 57524111, 115048217, 230096423, 460192829
79 /* larger values can't be represented as INUMs */
80 #endif
81 };
82
83 #define HASHTABLE_SIZE_N (sizeof(hashtable_size)/sizeof(unsigned long))
84
85 static char *s_hashtable = "hashtable";
86
87
88 \f
89 /* Helper functions and macros to deal with weak pairs.
90
91 Weak pairs need to be accessed very carefully since their components can
92 be nullified by the GC when the object they refer to becomes unreachable.
93 Hence the macros and functions below that detect such weak pairs within
94 buckets and remove them. */
95
96
97 /* Return a ``usable'' version of ALIST, an alist of weak pairs. By
98 ``usable'', we mean that it contains only valid Scheme objects. On
99 return, REMOVED_ITEMS is set to the number of pairs that have been
100 deleted. */
101 static SCM
102 scm_fixup_weak_alist (SCM alist, size_t *removed_items)
103 {
104 SCM result;
105 SCM prev = SCM_EOL;
106
107 *removed_items = 0;
108 for (result = alist;
109 scm_is_pair (alist);
110 prev = alist, alist = SCM_CDR (alist))
111 {
112 SCM pair = SCM_CAR (alist);
113
114 if (scm_is_pair (pair))
115 {
116 if (SCM_WEAK_PAIR_DELETED_P (pair))
117 {
118 /* Remove from ALIST weak pair PAIR whose car/cdr has been
119 nullified by the GC. */
120 if (prev == SCM_EOL)
121 result = SCM_CDR (alist);
122 else
123 SCM_SETCDR (prev, SCM_CDR (alist));
124
125 (*removed_items)++;
126 continue;
127 }
128 }
129 }
130
131 return result;
132 }
133
134
135 /* Helper macros. */
136
137 /* Return true if OBJ is either a weak hash table or a weak alist vector (as
138 defined in `weaks.[ch]').
139 FIXME: We should eventually keep only weah hash tables. Actually, the
140 procs in `weaks.c' already no longer return vectors. */
141 /* XXX: We assume that if OBJ is a vector, then it's a _weak_ alist vector. */
142 #define IS_WEAK_THING(_obj) \
143 ((SCM_HASHTABLE_P (table) && (SCM_HASHTABLE_WEAK_P (table))) \
144 || (SCM_I_IS_VECTOR (table)))
145
146
147
148 /* Fixup BUCKET, an alist part of weak hash table OBJ. BUCKETS is the full
149 bucket vector for OBJ and IDX is the index of BUCKET within this
150 vector. See also `scm_internal_hash_fold ()'. */
151 #define START_WEAK_BUCKET_FIXUP(_obj, _buckets, _idx, _bucket, _hashfn) \
152 do \
153 { \
154 size_t _removed; \
155 \
156 /* Disable the GC so that BUCKET remains valid until ASSOC_FN has \
157 returned. */ \
158 /* FIXME: We could maybe trigger a rehash here depending on whether \
159 `scm_fixup_weak_alist ()' noticed some change. */ \
160 GC_disable (); \
161 (_bucket) = scm_fixup_weak_alist ((_bucket), &_removed); \
162 SCM_SIMPLE_VECTOR_SET ((_buckets), (_idx), (_bucket)); \
163 \
164 if ((_removed) && (SCM_HASHTABLE_P (_obj))) \
165 { \
166 SCM_SET_HASHTABLE_N_ITEMS ((_obj), \
167 SCM_HASHTABLE_N_ITEMS (_obj) - _removed); \
168 scm_i_rehash ((_obj), (_hashfn), \
169 NULL, "START_WEAK_BUCKET_FIXUP"); \
170 } \
171 } \
172 while (0)
173
174 /* Terminate a weak bucket fixup phase. */
175 #define END_WEAK_BUCKET_FIXUP(_obj, _buckets, _idx, _bucket, _hashfn) \
176 do { GC_enable (); } while (0)
177
178
179 \f
180 static SCM
181 make_hash_table (int flags, unsigned long k, const char *func_name)
182 {
183 SCM table, vector;
184 scm_t_hashtable *t;
185 int i = 0, n = k ? k : 31;
186 while (i < HASHTABLE_SIZE_N && n > hashtable_size[i])
187 ++i;
188 n = hashtable_size[i];
189
190 /* In both cases, i.e., regardless of whether we are creating a weak hash
191 table, we return a non-weak vector. This is because the vector itself
192 is not weak in the case of a weak hash table: the alist pairs are. */
193 vector = scm_c_make_vector (n, SCM_EOL);
194
195 t = scm_gc_malloc_pointerless (sizeof (*t), s_hashtable);
196 t->min_size_index = t->size_index = i;
197 t->n_items = 0;
198 t->lower = 0;
199 t->upper = 9 * n / 10;
200 t->flags = flags;
201 t->hash_fn = NULL;
202
203 SCM_NEWSMOB2 (table, scm_tc16_hashtable, vector, t);
204
205 return table;
206 }
207
208 void
209 scm_i_rehash (SCM table,
210 unsigned long (*hash_fn)(),
211 void *closure,
212 const char* func_name)
213 {
214 SCM buckets, new_buckets;
215 int i;
216 unsigned long old_size;
217 unsigned long new_size;
218
219 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
220 {
221 /* rehashing is not triggered when i <= min_size */
222 i = SCM_HASHTABLE (table)->size_index;
223 do
224 --i;
225 while (i > SCM_HASHTABLE (table)->min_size_index
226 && SCM_HASHTABLE_N_ITEMS (table) < hashtable_size[i] / 4);
227 }
228 else
229 {
230 i = SCM_HASHTABLE (table)->size_index + 1;
231 if (i >= HASHTABLE_SIZE_N)
232 /* don't rehash */
233 return;
234
235 /* Remember HASH_FN for rehash_after_gc, but only when CLOSURE
236 is not needed since CLOSURE can not be guaranteed to be valid
237 after this function returns.
238 */
239 if (closure == NULL)
240 SCM_HASHTABLE (table)->hash_fn = hash_fn;
241 }
242 SCM_HASHTABLE (table)->size_index = i;
243
244 new_size = hashtable_size[i];
245 if (i <= SCM_HASHTABLE (table)->min_size_index)
246 SCM_HASHTABLE (table)->lower = 0;
247 else
248 SCM_HASHTABLE (table)->lower = new_size / 4;
249 SCM_HASHTABLE (table)->upper = 9 * new_size / 10;
250 buckets = SCM_HASHTABLE_VECTOR (table);
251
252 new_buckets = scm_c_make_vector (new_size, SCM_EOL);
253
254 /* When this is a weak hashtable, running the GC might change it.
255 We need to cope with this while rehashing its elements. We do
256 this by first installing the new, empty bucket vector. Then we
257 remove the elements from the old bucket vector and insert them
258 into the new one.
259 */
260
261 SCM_SET_HASHTABLE_VECTOR (table, new_buckets);
262 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
263
264 old_size = SCM_SIMPLE_VECTOR_LENGTH (buckets);
265 for (i = 0; i < old_size; ++i)
266 {
267 SCM ls, cell, handle;
268
269 ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
270 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_EOL);
271
272 while (scm_is_pair (ls))
273 {
274 unsigned long h;
275
276 cell = ls;
277 handle = SCM_CAR (cell);
278 ls = SCM_CDR (ls);
279
280 if (SCM_WEAK_PAIR_DELETED_P (handle))
281 /* HANDLE is a nullified weak pair: skip it. */
282 continue;
283
284 h = hash_fn (SCM_CAR (handle), new_size, closure);
285 if (h >= new_size)
286 scm_out_of_range (func_name, scm_from_ulong (h));
287 SCM_SETCDR (cell, SCM_SIMPLE_VECTOR_REF (new_buckets, h));
288 SCM_SIMPLE_VECTOR_SET (new_buckets, h, cell);
289 SCM_HASHTABLE_INCREMENT (table);
290 }
291 }
292 }
293
294
295 static int
296 hashtable_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
297 {
298 scm_puts ("#<", port);
299 if (SCM_HASHTABLE_WEAK_KEY_P (exp))
300 scm_puts ("weak-key-", port);
301 else if (SCM_HASHTABLE_WEAK_VALUE_P (exp))
302 scm_puts ("weak-value-", port);
303 else if (SCM_HASHTABLE_DOUBLY_WEAK_P (exp))
304 scm_puts ("doubly-weak-", port);
305 scm_puts ("hash-table ", port);
306 scm_uintprint (SCM_HASHTABLE_N_ITEMS (exp), 10, port);
307 scm_putc ('/', port);
308 scm_uintprint (SCM_SIMPLE_VECTOR_LENGTH (SCM_HASHTABLE_VECTOR (exp)),
309 10, port);
310 scm_puts (">", port);
311 return 1;
312 }
313
314
315 SCM
316 scm_c_make_hash_table (unsigned long k)
317 {
318 return make_hash_table (0, k, "scm_c_make_hash_table");
319 }
320
321 SCM_DEFINE (scm_make_hash_table, "make-hash-table", 0, 1, 0,
322 (SCM n),
323 "Make a new abstract hash table object with minimum number of buckets @var{n}\n")
324 #define FUNC_NAME s_scm_make_hash_table
325 {
326 if (SCM_UNBNDP (n))
327 return make_hash_table (0, 0, FUNC_NAME);
328 else
329 return make_hash_table (0, scm_to_ulong (n), FUNC_NAME);
330 }
331 #undef FUNC_NAME
332
333 SCM_DEFINE (scm_make_weak_key_hash_table, "make-weak-key-hash-table", 0, 1, 0,
334 (SCM n),
335 "@deffnx {Scheme Procedure} make-weak-value-hash-table size\n"
336 "@deffnx {Scheme Procedure} make-doubly-weak-hash-table size\n"
337 "Return a weak hash table with @var{size} buckets.\n"
338 "\n"
339 "You can modify weak hash tables in exactly the same way you\n"
340 "would modify regular hash tables. (@pxref{Hash Tables})")
341 #define FUNC_NAME s_scm_make_weak_key_hash_table
342 {
343 if (SCM_UNBNDP (n))
344 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR, 0, FUNC_NAME);
345 else
346 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR,
347 scm_to_ulong (n), FUNC_NAME);
348 }
349 #undef FUNC_NAME
350
351
352 SCM_DEFINE (scm_make_weak_value_hash_table, "make-weak-value-hash-table", 0, 1, 0,
353 (SCM n),
354 "Return a hash table with weak values with @var{size} buckets.\n"
355 "(@pxref{Hash Tables})")
356 #define FUNC_NAME s_scm_make_weak_value_hash_table
357 {
358 if (SCM_UNBNDP (n))
359 return make_hash_table (SCM_HASHTABLEF_WEAK_CDR, 0, FUNC_NAME);
360 else
361 {
362 return make_hash_table (SCM_HASHTABLEF_WEAK_CDR,
363 scm_to_ulong (n), FUNC_NAME);
364 }
365 }
366 #undef FUNC_NAME
367
368
369 SCM_DEFINE (scm_make_doubly_weak_hash_table, "make-doubly-weak-hash-table", 1, 0, 0,
370 (SCM n),
371 "Return a hash table with weak keys and values with @var{size}\n"
372 "buckets. (@pxref{Hash Tables})")
373 #define FUNC_NAME s_scm_make_doubly_weak_hash_table
374 {
375 if (SCM_UNBNDP (n))
376 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
377 0,
378 FUNC_NAME);
379 else
380 {
381 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
382 scm_to_ulong (n),
383 FUNC_NAME);
384 }
385 }
386 #undef FUNC_NAME
387
388
389 SCM_DEFINE (scm_hash_table_p, "hash-table?", 1, 0, 0,
390 (SCM obj),
391 "Return @code{#t} if @var{obj} is an abstract hash table object.")
392 #define FUNC_NAME s_scm_hash_table_p
393 {
394 return scm_from_bool (SCM_HASHTABLE_P (obj));
395 }
396 #undef FUNC_NAME
397
398
399 SCM_DEFINE (scm_weak_key_hash_table_p, "weak-key-hash-table?", 1, 0, 0,
400 (SCM obj),
401 "@deffnx {Scheme Procedure} weak-value-hash-table? obj\n"
402 "@deffnx {Scheme Procedure} doubly-weak-hash-table? obj\n"
403 "Return @code{#t} if @var{obj} is the specified weak hash\n"
404 "table. Note that a doubly weak hash table is neither a weak key\n"
405 "nor a weak value hash table.")
406 #define FUNC_NAME s_scm_weak_key_hash_table_p
407 {
408 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_KEY_P (obj));
409 }
410 #undef FUNC_NAME
411
412
413 SCM_DEFINE (scm_weak_value_hash_table_p, "weak-value-hash-table?", 1, 0, 0,
414 (SCM obj),
415 "Return @code{#t} if @var{obj} is a weak value hash table.")
416 #define FUNC_NAME s_scm_weak_value_hash_table_p
417 {
418 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_VALUE_P (obj));
419 }
420 #undef FUNC_NAME
421
422
423 SCM_DEFINE (scm_doubly_weak_hash_table_p, "doubly-weak-hash-table?", 1, 0, 0,
424 (SCM obj),
425 "Return @code{#t} if @var{obj} is a doubly weak hash table.")
426 #define FUNC_NAME s_scm_doubly_weak_hash_table_p
427 {
428 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_DOUBLY_WEAK_P (obj));
429 }
430 #undef FUNC_NAME
431
432
433 SCM
434 scm_hash_fn_get_handle (SCM table, SCM obj, unsigned long (*hash_fn)(), SCM (*assoc_fn)(), void * closure)
435 #define FUNC_NAME "scm_hash_fn_get_handle"
436 {
437 int weak = 0;
438 unsigned long k;
439 SCM buckets, alist, h;
440
441 if (SCM_HASHTABLE_P (table))
442 buckets = SCM_HASHTABLE_VECTOR (table);
443 else
444 {
445 SCM_VALIDATE_VECTOR (1, table);
446 buckets = table;
447 }
448
449 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
450 return SCM_BOOL_F;
451 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
452 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
453 scm_out_of_range ("hash_fn_get_handle", scm_from_ulong (k));
454
455 weak = IS_WEAK_THING (table);
456 alist = SCM_SIMPLE_VECTOR_REF (buckets, k);
457
458 if (weak)
459 START_WEAK_BUCKET_FIXUP (table, buckets, k, alist, hash_fn);
460
461 h = assoc_fn (obj, alist, closure);
462 if (weak)
463 END_WEAK_BUCKET_FIXUP (table, buckets, k, alist, hash_fn);
464
465 return h;
466 }
467 #undef FUNC_NAME
468
469
470 SCM
471 scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init, unsigned long (*hash_fn)(),
472 SCM (*assoc_fn)(), void * closure)
473 #define FUNC_NAME "scm_hash_fn_create_handle_x"
474 {
475 int weak = 0;
476 unsigned long k;
477 SCM buckets, alist, it;
478
479 if (SCM_HASHTABLE_P (table))
480 buckets = SCM_HASHTABLE_VECTOR (table);
481 else
482 {
483 SCM_ASSERT (scm_is_simple_vector (table),
484 table, SCM_ARG1, "hash_fn_create_handle_x");
485 buckets = table;
486 }
487 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
488 SCM_MISC_ERROR ("void hashtable", SCM_EOL);
489
490 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
491 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
492 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
493
494 weak = IS_WEAK_THING (table);
495 alist = SCM_SIMPLE_VECTOR_REF (buckets, k);
496 if (weak)
497 START_WEAK_BUCKET_FIXUP (table, buckets, k, alist, hash_fn);
498
499 it = assoc_fn (obj, alist, closure);
500 if (weak)
501 END_WEAK_BUCKET_FIXUP (table, buckets, k, alist, hash_fn);
502
503 if (scm_is_pair (it))
504 return it;
505 else if (scm_is_true (it))
506 scm_wrong_type_arg_msg (NULL, 0, it, "a pair");
507 else
508 {
509 /* When this is a weak hashtable, running the GC can change it.
510 Thus, we must allocate the new cells first and can only then
511 access BUCKETS. Also, we need to fetch the bucket vector
512 again since the hashtable might have been rehashed. This
513 necessitates a new hash value as well.
514 */
515 SCM handle, new_bucket;
516
517 if ((SCM_HASHTABLE_P (table)) && (SCM_HASHTABLE_WEAK_P (table)))
518 {
519 /* FIXME: We don't support weak alist vectors. */
520 /* Use a weak cell. */
521 if (SCM_HASHTABLE_DOUBLY_WEAK_P (table))
522 handle = scm_doubly_weak_pair (obj, init);
523 else if (SCM_HASHTABLE_WEAK_KEY_P (table))
524 handle = scm_weak_car_pair (obj, init);
525 else
526 handle = scm_weak_cdr_pair (obj, init);
527 }
528 else
529 /* Use a regular, non-weak cell. */
530 handle = scm_cons (obj, init);
531
532 new_bucket = scm_cons (handle, SCM_EOL);
533
534 if (!scm_is_eq (table, buckets)
535 && !scm_is_eq (SCM_HASHTABLE_VECTOR (table), buckets))
536 {
537 buckets = SCM_HASHTABLE_VECTOR (table);
538 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
539 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
540 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
541 }
542 SCM_SETCDR (new_bucket, SCM_SIMPLE_VECTOR_REF (buckets, k));
543 SCM_SIMPLE_VECTOR_SET (buckets, k, new_bucket);
544 if (!scm_is_eq (table, buckets))
545 {
546 /* Update element count and maybe rehash the table. The
547 table might have too few entries here since weak hash
548 tables used with the hashx_* functions can not be
549 rehashed after GC.
550 */
551 SCM_HASHTABLE_INCREMENT (table);
552 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table)
553 || SCM_HASHTABLE_N_ITEMS (table) > SCM_HASHTABLE_UPPER (table))
554 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
555 }
556 return SCM_CAR (new_bucket);
557 }
558 }
559 #undef FUNC_NAME
560
561
562 SCM
563 scm_hash_fn_ref (SCM table, SCM obj, SCM dflt, unsigned long (*hash_fn)(),
564 SCM (*assoc_fn)(), void * closure)
565 {
566 SCM it = scm_hash_fn_get_handle (table, obj, hash_fn, assoc_fn, closure);
567 if (scm_is_pair (it))
568 return SCM_CDR (it);
569 else
570 return dflt;
571 }
572
573
574
575
576 SCM
577 scm_hash_fn_set_x (SCM table, SCM obj, SCM val, unsigned long (*hash_fn)(),
578 SCM (*assoc_fn)(), void * closure)
579 {
580 SCM it;
581
582 it = scm_hash_fn_create_handle_x (table, obj, SCM_BOOL_F, hash_fn, assoc_fn, closure);
583 SCM_SETCDR (it, val);
584 return val;
585 }
586
587
588 SCM
589 scm_hash_fn_remove_x (SCM table, SCM obj,
590 unsigned long (*hash_fn)(),
591 SCM (*assoc_fn)(),
592 void *closure)
593 {
594 int weak = 0;
595 unsigned long k;
596 SCM buckets, alist, h;
597
598 if (SCM_HASHTABLE_P (table))
599 buckets = SCM_HASHTABLE_VECTOR (table);
600 else
601 {
602 SCM_ASSERT (scm_is_simple_vector (table), table,
603 SCM_ARG1, "hash_fn_remove_x");
604 buckets = table;
605 }
606 if (SCM_SIMPLE_VECTOR_LENGTH (table) == 0)
607 return SCM_EOL;
608
609 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
610 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
611 scm_out_of_range ("hash_fn_remove_x", scm_from_ulong (k));
612
613 weak = IS_WEAK_THING (table);
614 alist = SCM_SIMPLE_VECTOR_REF (buckets, k);
615 if (weak)
616 START_WEAK_BUCKET_FIXUP (table, buckets, k, alist, hash_fn);
617
618 h = assoc_fn (obj, alist, closure);
619 if (weak)
620 END_WEAK_BUCKET_FIXUP (table, buckets, k, alist, hash_fn);
621
622 if (scm_is_true (h))
623 {
624 SCM_SIMPLE_VECTOR_SET
625 (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
626 if (!scm_is_eq (table, buckets))
627 {
628 SCM_HASHTABLE_DECREMENT (table);
629 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
630 scm_i_rehash (table, hash_fn, closure, "scm_hash_fn_remove_x");
631 }
632 }
633 return h;
634 }
635
636 SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
637 (SCM table),
638 "Remove all items from @var{table} (without triggering a resize).")
639 #define FUNC_NAME s_scm_hash_clear_x
640 {
641 if (SCM_HASHTABLE_P (table))
642 {
643 scm_vector_fill_x (SCM_HASHTABLE_VECTOR (table), SCM_EOL);
644 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
645 }
646 else
647 scm_vector_fill_x (table, SCM_EOL);
648 return SCM_UNSPECIFIED;
649 }
650 #undef FUNC_NAME
651
652 \f
653
654 SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
655 (SCM table, SCM key),
656 "This procedure returns the @code{(key . value)} pair from the\n"
657 "hash table @var{table}. If @var{table} does not hold an\n"
658 "associated value for @var{key}, @code{#f} is returned.\n"
659 "Uses @code{eq?} for equality testing.")
660 #define FUNC_NAME s_scm_hashq_get_handle
661 {
662 return scm_hash_fn_get_handle (table, key, scm_ihashq, scm_sloppy_assq, 0);
663 }
664 #undef FUNC_NAME
665
666
667 SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
668 (SCM table, SCM key, SCM init),
669 "This function looks up @var{key} in @var{table} and returns its handle.\n"
670 "If @var{key} is not already present, a new handle is created which\n"
671 "associates @var{key} with @var{init}.")
672 #define FUNC_NAME s_scm_hashq_create_handle_x
673 {
674 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashq, scm_sloppy_assq, 0);
675 }
676 #undef FUNC_NAME
677
678
679 SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
680 (SCM table, SCM key, SCM dflt),
681 "Look up @var{key} in the hash table @var{table}, and return the\n"
682 "value (if any) associated with it. If @var{key} is not found,\n"
683 "return @var{default} (or @code{#f} if no @var{default} argument\n"
684 "is supplied). Uses @code{eq?} for equality testing.")
685 #define FUNC_NAME s_scm_hashq_ref
686 {
687 if (SCM_UNBNDP (dflt))
688 dflt = SCM_BOOL_F;
689 return scm_hash_fn_ref (table, key, dflt, scm_ihashq, scm_sloppy_assq, 0);
690 }
691 #undef FUNC_NAME
692
693
694
695 SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
696 (SCM table, SCM key, SCM val),
697 "Find the entry in @var{table} associated with @var{key}, and\n"
698 "store @var{value} there. Uses @code{eq?} for equality testing.")
699 #define FUNC_NAME s_scm_hashq_set_x
700 {
701 return scm_hash_fn_set_x (table, key, val, scm_ihashq, scm_sloppy_assq, 0);
702 }
703 #undef FUNC_NAME
704
705
706
707 SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
708 (SCM table, SCM key),
709 "Remove @var{key} (and any value associated with it) from\n"
710 "@var{table}. Uses @code{eq?} for equality tests.")
711 #define FUNC_NAME s_scm_hashq_remove_x
712 {
713 return scm_hash_fn_remove_x (table, key, scm_ihashq, scm_sloppy_assq, 0);
714 }
715 #undef FUNC_NAME
716
717
718 \f
719
720 SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
721 (SCM table, SCM key),
722 "This procedure returns the @code{(key . value)} pair from the\n"
723 "hash table @var{table}. If @var{table} does not hold an\n"
724 "associated value for @var{key}, @code{#f} is returned.\n"
725 "Uses @code{eqv?} for equality testing.")
726 #define FUNC_NAME s_scm_hashv_get_handle
727 {
728 return scm_hash_fn_get_handle (table, key, scm_ihashv, scm_sloppy_assv, 0);
729 }
730 #undef FUNC_NAME
731
732
733 SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
734 (SCM table, SCM key, SCM init),
735 "This function looks up @var{key} in @var{table} and returns its handle.\n"
736 "If @var{key} is not already present, a new handle is created which\n"
737 "associates @var{key} with @var{init}.")
738 #define FUNC_NAME s_scm_hashv_create_handle_x
739 {
740 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashv,
741 scm_sloppy_assv, 0);
742 }
743 #undef FUNC_NAME
744
745
746 SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
747 (SCM table, SCM key, SCM dflt),
748 "Look up @var{key} in the hash table @var{table}, and return the\n"
749 "value (if any) associated with it. If @var{key} is not found,\n"
750 "return @var{default} (or @code{#f} if no @var{default} argument\n"
751 "is supplied). Uses @code{eqv?} for equality testing.")
752 #define FUNC_NAME s_scm_hashv_ref
753 {
754 if (SCM_UNBNDP (dflt))
755 dflt = SCM_BOOL_F;
756 return scm_hash_fn_ref (table, key, dflt, scm_ihashv, scm_sloppy_assv, 0);
757 }
758 #undef FUNC_NAME
759
760
761
762 SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
763 (SCM table, SCM key, SCM val),
764 "Find the entry in @var{table} associated with @var{key}, and\n"
765 "store @var{value} there. Uses @code{eqv?} for equality testing.")
766 #define FUNC_NAME s_scm_hashv_set_x
767 {
768 return scm_hash_fn_set_x (table, key, val, scm_ihashv, scm_sloppy_assv, 0);
769 }
770 #undef FUNC_NAME
771
772
773 SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
774 (SCM table, SCM key),
775 "Remove @var{key} (and any value associated with it) from\n"
776 "@var{table}. Uses @code{eqv?} for equality tests.")
777 #define FUNC_NAME s_scm_hashv_remove_x
778 {
779 return scm_hash_fn_remove_x (table, key, scm_ihashv, scm_sloppy_assv, 0);
780 }
781 #undef FUNC_NAME
782
783 \f
784
785 SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
786 (SCM table, SCM key),
787 "This procedure returns the @code{(key . value)} pair from the\n"
788 "hash table @var{table}. If @var{table} does not hold an\n"
789 "associated value for @var{key}, @code{#f} is returned.\n"
790 "Uses @code{equal?} for equality testing.")
791 #define FUNC_NAME s_scm_hash_get_handle
792 {
793 return scm_hash_fn_get_handle (table, key, scm_ihash, scm_sloppy_assoc, 0);
794 }
795 #undef FUNC_NAME
796
797
798 SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
799 (SCM table, SCM key, SCM init),
800 "This function looks up @var{key} in @var{table} and returns its handle.\n"
801 "If @var{key} is not already present, a new handle is created which\n"
802 "associates @var{key} with @var{init}.")
803 #define FUNC_NAME s_scm_hash_create_handle_x
804 {
805 return scm_hash_fn_create_handle_x (table, key, init, scm_ihash, scm_sloppy_assoc, 0);
806 }
807 #undef FUNC_NAME
808
809
810 SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
811 (SCM table, SCM key, SCM dflt),
812 "Look up @var{key} in the hash table @var{table}, and return the\n"
813 "value (if any) associated with it. If @var{key} is not found,\n"
814 "return @var{default} (or @code{#f} if no @var{default} argument\n"
815 "is supplied). Uses @code{equal?} for equality testing.")
816 #define FUNC_NAME s_scm_hash_ref
817 {
818 if (SCM_UNBNDP (dflt))
819 dflt = SCM_BOOL_F;
820 return scm_hash_fn_ref (table, key, dflt, scm_ihash, scm_sloppy_assoc, 0);
821 }
822 #undef FUNC_NAME
823
824
825
826 SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
827 (SCM table, SCM key, SCM val),
828 "Find the entry in @var{table} associated with @var{key}, and\n"
829 "store @var{value} there. Uses @code{equal?} for equality\n"
830 "testing.")
831 #define FUNC_NAME s_scm_hash_set_x
832 {
833 return scm_hash_fn_set_x (table, key, val, scm_ihash, scm_sloppy_assoc, 0);
834 }
835 #undef FUNC_NAME
836
837
838
839 SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
840 (SCM table, SCM key),
841 "Remove @var{key} (and any value associated with it) from\n"
842 "@var{table}. Uses @code{equal?} for equality tests.")
843 #define FUNC_NAME s_scm_hash_remove_x
844 {
845 return scm_hash_fn_remove_x (table, key, scm_ihash, scm_sloppy_assoc, 0);
846 }
847 #undef FUNC_NAME
848
849 \f
850
851
852 typedef struct scm_t_ihashx_closure
853 {
854 SCM hash;
855 SCM assoc;
856 } scm_t_ihashx_closure;
857
858
859
860 static unsigned long
861 scm_ihashx (SCM obj, unsigned long n, scm_t_ihashx_closure *closure)
862 {
863 SCM answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
864 return scm_to_ulong (answer);
865 }
866
867
868
869 static SCM
870 scm_sloppy_assx (SCM obj, SCM alist, scm_t_ihashx_closure *closure)
871 {
872 return scm_call_2 (closure->assoc, obj, alist);
873 }
874
875
876 SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
877 (SCM hash, SCM assoc, SCM table, SCM key),
878 "This behaves the same way as the corresponding\n"
879 "@code{-get-handle} function, but uses @var{hash} as a hash\n"
880 "function and @var{assoc} to compare keys. @code{hash} must be\n"
881 "a function that takes two arguments, a key to be hashed and a\n"
882 "table size. @code{assoc} must be an associator function, like\n"
883 "@code{assoc}, @code{assq} or @code{assv}.")
884 #define FUNC_NAME s_scm_hashx_get_handle
885 {
886 scm_t_ihashx_closure closure;
887 closure.hash = hash;
888 closure.assoc = assoc;
889 return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
890 (void *) &closure);
891 }
892 #undef FUNC_NAME
893
894
895 SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
896 (SCM hash, SCM assoc, SCM table, SCM key, SCM init),
897 "This behaves the same way as the corresponding\n"
898 "@code{-create-handle} function, but uses @var{hash} as a hash\n"
899 "function and @var{assoc} to compare keys. @code{hash} must be\n"
900 "a function that takes two arguments, a key to be hashed and a\n"
901 "table size. @code{assoc} must be an associator function, like\n"
902 "@code{assoc}, @code{assq} or @code{assv}.")
903 #define FUNC_NAME s_scm_hashx_create_handle_x
904 {
905 scm_t_ihashx_closure closure;
906 closure.hash = hash;
907 closure.assoc = assoc;
908 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
909 scm_sloppy_assx, (void *)&closure);
910 }
911 #undef FUNC_NAME
912
913
914
915 SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
916 (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
917 "This behaves the same way as the corresponding @code{ref}\n"
918 "function, but uses @var{hash} as a hash function and\n"
919 "@var{assoc} to compare keys. @code{hash} must be a function\n"
920 "that takes two arguments, a key to be hashed and a table size.\n"
921 "@code{assoc} must be an associator function, like @code{assoc},\n"
922 "@code{assq} or @code{assv}.\n"
923 "\n"
924 "By way of illustration, @code{hashq-ref table key} is\n"
925 "equivalent to @code{hashx-ref hashq assq table key}.")
926 #define FUNC_NAME s_scm_hashx_ref
927 {
928 scm_t_ihashx_closure closure;
929 if (SCM_UNBNDP (dflt))
930 dflt = SCM_BOOL_F;
931 closure.hash = hash;
932 closure.assoc = assoc;
933 return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
934 (void *)&closure);
935 }
936 #undef FUNC_NAME
937
938
939
940
941 SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
942 (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
943 "This behaves the same way as the corresponding @code{set!}\n"
944 "function, but uses @var{hash} as a hash function and\n"
945 "@var{assoc} to compare keys. @code{hash} must be a function\n"
946 "that takes two arguments, a key to be hashed and a table size.\n"
947 "@code{assoc} must be an associator function, like @code{assoc},\n"
948 "@code{assq} or @code{assv}.\n"
949 "\n"
950 " By way of illustration, @code{hashq-set! table key} is\n"
951 "equivalent to @code{hashx-set! hashq assq table key}.")
952 #define FUNC_NAME s_scm_hashx_set_x
953 {
954 scm_t_ihashx_closure closure;
955 closure.hash = hash;
956 closure.assoc = assoc;
957 return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
958 (void *)&closure);
959 }
960 #undef FUNC_NAME
961
962 SCM_DEFINE (scm_hashx_remove_x, "hashx-remove!", 4, 0, 0,
963 (SCM hash, SCM assoc, SCM table, SCM obj),
964 "This behaves the same way as the corresponding @code{remove!}\n"
965 "function, but uses @var{hash} as a hash function and\n"
966 "@var{assoc} to compare keys. @code{hash} must be a function\n"
967 "that takes two arguments, a key to be hashed and a table size.\n"
968 "@code{assoc} must be an associator function, like @code{assoc},\n"
969 "@code{assq} or @code{assv}.\n"
970 "\n"
971 " By way of illustration, @code{hashq-remove! table key} is\n"
972 "equivalent to @code{hashx-remove! hashq assq #f table key}.")
973 #define FUNC_NAME s_scm_hashx_remove_x
974 {
975 scm_t_ihashx_closure closure;
976 closure.hash = hash;
977 closure.assoc = assoc;
978 return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
979 (void *) &closure);
980 }
981 #undef FUNC_NAME
982
983 /* Hash table iterators */
984
985 static const char s_scm_hash_fold[];
986
987 SCM
988 scm_internal_hash_fold (SCM (*fn) (), void *closure, SCM init, SCM table)
989 {
990 long i, n;
991 SCM buckets, result = init;
992
993 if (SCM_HASHTABLE_P (table))
994 buckets = SCM_HASHTABLE_VECTOR (table);
995 else
996 /* Weak alist vector. */
997 buckets = table;
998
999 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
1000 for (i = 0; i < n; ++i)
1001 {
1002 SCM prev, ls;
1003
1004 for (prev = SCM_BOOL_F, ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
1005 !scm_is_null (ls);
1006 prev = ls, ls = SCM_CDR (ls))
1007 {
1008 SCM handle;
1009
1010 if (!scm_is_pair (ls))
1011 scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
1012
1013 handle = SCM_CAR (ls);
1014 if (!scm_is_pair (handle))
1015 scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
1016
1017 if (IS_WEAK_THING (table))
1018 {
1019 if (SCM_WEAK_PAIR_DELETED_P (handle))
1020 {
1021 /* We hit a weak pair whose car/cdr has become
1022 unreachable: unlink it from the bucket. */
1023 if (prev != SCM_BOOL_F)
1024 SCM_SETCDR (prev, SCM_CDR (ls));
1025 else
1026 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_CDR (ls));
1027
1028 if (SCM_HASHTABLE_P (table))
1029 /* Update the item count. */
1030 SCM_HASHTABLE_DECREMENT (table);
1031
1032 continue;
1033 }
1034 }
1035
1036 result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
1037 }
1038 }
1039
1040 return result;
1041 }
1042
1043 /* The following redundant code is here in order to be able to support
1044 hash-for-each-handle. An alternative would have been to replace
1045 this code and scm_internal_hash_fold above with a single
1046 scm_internal_hash_fold_handles, but we don't want to promote such
1047 an API. */
1048
1049 static const char s_scm_hash_for_each[];
1050
1051 void
1052 scm_internal_hash_for_each_handle (SCM (*fn) (), void *closure, SCM table)
1053 {
1054 long i, n;
1055 SCM buckets;
1056
1057 if (SCM_HASHTABLE_P (table))
1058 buckets = SCM_HASHTABLE_VECTOR (table);
1059 else
1060 buckets = table;
1061
1062 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
1063 for (i = 0; i < n; ++i)
1064 {
1065 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
1066 while (!scm_is_null (ls))
1067 {
1068 if (!scm_is_pair (ls))
1069 scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
1070 handle = SCM_CAR (ls);
1071 if (!scm_is_pair (handle))
1072 scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
1073 fn (closure, handle);
1074 ls = SCM_CDR (ls);
1075 }
1076 }
1077 }
1078
1079 SCM_DEFINE (scm_hash_fold, "hash-fold", 3, 0, 0,
1080 (SCM proc, SCM init, SCM table),
1081 "An iterator over hash-table elements.\n"
1082 "Accumulates and returns a result by applying PROC successively.\n"
1083 "The arguments to PROC are \"(key value prior-result)\" where key\n"
1084 "and value are successive pairs from the hash table TABLE, and\n"
1085 "prior-result is either INIT (for the first application of PROC)\n"
1086 "or the return value of the previous application of PROC.\n"
1087 "For example, @code{(hash-fold acons '() tab)} will convert a hash\n"
1088 "table into an a-list of key-value pairs.")
1089 #define FUNC_NAME s_scm_hash_fold
1090 {
1091 SCM_VALIDATE_PROC (1, proc);
1092 if (!SCM_HASHTABLE_P (table))
1093 SCM_VALIDATE_VECTOR (3, table);
1094 return scm_internal_hash_fold (scm_call_3, (void *) SCM_UNPACK (proc), init, table);
1095 }
1096 #undef FUNC_NAME
1097
1098 static SCM
1099 for_each_proc (void *proc, SCM handle)
1100 {
1101 return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
1102 }
1103
1104 SCM_DEFINE (scm_hash_for_each, "hash-for-each", 2, 0, 0,
1105 (SCM proc, SCM table),
1106 "An iterator over hash-table elements.\n"
1107 "Applies PROC successively on all hash table items.\n"
1108 "The arguments to PROC are \"(key value)\" where key\n"
1109 "and value are successive pairs from the hash table TABLE.")
1110 #define FUNC_NAME s_scm_hash_for_each
1111 {
1112 SCM_VALIDATE_PROC (1, proc);
1113 if (!SCM_HASHTABLE_P (table))
1114 SCM_VALIDATE_VECTOR (2, table);
1115
1116 scm_internal_hash_for_each_handle (for_each_proc,
1117 (void *) SCM_UNPACK (proc),
1118 table);
1119 return SCM_UNSPECIFIED;
1120 }
1121 #undef FUNC_NAME
1122
1123 SCM_DEFINE (scm_hash_for_each_handle, "hash-for-each-handle", 2, 0, 0,
1124 (SCM proc, SCM table),
1125 "An iterator over hash-table elements.\n"
1126 "Applies PROC successively on all hash table handles.")
1127 #define FUNC_NAME s_scm_hash_for_each_handle
1128 {
1129 scm_t_trampoline_1 call = scm_trampoline_1 (proc);
1130 SCM_ASSERT (call, proc, 1, FUNC_NAME);
1131 if (!SCM_HASHTABLE_P (table))
1132 SCM_VALIDATE_VECTOR (2, table);
1133
1134 scm_internal_hash_for_each_handle (call,
1135 (void *) SCM_UNPACK (proc),
1136 table);
1137 return SCM_UNSPECIFIED;
1138 }
1139 #undef FUNC_NAME
1140
1141 static SCM
1142 map_proc (void *proc, SCM key, SCM data, SCM value)
1143 {
1144 return scm_cons (scm_call_2 (SCM_PACK (proc), key, data), value);
1145 }
1146
1147 SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
1148 (SCM proc, SCM table),
1149 "An iterator over hash-table elements.\n"
1150 "Accumulates and returns as a list the results of applying PROC successively.\n"
1151 "The arguments to PROC are \"(key value)\" where key\n"
1152 "and value are successive pairs from the hash table TABLE.")
1153 #define FUNC_NAME s_scm_hash_map_to_list
1154 {
1155 SCM_VALIDATE_PROC (1, proc);
1156 if (!SCM_HASHTABLE_P (table))
1157 SCM_VALIDATE_VECTOR (2, table);
1158 return scm_internal_hash_fold (map_proc,
1159 (void *) SCM_UNPACK (proc),
1160 SCM_EOL,
1161 table);
1162 }
1163 #undef FUNC_NAME
1164
1165 \f
1166
1167
1168 void
1169 scm_hashtab_prehistory ()
1170 {
1171 /* Initialize the hashtab SMOB type. */
1172 scm_tc16_hashtable = scm_make_smob_type (s_hashtable, 0);
1173 scm_set_smob_print (scm_tc16_hashtable, hashtable_print);
1174 }
1175
1176 void
1177 scm_init_hashtab ()
1178 {
1179 #include "libguile/hashtab.x"
1180 }
1181
1182 /*
1183 Local Variables:
1184 c-file-style: "gnu"
1185 End:
1186 */