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