pre-GC_set_start_callback compatibility
[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. */
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. */
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);
63229905
LC
214 assert (!scm_is_pair (result) ||
215 !SCM_WEAK_PAIR_DELETED_P (GC_is_visible (result)));
216
e9bac3be
LC
217 scm_remember_upto_here_1 (strong_refs);
218
f0554ee7 219 if (args.removed_items > 0)
63229905
LC
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
70249b98
LC
229 if (remaining < SCM_HASHTABLE_LOWER (table))
230 scm_i_rehash (table, hash_fn, closure, "weak_bucket_assoc");
63229905
LC
231 }
232
233 return result;
234}
d9c82e20
LC
235
236
6efbc280
AW
237/* Packed arguments for `weak_bucket_assoc_by_hash'. */
238struct 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. */
247static void*
248weak_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
3a2de079 268\f
c35738c1 269static SCM
a9cf5c71
MV
270make_hash_table (int flags, unsigned long k, const char *func_name)
271{
c99de5aa 272 SCM vector;
9358af6a 273 scm_t_hashtable *t;
110beb83
MD
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];
3a2de079
LC
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);
c35738c1 285 t->min_size_index = t->size_index = i;
f59a096e 286 t->n_items = 0;
c35738c1 287 t->lower = 0;
110beb83 288 t->upper = 9 * n / 10;
c35738c1 289 t->flags = flags;
d3a80924 290 t->hash_fn = NULL;
c6a35e35 291
c99de5aa
AW
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);
f59a096e
MD
295}
296
c35738c1
MD
297void
298scm_i_rehash (SCM table,
f044da55 299 scm_t_hash_fn hash_fn,
c35738c1
MD
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;
d3a80924
MV
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;
c35738c1
MD
330 }
331 SCM_HASHTABLE (table)->size_index = i;
76da80e7 332
c35738c1
MD
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);
3a2de079
LC
340
341 new_buckets = scm_c_make_vector (new_size, SCM_EOL);
c35738c1 342
bc6580eb
MV
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
06c1d900
MV
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.
bc6580eb
MV
348 */
349
350 SCM_SET_HASHTABLE_VECTOR (table, new_buckets);
351 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
bc6580eb 352
3ebc1832 353 old_size = SCM_SIMPLE_VECTOR_LENGTH (buckets);
c35738c1
MD
354 for (i = 0; i < old_size; ++i)
355 {
bc6580eb
MV
356 SCM ls, cell, handle;
357
358 ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
c2f21af5
MV
359 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_EOL);
360
bc6580eb 361 while (scm_is_pair (ls))
c35738c1
MD
362 {
363 unsigned long h;
c6a35e35 364
bc6580eb
MV
365 cell = ls;
366 handle = SCM_CAR (cell);
367 ls = SCM_CDR (ls);
c6a35e35 368
986ec822 369 if (SCM_WEAK_PAIR_DELETED_P (handle))
639e56a4
LC
370 /* HANDLE is a nullified weak pair: skip it. */
371 continue;
c6a35e35 372
c35738c1
MD
373 h = hash_fn (SCM_CAR (handle), new_size, closure);
374 if (h >= new_size)
b9bd8526 375 scm_out_of_range (func_name, scm_from_ulong (h));
bc6580eb
MV
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);
c35738c1
MD
379 }
380 }
c35738c1
MD
381}
382
383
c99de5aa
AW
384void
385scm_i_hashtable_print (SCM exp, SCM port, scm_print_state *pstate)
f59a096e 386{
c35738c1
MD
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);
06c1d900 395 scm_uintprint (SCM_HASHTABLE_N_ITEMS (exp), 10, port);
f59a096e 396 scm_putc ('/', port);
3ebc1832
MV
397 scm_uintprint (SCM_SIMPLE_VECTOR_LENGTH (SCM_HASHTABLE_VECTOR (exp)),
398 10, port);
f59a096e 399 scm_puts (">", port);
c99de5aa
AW
400}
401
f59a096e 402
00ffa0e7 403SCM
c014a02e 404scm_c_make_hash_table (unsigned long k)
00ffa0e7 405{
c35738c1 406 return make_hash_table (0, k, "scm_c_make_hash_table");
f59a096e
MD
407}
408
409SCM_DEFINE (scm_make_hash_table, "make-hash-table", 0, 1, 0,
410 (SCM n),
a9cf5c71 411 "Make a new abstract hash table object with minimum number of buckets @var{n}\n")
f59a096e
MD
412#define FUNC_NAME s_scm_make_hash_table
413{
414 if (SCM_UNBNDP (n))
c35738c1 415 return make_hash_table (0, 0, FUNC_NAME);
f59a096e 416 else
a55c2b68 417 return make_hash_table (0, scm_to_ulong (n), FUNC_NAME);
f59a096e
MD
418}
419#undef FUNC_NAME
420
66b229d5
AW
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. */
423static int
424weak_gc_callback (void **weak)
62c290e9 425{
631e49ed
AW
426 void *val = weak[0];
427 void (*callback) (SCM) = weak[1];
62c290e9 428
66b229d5
AW
429 if (!val)
430 return 0;
431
432 callback (PTR2SCM (val));
433
434 return 1;
435}
436
437#ifdef HAVE_GC_SET_START_CALLBACK
438static void*
439weak_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);
62c290e9 443
631e49ed 444 return NULL;
62c290e9 445}
66b229d5
AW
446#else
447static void
448weak_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
62c290e9
AW
454
455static void
456scm_c_register_weak_gc_callback (SCM obj, void (*callback) (SCM))
457{
631e49ed 458 void **weak = GC_MALLOC_ATOMIC (sizeof (void*) * 2);
62c290e9 459
631e49ed
AW
460 weak[0] = SCM2PTR (obj);
461 weak[1] = (void*)callback;
62c290e9
AW
462 GC_GENERAL_REGISTER_DISAPPEARING_LINK (weak, SCM2PTR (obj));
463
66b229d5
AW
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
62c290e9
AW
469}
470
c35738c1
MD
471SCM_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"
a9cf5c71 475 "Return a weak hash table with @var{size} buckets.\n"
c35738c1
MD
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
f59a096e 480{
6800f86d
AW
481 SCM ret;
482
c35738c1 483 if (SCM_UNBNDP (n))
6800f86d 484 ret = make_hash_table (SCM_HASHTABLEF_WEAK_CAR, 0, FUNC_NAME);
f59a096e 485 else
6800f86d
AW
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;
c35738c1
MD
492}
493#undef FUNC_NAME
494
495
496SCM_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{
62c290e9
AW
502 SCM ret;
503
c35738c1 504 if (SCM_UNBNDP (n))
62c290e9 505 ret = make_hash_table (SCM_HASHTABLEF_WEAK_CDR, 0, FUNC_NAME);
f59a096e 506 else
62c290e9
AW
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;
c35738c1
MD
513}
514#undef FUNC_NAME
f59a096e 515
c35738c1
MD
516
517SCM_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{
62c290e9
AW
523 SCM ret;
524
c35738c1 525 if (SCM_UNBNDP (n))
62c290e9
AW
526 ret = make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
527 0, FUNC_NAME);
c35738c1 528 else
62c290e9
AW
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;
f59a096e 535}
c35738c1
MD
536#undef FUNC_NAME
537
538
539SCM_DEFINE (scm_hash_table_p, "hash-table?", 1, 0, 0,
540 (SCM obj),
a9cf5c71 541 "Return @code{#t} if @var{obj} is an abstract hash table object.")
c35738c1
MD
542#define FUNC_NAME s_scm_hash_table_p
543{
7888309b 544 return scm_from_bool (SCM_HASHTABLE_P (obj));
c35738c1
MD
545}
546#undef FUNC_NAME
547
548
549SCM_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{
7888309b 558 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_KEY_P (obj));
c35738c1
MD
559}
560#undef FUNC_NAME
561
562
563SCM_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{
7888309b 568 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_VALUE_P (obj));
c35738c1
MD
569}
570#undef FUNC_NAME
571
572
573SCM_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{
7888309b 578 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_DOUBLY_WEAK_P (obj));
c35738c1
MD
579}
580#undef FUNC_NAME
581
d587c9e8
LC
582\f
583/* Accessing hash table entries. */
22a52da1 584
0f2d19dd 585SCM
d587c9e8
LC
586scm_hash_fn_get_handle (SCM table, SCM obj,
587 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
588 void * closure)
22a52da1 589#define FUNC_NAME "scm_hash_fn_get_handle"
0f2d19dd 590{
c014a02e 591 unsigned long k;
63229905 592 SCM buckets, h;
0f2d19dd 593
f0554ee7
AW
594 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
595 buckets = SCM_HASHTABLE_VECTOR (table);
d9c82e20
LC
596
597 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
22a52da1 598 return SCM_BOOL_F;
d9c82e20
LC
599 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
600 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
f0554ee7 601 scm_out_of_range (FUNC_NAME, scm_from_ulong (k));
3a2de079 602
f0554ee7 603 if (SCM_HASHTABLE_WEAK_P (table))
63229905
LC
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);
e4d21e6b 608
0f2d19dd
JB
609 return h;
610}
22a52da1 611#undef FUNC_NAME
0f2d19dd
JB
612
613
6efbc280
AW
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 */
627SCM
628scm_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
0f2d19dd 672SCM
f044da55
LC
673scm_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)
cbaadf02 676#define FUNC_NAME "scm_hash_fn_create_handle_x"
0f2d19dd 677{
c014a02e 678 unsigned long k;
63229905 679 SCM buckets, it;
0f2d19dd 680
f0554ee7
AW
681 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
682 buckets = SCM_HASHTABLE_VECTOR (table);
683
3ebc1832 684 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
cbaadf02
DH
685 SCM_MISC_ERROR ("void hashtable", SCM_EOL);
686
3ebc1832
MV
687 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
688 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
b9bd8526 689 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
3a2de079 690
f0554ee7 691 if (SCM_HASHTABLE_WEAK_P (table))
63229905
LC
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);
e4d21e6b 696
0306509b 697 if (scm_is_pair (it))
0a4c1355 698 return it;
15bd90ea
NJ
699 else if (scm_is_true (it))
700 scm_wrong_type_arg_msg (NULL, 0, it, "a pair");
ee083ac2
DH
701 else
702 {
5b582466
MV
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.
bc6580eb 708 */
3a2de079
LC
709 SCM handle, new_bucket;
710
f0554ee7 711 if (SCM_HASHTABLE_WEAK_P (table))
3a2de079 712 {
741e83fc 713 /* FIXME: We don't support weak alist vectors. */
3a2de079
LC
714 /* Use a weak cell. */
715 if (SCM_HASHTABLE_DOUBLY_WEAK_P (table))
986ec822 716 handle = scm_doubly_weak_pair (obj, init);
3a2de079 717 else if (SCM_HASHTABLE_WEAK_KEY_P (table))
986ec822 718 handle = scm_weak_car_pair (obj, init);
3a2de079 719 else
986ec822 720 handle = scm_weak_cdr_pair (obj, init);
3a2de079
LC
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
f0554ee7 728 if (!scm_is_eq (SCM_HASHTABLE_VECTOR (table), buckets))
5b582466
MV
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 }
bc6580eb 735 SCM_SETCDR (new_bucket, SCM_SIMPLE_VECTOR_REF (buckets, k));
3ebc1832 736 SCM_SIMPLE_VECTOR_SET (buckets, k, new_bucket);
f0554ee7 737 SCM_HASHTABLE_INCREMENT (table);
40d2a007 738
62c290e9 739 /* Maybe rehash the table. */
f0554ee7
AW
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);
ee083ac2
DH
743 return SCM_CAR (new_bucket);
744 }
0f2d19dd 745}
cbaadf02 746#undef FUNC_NAME
0f2d19dd 747
1cc91f1b 748
f044da55
LC
749SCM
750scm_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)
0f2d19dd 753{
22a52da1 754 SCM it = scm_hash_fn_get_handle (table, obj, hash_fn, assoc_fn, closure);
d2e53ed6 755 if (scm_is_pair (it))
0f2d19dd 756 return SCM_CDR (it);
22a52da1
DH
757 else
758 return dflt;
0f2d19dd
JB
759}
760
761
762
1cc91f1b 763
f044da55
LC
764SCM
765scm_hash_fn_set_x (SCM table, SCM obj, SCM val,
766 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
767 void *closure)
0f2d19dd
JB
768{
769 SCM it;
770
771 it = scm_hash_fn_create_handle_x (table, obj, SCM_BOOL_F, hash_fn, assoc_fn, closure);
772 SCM_SETCDR (it, val);
5a99a574 773
f0554ee7 774 if (SCM_HASHTABLE_WEAK_VALUE_P (table) && SCM_NIMP (val))
5a99a574
LC
775 /* IT is a weak-cdr pair. Register a disappearing link from IT's
776 cdr to VAL like `scm_weak_cdr_pair' does. */
777 SCM_I_REGISTER_DISAPPEARING_LINK ((void *) SCM_CDRLOC (it), SCM2PTR (val));
778
0f2d19dd
JB
779 return val;
780}
781
782
d9c82e20 783SCM
a9cf5c71 784scm_hash_fn_remove_x (SCM table, SCM obj,
f044da55
LC
785 scm_t_hash_fn hash_fn,
786 scm_t_assoc_fn assoc_fn,
a9cf5c71 787 void *closure)
f0554ee7 788#define FUNC_NAME "hash_fn_remove_x"
0f2d19dd 789{
c014a02e 790 unsigned long k;
63229905 791 SCM buckets, h;
0f2d19dd 792
f0554ee7
AW
793 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
794
795 buckets = SCM_HASHTABLE_VECTOR (table);
796
c99de5aa 797 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
0f2d19dd 798 return SCM_EOL;
87ca11ff 799
3ebc1832
MV
800 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
801 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
f0554ee7 802 scm_out_of_range (FUNC_NAME, scm_from_ulong (k));
3a2de079 803
f0554ee7 804 if (SCM_HASHTABLE_WEAK_P (table))
63229905
LC
805 h = weak_bucket_assoc (table, buckets, k, hash_fn,
806 assoc_fn, obj, closure);
807 else
808 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
e4d21e6b 809
7888309b 810 if (scm_is_true (h))
87ca11ff 811 {
3ebc1832 812 SCM_SIMPLE_VECTOR_SET
a9cf5c71 813 (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
f0554ee7
AW
814 SCM_HASHTABLE_DECREMENT (table);
815 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
816 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
87ca11ff 817 }
0f2d19dd
JB
818 return h;
819}
f0554ee7 820#undef FUNC_NAME
0f2d19dd 821
c35738c1
MD
822SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
823 (SCM table),
a9cf5c71 824 "Remove all items from @var{table} (without triggering a resize).")
c35738c1
MD
825#define FUNC_NAME s_scm_hash_clear_x
826{
f0554ee7
AW
827 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
828
829 scm_vector_fill_x (SCM_HASHTABLE_VECTOR (table), SCM_EOL);
830 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
831
c35738c1
MD
832 return SCM_UNSPECIFIED;
833}
834#undef FUNC_NAME
0f2d19dd
JB
835
836\f
837
a1ec6916 838SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
22a52da1
DH
839 (SCM table, SCM key),
840 "This procedure returns the @code{(key . value)} pair from the\n"
841 "hash table @var{table}. If @var{table} does not hold an\n"
842 "associated value for @var{key}, @code{#f} is returned.\n"
843 "Uses @code{eq?} for equality testing.")
1bbd0b84 844#define FUNC_NAME s_scm_hashq_get_handle
0f2d19dd 845{
d587c9e8
LC
846 return scm_hash_fn_get_handle (table, key,
847 (scm_t_hash_fn) scm_ihashq,
848 (scm_t_assoc_fn) scm_sloppy_assq,
849 0);
0f2d19dd 850}
1bbd0b84 851#undef FUNC_NAME
0f2d19dd
JB
852
853
a1ec6916 854SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
d550d22a
GB
855 (SCM table, SCM key, SCM init),
856 "This function looks up @var{key} in @var{table} and returns its handle.\n"
b380b885
MD
857 "If @var{key} is not already present, a new handle is created which\n"
858 "associates @var{key} with @var{init}.")
1bbd0b84 859#define FUNC_NAME s_scm_hashq_create_handle_x
0f2d19dd 860{
d587c9e8
LC
861 return scm_hash_fn_create_handle_x (table, key, init,
862 (scm_t_hash_fn) scm_ihashq,
863 (scm_t_assoc_fn) scm_sloppy_assq,
864 0);
0f2d19dd 865}
1bbd0b84 866#undef FUNC_NAME
0f2d19dd
JB
867
868
a1ec6916 869SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
1e6808ea 870 (SCM table, SCM key, SCM dflt),
b380b885
MD
871 "Look up @var{key} in the hash table @var{table}, and return the\n"
872 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
873 "return @var{default} (or @code{#f} if no @var{default} argument\n"
874 "is supplied). Uses @code{eq?} for equality testing.")
1bbd0b84 875#define FUNC_NAME s_scm_hashq_ref
0f2d19dd 876{
54778cd3 877 if (SCM_UNBNDP (dflt))
0f2d19dd 878 dflt = SCM_BOOL_F;
d587c9e8
LC
879 return scm_hash_fn_ref (table, key, dflt,
880 (scm_t_hash_fn) scm_ihashq,
881 (scm_t_assoc_fn) scm_sloppy_assq,
882 0);
0f2d19dd 883}
1bbd0b84 884#undef FUNC_NAME
0f2d19dd
JB
885
886
887
a1ec6916 888SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
1e6808ea 889 (SCM table, SCM key, SCM val),
5352393c
MG
890 "Find the entry in @var{table} associated with @var{key}, and\n"
891 "store @var{value} there. Uses @code{eq?} for equality testing.")
1bbd0b84 892#define FUNC_NAME s_scm_hashq_set_x
0f2d19dd 893{
d587c9e8
LC
894 return scm_hash_fn_set_x (table, key, val,
895 (scm_t_hash_fn) scm_ihashq,
896 (scm_t_assoc_fn) scm_sloppy_assq,
897 0);
0f2d19dd 898}
1bbd0b84 899#undef FUNC_NAME
0f2d19dd
JB
900
901
902
a1ec6916 903SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
1e6808ea 904 (SCM table, SCM key),
5352393c
MG
905 "Remove @var{key} (and any value associated with it) from\n"
906 "@var{table}. Uses @code{eq?} for equality tests.")
1bbd0b84 907#define FUNC_NAME s_scm_hashq_remove_x
0f2d19dd 908{
d587c9e8
LC
909 return scm_hash_fn_remove_x (table, key,
910 (scm_t_hash_fn) scm_ihashq,
911 (scm_t_assoc_fn) scm_sloppy_assq,
912 0);
0f2d19dd 913}
1bbd0b84 914#undef FUNC_NAME
0f2d19dd
JB
915
916
917\f
918
a1ec6916 919SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
22a52da1
DH
920 (SCM table, SCM key),
921 "This procedure returns the @code{(key . value)} pair from the\n"
922 "hash table @var{table}. If @var{table} does not hold an\n"
923 "associated value for @var{key}, @code{#f} is returned.\n"
924 "Uses @code{eqv?} for equality testing.")
1bbd0b84 925#define FUNC_NAME s_scm_hashv_get_handle
0f2d19dd 926{
d587c9e8
LC
927 return scm_hash_fn_get_handle (table, key,
928 (scm_t_hash_fn) scm_ihashv,
929 (scm_t_assoc_fn) scm_sloppy_assv,
930 0);
0f2d19dd 931}
1bbd0b84 932#undef FUNC_NAME
0f2d19dd
JB
933
934
a1ec6916 935SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
d550d22a
GB
936 (SCM table, SCM key, SCM init),
937 "This function looks up @var{key} in @var{table} and returns its handle.\n"
938 "If @var{key} is not already present, a new handle is created which\n"
939 "associates @var{key} with @var{init}.")
1bbd0b84 940#define FUNC_NAME s_scm_hashv_create_handle_x
0f2d19dd 941{
d587c9e8
LC
942 return scm_hash_fn_create_handle_x (table, key, init,
943 (scm_t_hash_fn) scm_ihashv,
944 (scm_t_assoc_fn) scm_sloppy_assv,
945 0);
0f2d19dd 946}
1bbd0b84 947#undef FUNC_NAME
0f2d19dd
JB
948
949
a1ec6916 950SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
1e6808ea 951 (SCM table, SCM key, SCM dflt),
d550d22a
GB
952 "Look up @var{key} in the hash table @var{table}, and return the\n"
953 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
954 "return @var{default} (or @code{#f} if no @var{default} argument\n"
955 "is supplied). Uses @code{eqv?} for equality testing.")
1bbd0b84 956#define FUNC_NAME s_scm_hashv_ref
0f2d19dd 957{
54778cd3 958 if (SCM_UNBNDP (dflt))
0f2d19dd 959 dflt = SCM_BOOL_F;
d587c9e8
LC
960 return scm_hash_fn_ref (table, key, dflt,
961 (scm_t_hash_fn) scm_ihashv,
962 (scm_t_assoc_fn) scm_sloppy_assv,
963 0);
0f2d19dd 964}
1bbd0b84 965#undef FUNC_NAME
0f2d19dd
JB
966
967
968
a1ec6916 969SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
1e6808ea 970 (SCM table, SCM key, SCM val),
5352393c
MG
971 "Find the entry in @var{table} associated with @var{key}, and\n"
972 "store @var{value} there. Uses @code{eqv?} for equality testing.")
1bbd0b84 973#define FUNC_NAME s_scm_hashv_set_x
0f2d19dd 974{
d587c9e8
LC
975 return scm_hash_fn_set_x (table, key, val,
976 (scm_t_hash_fn) scm_ihashv,
977 (scm_t_assoc_fn) scm_sloppy_assv,
978 0);
0f2d19dd 979}
1bbd0b84 980#undef FUNC_NAME
0f2d19dd
JB
981
982
a1ec6916 983SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
1e6808ea 984 (SCM table, SCM key),
5352393c
MG
985 "Remove @var{key} (and any value associated with it) from\n"
986 "@var{table}. Uses @code{eqv?} for equality tests.")
1bbd0b84 987#define FUNC_NAME s_scm_hashv_remove_x
0f2d19dd 988{
d587c9e8
LC
989 return scm_hash_fn_remove_x (table, key,
990 (scm_t_hash_fn) scm_ihashv,
991 (scm_t_assoc_fn) scm_sloppy_assv,
992 0);
0f2d19dd 993}
1bbd0b84 994#undef FUNC_NAME
0f2d19dd
JB
995
996\f
997
a1ec6916 998SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
22a52da1
DH
999 (SCM table, SCM key),
1000 "This procedure returns the @code{(key . value)} pair from the\n"
1001 "hash table @var{table}. If @var{table} does not hold an\n"
1002 "associated value for @var{key}, @code{#f} is returned.\n"
1003 "Uses @code{equal?} for equality testing.")
1bbd0b84 1004#define FUNC_NAME s_scm_hash_get_handle
0f2d19dd 1005{
d587c9e8
LC
1006 return scm_hash_fn_get_handle (table, key,
1007 (scm_t_hash_fn) scm_ihash,
1008 (scm_t_assoc_fn) scm_sloppy_assoc,
1009 0);
0f2d19dd 1010}
1bbd0b84 1011#undef FUNC_NAME
0f2d19dd
JB
1012
1013
a1ec6916 1014SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
d550d22a
GB
1015 (SCM table, SCM key, SCM init),
1016 "This function looks up @var{key} in @var{table} and returns its handle.\n"
1017 "If @var{key} is not already present, a new handle is created which\n"
1018 "associates @var{key} with @var{init}.")
1bbd0b84 1019#define FUNC_NAME s_scm_hash_create_handle_x
0f2d19dd 1020{
d587c9e8
LC
1021 return scm_hash_fn_create_handle_x (table, key, init,
1022 (scm_t_hash_fn) scm_ihash,
1023 (scm_t_assoc_fn) scm_sloppy_assoc,
1024 0);
0f2d19dd 1025}
1bbd0b84 1026#undef FUNC_NAME
0f2d19dd
JB
1027
1028
a1ec6916 1029SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
1e6808ea 1030 (SCM table, SCM key, SCM dflt),
d550d22a
GB
1031 "Look up @var{key} in the hash table @var{table}, and return the\n"
1032 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
1033 "return @var{default} (or @code{#f} if no @var{default} argument\n"
1034 "is supplied). Uses @code{equal?} for equality testing.")
1bbd0b84 1035#define FUNC_NAME s_scm_hash_ref
0f2d19dd 1036{
54778cd3 1037 if (SCM_UNBNDP (dflt))
0f2d19dd 1038 dflt = SCM_BOOL_F;
d587c9e8
LC
1039 return scm_hash_fn_ref (table, key, dflt,
1040 (scm_t_hash_fn) scm_ihash,
1041 (scm_t_assoc_fn) scm_sloppy_assoc,
1042 0);
0f2d19dd 1043}
1bbd0b84 1044#undef FUNC_NAME
0f2d19dd
JB
1045
1046
1047
a1ec6916 1048SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
1e6808ea 1049 (SCM table, SCM key, SCM val),
5352393c
MG
1050 "Find the entry in @var{table} associated with @var{key}, and\n"
1051 "store @var{value} there. Uses @code{equal?} for equality\n"
1052 "testing.")
1bbd0b84 1053#define FUNC_NAME s_scm_hash_set_x
0f2d19dd 1054{
d587c9e8
LC
1055 return scm_hash_fn_set_x (table, key, val,
1056 (scm_t_hash_fn) scm_ihash,
1057 (scm_t_assoc_fn) scm_sloppy_assoc,
1058 0);
0f2d19dd 1059}
1bbd0b84 1060#undef FUNC_NAME
0f2d19dd
JB
1061
1062
1063
a1ec6916 1064SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
1e6808ea 1065 (SCM table, SCM key),
5352393c
MG
1066 "Remove @var{key} (and any value associated with it) from\n"
1067 "@var{table}. Uses @code{equal?} for equality tests.")
1bbd0b84 1068#define FUNC_NAME s_scm_hash_remove_x
0f2d19dd 1069{
d587c9e8
LC
1070 return scm_hash_fn_remove_x (table, key,
1071 (scm_t_hash_fn) scm_ihash,
1072 (scm_t_assoc_fn) scm_sloppy_assoc,
1073 0);
0f2d19dd 1074}
1bbd0b84 1075#undef FUNC_NAME
0f2d19dd
JB
1076
1077\f
1078
1079
92c2555f 1080typedef struct scm_t_ihashx_closure
0f2d19dd
JB
1081{
1082 SCM hash;
1083 SCM assoc;
92c2555f 1084} scm_t_ihashx_closure;
0f2d19dd
JB
1085
1086
1cc91f1b 1087
c014a02e 1088static unsigned long
d587c9e8 1089scm_ihashx (SCM obj, unsigned long n, void *arg)
0f2d19dd 1090{
d587c9e8
LC
1091 SCM answer;
1092 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
1093 answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
a55c2b68 1094 return scm_to_ulong (answer);
0f2d19dd
JB
1095}
1096
1097
1cc91f1b 1098
0f2d19dd 1099static SCM
d587c9e8 1100scm_sloppy_assx (SCM obj, SCM alist, void *arg)
0f2d19dd 1101{
d587c9e8 1102 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
87ca11ff 1103 return scm_call_2 (closure->assoc, obj, alist);
0f2d19dd
JB
1104}
1105
1106
a1ec6916 1107SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
1e6808ea
MG
1108 (SCM hash, SCM assoc, SCM table, SCM key),
1109 "This behaves the same way as the corresponding\n"
1110 "@code{-get-handle} function, but uses @var{hash} as a hash\n"
1111 "function and @var{assoc} to compare keys. @code{hash} must be\n"
1112 "a function that takes two arguments, a key to be hashed and a\n"
d550d22a
GB
1113 "table size. @code{assoc} must be an associator function, like\n"
1114 "@code{assoc}, @code{assq} or @code{assv}.")
1bbd0b84 1115#define FUNC_NAME s_scm_hashx_get_handle
0f2d19dd 1116{
92c2555f 1117 scm_t_ihashx_closure closure;
0f2d19dd
JB
1118 closure.hash = hash;
1119 closure.assoc = assoc;
1e6808ea 1120 return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
87ca11ff 1121 (void *) &closure);
0f2d19dd 1122}
1bbd0b84 1123#undef FUNC_NAME
0f2d19dd
JB
1124
1125
a1ec6916 1126SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
1e6808ea
MG
1127 (SCM hash, SCM assoc, SCM table, SCM key, SCM init),
1128 "This behaves the same way as the corresponding\n"
1129 "@code{-create-handle} function, but uses @var{hash} as a hash\n"
1130 "function and @var{assoc} to compare keys. @code{hash} must be\n"
1131 "a function that takes two arguments, a key to be hashed and a\n"
d550d22a
GB
1132 "table size. @code{assoc} must be an associator function, like\n"
1133 "@code{assoc}, @code{assq} or @code{assv}.")
1bbd0b84 1134#define FUNC_NAME s_scm_hashx_create_handle_x
0f2d19dd 1135{
92c2555f 1136 scm_t_ihashx_closure closure;
0f2d19dd
JB
1137 closure.hash = hash;
1138 closure.assoc = assoc;
1e6808ea
MG
1139 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
1140 scm_sloppy_assx, (void *)&closure);
0f2d19dd 1141}
1bbd0b84 1142#undef FUNC_NAME
0f2d19dd
JB
1143
1144
1145
a1ec6916 1146SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
1e6808ea 1147 (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
d550d22a 1148 "This behaves the same way as the corresponding @code{ref}\n"
1e6808ea
MG
1149 "function, but uses @var{hash} as a hash function and\n"
1150 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1151 "that takes two arguments, a key to be hashed and a table size.\n"
1152 "@code{assoc} must be an associator function, like @code{assoc},\n"
1153 "@code{assq} or @code{assv}.\n"
1154 "\n"
1155 "By way of illustration, @code{hashq-ref table key} is\n"
1156 "equivalent to @code{hashx-ref hashq assq table key}.")
1bbd0b84 1157#define FUNC_NAME s_scm_hashx_ref
0f2d19dd 1158{
92c2555f 1159 scm_t_ihashx_closure closure;
54778cd3 1160 if (SCM_UNBNDP (dflt))
0f2d19dd
JB
1161 dflt = SCM_BOOL_F;
1162 closure.hash = hash;
1163 closure.assoc = assoc;
1e6808ea
MG
1164 return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
1165 (void *)&closure);
0f2d19dd 1166}
1bbd0b84 1167#undef FUNC_NAME
0f2d19dd
JB
1168
1169
1170
1171
a1ec6916 1172SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
1e6808ea 1173 (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
d550d22a 1174 "This behaves the same way as the corresponding @code{set!}\n"
1e6808ea
MG
1175 "function, but uses @var{hash} as a hash function and\n"
1176 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1177 "that takes two arguments, a key to be hashed and a table size.\n"
1178 "@code{assoc} must be an associator function, like @code{assoc},\n"
1179 "@code{assq} or @code{assv}.\n"
1180 "\n"
1181 " By way of illustration, @code{hashq-set! table key} is\n"
1182 "equivalent to @code{hashx-set! hashq assq table key}.")
1bbd0b84 1183#define FUNC_NAME s_scm_hashx_set_x
0f2d19dd 1184{
92c2555f 1185 scm_t_ihashx_closure closure;
0f2d19dd
JB
1186 closure.hash = hash;
1187 closure.assoc = assoc;
1e6808ea
MG
1188 return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
1189 (void *)&closure);
0f2d19dd 1190}
1bbd0b84 1191#undef FUNC_NAME
0f2d19dd 1192
a9cf5c71
MV
1193SCM_DEFINE (scm_hashx_remove_x, "hashx-remove!", 4, 0, 0,
1194 (SCM hash, SCM assoc, SCM table, SCM obj),
1195 "This behaves the same way as the corresponding @code{remove!}\n"
1196 "function, but uses @var{hash} as a hash function and\n"
1197 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1198 "that takes two arguments, a key to be hashed and a table size.\n"
1199 "@code{assoc} must be an associator function, like @code{assoc},\n"
1200 "@code{assq} or @code{assv}.\n"
1201 "\n"
1202 " By way of illustration, @code{hashq-remove! table key} is\n"
1203 "equivalent to @code{hashx-remove! hashq assq #f table key}.")
1204#define FUNC_NAME s_scm_hashx_remove_x
0f2d19dd 1205{
92c2555f 1206 scm_t_ihashx_closure closure;
0f2d19dd
JB
1207 closure.hash = hash;
1208 closure.assoc = assoc;
4cff503f
KR
1209 return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
1210 (void *) &closure);
0f2d19dd 1211}
a9cf5c71 1212#undef FUNC_NAME
0f2d19dd 1213
711a9fd7 1214/* Hash table iterators */
b94903c2 1215
162125af
AW
1216SCM_DEFINE (scm_hash_fold, "hash-fold", 3, 0, 0,
1217 (SCM proc, SCM init, SCM table),
1218 "An iterator over hash-table elements.\n"
1219 "Accumulates and returns a result by applying PROC successively.\n"
1220 "The arguments to PROC are \"(key value prior-result)\" where key\n"
1221 "and value are successive pairs from the hash table TABLE, and\n"
1222 "prior-result is either INIT (for the first application of PROC)\n"
1223 "or the return value of the previous application of PROC.\n"
1224 "For example, @code{(hash-fold acons '() tab)} will convert a hash\n"
1225 "table into an a-list of key-value pairs.")
1226#define FUNC_NAME s_scm_hash_fold
1227{
1228 SCM_VALIDATE_PROC (1, proc);
2dd7d8ce 1229 SCM_VALIDATE_HASHTABLE (3, table);
162125af
AW
1230 return scm_internal_hash_fold ((scm_t_hash_fold_fn) scm_call_3,
1231 (void *) SCM_UNPACK (proc), init, table);
1232}
1233#undef FUNC_NAME
1234
1235static SCM
1236for_each_proc (void *proc, SCM handle)
1237{
1238 return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
1239}
1240
1241SCM_DEFINE (scm_hash_for_each, "hash-for-each", 2, 0, 0,
1242 (SCM proc, SCM table),
1243 "An iterator over hash-table elements.\n"
1244 "Applies PROC successively on all hash table items.\n"
1245 "The arguments to PROC are \"(key value)\" where key\n"
1246 "and value are successive pairs from the hash table TABLE.")
1247#define FUNC_NAME s_scm_hash_for_each
1248{
1249 SCM_VALIDATE_PROC (1, proc);
2dd7d8ce 1250 SCM_VALIDATE_HASHTABLE (2, table);
162125af
AW
1251
1252 scm_internal_hash_for_each_handle (for_each_proc,
1253 (void *) SCM_UNPACK (proc),
1254 table);
1255 return SCM_UNSPECIFIED;
1256}
1257#undef FUNC_NAME
1258
1259SCM_DEFINE (scm_hash_for_each_handle, "hash-for-each-handle", 2, 0, 0,
1260 (SCM proc, SCM table),
1261 "An iterator over hash-table elements.\n"
1262 "Applies PROC successively on all hash table handles.")
1263#define FUNC_NAME s_scm_hash_for_each_handle
1264{
1265 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)), proc, 1, FUNC_NAME);
2dd7d8ce 1266 SCM_VALIDATE_HASHTABLE (2, table);
162125af
AW
1267
1268 scm_internal_hash_for_each_handle ((scm_t_hash_handle_fn) scm_call_1,
1269 (void *) SCM_UNPACK (proc),
1270 table);
1271 return SCM_UNSPECIFIED;
1272}
1273#undef FUNC_NAME
1274
1275static SCM
1276map_proc (void *proc, SCM key, SCM data, SCM value)
1277{
1278 return scm_cons (scm_call_2 (SCM_PACK (proc), key, data), value);
1279}
1280
1281SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
1282 (SCM proc, SCM table),
1283 "An iterator over hash-table elements.\n"
1284 "Accumulates and returns as a list the results of applying PROC successively.\n"
1285 "The arguments to PROC are \"(key value)\" where key\n"
1286 "and value are successive pairs from the hash table TABLE.")
1287#define FUNC_NAME s_scm_hash_map_to_list
1288{
1289 SCM_VALIDATE_PROC (1, proc);
2dd7d8ce 1290 SCM_VALIDATE_HASHTABLE (2, table);
162125af
AW
1291 return scm_internal_hash_fold (map_proc,
1292 (void *) SCM_UNPACK (proc),
1293 SCM_EOL,
1294 table);
1295}
1296#undef FUNC_NAME
1297
1298\f
c7df61cd
MD
1299
1300SCM
a07010bf
LC
1301scm_internal_hash_fold (scm_t_hash_fold_fn fn, void *closure,
1302 SCM init, SCM table)
2dd7d8ce 1303#define FUNC_NAME s_scm_hash_fold
c7df61cd 1304{
87ca11ff
MD
1305 long i, n;
1306 SCM buckets, result = init;
87ca11ff 1307
2dd7d8ce
AW
1308 SCM_VALIDATE_HASHTABLE (0, table);
1309 buckets = SCM_HASHTABLE_VECTOR (table);
0a4c1355 1310
3ebc1832 1311 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
c7df61cd
MD
1312 for (i = 0; i < n; ++i)
1313 {
741e83fc
LC
1314 SCM prev, ls;
1315
1316 for (prev = SCM_BOOL_F, ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
1317 !scm_is_null (ls);
1318 prev = ls, ls = SCM_CDR (ls))
c7df61cd 1319 {
741e83fc
LC
1320 SCM handle;
1321
d2e53ed6 1322 if (!scm_is_pair (ls))
2dd7d8ce 1323 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
741e83fc 1324
c7df61cd 1325 handle = SCM_CAR (ls);
d2e53ed6 1326 if (!scm_is_pair (handle))
2dd7d8ce 1327 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
741e83fc 1328
f0554ee7 1329 if (SCM_HASHTABLE_WEAK_P (table))
741e83fc 1330 {
986ec822 1331 if (SCM_WEAK_PAIR_DELETED_P (handle))
741e83fc
LC
1332 {
1333 /* We hit a weak pair whose car/cdr has become
1334 unreachable: unlink it from the bucket. */
1335 if (prev != SCM_BOOL_F)
1336 SCM_SETCDR (prev, SCM_CDR (ls));
1337 else
1338 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_CDR (ls));
1339
2dd7d8ce
AW
1340 /* Update the item count. */
1341 SCM_HASHTABLE_DECREMENT (table);
741e83fc
LC
1342
1343 continue;
1344 }
1345 }
1346
c7df61cd 1347 result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
c7df61cd
MD
1348 }
1349 }
87ca11ff 1350
c7df61cd
MD
1351 return result;
1352}
2dd7d8ce 1353#undef FUNC_NAME
c7df61cd 1354
711a9fd7
MD
1355/* The following redundant code is here in order to be able to support
1356 hash-for-each-handle. An alternative would have been to replace
1357 this code and scm_internal_hash_fold above with a single
1358 scm_internal_hash_fold_handles, but we don't want to promote such
1359 an API. */
1360
711a9fd7 1361void
a07010bf
LC
1362scm_internal_hash_for_each_handle (scm_t_hash_handle_fn fn, void *closure,
1363 SCM table)
2dd7d8ce 1364#define FUNC_NAME s_scm_hash_for_each
711a9fd7
MD
1365{
1366 long i, n;
1367 SCM buckets;
1368
2dd7d8ce
AW
1369 SCM_VALIDATE_HASHTABLE (0, table);
1370 buckets = SCM_HASHTABLE_VECTOR (table);
3ebc1832 1371 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
2dd7d8ce 1372
711a9fd7
MD
1373 for (i = 0; i < n; ++i)
1374 {
3ebc1832 1375 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
d2e53ed6 1376 while (!scm_is_null (ls))
711a9fd7 1377 {
d2e53ed6 1378 if (!scm_is_pair (ls))
2dd7d8ce 1379 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
711a9fd7 1380 handle = SCM_CAR (ls);
d2e53ed6 1381 if (!scm_is_pair (handle))
2dd7d8ce 1382 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
711a9fd7
MD
1383 fn (closure, handle);
1384 ls = SCM_CDR (ls);
1385 }
1386 }
1387}
2dd7d8ce 1388#undef FUNC_NAME
711a9fd7 1389
0f2d19dd
JB
1390\f
1391
1cc91f1b 1392
c35738c1
MD
1393void
1394scm_init_hashtab ()
1395{
a0599745 1396#include "libguile/hashtab.x"
0f2d19dd 1397}
89e00824
ML
1398
1399/*
1400 Local Variables:
1401 c-file-style: "gnu"
1402 End:
1403*/