fix hash-set! in weak-value table from non-immediate to immediate
[bpt/guile.git] / libguile / hashtab.c
1 /* Copyright (C) 1995,1996,1998,1999,2000,2001, 2003, 2004, 2006, 2008, 2009, 2010, 2011 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 <alloca.h>
26 #include <stdio.h>
27 #include <assert.h>
28
29 #include "libguile/_scm.h"
30 #include "libguile/alist.h"
31 #include "libguile/hash.h"
32 #include "libguile/eval.h"
33 #include "libguile/root.h"
34 #include "libguile/vectors.h"
35 #include "libguile/ports.h"
36 #include "libguile/bdw-gc.h"
37
38 #include "libguile/validate.h"
39 #include "libguile/hashtab.h"
40
41
42 \f
43
44 /* A hash table is a cell containing a vector of association lists.
45 *
46 * Growing or shrinking, with following rehashing, is triggered when
47 * the load factor
48 *
49 * L = N / S (N: number of items in table, S: bucket vector length)
50 *
51 * passes an upper limit of 0.9 or a lower limit of 0.25.
52 *
53 * The implementation stores the upper and lower number of items which
54 * trigger a resize in the hashtable object.
55 *
56 * Weak hash tables use weak pairs in the bucket lists rather than
57 * normal pairs.
58 *
59 * Possible hash table sizes (primes) are stored in the array
60 * hashtable_size.
61 */
62
63 static unsigned long hashtable_size[] = {
64 31, 61, 113, 223, 443, 883, 1759, 3517, 7027, 14051, 28099, 56197, 112363,
65 224717, 449419, 898823, 1797641, 3595271, 7190537, 14381041
66 #if SIZEOF_SCM_T_BITS > 4
67 /* vector lengths are stored in the first word of vectors, shifted by
68 8 bits for the tc8, so for 32-bit we only get 2^24-1 = 16777215
69 elements. But we allow a few more sizes for 64-bit. */
70 , 28762081, 57524111, 115048217, 230096423, 460192829
71 #endif
72 };
73
74 #define HASHTABLE_SIZE_N (sizeof(hashtable_size)/sizeof(unsigned long))
75
76 static char *s_hashtable = "hashtable";
77
78
79 \f
80 /* Helper functions and macros to deal with weak pairs.
81
82 Weak pairs need to be accessed very carefully since their components can
83 be nullified by the GC when the object they refer to becomes unreachable.
84 Hence the macros and functions below that detect such weak pairs within
85 buckets and remove them. */
86
87
88 /* Remove nullified weak pairs from ALIST such that the result contains only
89 valid pairs. Set REMOVED_ITEMS to the number of pairs that have been
90 deleted. */
91 static SCM
92 scm_fixup_weak_alist (SCM alist, size_t *removed_items)
93 {
94 SCM result;
95 SCM prev = SCM_EOL;
96
97 *removed_items = 0;
98 for (result = alist;
99 scm_is_pair (alist);
100 alist = SCM_CDR (alist))
101 {
102 SCM pair = SCM_CAR (alist);
103
104 if (SCM_WEAK_PAIR_DELETED_P (pair))
105 {
106 /* Remove from ALIST weak pair PAIR whose car/cdr has been
107 nullified by the GC. */
108 if (prev == SCM_EOL)
109 result = SCM_CDR (alist);
110 else
111 SCM_SETCDR (prev, SCM_CDR (alist));
112
113 (*removed_items)++;
114
115 /* Leave PREV unchanged. */
116 }
117 else
118 prev = alist;
119 }
120
121 return result;
122 }
123
124 static void
125 vacuum_weak_hash_table (SCM table)
126 {
127 SCM buckets = SCM_HASHTABLE_VECTOR (table);
128 unsigned long k = SCM_SIMPLE_VECTOR_LENGTH (buckets);
129 size_t len = SCM_HASHTABLE_N_ITEMS (table);
130
131 while (k--)
132 {
133 size_t removed;
134 SCM alist = SCM_SIMPLE_VECTOR_REF (buckets, k);
135 alist = scm_fixup_weak_alist (alist, &removed);
136 assert (removed <= len);
137 len -= removed;
138 SCM_SIMPLE_VECTOR_SET (buckets, k, alist);
139 }
140
141 SCM_SET_HASHTABLE_N_ITEMS (table, len);
142 }
143
144
145 /* Packed arguments for `do_weak_bucket_fixup'. */
146 struct t_fixup_args
147 {
148 SCM bucket;
149 SCM *bucket_copy;
150 size_t removed_items;
151 };
152
153 static void *
154 do_weak_bucket_fixup (void *data)
155 {
156 struct t_fixup_args *args;
157 SCM pair, *copy;
158
159 args = (struct t_fixup_args *) data;
160
161 args->bucket = scm_fixup_weak_alist (args->bucket, &args->removed_items);
162
163 for (pair = args->bucket, copy = args->bucket_copy;
164 scm_is_pair (pair);
165 pair = SCM_CDR (pair), copy += 2)
166 {
167 /* At this point, all weak pairs have been removed. */
168 assert (!SCM_WEAK_PAIR_DELETED_P (SCM_CAR (pair)));
169
170 /* Copy the key and value. */
171 copy[0] = SCM_CAAR (pair);
172 copy[1] = SCM_CDAR (pair);
173 }
174
175 return args;
176 }
177
178 /* Lookup OBJECT in weak hash table TABLE using ASSOC. OBJECT is searched
179 for in the alist that is the BUCKET_INDEXth element of BUCKETS.
180 Optionally update TABLE and rehash it. */
181 static SCM
182 weak_bucket_assoc (SCM table, SCM buckets, size_t bucket_index,
183 scm_t_hash_fn hash_fn,
184 scm_t_assoc_fn assoc, SCM object, void *closure)
185 {
186 SCM result;
187 SCM bucket, *strong_refs;
188 struct t_fixup_args args;
189
190 bucket = SCM_SIMPLE_VECTOR_REF (buckets, bucket_index);
191
192 /* Prepare STRONG_REFS as an array large enough to hold all the keys
193 and values in BUCKET. */
194 strong_refs = alloca (scm_ilength (bucket) * 2 * sizeof (SCM));
195
196 args.bucket = bucket;
197 args.bucket_copy = strong_refs;
198
199 /* Fixup BUCKET. Do that with the allocation lock held to avoid
200 seeing disappearing links pointing to objects that have already
201 been reclaimed (this happens when the disappearing links that point
202 to it haven't yet been cleared.)
203
204 The `do_weak_bucket_fixup' call populates STRONG_REFS with a copy
205 of BUCKET's entries after it's been fixed up. Thus, all the
206 entries kept in BUCKET are still reachable when ASSOC sees
207 them. */
208 GC_call_with_alloc_lock (do_weak_bucket_fixup, &args);
209
210 bucket = args.bucket;
211 SCM_SIMPLE_VECTOR_SET (buckets, bucket_index, bucket);
212
213 result = assoc (object, bucket, closure);
214 assert (!scm_is_pair (result) ||
215 !SCM_WEAK_PAIR_DELETED_P (GC_is_visible (result)));
216
217 scm_remember_upto_here_1 (strong_refs);
218
219 if (args.removed_items > 0)
220 {
221 /* Update TABLE's item count and optionally trigger a rehash. */
222 size_t remaining;
223
224 assert (SCM_HASHTABLE_N_ITEMS (table) >= args.removed_items);
225
226 remaining = SCM_HASHTABLE_N_ITEMS (table) - args.removed_items;
227 SCM_SET_HASHTABLE_N_ITEMS (table, remaining);
228
229 if (remaining < SCM_HASHTABLE_LOWER (table))
230 scm_i_rehash (table, hash_fn, closure, "weak_bucket_assoc");
231 }
232
233 return result;
234 }
235
236
237 /* Packed arguments for `weak_bucket_assoc_by_hash'. */
238 struct assoc_by_hash_data
239 {
240 SCM alist;
241 SCM ret;
242 scm_t_hash_predicate_fn predicate;
243 void *closure;
244 };
245
246 /* See scm_hash_fn_get_handle_by_hash below. */
247 static void*
248 weak_bucket_assoc_by_hash (void *args)
249 {
250 struct assoc_by_hash_data *data = args;
251 SCM alist = data->alist;
252
253 for (; scm_is_pair (alist); alist = SCM_CDR (alist))
254 {
255 SCM pair = SCM_CAR (alist);
256
257 if (!SCM_WEAK_PAIR_DELETED_P (pair)
258 && data->predicate (SCM_CAR (pair), data->closure))
259 {
260 data->ret = pair;
261 break;
262 }
263 }
264 return args;
265 }
266
267
268 \f
269 static SCM
270 make_hash_table (int flags, unsigned long k, const char *func_name)
271 {
272 SCM vector;
273 scm_t_hashtable *t;
274 int i = 0, n = k ? k : 31;
275 while (i < HASHTABLE_SIZE_N && n > hashtable_size[i])
276 ++i;
277 n = hashtable_size[i];
278
279 /* In both cases, i.e., regardless of whether we are creating a weak hash
280 table, we return a non-weak vector. This is because the vector itself
281 is not weak in the case of a weak hash table: the alist pairs are. */
282 vector = scm_c_make_vector (n, SCM_EOL);
283
284 t = scm_gc_malloc_pointerless (sizeof (*t), s_hashtable);
285 t->min_size_index = t->size_index = i;
286 t->n_items = 0;
287 t->lower = 0;
288 t->upper = 9 * n / 10;
289 t->flags = flags;
290 t->hash_fn = NULL;
291
292 /* FIXME: we just need two words of storage, not three */
293 return scm_double_cell (scm_tc7_hashtable, SCM_UNPACK (vector),
294 (scm_t_bits)t, 0);
295 }
296
297 void
298 scm_i_rehash (SCM table,
299 scm_t_hash_fn hash_fn,
300 void *closure,
301 const char* func_name)
302 {
303 SCM buckets, new_buckets;
304 int i;
305 unsigned long old_size;
306 unsigned long new_size;
307
308 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
309 {
310 /* rehashing is not triggered when i <= min_size */
311 i = SCM_HASHTABLE (table)->size_index;
312 do
313 --i;
314 while (i > SCM_HASHTABLE (table)->min_size_index
315 && SCM_HASHTABLE_N_ITEMS (table) < hashtable_size[i] / 4);
316 }
317 else
318 {
319 i = SCM_HASHTABLE (table)->size_index + 1;
320 if (i >= HASHTABLE_SIZE_N)
321 /* don't rehash */
322 return;
323
324 /* Remember HASH_FN for rehash_after_gc, but only when CLOSURE
325 is not needed since CLOSURE can not be guaranteed to be valid
326 after this function returns.
327 */
328 if (closure == NULL)
329 SCM_HASHTABLE (table)->hash_fn = hash_fn;
330 }
331 SCM_HASHTABLE (table)->size_index = i;
332
333 new_size = hashtable_size[i];
334 if (i <= SCM_HASHTABLE (table)->min_size_index)
335 SCM_HASHTABLE (table)->lower = 0;
336 else
337 SCM_HASHTABLE (table)->lower = new_size / 4;
338 SCM_HASHTABLE (table)->upper = 9 * new_size / 10;
339 buckets = SCM_HASHTABLE_VECTOR (table);
340
341 new_buckets = scm_c_make_vector (new_size, SCM_EOL);
342
343 /* When this is a weak hashtable, running the GC might change it.
344 We need to cope with this while rehashing its elements. We do
345 this by first installing the new, empty bucket vector. Then we
346 remove the elements from the old bucket vector and insert them
347 into the new one.
348 */
349
350 SCM_SET_HASHTABLE_VECTOR (table, new_buckets);
351 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
352
353 old_size = SCM_SIMPLE_VECTOR_LENGTH (buckets);
354 for (i = 0; i < old_size; ++i)
355 {
356 SCM ls, cell, handle;
357
358 ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
359 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_EOL);
360
361 while (scm_is_pair (ls))
362 {
363 unsigned long h;
364
365 cell = ls;
366 handle = SCM_CAR (cell);
367 ls = SCM_CDR (ls);
368
369 if (SCM_WEAK_PAIR_DELETED_P (handle))
370 /* HANDLE is a nullified weak pair: skip it. */
371 continue;
372
373 h = hash_fn (SCM_CAR (handle), new_size, closure);
374 if (h >= new_size)
375 scm_out_of_range (func_name, scm_from_ulong (h));
376 SCM_SETCDR (cell, SCM_SIMPLE_VECTOR_REF (new_buckets, h));
377 SCM_SIMPLE_VECTOR_SET (new_buckets, h, cell);
378 SCM_HASHTABLE_INCREMENT (table);
379 }
380 }
381 }
382
383
384 void
385 scm_i_hashtable_print (SCM exp, SCM port, scm_print_state *pstate)
386 {
387 scm_puts ("#<", port);
388 if (SCM_HASHTABLE_WEAK_KEY_P (exp))
389 scm_puts ("weak-key-", port);
390 else if (SCM_HASHTABLE_WEAK_VALUE_P (exp))
391 scm_puts ("weak-value-", port);
392 else if (SCM_HASHTABLE_DOUBLY_WEAK_P (exp))
393 scm_puts ("doubly-weak-", port);
394 scm_puts ("hash-table ", port);
395 scm_uintprint (SCM_HASHTABLE_N_ITEMS (exp), 10, port);
396 scm_putc ('/', port);
397 scm_uintprint (SCM_SIMPLE_VECTOR_LENGTH (SCM_HASHTABLE_VECTOR (exp)),
398 10, port);
399 scm_puts (">", port);
400 }
401
402
403 SCM
404 scm_c_make_hash_table (unsigned long k)
405 {
406 return make_hash_table (0, k, "scm_c_make_hash_table");
407 }
408
409 SCM_DEFINE (scm_make_hash_table, "make-hash-table", 0, 1, 0,
410 (SCM n),
411 "Make a new abstract hash table object with minimum number of buckets @var{n}\n")
412 #define FUNC_NAME s_scm_make_hash_table
413 {
414 if (SCM_UNBNDP (n))
415 return make_hash_table (0, 0, FUNC_NAME);
416 else
417 return make_hash_table (0, scm_to_ulong (n), FUNC_NAME);
418 }
419 #undef FUNC_NAME
420
421 /* The before-gc C hook only runs if GC_set_start_callback is available,
422 so if not, fall back on a finalizer-based implementation. */
423 static int
424 weak_gc_callback (void **weak)
425 {
426 void *val = weak[0];
427 void (*callback) (SCM) = weak[1];
428
429 if (!val)
430 return 0;
431
432 callback (PTR2SCM (val));
433
434 return 1;
435 }
436
437 #ifdef HAVE_GC_SET_START_CALLBACK
438 static void*
439 weak_gc_hook (void *hook_data, void *fn_data, void *data)
440 {
441 if (!weak_gc_callback (fn_data))
442 scm_c_hook_remove (&scm_before_gc_c_hook, weak_gc_hook, fn_data);
443
444 return NULL;
445 }
446 #else
447 static void
448 weak_gc_finalizer (void *ptr, void *data)
449 {
450 if (weak_gc_callback (ptr))
451 GC_REGISTER_FINALIZER_NO_ORDER (ptr, weak_gc_finalizer, data, NULL, NULL);
452 }
453 #endif
454
455 static void
456 scm_c_register_weak_gc_callback (SCM obj, void (*callback) (SCM))
457 {
458 void **weak = GC_MALLOC_ATOMIC (sizeof (void*) * 2);
459
460 weak[0] = SCM2PTR (obj);
461 weak[1] = (void*)callback;
462 GC_GENERAL_REGISTER_DISAPPEARING_LINK (weak, SCM2PTR (obj));
463
464 #ifdef HAVE_GC_SET_START_CALLBACK
465 scm_c_hook_add (&scm_before_gc_c_hook, weak_gc_hook, weak, 0);
466 #else
467 GC_REGISTER_FINALIZER_NO_ORDER (weak, weak_gc_finalizer, NULL, NULL, NULL);
468 #endif
469 }
470
471 SCM_DEFINE (scm_make_weak_key_hash_table, "make-weak-key-hash-table", 0, 1, 0,
472 (SCM n),
473 "@deffnx {Scheme Procedure} make-weak-value-hash-table size\n"
474 "@deffnx {Scheme Procedure} make-doubly-weak-hash-table size\n"
475 "Return a weak hash table with @var{size} buckets.\n"
476 "\n"
477 "You can modify weak hash tables in exactly the same way you\n"
478 "would modify regular hash tables. (@pxref{Hash Tables})")
479 #define FUNC_NAME s_scm_make_weak_key_hash_table
480 {
481 SCM ret;
482
483 if (SCM_UNBNDP (n))
484 ret = make_hash_table (SCM_HASHTABLEF_WEAK_CAR, 0, FUNC_NAME);
485 else
486 ret = make_hash_table (SCM_HASHTABLEF_WEAK_CAR,
487 scm_to_ulong (n), FUNC_NAME);
488
489 scm_c_register_weak_gc_callback (ret, vacuum_weak_hash_table);
490
491 return ret;
492 }
493 #undef FUNC_NAME
494
495
496 SCM_DEFINE (scm_make_weak_value_hash_table, "make-weak-value-hash-table", 0, 1, 0,
497 (SCM n),
498 "Return a hash table with weak values with @var{size} buckets.\n"
499 "(@pxref{Hash Tables})")
500 #define FUNC_NAME s_scm_make_weak_value_hash_table
501 {
502 SCM ret;
503
504 if (SCM_UNBNDP (n))
505 ret = make_hash_table (SCM_HASHTABLEF_WEAK_CDR, 0, FUNC_NAME);
506 else
507 ret = make_hash_table (SCM_HASHTABLEF_WEAK_CDR,
508 scm_to_ulong (n), FUNC_NAME);
509
510 scm_c_register_weak_gc_callback (ret, vacuum_weak_hash_table);
511
512 return ret;
513 }
514 #undef FUNC_NAME
515
516
517 SCM_DEFINE (scm_make_doubly_weak_hash_table, "make-doubly-weak-hash-table", 1, 0, 0,
518 (SCM n),
519 "Return a hash table with weak keys and values with @var{size}\n"
520 "buckets. (@pxref{Hash Tables})")
521 #define FUNC_NAME s_scm_make_doubly_weak_hash_table
522 {
523 SCM ret;
524
525 if (SCM_UNBNDP (n))
526 ret = make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
527 0, FUNC_NAME);
528 else
529 ret = make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
530 scm_to_ulong (n), FUNC_NAME);
531
532 scm_c_register_weak_gc_callback (ret, vacuum_weak_hash_table);
533
534 return ret;
535 }
536 #undef FUNC_NAME
537
538
539 SCM_DEFINE (scm_hash_table_p, "hash-table?", 1, 0, 0,
540 (SCM obj),
541 "Return @code{#t} if @var{obj} is an abstract hash table object.")
542 #define FUNC_NAME s_scm_hash_table_p
543 {
544 return scm_from_bool (SCM_HASHTABLE_P (obj));
545 }
546 #undef FUNC_NAME
547
548
549 SCM_DEFINE (scm_weak_key_hash_table_p, "weak-key-hash-table?", 1, 0, 0,
550 (SCM obj),
551 "@deffnx {Scheme Procedure} weak-value-hash-table? obj\n"
552 "@deffnx {Scheme Procedure} doubly-weak-hash-table? obj\n"
553 "Return @code{#t} if @var{obj} is the specified weak hash\n"
554 "table. Note that a doubly weak hash table is neither a weak key\n"
555 "nor a weak value hash table.")
556 #define FUNC_NAME s_scm_weak_key_hash_table_p
557 {
558 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_KEY_P (obj));
559 }
560 #undef FUNC_NAME
561
562
563 SCM_DEFINE (scm_weak_value_hash_table_p, "weak-value-hash-table?", 1, 0, 0,
564 (SCM obj),
565 "Return @code{#t} if @var{obj} is a weak value hash table.")
566 #define FUNC_NAME s_scm_weak_value_hash_table_p
567 {
568 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_VALUE_P (obj));
569 }
570 #undef FUNC_NAME
571
572
573 SCM_DEFINE (scm_doubly_weak_hash_table_p, "doubly-weak-hash-table?", 1, 0, 0,
574 (SCM obj),
575 "Return @code{#t} if @var{obj} is a doubly weak hash table.")
576 #define FUNC_NAME s_scm_doubly_weak_hash_table_p
577 {
578 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_DOUBLY_WEAK_P (obj));
579 }
580 #undef FUNC_NAME
581
582 \f
583 /* Accessing hash table entries. */
584
585 SCM
586 scm_hash_fn_get_handle (SCM table, SCM obj,
587 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
588 void * closure)
589 #define FUNC_NAME "scm_hash_fn_get_handle"
590 {
591 unsigned long k;
592 SCM buckets, h;
593
594 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
595 buckets = SCM_HASHTABLE_VECTOR (table);
596
597 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
598 return SCM_BOOL_F;
599 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
600 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
601 scm_out_of_range (FUNC_NAME, scm_from_ulong (k));
602
603 if (SCM_HASHTABLE_WEAK_P (table))
604 h = weak_bucket_assoc (table, buckets, k, hash_fn,
605 assoc_fn, obj, closure);
606 else
607 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
608
609 return h;
610 }
611 #undef FUNC_NAME
612
613
614 /* This procedure implements three optimizations, with respect to the
615 raw get_handle():
616
617 1. For weak tables, it's assumed that calling the predicate in the
618 allocation lock is safe. In practice this means that the predicate
619 cannot call arbitrary scheme functions.
620
621 2. We don't check for overflow / underflow and rehash.
622
623 3. We don't actually have to allocate a key -- instead we get the
624 hash value directly. This is useful for, for example, looking up
625 strings in the symbol table.
626 */
627 SCM
628 scm_hash_fn_get_handle_by_hash (SCM table, unsigned long raw_hash,
629 scm_t_hash_predicate_fn predicate_fn,
630 void *closure)
631 #define FUNC_NAME "scm_hash_fn_ref_by_hash"
632 {
633 unsigned long k;
634 SCM buckets, alist, h = SCM_BOOL_F;
635
636 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
637 buckets = SCM_HASHTABLE_VECTOR (table);
638
639 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
640 return SCM_BOOL_F;
641
642 k = raw_hash % SCM_SIMPLE_VECTOR_LENGTH (buckets);
643 alist = SCM_SIMPLE_VECTOR_REF (buckets, k);
644
645 if (SCM_HASHTABLE_WEAK_P (table))
646 {
647 struct assoc_by_hash_data args;
648
649 args.alist = alist;
650 args.ret = SCM_BOOL_F;
651 args.predicate = predicate_fn;
652 args.closure = closure;
653 GC_call_with_alloc_lock (weak_bucket_assoc_by_hash, &args);
654 h = args.ret;
655 }
656 else
657 for (; scm_is_pair (alist); alist = SCM_CDR (alist))
658 {
659 SCM pair = SCM_CAR (alist);
660 if (predicate_fn (SCM_CAR (pair), closure))
661 {
662 h = pair;
663 break;
664 }
665 }
666
667 return h;
668 }
669 #undef FUNC_NAME
670
671
672 SCM
673 scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init,
674 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
675 void * closure)
676 #define FUNC_NAME "scm_hash_fn_create_handle_x"
677 {
678 unsigned long k;
679 SCM buckets, it;
680
681 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
682 buckets = SCM_HASHTABLE_VECTOR (table);
683
684 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
685 SCM_MISC_ERROR ("void hashtable", SCM_EOL);
686
687 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
688 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
689 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
690
691 if (SCM_HASHTABLE_WEAK_P (table))
692 it = weak_bucket_assoc (table, buckets, k, hash_fn,
693 assoc_fn, obj, closure);
694 else
695 it = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
696
697 if (scm_is_pair (it))
698 return it;
699 else if (scm_is_true (it))
700 scm_wrong_type_arg_msg (NULL, 0, it, "a pair");
701 else
702 {
703 /* When this is a weak hashtable, running the GC can change it.
704 Thus, we must allocate the new cells first and can only then
705 access BUCKETS. Also, we need to fetch the bucket vector
706 again since the hashtable might have been rehashed. This
707 necessitates a new hash value as well.
708 */
709 SCM handle, new_bucket;
710
711 if (SCM_HASHTABLE_WEAK_P (table))
712 {
713 /* FIXME: We don't support weak alist vectors. */
714 /* Use a weak cell. */
715 if (SCM_HASHTABLE_DOUBLY_WEAK_P (table))
716 handle = scm_doubly_weak_pair (obj, init);
717 else if (SCM_HASHTABLE_WEAK_KEY_P (table))
718 handle = scm_weak_car_pair (obj, init);
719 else
720 handle = scm_weak_cdr_pair (obj, init);
721 }
722 else
723 /* Use a regular, non-weak cell. */
724 handle = scm_cons (obj, init);
725
726 new_bucket = scm_cons (handle, SCM_EOL);
727
728 if (!scm_is_eq (SCM_HASHTABLE_VECTOR (table), buckets))
729 {
730 buckets = SCM_HASHTABLE_VECTOR (table);
731 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
732 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
733 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
734 }
735 SCM_SETCDR (new_bucket, SCM_SIMPLE_VECTOR_REF (buckets, k));
736 SCM_SIMPLE_VECTOR_SET (buckets, k, new_bucket);
737 SCM_HASHTABLE_INCREMENT (table);
738
739 /* Maybe rehash the table. */
740 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table)
741 || SCM_HASHTABLE_N_ITEMS (table) > SCM_HASHTABLE_UPPER (table))
742 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
743 return SCM_CAR (new_bucket);
744 }
745 }
746 #undef FUNC_NAME
747
748
749 SCM
750 scm_hash_fn_ref (SCM table, SCM obj, SCM dflt,
751 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
752 void *closure)
753 {
754 SCM it = scm_hash_fn_get_handle (table, obj, hash_fn, assoc_fn, closure);
755 if (scm_is_pair (it))
756 return SCM_CDR (it);
757 else
758 return dflt;
759 }
760
761
762
763
764 struct set_weak_cdr_data
765 {
766 SCM pair;
767 SCM new_val;
768 };
769
770 static void*
771 set_weak_cdr (void *data)
772 {
773 struct set_weak_cdr_data *d = data;
774
775 if (SCM_NIMP (SCM_WEAK_PAIR_CDR (d->pair)) && !SCM_NIMP (d->new_val))
776 {
777 GC_unregister_disappearing_link ((void *) SCM_CDRLOC (d->pair));
778 SCM_SETCDR (d->pair, d->new_val);
779 }
780 else
781 {
782 SCM_SETCDR (d->pair, d->new_val);
783 SCM_I_REGISTER_DISAPPEARING_LINK ((void *) SCM_CDRLOC (d->pair),
784 SCM2PTR (d->new_val));
785 }
786 return NULL;
787 }
788
789 SCM
790 scm_hash_fn_set_x (SCM table, SCM obj, SCM val,
791 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
792 void *closure)
793 {
794 SCM pair;
795
796 pair = scm_hash_fn_create_handle_x (table, obj, val,
797 hash_fn, assoc_fn, closure);
798
799 if (SCM_UNLIKELY (!scm_is_eq (SCM_CDR (pair), val)))
800 {
801 if (SCM_UNLIKELY (SCM_HASHTABLE_WEAK_VALUE_P (table)))
802 {
803 struct set_weak_cdr_data data;
804
805 data.pair = pair;
806 data.new_val = val;
807
808 GC_call_with_alloc_lock (set_weak_cdr, &data);
809 }
810 else
811 SCM_SETCDR (pair, val);
812 }
813
814 return val;
815 }
816
817
818 SCM
819 scm_hash_fn_remove_x (SCM table, SCM obj,
820 scm_t_hash_fn hash_fn,
821 scm_t_assoc_fn assoc_fn,
822 void *closure)
823 #define FUNC_NAME "hash_fn_remove_x"
824 {
825 unsigned long k;
826 SCM buckets, h;
827
828 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
829
830 buckets = SCM_HASHTABLE_VECTOR (table);
831
832 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
833 return SCM_EOL;
834
835 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
836 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
837 scm_out_of_range (FUNC_NAME, scm_from_ulong (k));
838
839 if (SCM_HASHTABLE_WEAK_P (table))
840 h = weak_bucket_assoc (table, buckets, k, hash_fn,
841 assoc_fn, obj, closure);
842 else
843 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
844
845 if (scm_is_true (h))
846 {
847 SCM_SIMPLE_VECTOR_SET
848 (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
849 SCM_HASHTABLE_DECREMENT (table);
850 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
851 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
852 }
853 return h;
854 }
855 #undef FUNC_NAME
856
857 SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
858 (SCM table),
859 "Remove all items from @var{table} (without triggering a resize).")
860 #define FUNC_NAME s_scm_hash_clear_x
861 {
862 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
863
864 scm_vector_fill_x (SCM_HASHTABLE_VECTOR (table), SCM_EOL);
865 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
866
867 return SCM_UNSPECIFIED;
868 }
869 #undef FUNC_NAME
870
871 \f
872
873 SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
874 (SCM table, SCM key),
875 "This procedure returns the @code{(key . value)} pair from the\n"
876 "hash table @var{table}. If @var{table} does not hold an\n"
877 "associated value for @var{key}, @code{#f} is returned.\n"
878 "Uses @code{eq?} for equality testing.")
879 #define FUNC_NAME s_scm_hashq_get_handle
880 {
881 return scm_hash_fn_get_handle (table, key,
882 (scm_t_hash_fn) scm_ihashq,
883 (scm_t_assoc_fn) scm_sloppy_assq,
884 0);
885 }
886 #undef FUNC_NAME
887
888
889 SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
890 (SCM table, SCM key, SCM init),
891 "This function looks up @var{key} in @var{table} and returns its handle.\n"
892 "If @var{key} is not already present, a new handle is created which\n"
893 "associates @var{key} with @var{init}.")
894 #define FUNC_NAME s_scm_hashq_create_handle_x
895 {
896 return scm_hash_fn_create_handle_x (table, key, init,
897 (scm_t_hash_fn) scm_ihashq,
898 (scm_t_assoc_fn) scm_sloppy_assq,
899 0);
900 }
901 #undef FUNC_NAME
902
903
904 SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
905 (SCM table, SCM key, SCM dflt),
906 "Look up @var{key} in the hash table @var{table}, and return the\n"
907 "value (if any) associated with it. If @var{key} is not found,\n"
908 "return @var{default} (or @code{#f} if no @var{default} argument\n"
909 "is supplied). Uses @code{eq?} for equality testing.")
910 #define FUNC_NAME s_scm_hashq_ref
911 {
912 if (SCM_UNBNDP (dflt))
913 dflt = SCM_BOOL_F;
914 return scm_hash_fn_ref (table, key, dflt,
915 (scm_t_hash_fn) scm_ihashq,
916 (scm_t_assoc_fn) scm_sloppy_assq,
917 0);
918 }
919 #undef FUNC_NAME
920
921
922
923 SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
924 (SCM table, SCM key, SCM val),
925 "Find the entry in @var{table} associated with @var{key}, and\n"
926 "store @var{value} there. Uses @code{eq?} for equality testing.")
927 #define FUNC_NAME s_scm_hashq_set_x
928 {
929 return scm_hash_fn_set_x (table, key, val,
930 (scm_t_hash_fn) scm_ihashq,
931 (scm_t_assoc_fn) scm_sloppy_assq,
932 0);
933 }
934 #undef FUNC_NAME
935
936
937
938 SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
939 (SCM table, SCM key),
940 "Remove @var{key} (and any value associated with it) from\n"
941 "@var{table}. Uses @code{eq?} for equality tests.")
942 #define FUNC_NAME s_scm_hashq_remove_x
943 {
944 return scm_hash_fn_remove_x (table, key,
945 (scm_t_hash_fn) scm_ihashq,
946 (scm_t_assoc_fn) scm_sloppy_assq,
947 0);
948 }
949 #undef FUNC_NAME
950
951
952 \f
953
954 SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
955 (SCM table, SCM key),
956 "This procedure returns the @code{(key . value)} pair from the\n"
957 "hash table @var{table}. If @var{table} does not hold an\n"
958 "associated value for @var{key}, @code{#f} is returned.\n"
959 "Uses @code{eqv?} for equality testing.")
960 #define FUNC_NAME s_scm_hashv_get_handle
961 {
962 return scm_hash_fn_get_handle (table, key,
963 (scm_t_hash_fn) scm_ihashv,
964 (scm_t_assoc_fn) scm_sloppy_assv,
965 0);
966 }
967 #undef FUNC_NAME
968
969
970 SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
971 (SCM table, SCM key, SCM init),
972 "This function looks up @var{key} in @var{table} and returns its handle.\n"
973 "If @var{key} is not already present, a new handle is created which\n"
974 "associates @var{key} with @var{init}.")
975 #define FUNC_NAME s_scm_hashv_create_handle_x
976 {
977 return scm_hash_fn_create_handle_x (table, key, init,
978 (scm_t_hash_fn) scm_ihashv,
979 (scm_t_assoc_fn) scm_sloppy_assv,
980 0);
981 }
982 #undef FUNC_NAME
983
984
985 SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
986 (SCM table, SCM key, SCM dflt),
987 "Look up @var{key} in the hash table @var{table}, and return the\n"
988 "value (if any) associated with it. If @var{key} is not found,\n"
989 "return @var{default} (or @code{#f} if no @var{default} argument\n"
990 "is supplied). Uses @code{eqv?} for equality testing.")
991 #define FUNC_NAME s_scm_hashv_ref
992 {
993 if (SCM_UNBNDP (dflt))
994 dflt = SCM_BOOL_F;
995 return scm_hash_fn_ref (table, key, dflt,
996 (scm_t_hash_fn) scm_ihashv,
997 (scm_t_assoc_fn) scm_sloppy_assv,
998 0);
999 }
1000 #undef FUNC_NAME
1001
1002
1003
1004 SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
1005 (SCM table, SCM key, SCM val),
1006 "Find the entry in @var{table} associated with @var{key}, and\n"
1007 "store @var{value} there. Uses @code{eqv?} for equality testing.")
1008 #define FUNC_NAME s_scm_hashv_set_x
1009 {
1010 return scm_hash_fn_set_x (table, key, val,
1011 (scm_t_hash_fn) scm_ihashv,
1012 (scm_t_assoc_fn) scm_sloppy_assv,
1013 0);
1014 }
1015 #undef FUNC_NAME
1016
1017
1018 SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
1019 (SCM table, SCM key),
1020 "Remove @var{key} (and any value associated with it) from\n"
1021 "@var{table}. Uses @code{eqv?} for equality tests.")
1022 #define FUNC_NAME s_scm_hashv_remove_x
1023 {
1024 return scm_hash_fn_remove_x (table, key,
1025 (scm_t_hash_fn) scm_ihashv,
1026 (scm_t_assoc_fn) scm_sloppy_assv,
1027 0);
1028 }
1029 #undef FUNC_NAME
1030
1031 \f
1032
1033 SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
1034 (SCM table, SCM key),
1035 "This procedure returns the @code{(key . value)} pair from the\n"
1036 "hash table @var{table}. If @var{table} does not hold an\n"
1037 "associated value for @var{key}, @code{#f} is returned.\n"
1038 "Uses @code{equal?} for equality testing.")
1039 #define FUNC_NAME s_scm_hash_get_handle
1040 {
1041 return scm_hash_fn_get_handle (table, key,
1042 (scm_t_hash_fn) scm_ihash,
1043 (scm_t_assoc_fn) scm_sloppy_assoc,
1044 0);
1045 }
1046 #undef FUNC_NAME
1047
1048
1049 SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
1050 (SCM table, SCM key, SCM init),
1051 "This function looks up @var{key} in @var{table} and returns its handle.\n"
1052 "If @var{key} is not already present, a new handle is created which\n"
1053 "associates @var{key} with @var{init}.")
1054 #define FUNC_NAME s_scm_hash_create_handle_x
1055 {
1056 return scm_hash_fn_create_handle_x (table, key, init,
1057 (scm_t_hash_fn) scm_ihash,
1058 (scm_t_assoc_fn) scm_sloppy_assoc,
1059 0);
1060 }
1061 #undef FUNC_NAME
1062
1063
1064 SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
1065 (SCM table, SCM key, SCM dflt),
1066 "Look up @var{key} in the hash table @var{table}, and return the\n"
1067 "value (if any) associated with it. If @var{key} is not found,\n"
1068 "return @var{default} (or @code{#f} if no @var{default} argument\n"
1069 "is supplied). Uses @code{equal?} for equality testing.")
1070 #define FUNC_NAME s_scm_hash_ref
1071 {
1072 if (SCM_UNBNDP (dflt))
1073 dflt = SCM_BOOL_F;
1074 return scm_hash_fn_ref (table, key, dflt,
1075 (scm_t_hash_fn) scm_ihash,
1076 (scm_t_assoc_fn) scm_sloppy_assoc,
1077 0);
1078 }
1079 #undef FUNC_NAME
1080
1081
1082
1083 SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
1084 (SCM table, SCM key, SCM val),
1085 "Find the entry in @var{table} associated with @var{key}, and\n"
1086 "store @var{value} there. Uses @code{equal?} for equality\n"
1087 "testing.")
1088 #define FUNC_NAME s_scm_hash_set_x
1089 {
1090 return scm_hash_fn_set_x (table, key, val,
1091 (scm_t_hash_fn) scm_ihash,
1092 (scm_t_assoc_fn) scm_sloppy_assoc,
1093 0);
1094 }
1095 #undef FUNC_NAME
1096
1097
1098
1099 SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
1100 (SCM table, SCM key),
1101 "Remove @var{key} (and any value associated with it) from\n"
1102 "@var{table}. Uses @code{equal?} for equality tests.")
1103 #define FUNC_NAME s_scm_hash_remove_x
1104 {
1105 return scm_hash_fn_remove_x (table, key,
1106 (scm_t_hash_fn) scm_ihash,
1107 (scm_t_assoc_fn) scm_sloppy_assoc,
1108 0);
1109 }
1110 #undef FUNC_NAME
1111
1112 \f
1113
1114
1115 typedef struct scm_t_ihashx_closure
1116 {
1117 SCM hash;
1118 SCM assoc;
1119 } scm_t_ihashx_closure;
1120
1121
1122
1123 static unsigned long
1124 scm_ihashx (SCM obj, unsigned long n, void *arg)
1125 {
1126 SCM answer;
1127 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
1128 answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
1129 return scm_to_ulong (answer);
1130 }
1131
1132
1133
1134 static SCM
1135 scm_sloppy_assx (SCM obj, SCM alist, void *arg)
1136 {
1137 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
1138 return scm_call_2 (closure->assoc, obj, alist);
1139 }
1140
1141
1142 SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
1143 (SCM hash, SCM assoc, SCM table, SCM key),
1144 "This behaves the same way as the corresponding\n"
1145 "@code{-get-handle} function, but uses @var{hash} as a hash\n"
1146 "function and @var{assoc} to compare keys. @code{hash} must be\n"
1147 "a function that takes two arguments, a key to be hashed and a\n"
1148 "table size. @code{assoc} must be an associator function, like\n"
1149 "@code{assoc}, @code{assq} or @code{assv}.")
1150 #define FUNC_NAME s_scm_hashx_get_handle
1151 {
1152 scm_t_ihashx_closure closure;
1153 closure.hash = hash;
1154 closure.assoc = assoc;
1155 return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
1156 (void *) &closure);
1157 }
1158 #undef FUNC_NAME
1159
1160
1161 SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
1162 (SCM hash, SCM assoc, SCM table, SCM key, SCM init),
1163 "This behaves the same way as the corresponding\n"
1164 "@code{-create-handle} function, but uses @var{hash} as a hash\n"
1165 "function and @var{assoc} to compare keys. @code{hash} must be\n"
1166 "a function that takes two arguments, a key to be hashed and a\n"
1167 "table size. @code{assoc} must be an associator function, like\n"
1168 "@code{assoc}, @code{assq} or @code{assv}.")
1169 #define FUNC_NAME s_scm_hashx_create_handle_x
1170 {
1171 scm_t_ihashx_closure closure;
1172 closure.hash = hash;
1173 closure.assoc = assoc;
1174 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
1175 scm_sloppy_assx, (void *)&closure);
1176 }
1177 #undef FUNC_NAME
1178
1179
1180
1181 SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
1182 (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
1183 "This behaves the same way as the corresponding @code{ref}\n"
1184 "function, but uses @var{hash} as a hash function and\n"
1185 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1186 "that takes two arguments, a key to be hashed and a table size.\n"
1187 "@code{assoc} must be an associator function, like @code{assoc},\n"
1188 "@code{assq} or @code{assv}.\n"
1189 "\n"
1190 "By way of illustration, @code{hashq-ref table key} is\n"
1191 "equivalent to @code{hashx-ref hashq assq table key}.")
1192 #define FUNC_NAME s_scm_hashx_ref
1193 {
1194 scm_t_ihashx_closure closure;
1195 if (SCM_UNBNDP (dflt))
1196 dflt = SCM_BOOL_F;
1197 closure.hash = hash;
1198 closure.assoc = assoc;
1199 return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
1200 (void *)&closure);
1201 }
1202 #undef FUNC_NAME
1203
1204
1205
1206
1207 SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
1208 (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
1209 "This behaves the same way as the corresponding @code{set!}\n"
1210 "function, but uses @var{hash} as a hash function and\n"
1211 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1212 "that takes two arguments, a key to be hashed and a table size.\n"
1213 "@code{assoc} must be an associator function, like @code{assoc},\n"
1214 "@code{assq} or @code{assv}.\n"
1215 "\n"
1216 " By way of illustration, @code{hashq-set! table key} is\n"
1217 "equivalent to @code{hashx-set! hashq assq table key}.")
1218 #define FUNC_NAME s_scm_hashx_set_x
1219 {
1220 scm_t_ihashx_closure closure;
1221 closure.hash = hash;
1222 closure.assoc = assoc;
1223 return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
1224 (void *)&closure);
1225 }
1226 #undef FUNC_NAME
1227
1228 SCM_DEFINE (scm_hashx_remove_x, "hashx-remove!", 4, 0, 0,
1229 (SCM hash, SCM assoc, SCM table, SCM obj),
1230 "This behaves the same way as the corresponding @code{remove!}\n"
1231 "function, but uses @var{hash} as a hash function and\n"
1232 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1233 "that takes two arguments, a key to be hashed and a table size.\n"
1234 "@code{assoc} must be an associator function, like @code{assoc},\n"
1235 "@code{assq} or @code{assv}.\n"
1236 "\n"
1237 " By way of illustration, @code{hashq-remove! table key} is\n"
1238 "equivalent to @code{hashx-remove! hashq assq #f table key}.")
1239 #define FUNC_NAME s_scm_hashx_remove_x
1240 {
1241 scm_t_ihashx_closure closure;
1242 closure.hash = hash;
1243 closure.assoc = assoc;
1244 return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
1245 (void *) &closure);
1246 }
1247 #undef FUNC_NAME
1248
1249 /* Hash table iterators */
1250
1251 SCM_DEFINE (scm_hash_fold, "hash-fold", 3, 0, 0,
1252 (SCM proc, SCM init, SCM table),
1253 "An iterator over hash-table elements.\n"
1254 "Accumulates and returns a result by applying PROC successively.\n"
1255 "The arguments to PROC are \"(key value prior-result)\" where key\n"
1256 "and value are successive pairs from the hash table TABLE, and\n"
1257 "prior-result is either INIT (for the first application of PROC)\n"
1258 "or the return value of the previous application of PROC.\n"
1259 "For example, @code{(hash-fold acons '() tab)} will convert a hash\n"
1260 "table into an a-list of key-value pairs.")
1261 #define FUNC_NAME s_scm_hash_fold
1262 {
1263 SCM_VALIDATE_PROC (1, proc);
1264 SCM_VALIDATE_HASHTABLE (3, table);
1265 return scm_internal_hash_fold ((scm_t_hash_fold_fn) scm_call_3,
1266 (void *) SCM_UNPACK (proc), init, table);
1267 }
1268 #undef FUNC_NAME
1269
1270 static SCM
1271 for_each_proc (void *proc, SCM handle)
1272 {
1273 return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
1274 }
1275
1276 SCM_DEFINE (scm_hash_for_each, "hash-for-each", 2, 0, 0,
1277 (SCM proc, SCM table),
1278 "An iterator over hash-table elements.\n"
1279 "Applies PROC successively on all hash table items.\n"
1280 "The arguments to PROC are \"(key value)\" where key\n"
1281 "and value are successive pairs from the hash table TABLE.")
1282 #define FUNC_NAME s_scm_hash_for_each
1283 {
1284 SCM_VALIDATE_PROC (1, proc);
1285 SCM_VALIDATE_HASHTABLE (2, table);
1286
1287 scm_internal_hash_for_each_handle (for_each_proc,
1288 (void *) SCM_UNPACK (proc),
1289 table);
1290 return SCM_UNSPECIFIED;
1291 }
1292 #undef FUNC_NAME
1293
1294 SCM_DEFINE (scm_hash_for_each_handle, "hash-for-each-handle", 2, 0, 0,
1295 (SCM proc, SCM table),
1296 "An iterator over hash-table elements.\n"
1297 "Applies PROC successively on all hash table handles.")
1298 #define FUNC_NAME s_scm_hash_for_each_handle
1299 {
1300 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)), proc, 1, FUNC_NAME);
1301 SCM_VALIDATE_HASHTABLE (2, table);
1302
1303 scm_internal_hash_for_each_handle ((scm_t_hash_handle_fn) scm_call_1,
1304 (void *) SCM_UNPACK (proc),
1305 table);
1306 return SCM_UNSPECIFIED;
1307 }
1308 #undef FUNC_NAME
1309
1310 static SCM
1311 map_proc (void *proc, SCM key, SCM data, SCM value)
1312 {
1313 return scm_cons (scm_call_2 (SCM_PACK (proc), key, data), value);
1314 }
1315
1316 SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
1317 (SCM proc, SCM table),
1318 "An iterator over hash-table elements.\n"
1319 "Accumulates and returns as a list the results of applying PROC successively.\n"
1320 "The arguments to PROC are \"(key value)\" where key\n"
1321 "and value are successive pairs from the hash table TABLE.")
1322 #define FUNC_NAME s_scm_hash_map_to_list
1323 {
1324 SCM_VALIDATE_PROC (1, proc);
1325 SCM_VALIDATE_HASHTABLE (2, table);
1326 return scm_internal_hash_fold (map_proc,
1327 (void *) SCM_UNPACK (proc),
1328 SCM_EOL,
1329 table);
1330 }
1331 #undef FUNC_NAME
1332
1333 \f
1334
1335 SCM
1336 scm_internal_hash_fold (scm_t_hash_fold_fn fn, void *closure,
1337 SCM init, SCM table)
1338 #define FUNC_NAME s_scm_hash_fold
1339 {
1340 long i, n;
1341 SCM buckets, result = init;
1342
1343 SCM_VALIDATE_HASHTABLE (0, table);
1344 buckets = SCM_HASHTABLE_VECTOR (table);
1345
1346 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
1347 for (i = 0; i < n; ++i)
1348 {
1349 SCM prev, ls;
1350
1351 for (prev = SCM_BOOL_F, ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
1352 !scm_is_null (ls);
1353 prev = ls, ls = SCM_CDR (ls))
1354 {
1355 SCM handle;
1356
1357 if (!scm_is_pair (ls))
1358 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
1359
1360 handle = SCM_CAR (ls);
1361 if (!scm_is_pair (handle))
1362 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
1363
1364 if (SCM_HASHTABLE_WEAK_P (table))
1365 {
1366 if (SCM_WEAK_PAIR_DELETED_P (handle))
1367 {
1368 /* We hit a weak pair whose car/cdr has become
1369 unreachable: unlink it from the bucket. */
1370 if (prev != SCM_BOOL_F)
1371 SCM_SETCDR (prev, SCM_CDR (ls));
1372 else
1373 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_CDR (ls));
1374
1375 /* Update the item count. */
1376 SCM_HASHTABLE_DECREMENT (table);
1377
1378 continue;
1379 }
1380 }
1381
1382 result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
1383 }
1384 }
1385
1386 return result;
1387 }
1388 #undef FUNC_NAME
1389
1390 /* The following redundant code is here in order to be able to support
1391 hash-for-each-handle. An alternative would have been to replace
1392 this code and scm_internal_hash_fold above with a single
1393 scm_internal_hash_fold_handles, but we don't want to promote such
1394 an API. */
1395
1396 void
1397 scm_internal_hash_for_each_handle (scm_t_hash_handle_fn fn, void *closure,
1398 SCM table)
1399 #define FUNC_NAME s_scm_hash_for_each
1400 {
1401 long i, n;
1402 SCM buckets;
1403
1404 SCM_VALIDATE_HASHTABLE (0, table);
1405 buckets = SCM_HASHTABLE_VECTOR (table);
1406 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
1407
1408 for (i = 0; i < n; ++i)
1409 {
1410 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
1411 while (!scm_is_null (ls))
1412 {
1413 if (!scm_is_pair (ls))
1414 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
1415 handle = SCM_CAR (ls);
1416 if (!scm_is_pair (handle))
1417 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
1418 fn (closure, handle);
1419 ls = SCM_CDR (ls);
1420 }
1421 }
1422 }
1423 #undef FUNC_NAME
1424
1425 \f
1426
1427
1428 void
1429 scm_init_hashtab ()
1430 {
1431 #include "libguile/hashtab.x"
1432 }
1433
1434 /*
1435 Local Variables:
1436 c-file-style: "gnu"
1437 End:
1438 */