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