disallow get-handle / create-handle! of weak hash tables
[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
ecc9d1b5
AW
764struct set_weak_cdr_data
765{
766 SCM pair;
767 SCM new_val;
768};
769
770static void*
771set_weak_cdr (void *data)
772{
773 struct set_weak_cdr_data *d = data;
774
775 if (SCM_NIMP (SCM_WEAK_PAIR_CDR (d->pair)) && !SCM_NIMP (d->new_val))
776 {
777 GC_unregister_disappearing_link ((void *) SCM_CDRLOC (d->pair));
778 SCM_SETCDR (d->pair, d->new_val);
779 }
780 else
781 {
782 SCM_SETCDR (d->pair, d->new_val);
783 SCM_I_REGISTER_DISAPPEARING_LINK ((void *) SCM_CDRLOC (d->pair),
784 SCM2PTR (d->new_val));
785 }
786 return NULL;
787}
788
f044da55
LC
789SCM
790scm_hash_fn_set_x (SCM table, SCM obj, SCM val,
791 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
792 void *closure)
0f2d19dd 793{
ecc9d1b5 794 SCM pair;
0f2d19dd 795
ecc9d1b5
AW
796 pair = scm_hash_fn_create_handle_x (table, obj, val,
797 hash_fn, assoc_fn, closure);
5a99a574 798
ecc9d1b5
AW
799 if (SCM_UNLIKELY (!scm_is_eq (SCM_CDR (pair), val)))
800 {
801 if (SCM_UNLIKELY (SCM_HASHTABLE_WEAK_VALUE_P (table)))
802 {
803 struct set_weak_cdr_data data;
5a99a574 804
ecc9d1b5
AW
805 data.pair = pair;
806 data.new_val = val;
807
808 GC_call_with_alloc_lock (set_weak_cdr, &data);
809 }
810 else
811 SCM_SETCDR (pair, val);
812 }
813
0f2d19dd
JB
814 return val;
815}
816
817
d9c82e20 818SCM
a9cf5c71 819scm_hash_fn_remove_x (SCM table, SCM obj,
f044da55
LC
820 scm_t_hash_fn hash_fn,
821 scm_t_assoc_fn assoc_fn,
a9cf5c71 822 void *closure)
f0554ee7 823#define FUNC_NAME "hash_fn_remove_x"
0f2d19dd 824{
c014a02e 825 unsigned long k;
63229905 826 SCM buckets, h;
0f2d19dd 827
f0554ee7
AW
828 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
829
830 buckets = SCM_HASHTABLE_VECTOR (table);
831
c99de5aa 832 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
0f2d19dd 833 return SCM_EOL;
87ca11ff 834
3ebc1832
MV
835 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
836 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
f0554ee7 837 scm_out_of_range (FUNC_NAME, scm_from_ulong (k));
3a2de079 838
f0554ee7 839 if (SCM_HASHTABLE_WEAK_P (table))
63229905
LC
840 h = weak_bucket_assoc (table, buckets, k, hash_fn,
841 assoc_fn, obj, closure);
842 else
843 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
e4d21e6b 844
7888309b 845 if (scm_is_true (h))
87ca11ff 846 {
3ebc1832 847 SCM_SIMPLE_VECTOR_SET
a9cf5c71 848 (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
f0554ee7
AW
849 SCM_HASHTABLE_DECREMENT (table);
850 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
851 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
87ca11ff 852 }
0f2d19dd
JB
853 return h;
854}
f0554ee7 855#undef FUNC_NAME
0f2d19dd 856
c35738c1
MD
857SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
858 (SCM table),
a9cf5c71 859 "Remove all items from @var{table} (without triggering a resize).")
c35738c1
MD
860#define FUNC_NAME s_scm_hash_clear_x
861{
f0554ee7
AW
862 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
863
864 scm_vector_fill_x (SCM_HASHTABLE_VECTOR (table), SCM_EOL);
865 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
866
c35738c1
MD
867 return SCM_UNSPECIFIED;
868}
869#undef FUNC_NAME
0f2d19dd
JB
870
871\f
872
a1ec6916 873SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
22a52da1
DH
874 (SCM table, SCM key),
875 "This procedure returns the @code{(key . value)} pair from the\n"
876 "hash table @var{table}. If @var{table} does not hold an\n"
877 "associated value for @var{key}, @code{#f} is returned.\n"
878 "Uses @code{eq?} for equality testing.")
1bbd0b84 879#define FUNC_NAME s_scm_hashq_get_handle
0f2d19dd 880{
1d9c2e62
AW
881 if (SCM_UNLIKELY (SCM_HASHTABLE_P (table) && SCM_HASHTABLE_WEAK_P (table)))
882 SCM_MISC_ERROR ("Handle access not permitted on weak table", SCM_EOL);
883
d587c9e8
LC
884 return scm_hash_fn_get_handle (table, key,
885 (scm_t_hash_fn) scm_ihashq,
886 (scm_t_assoc_fn) scm_sloppy_assq,
887 0);
0f2d19dd 888}
1bbd0b84 889#undef FUNC_NAME
0f2d19dd
JB
890
891
a1ec6916 892SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
d550d22a
GB
893 (SCM table, SCM key, SCM init),
894 "This function looks up @var{key} in @var{table} and returns its handle.\n"
b380b885
MD
895 "If @var{key} is not already present, a new handle is created which\n"
896 "associates @var{key} with @var{init}.")
1bbd0b84 897#define FUNC_NAME s_scm_hashq_create_handle_x
0f2d19dd 898{
1d9c2e62
AW
899 if (SCM_UNLIKELY (SCM_HASHTABLE_P (table) && SCM_HASHTABLE_WEAK_P (table)))
900 SCM_MISC_ERROR ("Handle access not permitted on weak table", SCM_EOL);
901
d587c9e8
LC
902 return scm_hash_fn_create_handle_x (table, key, init,
903 (scm_t_hash_fn) scm_ihashq,
904 (scm_t_assoc_fn) scm_sloppy_assq,
905 0);
0f2d19dd 906}
1bbd0b84 907#undef FUNC_NAME
0f2d19dd
JB
908
909
a1ec6916 910SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
1e6808ea 911 (SCM table, SCM key, SCM dflt),
b380b885
MD
912 "Look up @var{key} in the hash table @var{table}, and return the\n"
913 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
914 "return @var{default} (or @code{#f} if no @var{default} argument\n"
915 "is supplied). Uses @code{eq?} for equality testing.")
1bbd0b84 916#define FUNC_NAME s_scm_hashq_ref
0f2d19dd 917{
54778cd3 918 if (SCM_UNBNDP (dflt))
0f2d19dd 919 dflt = SCM_BOOL_F;
d587c9e8
LC
920 return scm_hash_fn_ref (table, key, dflt,
921 (scm_t_hash_fn) scm_ihashq,
922 (scm_t_assoc_fn) scm_sloppy_assq,
923 0);
0f2d19dd 924}
1bbd0b84 925#undef FUNC_NAME
0f2d19dd
JB
926
927
928
a1ec6916 929SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
1e6808ea 930 (SCM table, SCM key, SCM val),
5352393c
MG
931 "Find the entry in @var{table} associated with @var{key}, and\n"
932 "store @var{value} there. Uses @code{eq?} for equality testing.")
1bbd0b84 933#define FUNC_NAME s_scm_hashq_set_x
0f2d19dd 934{
d587c9e8
LC
935 return scm_hash_fn_set_x (table, key, val,
936 (scm_t_hash_fn) scm_ihashq,
937 (scm_t_assoc_fn) scm_sloppy_assq,
938 0);
0f2d19dd 939}
1bbd0b84 940#undef FUNC_NAME
0f2d19dd
JB
941
942
943
a1ec6916 944SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
1e6808ea 945 (SCM table, SCM key),
5352393c
MG
946 "Remove @var{key} (and any value associated with it) from\n"
947 "@var{table}. Uses @code{eq?} for equality tests.")
1bbd0b84 948#define FUNC_NAME s_scm_hashq_remove_x
0f2d19dd 949{
d587c9e8
LC
950 return scm_hash_fn_remove_x (table, key,
951 (scm_t_hash_fn) scm_ihashq,
952 (scm_t_assoc_fn) scm_sloppy_assq,
953 0);
0f2d19dd 954}
1bbd0b84 955#undef FUNC_NAME
0f2d19dd
JB
956
957
958\f
959
a1ec6916 960SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
22a52da1
DH
961 (SCM table, SCM key),
962 "This procedure returns the @code{(key . value)} pair from the\n"
963 "hash table @var{table}. If @var{table} does not hold an\n"
964 "associated value for @var{key}, @code{#f} is returned.\n"
965 "Uses @code{eqv?} for equality testing.")
1bbd0b84 966#define FUNC_NAME s_scm_hashv_get_handle
0f2d19dd 967{
1d9c2e62
AW
968 if (SCM_UNLIKELY (SCM_HASHTABLE_P (table) && SCM_HASHTABLE_WEAK_P (table)))
969 SCM_MISC_ERROR ("Handle access not permitted on weak table", SCM_EOL);
970
d587c9e8
LC
971 return scm_hash_fn_get_handle (table, key,
972 (scm_t_hash_fn) scm_ihashv,
973 (scm_t_assoc_fn) scm_sloppy_assv,
974 0);
0f2d19dd 975}
1bbd0b84 976#undef FUNC_NAME
0f2d19dd
JB
977
978
a1ec6916 979SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
d550d22a
GB
980 (SCM table, SCM key, SCM init),
981 "This function looks up @var{key} in @var{table} and returns its handle.\n"
982 "If @var{key} is not already present, a new handle is created which\n"
983 "associates @var{key} with @var{init}.")
1bbd0b84 984#define FUNC_NAME s_scm_hashv_create_handle_x
0f2d19dd 985{
1d9c2e62
AW
986 if (SCM_UNLIKELY (SCM_HASHTABLE_P (table) && SCM_HASHTABLE_WEAK_P (table)))
987 SCM_MISC_ERROR ("Handle access not permitted on weak table", SCM_EOL);
988
d587c9e8
LC
989 return scm_hash_fn_create_handle_x (table, key, init,
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
a1ec6916 997SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
1e6808ea 998 (SCM table, SCM key, SCM dflt),
d550d22a
GB
999 "Look up @var{key} in the hash table @var{table}, and return the\n"
1000 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
1001 "return @var{default} (or @code{#f} if no @var{default} argument\n"
1002 "is supplied). Uses @code{eqv?} for equality testing.")
1bbd0b84 1003#define FUNC_NAME s_scm_hashv_ref
0f2d19dd 1004{
54778cd3 1005 if (SCM_UNBNDP (dflt))
0f2d19dd 1006 dflt = SCM_BOOL_F;
d587c9e8
LC
1007 return scm_hash_fn_ref (table, key, dflt,
1008 (scm_t_hash_fn) scm_ihashv,
1009 (scm_t_assoc_fn) scm_sloppy_assv,
1010 0);
0f2d19dd 1011}
1bbd0b84 1012#undef FUNC_NAME
0f2d19dd
JB
1013
1014
1015
a1ec6916 1016SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
1e6808ea 1017 (SCM table, SCM key, SCM val),
5352393c
MG
1018 "Find the entry in @var{table} associated with @var{key}, and\n"
1019 "store @var{value} there. Uses @code{eqv?} for equality testing.")
1bbd0b84 1020#define FUNC_NAME s_scm_hashv_set_x
0f2d19dd 1021{
d587c9e8
LC
1022 return scm_hash_fn_set_x (table, key, val,
1023 (scm_t_hash_fn) scm_ihashv,
1024 (scm_t_assoc_fn) scm_sloppy_assv,
1025 0);
0f2d19dd 1026}
1bbd0b84 1027#undef FUNC_NAME
0f2d19dd
JB
1028
1029
a1ec6916 1030SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
1e6808ea 1031 (SCM table, SCM key),
5352393c
MG
1032 "Remove @var{key} (and any value associated with it) from\n"
1033 "@var{table}. Uses @code{eqv?} for equality tests.")
1bbd0b84 1034#define FUNC_NAME s_scm_hashv_remove_x
0f2d19dd 1035{
d587c9e8
LC
1036 return scm_hash_fn_remove_x (table, key,
1037 (scm_t_hash_fn) scm_ihashv,
1038 (scm_t_assoc_fn) scm_sloppy_assv,
1039 0);
0f2d19dd 1040}
1bbd0b84 1041#undef FUNC_NAME
0f2d19dd
JB
1042
1043\f
1044
a1ec6916 1045SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
22a52da1
DH
1046 (SCM table, SCM key),
1047 "This procedure returns the @code{(key . value)} pair from the\n"
1048 "hash table @var{table}. If @var{table} does not hold an\n"
1049 "associated value for @var{key}, @code{#f} is returned.\n"
1050 "Uses @code{equal?} for equality testing.")
1bbd0b84 1051#define FUNC_NAME s_scm_hash_get_handle
0f2d19dd 1052{
1d9c2e62
AW
1053 if (SCM_UNLIKELY (SCM_HASHTABLE_P (table) && SCM_HASHTABLE_WEAK_P (table)))
1054 SCM_MISC_ERROR ("Handle access not permitted on weak table", SCM_EOL);
1055
d587c9e8
LC
1056 return scm_hash_fn_get_handle (table, key,
1057 (scm_t_hash_fn) scm_ihash,
1058 (scm_t_assoc_fn) scm_sloppy_assoc,
1059 0);
0f2d19dd 1060}
1bbd0b84 1061#undef FUNC_NAME
0f2d19dd
JB
1062
1063
a1ec6916 1064SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
d550d22a
GB
1065 (SCM table, SCM key, SCM init),
1066 "This function looks up @var{key} in @var{table} and returns its handle.\n"
1067 "If @var{key} is not already present, a new handle is created which\n"
1068 "associates @var{key} with @var{init}.")
1bbd0b84 1069#define FUNC_NAME s_scm_hash_create_handle_x
0f2d19dd 1070{
1d9c2e62
AW
1071 if (SCM_UNLIKELY (SCM_HASHTABLE_P (table) && SCM_HASHTABLE_WEAK_P (table)))
1072 SCM_MISC_ERROR ("Handle access not permitted on weak table", SCM_EOL);
1073
d587c9e8
LC
1074 return scm_hash_fn_create_handle_x (table, key, init,
1075 (scm_t_hash_fn) scm_ihash,
1076 (scm_t_assoc_fn) scm_sloppy_assoc,
1077 0);
0f2d19dd 1078}
1bbd0b84 1079#undef FUNC_NAME
0f2d19dd
JB
1080
1081
a1ec6916 1082SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
1e6808ea 1083 (SCM table, SCM key, SCM dflt),
d550d22a
GB
1084 "Look up @var{key} in the hash table @var{table}, and return the\n"
1085 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
1086 "return @var{default} (or @code{#f} if no @var{default} argument\n"
1087 "is supplied). Uses @code{equal?} for equality testing.")
1bbd0b84 1088#define FUNC_NAME s_scm_hash_ref
0f2d19dd 1089{
54778cd3 1090 if (SCM_UNBNDP (dflt))
0f2d19dd 1091 dflt = SCM_BOOL_F;
d587c9e8
LC
1092 return scm_hash_fn_ref (table, key, dflt,
1093 (scm_t_hash_fn) scm_ihash,
1094 (scm_t_assoc_fn) scm_sloppy_assoc,
1095 0);
0f2d19dd 1096}
1bbd0b84 1097#undef FUNC_NAME
0f2d19dd
JB
1098
1099
1100
a1ec6916 1101SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
1e6808ea 1102 (SCM table, SCM key, SCM val),
5352393c
MG
1103 "Find the entry in @var{table} associated with @var{key}, and\n"
1104 "store @var{value} there. Uses @code{equal?} for equality\n"
1105 "testing.")
1bbd0b84 1106#define FUNC_NAME s_scm_hash_set_x
0f2d19dd 1107{
d587c9e8
LC
1108 return scm_hash_fn_set_x (table, key, val,
1109 (scm_t_hash_fn) scm_ihash,
1110 (scm_t_assoc_fn) scm_sloppy_assoc,
1111 0);
0f2d19dd 1112}
1bbd0b84 1113#undef FUNC_NAME
0f2d19dd
JB
1114
1115
1116
a1ec6916 1117SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
1e6808ea 1118 (SCM table, SCM key),
5352393c
MG
1119 "Remove @var{key} (and any value associated with it) from\n"
1120 "@var{table}. Uses @code{equal?} for equality tests.")
1bbd0b84 1121#define FUNC_NAME s_scm_hash_remove_x
0f2d19dd 1122{
d587c9e8
LC
1123 return scm_hash_fn_remove_x (table, key,
1124 (scm_t_hash_fn) scm_ihash,
1125 (scm_t_assoc_fn) scm_sloppy_assoc,
1126 0);
0f2d19dd 1127}
1bbd0b84 1128#undef FUNC_NAME
0f2d19dd
JB
1129
1130\f
1131
1132
92c2555f 1133typedef struct scm_t_ihashx_closure
0f2d19dd
JB
1134{
1135 SCM hash;
1136 SCM assoc;
92c2555f 1137} scm_t_ihashx_closure;
0f2d19dd
JB
1138
1139
1cc91f1b 1140
c014a02e 1141static unsigned long
d587c9e8 1142scm_ihashx (SCM obj, unsigned long n, void *arg)
0f2d19dd 1143{
d587c9e8
LC
1144 SCM answer;
1145 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
1146 answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
a55c2b68 1147 return scm_to_ulong (answer);
0f2d19dd
JB
1148}
1149
1150
1cc91f1b 1151
0f2d19dd 1152static SCM
d587c9e8 1153scm_sloppy_assx (SCM obj, SCM alist, void *arg)
0f2d19dd 1154{
d587c9e8 1155 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
87ca11ff 1156 return scm_call_2 (closure->assoc, obj, alist);
0f2d19dd
JB
1157}
1158
1159
a1ec6916 1160SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
1e6808ea
MG
1161 (SCM hash, SCM assoc, SCM table, SCM key),
1162 "This behaves the same way as the corresponding\n"
1163 "@code{-get-handle} function, but uses @var{hash} as a hash\n"
1164 "function and @var{assoc} to compare keys. @code{hash} must be\n"
1165 "a function that takes two arguments, a key to be hashed and a\n"
d550d22a
GB
1166 "table size. @code{assoc} must be an associator function, like\n"
1167 "@code{assoc}, @code{assq} or @code{assv}.")
1bbd0b84 1168#define FUNC_NAME s_scm_hashx_get_handle
0f2d19dd 1169{
92c2555f 1170 scm_t_ihashx_closure closure;
0f2d19dd
JB
1171 closure.hash = hash;
1172 closure.assoc = assoc;
1d9c2e62
AW
1173
1174 if (SCM_UNLIKELY (SCM_HASHTABLE_P (table) && SCM_HASHTABLE_WEAK_P (table)))
1175 SCM_MISC_ERROR ("Handle access not permitted on weak table", SCM_EOL);
1176
1e6808ea 1177 return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
87ca11ff 1178 (void *) &closure);
0f2d19dd 1179}
1bbd0b84 1180#undef FUNC_NAME
0f2d19dd
JB
1181
1182
a1ec6916 1183SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
1e6808ea
MG
1184 (SCM hash, SCM assoc, SCM table, SCM key, SCM init),
1185 "This behaves the same way as the corresponding\n"
1186 "@code{-create-handle} function, but uses @var{hash} as a hash\n"
1187 "function and @var{assoc} to compare keys. @code{hash} must be\n"
1188 "a function that takes two arguments, a key to be hashed and a\n"
d550d22a
GB
1189 "table size. @code{assoc} must be an associator function, like\n"
1190 "@code{assoc}, @code{assq} or @code{assv}.")
1bbd0b84 1191#define FUNC_NAME s_scm_hashx_create_handle_x
0f2d19dd 1192{
92c2555f 1193 scm_t_ihashx_closure closure;
0f2d19dd
JB
1194 closure.hash = hash;
1195 closure.assoc = assoc;
1d9c2e62
AW
1196
1197 if (SCM_UNLIKELY (SCM_HASHTABLE_P (table) && SCM_HASHTABLE_WEAK_P (table)))
1198 SCM_MISC_ERROR ("Handle access not permitted on weak table", SCM_EOL);
1199
1e6808ea
MG
1200 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
1201 scm_sloppy_assx, (void *)&closure);
0f2d19dd 1202}
1bbd0b84 1203#undef FUNC_NAME
0f2d19dd
JB
1204
1205
1206
a1ec6916 1207SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
1e6808ea 1208 (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
d550d22a 1209 "This behaves the same way as the corresponding @code{ref}\n"
1e6808ea
MG
1210 "function, but uses @var{hash} as a hash function and\n"
1211 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1212 "that takes two arguments, a key to be hashed and a table size.\n"
1213 "@code{assoc} must be an associator function, like @code{assoc},\n"
1214 "@code{assq} or @code{assv}.\n"
1215 "\n"
1216 "By way of illustration, @code{hashq-ref table key} is\n"
1217 "equivalent to @code{hashx-ref hashq assq table key}.")
1bbd0b84 1218#define FUNC_NAME s_scm_hashx_ref
0f2d19dd 1219{
92c2555f 1220 scm_t_ihashx_closure closure;
54778cd3 1221 if (SCM_UNBNDP (dflt))
0f2d19dd
JB
1222 dflt = SCM_BOOL_F;
1223 closure.hash = hash;
1224 closure.assoc = assoc;
1e6808ea
MG
1225 return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
1226 (void *)&closure);
0f2d19dd 1227}
1bbd0b84 1228#undef FUNC_NAME
0f2d19dd
JB
1229
1230
1231
1232
a1ec6916 1233SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
1e6808ea 1234 (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
d550d22a 1235 "This behaves the same way as the corresponding @code{set!}\n"
1e6808ea
MG
1236 "function, but uses @var{hash} as a hash function and\n"
1237 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1238 "that takes two arguments, a key to be hashed and a table size.\n"
1239 "@code{assoc} must be an associator function, like @code{assoc},\n"
1240 "@code{assq} or @code{assv}.\n"
1241 "\n"
1242 " By way of illustration, @code{hashq-set! table key} is\n"
1243 "equivalent to @code{hashx-set! hashq assq table key}.")
1bbd0b84 1244#define FUNC_NAME s_scm_hashx_set_x
0f2d19dd 1245{
92c2555f 1246 scm_t_ihashx_closure closure;
0f2d19dd
JB
1247 closure.hash = hash;
1248 closure.assoc = assoc;
1e6808ea
MG
1249 return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
1250 (void *)&closure);
0f2d19dd 1251}
1bbd0b84 1252#undef FUNC_NAME
0f2d19dd 1253
a9cf5c71
MV
1254SCM_DEFINE (scm_hashx_remove_x, "hashx-remove!", 4, 0, 0,
1255 (SCM hash, SCM assoc, SCM table, SCM obj),
1256 "This behaves the same way as the corresponding @code{remove!}\n"
1257 "function, but uses @var{hash} as a hash function and\n"
1258 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1259 "that takes two arguments, a key to be hashed and a table size.\n"
1260 "@code{assoc} must be an associator function, like @code{assoc},\n"
1261 "@code{assq} or @code{assv}.\n"
1262 "\n"
1263 " By way of illustration, @code{hashq-remove! table key} is\n"
1264 "equivalent to @code{hashx-remove! hashq assq #f table key}.")
1265#define FUNC_NAME s_scm_hashx_remove_x
0f2d19dd 1266{
92c2555f 1267 scm_t_ihashx_closure closure;
0f2d19dd
JB
1268 closure.hash = hash;
1269 closure.assoc = assoc;
4cff503f
KR
1270 return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
1271 (void *) &closure);
0f2d19dd 1272}
a9cf5c71 1273#undef FUNC_NAME
0f2d19dd 1274
711a9fd7 1275/* Hash table iterators */
b94903c2 1276
162125af
AW
1277SCM_DEFINE (scm_hash_fold, "hash-fold", 3, 0, 0,
1278 (SCM proc, SCM init, SCM table),
1279 "An iterator over hash-table elements.\n"
1280 "Accumulates and returns a result by applying PROC successively.\n"
1281 "The arguments to PROC are \"(key value prior-result)\" where key\n"
1282 "and value are successive pairs from the hash table TABLE, and\n"
1283 "prior-result is either INIT (for the first application of PROC)\n"
1284 "or the return value of the previous application of PROC.\n"
1285 "For example, @code{(hash-fold acons '() tab)} will convert a hash\n"
1286 "table into an a-list of key-value pairs.")
1287#define FUNC_NAME s_scm_hash_fold
1288{
1289 SCM_VALIDATE_PROC (1, proc);
2dd7d8ce 1290 SCM_VALIDATE_HASHTABLE (3, table);
162125af
AW
1291 return scm_internal_hash_fold ((scm_t_hash_fold_fn) scm_call_3,
1292 (void *) SCM_UNPACK (proc), init, table);
1293}
1294#undef FUNC_NAME
1295
1296static SCM
1297for_each_proc (void *proc, SCM handle)
1298{
1299 return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
1300}
1301
1302SCM_DEFINE (scm_hash_for_each, "hash-for-each", 2, 0, 0,
1303 (SCM proc, SCM table),
1304 "An iterator over hash-table elements.\n"
1305 "Applies PROC successively on all hash table items.\n"
1306 "The arguments to PROC are \"(key value)\" where key\n"
1307 "and value are successive pairs from the hash table TABLE.")
1308#define FUNC_NAME s_scm_hash_for_each
1309{
1310 SCM_VALIDATE_PROC (1, proc);
2dd7d8ce 1311 SCM_VALIDATE_HASHTABLE (2, table);
162125af
AW
1312
1313 scm_internal_hash_for_each_handle (for_each_proc,
1314 (void *) SCM_UNPACK (proc),
1315 table);
1316 return SCM_UNSPECIFIED;
1317}
1318#undef FUNC_NAME
1319
1320SCM_DEFINE (scm_hash_for_each_handle, "hash-for-each-handle", 2, 0, 0,
1321 (SCM proc, SCM table),
1322 "An iterator over hash-table elements.\n"
1323 "Applies PROC successively on all hash table handles.")
1324#define FUNC_NAME s_scm_hash_for_each_handle
1325{
1326 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)), proc, 1, FUNC_NAME);
2dd7d8ce 1327 SCM_VALIDATE_HASHTABLE (2, table);
162125af 1328
1d9c2e62
AW
1329 if (SCM_UNLIKELY (SCM_HASHTABLE_WEAK_P (table)))
1330 SCM_MISC_ERROR ("Handle access not permitted on weak table", SCM_EOL);
1331
162125af
AW
1332 scm_internal_hash_for_each_handle ((scm_t_hash_handle_fn) scm_call_1,
1333 (void *) SCM_UNPACK (proc),
1334 table);
1335 return SCM_UNSPECIFIED;
1336}
1337#undef FUNC_NAME
1338
1339static SCM
1340map_proc (void *proc, SCM key, SCM data, SCM value)
1341{
1342 return scm_cons (scm_call_2 (SCM_PACK (proc), key, data), value);
1343}
1344
1345SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
1346 (SCM proc, SCM table),
1347 "An iterator over hash-table elements.\n"
1348 "Accumulates and returns as a list the results of applying PROC successively.\n"
1349 "The arguments to PROC are \"(key value)\" where key\n"
1350 "and value are successive pairs from the hash table TABLE.")
1351#define FUNC_NAME s_scm_hash_map_to_list
1352{
1353 SCM_VALIDATE_PROC (1, proc);
2dd7d8ce 1354 SCM_VALIDATE_HASHTABLE (2, table);
162125af
AW
1355 return scm_internal_hash_fold (map_proc,
1356 (void *) SCM_UNPACK (proc),
1357 SCM_EOL,
1358 table);
1359}
1360#undef FUNC_NAME
1361
1362\f
c7df61cd
MD
1363
1364SCM
a07010bf
LC
1365scm_internal_hash_fold (scm_t_hash_fold_fn fn, void *closure,
1366 SCM init, SCM table)
2dd7d8ce 1367#define FUNC_NAME s_scm_hash_fold
c7df61cd 1368{
87ca11ff
MD
1369 long i, n;
1370 SCM buckets, result = init;
87ca11ff 1371
2dd7d8ce
AW
1372 SCM_VALIDATE_HASHTABLE (0, table);
1373 buckets = SCM_HASHTABLE_VECTOR (table);
0a4c1355 1374
3ebc1832 1375 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
c7df61cd
MD
1376 for (i = 0; i < n; ++i)
1377 {
741e83fc
LC
1378 SCM prev, ls;
1379
1380 for (prev = SCM_BOOL_F, ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
1381 !scm_is_null (ls);
1382 prev = ls, ls = SCM_CDR (ls))
c7df61cd 1383 {
741e83fc
LC
1384 SCM handle;
1385
d2e53ed6 1386 if (!scm_is_pair (ls))
2dd7d8ce 1387 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
741e83fc 1388
c7df61cd 1389 handle = SCM_CAR (ls);
d2e53ed6 1390 if (!scm_is_pair (handle))
2dd7d8ce 1391 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
741e83fc 1392
f0554ee7 1393 if (SCM_HASHTABLE_WEAK_P (table))
741e83fc 1394 {
986ec822 1395 if (SCM_WEAK_PAIR_DELETED_P (handle))
741e83fc
LC
1396 {
1397 /* We hit a weak pair whose car/cdr has become
1398 unreachable: unlink it from the bucket. */
1399 if (prev != SCM_BOOL_F)
1400 SCM_SETCDR (prev, SCM_CDR (ls));
1401 else
1402 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_CDR (ls));
1403
2dd7d8ce
AW
1404 /* Update the item count. */
1405 SCM_HASHTABLE_DECREMENT (table);
741e83fc
LC
1406
1407 continue;
1408 }
1409 }
1410
c7df61cd 1411 result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
c7df61cd
MD
1412 }
1413 }
87ca11ff 1414
c7df61cd
MD
1415 return result;
1416}
2dd7d8ce 1417#undef FUNC_NAME
c7df61cd 1418
711a9fd7
MD
1419/* The following redundant code is here in order to be able to support
1420 hash-for-each-handle. An alternative would have been to replace
1421 this code and scm_internal_hash_fold above with a single
1422 scm_internal_hash_fold_handles, but we don't want to promote such
1423 an API. */
1424
711a9fd7 1425void
a07010bf
LC
1426scm_internal_hash_for_each_handle (scm_t_hash_handle_fn fn, void *closure,
1427 SCM table)
2dd7d8ce 1428#define FUNC_NAME s_scm_hash_for_each
711a9fd7
MD
1429{
1430 long i, n;
1431 SCM buckets;
1432
2dd7d8ce
AW
1433 SCM_VALIDATE_HASHTABLE (0, table);
1434 buckets = SCM_HASHTABLE_VECTOR (table);
3ebc1832 1435 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
2dd7d8ce 1436
711a9fd7
MD
1437 for (i = 0; i < n; ++i)
1438 {
3ebc1832 1439 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
d2e53ed6 1440 while (!scm_is_null (ls))
711a9fd7 1441 {
d2e53ed6 1442 if (!scm_is_pair (ls))
2dd7d8ce 1443 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
711a9fd7 1444 handle = SCM_CAR (ls);
d2e53ed6 1445 if (!scm_is_pair (handle))
2dd7d8ce 1446 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
711a9fd7
MD
1447 fn (closure, handle);
1448 ls = SCM_CDR (ls);
1449 }
1450 }
1451}
2dd7d8ce 1452#undef FUNC_NAME
711a9fd7 1453
0f2d19dd
JB
1454\f
1455
1cc91f1b 1456
c35738c1
MD
1457void
1458scm_init_hashtab ()
1459{
a0599745 1460#include "libguile/hashtab.x"
0f2d19dd 1461}
89e00824
ML
1462
1463/*
1464 Local Variables:
1465 c-file-style: "gnu"
1466 End:
1467*/