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