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