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