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