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