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