Define `equal?' for pointer objects.
[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);
105 prev = alist, alist = SCM_CDR (alist))
106 {
107 SCM pair = SCM_CAR (alist);
108
109 if (scm_is_pair (pair))
110 {
72c9d17b 111 if (SCM_WEAK_PAIR_DELETED_P (pair))
3a2de079
LC
112 {
113 /* Remove from ALIST weak pair PAIR whose car/cdr has been
114 nullified by the GC. */
115 if (prev == SCM_EOL)
d9c82e20 116 result = SCM_CDR (alist);
3a2de079
LC
117 else
118 SCM_SETCDR (prev, SCM_CDR (alist));
119
d9c82e20 120 (*removed_items)++;
3a2de079
LC
121 continue;
122 }
123 }
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);
626 return val;
627}
628
629
d9c82e20 630SCM
a9cf5c71 631scm_hash_fn_remove_x (SCM table, SCM obj,
f044da55
LC
632 scm_t_hash_fn hash_fn,
633 scm_t_assoc_fn assoc_fn,
a9cf5c71 634 void *closure)
0f2d19dd 635{
c014a02e 636 unsigned long k;
63229905 637 SCM buckets, h;
0f2d19dd 638
87ca11ff 639 if (SCM_HASHTABLE_P (table))
0a4c1355 640 buckets = SCM_HASHTABLE_VECTOR (table);
87ca11ff
MD
641 else
642 {
3ebc1832
MV
643 SCM_ASSERT (scm_is_simple_vector (table), table,
644 SCM_ARG1, "hash_fn_remove_x");
87ca11ff 645 buckets = table;
87ca11ff 646 }
c99de5aa 647 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
0f2d19dd 648 return SCM_EOL;
87ca11ff 649
3ebc1832
MV
650 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
651 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
b9bd8526 652 scm_out_of_range ("hash_fn_remove_x", scm_from_ulong (k));
3a2de079 653
63229905
LC
654 if (IS_WEAK_THING (table))
655 h = weak_bucket_assoc (table, buckets, k, hash_fn,
656 assoc_fn, obj, closure);
657 else
658 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
e4d21e6b 659
7888309b 660 if (scm_is_true (h))
87ca11ff 661 {
3ebc1832 662 SCM_SIMPLE_VECTOR_SET
a9cf5c71 663 (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
a41666e5 664 if (!scm_is_eq (table, buckets))
87ca11ff
MD
665 {
666 SCM_HASHTABLE_DECREMENT (table);
667 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
c35738c1 668 scm_i_rehash (table, hash_fn, closure, "scm_hash_fn_remove_x");
87ca11ff
MD
669 }
670 }
0f2d19dd
JB
671 return h;
672}
673
c35738c1
MD
674SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
675 (SCM table),
a9cf5c71 676 "Remove all items from @var{table} (without triggering a resize).")
c35738c1
MD
677#define FUNC_NAME s_scm_hash_clear_x
678{
a9cf5c71
MV
679 if (SCM_HASHTABLE_P (table))
680 {
681 scm_vector_fill_x (SCM_HASHTABLE_VECTOR (table), SCM_EOL);
682 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
683 }
684 else
685 scm_vector_fill_x (table, SCM_EOL);
c35738c1
MD
686 return SCM_UNSPECIFIED;
687}
688#undef FUNC_NAME
0f2d19dd
JB
689
690\f
691
a1ec6916 692SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
22a52da1
DH
693 (SCM table, SCM key),
694 "This procedure returns the @code{(key . value)} pair from the\n"
695 "hash table @var{table}. If @var{table} does not hold an\n"
696 "associated value for @var{key}, @code{#f} is returned.\n"
697 "Uses @code{eq?} for equality testing.")
1bbd0b84 698#define FUNC_NAME s_scm_hashq_get_handle
0f2d19dd 699{
d587c9e8
LC
700 return scm_hash_fn_get_handle (table, key,
701 (scm_t_hash_fn) scm_ihashq,
702 (scm_t_assoc_fn) scm_sloppy_assq,
703 0);
0f2d19dd 704}
1bbd0b84 705#undef FUNC_NAME
0f2d19dd
JB
706
707
a1ec6916 708SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
d550d22a
GB
709 (SCM table, SCM key, SCM init),
710 "This function looks up @var{key} in @var{table} and returns its handle.\n"
b380b885
MD
711 "If @var{key} is not already present, a new handle is created which\n"
712 "associates @var{key} with @var{init}.")
1bbd0b84 713#define FUNC_NAME s_scm_hashq_create_handle_x
0f2d19dd 714{
d587c9e8
LC
715 return scm_hash_fn_create_handle_x (table, key, init,
716 (scm_t_hash_fn) scm_ihashq,
717 (scm_t_assoc_fn) scm_sloppy_assq,
718 0);
0f2d19dd 719}
1bbd0b84 720#undef FUNC_NAME
0f2d19dd
JB
721
722
a1ec6916 723SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
1e6808ea 724 (SCM table, SCM key, SCM dflt),
b380b885
MD
725 "Look up @var{key} in the hash table @var{table}, and return the\n"
726 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
727 "return @var{default} (or @code{#f} if no @var{default} argument\n"
728 "is supplied). Uses @code{eq?} for equality testing.")
1bbd0b84 729#define FUNC_NAME s_scm_hashq_ref
0f2d19dd 730{
54778cd3 731 if (SCM_UNBNDP (dflt))
0f2d19dd 732 dflt = SCM_BOOL_F;
d587c9e8
LC
733 return scm_hash_fn_ref (table, key, dflt,
734 (scm_t_hash_fn) scm_ihashq,
735 (scm_t_assoc_fn) scm_sloppy_assq,
736 0);
0f2d19dd 737}
1bbd0b84 738#undef FUNC_NAME
0f2d19dd
JB
739
740
741
a1ec6916 742SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
1e6808ea 743 (SCM table, SCM key, SCM val),
5352393c
MG
744 "Find the entry in @var{table} associated with @var{key}, and\n"
745 "store @var{value} there. Uses @code{eq?} for equality testing.")
1bbd0b84 746#define FUNC_NAME s_scm_hashq_set_x
0f2d19dd 747{
d587c9e8
LC
748 return scm_hash_fn_set_x (table, key, val,
749 (scm_t_hash_fn) scm_ihashq,
750 (scm_t_assoc_fn) scm_sloppy_assq,
751 0);
0f2d19dd 752}
1bbd0b84 753#undef FUNC_NAME
0f2d19dd
JB
754
755
756
a1ec6916 757SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
1e6808ea 758 (SCM table, SCM key),
5352393c
MG
759 "Remove @var{key} (and any value associated with it) from\n"
760 "@var{table}. Uses @code{eq?} for equality tests.")
1bbd0b84 761#define FUNC_NAME s_scm_hashq_remove_x
0f2d19dd 762{
d587c9e8
LC
763 return scm_hash_fn_remove_x (table, key,
764 (scm_t_hash_fn) scm_ihashq,
765 (scm_t_assoc_fn) scm_sloppy_assq,
766 0);
0f2d19dd 767}
1bbd0b84 768#undef FUNC_NAME
0f2d19dd
JB
769
770
771\f
772
a1ec6916 773SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
22a52da1
DH
774 (SCM table, SCM key),
775 "This procedure returns the @code{(key . value)} pair from the\n"
776 "hash table @var{table}. If @var{table} does not hold an\n"
777 "associated value for @var{key}, @code{#f} is returned.\n"
778 "Uses @code{eqv?} for equality testing.")
1bbd0b84 779#define FUNC_NAME s_scm_hashv_get_handle
0f2d19dd 780{
d587c9e8
LC
781 return scm_hash_fn_get_handle (table, key,
782 (scm_t_hash_fn) scm_ihashv,
783 (scm_t_assoc_fn) scm_sloppy_assv,
784 0);
0f2d19dd 785}
1bbd0b84 786#undef FUNC_NAME
0f2d19dd
JB
787
788
a1ec6916 789SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
d550d22a
GB
790 (SCM table, SCM key, SCM init),
791 "This function looks up @var{key} in @var{table} and returns its handle.\n"
792 "If @var{key} is not already present, a new handle is created which\n"
793 "associates @var{key} with @var{init}.")
1bbd0b84 794#define FUNC_NAME s_scm_hashv_create_handle_x
0f2d19dd 795{
d587c9e8
LC
796 return scm_hash_fn_create_handle_x (table, key, init,
797 (scm_t_hash_fn) scm_ihashv,
798 (scm_t_assoc_fn) scm_sloppy_assv,
799 0);
0f2d19dd 800}
1bbd0b84 801#undef FUNC_NAME
0f2d19dd
JB
802
803
a1ec6916 804SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
1e6808ea 805 (SCM table, SCM key, SCM dflt),
d550d22a
GB
806 "Look up @var{key} in the hash table @var{table}, and return the\n"
807 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
808 "return @var{default} (or @code{#f} if no @var{default} argument\n"
809 "is supplied). Uses @code{eqv?} for equality testing.")
1bbd0b84 810#define FUNC_NAME s_scm_hashv_ref
0f2d19dd 811{
54778cd3 812 if (SCM_UNBNDP (dflt))
0f2d19dd 813 dflt = SCM_BOOL_F;
d587c9e8
LC
814 return scm_hash_fn_ref (table, key, dflt,
815 (scm_t_hash_fn) scm_ihashv,
816 (scm_t_assoc_fn) scm_sloppy_assv,
817 0);
0f2d19dd 818}
1bbd0b84 819#undef FUNC_NAME
0f2d19dd
JB
820
821
822
a1ec6916 823SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
1e6808ea 824 (SCM table, SCM key, SCM val),
5352393c
MG
825 "Find the entry in @var{table} associated with @var{key}, and\n"
826 "store @var{value} there. Uses @code{eqv?} for equality testing.")
1bbd0b84 827#define FUNC_NAME s_scm_hashv_set_x
0f2d19dd 828{
d587c9e8
LC
829 return scm_hash_fn_set_x (table, key, val,
830 (scm_t_hash_fn) scm_ihashv,
831 (scm_t_assoc_fn) scm_sloppy_assv,
832 0);
0f2d19dd 833}
1bbd0b84 834#undef FUNC_NAME
0f2d19dd
JB
835
836
a1ec6916 837SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
1e6808ea 838 (SCM table, SCM key),
5352393c
MG
839 "Remove @var{key} (and any value associated with it) from\n"
840 "@var{table}. Uses @code{eqv?} for equality tests.")
1bbd0b84 841#define FUNC_NAME s_scm_hashv_remove_x
0f2d19dd 842{
d587c9e8
LC
843 return scm_hash_fn_remove_x (table, key,
844 (scm_t_hash_fn) scm_ihashv,
845 (scm_t_assoc_fn) scm_sloppy_assv,
846 0);
0f2d19dd 847}
1bbd0b84 848#undef FUNC_NAME
0f2d19dd
JB
849
850\f
851
a1ec6916 852SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
22a52da1
DH
853 (SCM table, SCM key),
854 "This procedure returns the @code{(key . value)} pair from the\n"
855 "hash table @var{table}. If @var{table} does not hold an\n"
856 "associated value for @var{key}, @code{#f} is returned.\n"
857 "Uses @code{equal?} for equality testing.")
1bbd0b84 858#define FUNC_NAME s_scm_hash_get_handle
0f2d19dd 859{
d587c9e8
LC
860 return scm_hash_fn_get_handle (table, key,
861 (scm_t_hash_fn) scm_ihash,
862 (scm_t_assoc_fn) scm_sloppy_assoc,
863 0);
0f2d19dd 864}
1bbd0b84 865#undef FUNC_NAME
0f2d19dd
JB
866
867
a1ec6916 868SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
d550d22a
GB
869 (SCM table, SCM key, SCM init),
870 "This function looks up @var{key} in @var{table} and returns its handle.\n"
871 "If @var{key} is not already present, a new handle is created which\n"
872 "associates @var{key} with @var{init}.")
1bbd0b84 873#define FUNC_NAME s_scm_hash_create_handle_x
0f2d19dd 874{
d587c9e8
LC
875 return scm_hash_fn_create_handle_x (table, key, init,
876 (scm_t_hash_fn) scm_ihash,
877 (scm_t_assoc_fn) scm_sloppy_assoc,
878 0);
0f2d19dd 879}
1bbd0b84 880#undef FUNC_NAME
0f2d19dd
JB
881
882
a1ec6916 883SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
1e6808ea 884 (SCM table, SCM key, SCM dflt),
d550d22a
GB
885 "Look up @var{key} in the hash table @var{table}, and return the\n"
886 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
887 "return @var{default} (or @code{#f} if no @var{default} argument\n"
888 "is supplied). Uses @code{equal?} for equality testing.")
1bbd0b84 889#define FUNC_NAME s_scm_hash_ref
0f2d19dd 890{
54778cd3 891 if (SCM_UNBNDP (dflt))
0f2d19dd 892 dflt = SCM_BOOL_F;
d587c9e8
LC
893 return scm_hash_fn_ref (table, key, dflt,
894 (scm_t_hash_fn) scm_ihash,
895 (scm_t_assoc_fn) scm_sloppy_assoc,
896 0);
0f2d19dd 897}
1bbd0b84 898#undef FUNC_NAME
0f2d19dd
JB
899
900
901
a1ec6916 902SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
1e6808ea 903 (SCM table, SCM key, SCM val),
5352393c
MG
904 "Find the entry in @var{table} associated with @var{key}, and\n"
905 "store @var{value} there. Uses @code{equal?} for equality\n"
906 "testing.")
1bbd0b84 907#define FUNC_NAME s_scm_hash_set_x
0f2d19dd 908{
d587c9e8
LC
909 return scm_hash_fn_set_x (table, key, val,
910 (scm_t_hash_fn) scm_ihash,
911 (scm_t_assoc_fn) scm_sloppy_assoc,
912 0);
0f2d19dd 913}
1bbd0b84 914#undef FUNC_NAME
0f2d19dd
JB
915
916
917
a1ec6916 918SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
1e6808ea 919 (SCM table, SCM key),
5352393c
MG
920 "Remove @var{key} (and any value associated with it) from\n"
921 "@var{table}. Uses @code{equal?} for equality tests.")
1bbd0b84 922#define FUNC_NAME s_scm_hash_remove_x
0f2d19dd 923{
d587c9e8
LC
924 return scm_hash_fn_remove_x (table, key,
925 (scm_t_hash_fn) scm_ihash,
926 (scm_t_assoc_fn) scm_sloppy_assoc,
927 0);
0f2d19dd 928}
1bbd0b84 929#undef FUNC_NAME
0f2d19dd
JB
930
931\f
932
933
92c2555f 934typedef struct scm_t_ihashx_closure
0f2d19dd
JB
935{
936 SCM hash;
937 SCM assoc;
92c2555f 938} scm_t_ihashx_closure;
0f2d19dd
JB
939
940
1cc91f1b 941
c014a02e 942static unsigned long
d587c9e8 943scm_ihashx (SCM obj, unsigned long n, void *arg)
0f2d19dd 944{
d587c9e8
LC
945 SCM answer;
946 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
947 answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
a55c2b68 948 return scm_to_ulong (answer);
0f2d19dd
JB
949}
950
951
1cc91f1b 952
0f2d19dd 953static SCM
d587c9e8 954scm_sloppy_assx (SCM obj, SCM alist, void *arg)
0f2d19dd 955{
d587c9e8 956 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
87ca11ff 957 return scm_call_2 (closure->assoc, obj, alist);
0f2d19dd
JB
958}
959
960
a1ec6916 961SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
1e6808ea
MG
962 (SCM hash, SCM assoc, SCM table, SCM key),
963 "This behaves the same way as the corresponding\n"
964 "@code{-get-handle} function, but uses @var{hash} as a hash\n"
965 "function and @var{assoc} to compare keys. @code{hash} must be\n"
966 "a function that takes two arguments, a key to be hashed and a\n"
d550d22a
GB
967 "table size. @code{assoc} must be an associator function, like\n"
968 "@code{assoc}, @code{assq} or @code{assv}.")
1bbd0b84 969#define FUNC_NAME s_scm_hashx_get_handle
0f2d19dd 970{
92c2555f 971 scm_t_ihashx_closure closure;
0f2d19dd
JB
972 closure.hash = hash;
973 closure.assoc = assoc;
1e6808ea 974 return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
87ca11ff 975 (void *) &closure);
0f2d19dd 976}
1bbd0b84 977#undef FUNC_NAME
0f2d19dd
JB
978
979
a1ec6916 980SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
1e6808ea
MG
981 (SCM hash, SCM assoc, SCM table, SCM key, SCM init),
982 "This behaves the same way as the corresponding\n"
983 "@code{-create-handle} function, but uses @var{hash} as a hash\n"
984 "function and @var{assoc} to compare keys. @code{hash} must be\n"
985 "a function that takes two arguments, a key to be hashed and a\n"
d550d22a
GB
986 "table size. @code{assoc} must be an associator function, like\n"
987 "@code{assoc}, @code{assq} or @code{assv}.")
1bbd0b84 988#define FUNC_NAME s_scm_hashx_create_handle_x
0f2d19dd 989{
92c2555f 990 scm_t_ihashx_closure closure;
0f2d19dd
JB
991 closure.hash = hash;
992 closure.assoc = assoc;
1e6808ea
MG
993 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
994 scm_sloppy_assx, (void *)&closure);
0f2d19dd 995}
1bbd0b84 996#undef FUNC_NAME
0f2d19dd
JB
997
998
999
a1ec6916 1000SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
1e6808ea 1001 (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
d550d22a 1002 "This behaves the same way as the corresponding @code{ref}\n"
1e6808ea
MG
1003 "function, but uses @var{hash} as a hash function and\n"
1004 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1005 "that takes two arguments, a key to be hashed and a table size.\n"
1006 "@code{assoc} must be an associator function, like @code{assoc},\n"
1007 "@code{assq} or @code{assv}.\n"
1008 "\n"
1009 "By way of illustration, @code{hashq-ref table key} is\n"
1010 "equivalent to @code{hashx-ref hashq assq table key}.")
1bbd0b84 1011#define FUNC_NAME s_scm_hashx_ref
0f2d19dd 1012{
92c2555f 1013 scm_t_ihashx_closure closure;
54778cd3 1014 if (SCM_UNBNDP (dflt))
0f2d19dd
JB
1015 dflt = SCM_BOOL_F;
1016 closure.hash = hash;
1017 closure.assoc = assoc;
1e6808ea
MG
1018 return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
1019 (void *)&closure);
0f2d19dd 1020}
1bbd0b84 1021#undef FUNC_NAME
0f2d19dd
JB
1022
1023
1024
1025
a1ec6916 1026SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
1e6808ea 1027 (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
d550d22a 1028 "This behaves the same way as the corresponding @code{set!}\n"
1e6808ea
MG
1029 "function, but uses @var{hash} as a hash function and\n"
1030 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1031 "that takes two arguments, a key to be hashed and a table size.\n"
1032 "@code{assoc} must be an associator function, like @code{assoc},\n"
1033 "@code{assq} or @code{assv}.\n"
1034 "\n"
1035 " By way of illustration, @code{hashq-set! table key} is\n"
1036 "equivalent to @code{hashx-set! hashq assq table key}.")
1bbd0b84 1037#define FUNC_NAME s_scm_hashx_set_x
0f2d19dd 1038{
92c2555f 1039 scm_t_ihashx_closure closure;
0f2d19dd
JB
1040 closure.hash = hash;
1041 closure.assoc = assoc;
1e6808ea
MG
1042 return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
1043 (void *)&closure);
0f2d19dd 1044}
1bbd0b84 1045#undef FUNC_NAME
0f2d19dd 1046
a9cf5c71
MV
1047SCM_DEFINE (scm_hashx_remove_x, "hashx-remove!", 4, 0, 0,
1048 (SCM hash, SCM assoc, SCM table, SCM obj),
1049 "This behaves the same way as the corresponding @code{remove!}\n"
1050 "function, but uses @var{hash} as a hash function and\n"
1051 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1052 "that takes two arguments, a key to be hashed and a table size.\n"
1053 "@code{assoc} must be an associator function, like @code{assoc},\n"
1054 "@code{assq} or @code{assv}.\n"
1055 "\n"
1056 " By way of illustration, @code{hashq-remove! table key} is\n"
1057 "equivalent to @code{hashx-remove! hashq assq #f table key}.")
1058#define FUNC_NAME s_scm_hashx_remove_x
0f2d19dd 1059{
92c2555f 1060 scm_t_ihashx_closure closure;
0f2d19dd
JB
1061 closure.hash = hash;
1062 closure.assoc = assoc;
4cff503f
KR
1063 return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
1064 (void *) &closure);
0f2d19dd 1065}
a9cf5c71 1066#undef FUNC_NAME
0f2d19dd 1067
711a9fd7 1068/* Hash table iterators */
b94903c2 1069
162125af
AW
1070SCM_DEFINE (scm_hash_fold, "hash-fold", 3, 0, 0,
1071 (SCM proc, SCM init, SCM table),
1072 "An iterator over hash-table elements.\n"
1073 "Accumulates and returns a result by applying PROC successively.\n"
1074 "The arguments to PROC are \"(key value prior-result)\" where key\n"
1075 "and value are successive pairs from the hash table TABLE, and\n"
1076 "prior-result is either INIT (for the first application of PROC)\n"
1077 "or the return value of the previous application of PROC.\n"
1078 "For example, @code{(hash-fold acons '() tab)} will convert a hash\n"
1079 "table into an a-list of key-value pairs.")
1080#define FUNC_NAME s_scm_hash_fold
1081{
1082 SCM_VALIDATE_PROC (1, proc);
1083 if (!SCM_HASHTABLE_P (table))
1084 SCM_VALIDATE_VECTOR (3, table);
1085 return scm_internal_hash_fold ((scm_t_hash_fold_fn) scm_call_3,
1086 (void *) SCM_UNPACK (proc), init, table);
1087}
1088#undef FUNC_NAME
1089
1090static SCM
1091for_each_proc (void *proc, SCM handle)
1092{
1093 return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
1094}
1095
1096SCM_DEFINE (scm_hash_for_each, "hash-for-each", 2, 0, 0,
1097 (SCM proc, SCM table),
1098 "An iterator over hash-table elements.\n"
1099 "Applies PROC successively on all hash table items.\n"
1100 "The arguments to PROC are \"(key value)\" where key\n"
1101 "and value are successive pairs from the hash table TABLE.")
1102#define FUNC_NAME s_scm_hash_for_each
1103{
1104 SCM_VALIDATE_PROC (1, proc);
1105 if (!SCM_HASHTABLE_P (table))
1106 SCM_VALIDATE_VECTOR (2, table);
1107
1108 scm_internal_hash_for_each_handle (for_each_proc,
1109 (void *) SCM_UNPACK (proc),
1110 table);
1111 return SCM_UNSPECIFIED;
1112}
1113#undef FUNC_NAME
1114
1115SCM_DEFINE (scm_hash_for_each_handle, "hash-for-each-handle", 2, 0, 0,
1116 (SCM proc, SCM table),
1117 "An iterator over hash-table elements.\n"
1118 "Applies PROC successively on all hash table handles.")
1119#define FUNC_NAME s_scm_hash_for_each_handle
1120{
1121 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)), proc, 1, FUNC_NAME);
1122 if (!SCM_HASHTABLE_P (table))
1123 SCM_VALIDATE_VECTOR (2, table);
1124
1125 scm_internal_hash_for_each_handle ((scm_t_hash_handle_fn) scm_call_1,
1126 (void *) SCM_UNPACK (proc),
1127 table);
1128 return SCM_UNSPECIFIED;
1129}
1130#undef FUNC_NAME
1131
1132static SCM
1133map_proc (void *proc, SCM key, SCM data, SCM value)
1134{
1135 return scm_cons (scm_call_2 (SCM_PACK (proc), key, data), value);
1136}
1137
1138SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
1139 (SCM proc, SCM table),
1140 "An iterator over hash-table elements.\n"
1141 "Accumulates and returns as a list the results of applying PROC successively.\n"
1142 "The arguments to PROC are \"(key value)\" where key\n"
1143 "and value are successive pairs from the hash table TABLE.")
1144#define FUNC_NAME s_scm_hash_map_to_list
1145{
1146 SCM_VALIDATE_PROC (1, proc);
1147 if (!SCM_HASHTABLE_P (table))
1148 SCM_VALIDATE_VECTOR (2, table);
1149 return scm_internal_hash_fold (map_proc,
1150 (void *) SCM_UNPACK (proc),
1151 SCM_EOL,
1152 table);
1153}
1154#undef FUNC_NAME
1155
1156\f
c7df61cd
MD
1157
1158SCM
a07010bf
LC
1159scm_internal_hash_fold (scm_t_hash_fold_fn fn, void *closure,
1160 SCM init, SCM table)
c7df61cd 1161{
87ca11ff
MD
1162 long i, n;
1163 SCM buckets, result = init;
87ca11ff
MD
1164
1165 if (SCM_HASHTABLE_P (table))
0a4c1355 1166 buckets = SCM_HASHTABLE_VECTOR (table);
87ca11ff 1167 else
741e83fc 1168 /* Weak alist vector. */
0a4c1355
MD
1169 buckets = table;
1170
3ebc1832 1171 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
c7df61cd
MD
1172 for (i = 0; i < n; ++i)
1173 {
741e83fc
LC
1174 SCM prev, ls;
1175
1176 for (prev = SCM_BOOL_F, ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
1177 !scm_is_null (ls);
1178 prev = ls, ls = SCM_CDR (ls))
c7df61cd 1179 {
741e83fc
LC
1180 SCM handle;
1181
d2e53ed6 1182 if (!scm_is_pair (ls))
0a4c1355 1183 scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
741e83fc 1184
c7df61cd 1185 handle = SCM_CAR (ls);
d2e53ed6 1186 if (!scm_is_pair (handle))
0a4c1355 1187 scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
741e83fc
LC
1188
1189 if (IS_WEAK_THING (table))
1190 {
986ec822 1191 if (SCM_WEAK_PAIR_DELETED_P (handle))
741e83fc
LC
1192 {
1193 /* We hit a weak pair whose car/cdr has become
1194 unreachable: unlink it from the bucket. */
1195 if (prev != SCM_BOOL_F)
1196 SCM_SETCDR (prev, SCM_CDR (ls));
1197 else
1198 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_CDR (ls));
1199
1200 if (SCM_HASHTABLE_P (table))
72c9d17b
LC
1201 /* Update the item count. */
1202 SCM_HASHTABLE_DECREMENT (table);
741e83fc
LC
1203
1204 continue;
1205 }
1206 }
1207
c7df61cd 1208 result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
c7df61cd
MD
1209 }
1210 }
87ca11ff 1211
c7df61cd
MD
1212 return result;
1213}
1214
711a9fd7
MD
1215/* The following redundant code is here in order to be able to support
1216 hash-for-each-handle. An alternative would have been to replace
1217 this code and scm_internal_hash_fold above with a single
1218 scm_internal_hash_fold_handles, but we don't want to promote such
1219 an API. */
1220
711a9fd7 1221void
a07010bf
LC
1222scm_internal_hash_for_each_handle (scm_t_hash_handle_fn fn, void *closure,
1223 SCM table)
711a9fd7
MD
1224{
1225 long i, n;
1226 SCM buckets;
1227
1228 if (SCM_HASHTABLE_P (table))
1229 buckets = SCM_HASHTABLE_VECTOR (table);
1230 else
1231 buckets = table;
1232
3ebc1832 1233 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
711a9fd7
MD
1234 for (i = 0; i < n; ++i)
1235 {
3ebc1832 1236 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
d2e53ed6 1237 while (!scm_is_null (ls))
711a9fd7 1238 {
d2e53ed6 1239 if (!scm_is_pair (ls))
711a9fd7
MD
1240 scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
1241 handle = SCM_CAR (ls);
d2e53ed6 1242 if (!scm_is_pair (handle))
711a9fd7
MD
1243 scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
1244 fn (closure, handle);
1245 ls = SCM_CDR (ls);
1246 }
1247 }
1248}
1249
0f2d19dd
JB
1250\f
1251
1cc91f1b 1252
c35738c1
MD
1253void
1254scm_init_hashtab ()
1255{
a0599745 1256#include "libguile/hashtab.x"
0f2d19dd 1257}
89e00824
ML
1258
1259/*
1260 Local Variables:
1261 c-file-style: "gnu"
1262 End:
1263*/