* gc-mark.c (scm_mark_all): Do not rely on hooks to run the weak
[bpt/guile.git] / libguile / hashtab.c
1 /* Copyright (C) 1995,1996,1998,1999,2000,2001, 2003, 2004 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 \f
34
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 *
44 */
45
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.
63 */
64
65 scm_t_bits scm_tc16_hashtable;
66
67 static unsigned long hashtable_size[] = {
68 31, 61, 113, 223, 443, 883, 1759, 3517, 7027, 14051, 28099, 56197, 112363,
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
75 };
76
77 #define HASHTABLE_SIZE_N (sizeof(hashtable_size)/sizeof(unsigned long))
78
79 static char *s_hashtable = "hashtable";
80
81 SCM weak_hashtables = SCM_EOL;
82
83 static SCM
84 make_hash_table (int flags, unsigned long k, const char *func_name)
85 {
86 SCM table, vector;
87 scm_t_hashtable *t;
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];
92 if (flags)
93 vector = scm_i_allocate_weak_vector (flags, scm_from_int (n), SCM_EOL);
94 else
95 vector = scm_c_make_vector (n, SCM_EOL);
96 t = scm_gc_malloc (sizeof (*t), s_hashtable);
97 t->min_size_index = t->size_index = i;
98 t->n_items = 0;
99 t->lower = 0;
100 t->upper = 9 * n / 10;
101 t->flags = flags;
102 t->hash_fn = NULL;
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);
110 return table;
111 }
112
113 void
114 scm_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;
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;
146 }
147 SCM_HASHTABLE (table)->size_index = i;
148
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))
158 new_buckets = scm_i_allocate_weak_vector (SCM_HASHTABLE_FLAGS (table),
159 scm_from_ulong (new_size),
160 SCM_EOL);
161 else
162 new_buckets = scm_c_make_vector (new_size, SCM_EOL);
163
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
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.
169 */
170
171 SCM_SET_HASHTABLE_VECTOR (table, new_buckets);
172 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
173
174 old_size = SCM_SIMPLE_VECTOR_LENGTH (buckets);
175 for (i = 0; i < old_size; ++i)
176 {
177 SCM ls, cell, handle;
178
179 ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
180 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_EOL);
181
182 while (scm_is_pair (ls))
183 {
184 unsigned long h;
185 cell = ls;
186 handle = SCM_CAR (cell);
187 ls = SCM_CDR (ls);
188 h = hash_fn (SCM_CAR (handle), new_size, closure);
189 if (h >= new_size)
190 scm_out_of_range (func_name, scm_from_ulong (h));
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);
194 }
195 }
196 }
197
198
199 static int
200 hashtable_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
201 {
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);
210 scm_uintprint (SCM_HASHTABLE_N_ITEMS (exp), 10, port);
211 scm_putc ('/', port);
212 scm_uintprint (SCM_SIMPLE_VECTOR_LENGTH (SCM_HASHTABLE_VECTOR (exp)),
213 10, port);
214 scm_puts (">", port);
215 return 1;
216 }
217
218 #define UNMARKED_CELL_P(x) (SCM_NIMP(x) && !SCM_GC_MARK_P (x))
219
220 /* keep track of hash tables that need to shrink after scan */
221 static SCM to_rehash = SCM_EOL;
222
223 /* scan hash tables and update hash tables item count */
224 void
225 scm_i_scan_weak_hashtables ()
226 {
227 SCM *next = &weak_hashtables;
228 SCM h = *next;
229 while (!scm_is_null (h))
230 {
231 if (!SCM_GC_MARK_P (h))
232 *next = h = SCM_HASHTABLE_NEXT (h);
233 else
234 {
235 SCM vec = SCM_HASHTABLE_VECTOR (h);
236 size_t delta = SCM_I_WVECT_DELTA (vec);
237 SCM_I_SET_WVECT_DELTA (vec, 0);
238 SCM_SET_HASHTABLE_N_ITEMS (h, SCM_HASHTABLE_N_ITEMS (h) - delta);
239
240 if (SCM_HASHTABLE_N_ITEMS (h) < SCM_HASHTABLE_LOWER (h))
241 {
242 SCM tmp = SCM_HASHTABLE_NEXT (h);
243 /* temporarily move table from weak_hashtables to to_rehash */
244 SCM_SET_HASHTABLE_NEXT (h, to_rehash);
245 to_rehash = h;
246 *next = h = tmp;
247 }
248 else
249 {
250 next = SCM_HASHTABLE_NEXTLOC (h);
251 h = SCM_HASHTABLE_NEXT (h);
252 }
253 }
254 }
255 }
256
257 static void *
258 rehash_after_gc (void *dummy1 SCM_UNUSED,
259 void *dummy2 SCM_UNUSED,
260 void *dummy3 SCM_UNUSED)
261 {
262 if (!scm_is_null (to_rehash))
263 {
264 SCM first = to_rehash, last, h;
265 /* important to clear to_rehash here so that we don't get stuck
266 in an infinite loop if scm_i_rehash causes GC */
267 to_rehash = SCM_EOL;
268 h = first;
269 do
270 {
271 /* Rehash only when we have a hash_fn.
272 */
273 if (SCM_HASHTABLE (h)->hash_fn)
274 scm_i_rehash (h, SCM_HASHTABLE (h)->hash_fn, NULL,
275 "rehash_after_gc");
276 last = h;
277 h = SCM_HASHTABLE_NEXT (h);
278 } while (!scm_is_null (h));
279 /* move tables back to weak_hashtables */
280 SCM_SET_HASHTABLE_NEXT (last, weak_hashtables);
281 weak_hashtables = first;
282 }
283 return 0;
284 }
285
286 static size_t
287 hashtable_free (SCM obj)
288 {
289 scm_gc_free (SCM_HASHTABLE (obj), sizeof (scm_t_hashtable), s_hashtable);
290 return 0;
291 }
292
293
294 SCM
295 scm_c_make_hash_table (unsigned long k)
296 {
297 return make_hash_table (0, k, "scm_c_make_hash_table");
298 }
299
300 SCM_DEFINE (scm_make_hash_table, "make-hash-table", 0, 1, 0,
301 (SCM n),
302 "Make a new abstract hash table object with minimum number of buckets @var{n}\n")
303 #define FUNC_NAME s_scm_make_hash_table
304 {
305 if (SCM_UNBNDP (n))
306 return make_hash_table (0, 0, FUNC_NAME);
307 else
308 return make_hash_table (0, scm_to_ulong (n), FUNC_NAME);
309 }
310 #undef FUNC_NAME
311
312 SCM_DEFINE (scm_make_weak_key_hash_table, "make-weak-key-hash-table", 0, 1, 0,
313 (SCM n),
314 "@deffnx {Scheme Procedure} make-weak-value-hash-table size\n"
315 "@deffnx {Scheme Procedure} make-doubly-weak-hash-table size\n"
316 "Return a weak hash table with @var{size} buckets.\n"
317 "\n"
318 "You can modify weak hash tables in exactly the same way you\n"
319 "would modify regular hash tables. (@pxref{Hash Tables})")
320 #define FUNC_NAME s_scm_make_weak_key_hash_table
321 {
322 if (SCM_UNBNDP (n))
323 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR, 0, FUNC_NAME);
324 else
325 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR,
326 scm_to_ulong (n), FUNC_NAME);
327 }
328 #undef FUNC_NAME
329
330
331 SCM_DEFINE (scm_make_weak_value_hash_table, "make-weak-value-hash-table", 0, 1, 0,
332 (SCM n),
333 "Return a hash table with weak values with @var{size} buckets.\n"
334 "(@pxref{Hash Tables})")
335 #define FUNC_NAME s_scm_make_weak_value_hash_table
336 {
337 if (SCM_UNBNDP (n))
338 return make_hash_table (SCM_HASHTABLEF_WEAK_CDR, 0, FUNC_NAME);
339 else
340 {
341 return make_hash_table (SCM_HASHTABLEF_WEAK_CDR,
342 scm_to_ulong (n), FUNC_NAME);
343 }
344 }
345 #undef FUNC_NAME
346
347
348 SCM_DEFINE (scm_make_doubly_weak_hash_table, "make-doubly-weak-hash-table", 1, 0, 0,
349 (SCM n),
350 "Return a hash table with weak keys and values with @var{size}\n"
351 "buckets. (@pxref{Hash Tables})")
352 #define FUNC_NAME s_scm_make_doubly_weak_hash_table
353 {
354 if (SCM_UNBNDP (n))
355 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
356 0,
357 FUNC_NAME);
358 else
359 {
360 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
361 scm_to_ulong (n),
362 FUNC_NAME);
363 }
364 }
365 #undef FUNC_NAME
366
367
368 SCM_DEFINE (scm_hash_table_p, "hash-table?", 1, 0, 0,
369 (SCM obj),
370 "Return @code{#t} if @var{obj} is an abstract hash table object.")
371 #define FUNC_NAME s_scm_hash_table_p
372 {
373 return scm_from_bool (SCM_HASHTABLE_P (obj));
374 }
375 #undef FUNC_NAME
376
377
378 SCM_DEFINE (scm_weak_key_hash_table_p, "weak-key-hash-table?", 1, 0, 0,
379 (SCM obj),
380 "@deffnx {Scheme Procedure} weak-value-hash-table? obj\n"
381 "@deffnx {Scheme Procedure} doubly-weak-hash-table? obj\n"
382 "Return @code{#t} if @var{obj} is the specified weak hash\n"
383 "table. Note that a doubly weak hash table is neither a weak key\n"
384 "nor a weak value hash table.")
385 #define FUNC_NAME s_scm_weak_key_hash_table_p
386 {
387 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_KEY_P (obj));
388 }
389 #undef FUNC_NAME
390
391
392 SCM_DEFINE (scm_weak_value_hash_table_p, "weak-value-hash-table?", 1, 0, 0,
393 (SCM obj),
394 "Return @code{#t} if @var{obj} is a weak value hash table.")
395 #define FUNC_NAME s_scm_weak_value_hash_table_p
396 {
397 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_VALUE_P (obj));
398 }
399 #undef FUNC_NAME
400
401
402 SCM_DEFINE (scm_doubly_weak_hash_table_p, "doubly-weak-hash-table?", 1, 0, 0,
403 (SCM obj),
404 "Return @code{#t} if @var{obj} is a doubly weak hash table.")
405 #define FUNC_NAME s_scm_doubly_weak_hash_table_p
406 {
407 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_DOUBLY_WEAK_P (obj));
408 }
409 #undef FUNC_NAME
410
411
412 SCM
413 scm_hash_fn_get_handle (SCM table, SCM obj, unsigned long (*hash_fn)(), SCM (*assoc_fn)(), void * closure)
414 #define FUNC_NAME "scm_hash_fn_get_handle"
415 {
416 unsigned long k;
417 SCM h;
418
419 if (SCM_HASHTABLE_P (table))
420 table = SCM_HASHTABLE_VECTOR (table);
421 else
422 SCM_VALIDATE_VECTOR (1, table);
423 if (SCM_SIMPLE_VECTOR_LENGTH (table) == 0)
424 return SCM_BOOL_F;
425 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (table), closure);
426 if (k >= SCM_SIMPLE_VECTOR_LENGTH (table))
427 scm_out_of_range ("hash_fn_get_handle", scm_from_ulong (k));
428 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (table, k), closure);
429 return h;
430 }
431 #undef FUNC_NAME
432
433
434 SCM
435 scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init, unsigned long (*hash_fn)(),
436 SCM (*assoc_fn)(), void * closure)
437 #define FUNC_NAME "scm_hash_fn_create_handle_x"
438 {
439 unsigned long k;
440 SCM buckets, it;
441
442 if (SCM_HASHTABLE_P (table))
443 buckets = SCM_HASHTABLE_VECTOR (table);
444 else
445 {
446 SCM_ASSERT (scm_is_simple_vector (table),
447 table, SCM_ARG1, "hash_fn_create_handle_x");
448 buckets = table;
449 }
450 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
451 SCM_MISC_ERROR ("void hashtable", SCM_EOL);
452
453 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
454 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
455 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
456 it = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
457 if (scm_is_true (it))
458 return it;
459 else
460 {
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.
466 */
467 SCM new_bucket = scm_acons (obj, init, SCM_EOL);
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 }
476 SCM_SETCDR (new_bucket, SCM_SIMPLE_VECTOR_REF (buckets, k));
477 SCM_SIMPLE_VECTOR_SET (buckets, k, new_bucket);
478 if (!scm_is_eq (table, buckets))
479 {
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 */
485 SCM_HASHTABLE_INCREMENT (table);
486 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table)
487 || SCM_HASHTABLE_N_ITEMS (table) > SCM_HASHTABLE_UPPER (table))
488 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
489 }
490 return SCM_CAR (new_bucket);
491 }
492 }
493 #undef FUNC_NAME
494
495
496 SCM
497 scm_hash_fn_ref (SCM table, SCM obj, SCM dflt, unsigned long (*hash_fn)(),
498 SCM (*assoc_fn)(), void * closure)
499 {
500 SCM it = scm_hash_fn_get_handle (table, obj, hash_fn, assoc_fn, closure);
501 if (scm_is_pair (it))
502 return SCM_CDR (it);
503 else
504 return dflt;
505 }
506
507
508
509
510 SCM
511 scm_hash_fn_set_x (SCM table, SCM obj, SCM val, unsigned long (*hash_fn)(),
512 SCM (*assoc_fn)(), void * closure)
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
522 SCM
523 scm_hash_fn_remove_x (SCM table, SCM obj,
524 unsigned long (*hash_fn)(),
525 SCM (*assoc_fn)(),
526 void *closure)
527 {
528 unsigned long k;
529 SCM buckets, h;
530
531 if (SCM_HASHTABLE_P (table))
532 buckets = SCM_HASHTABLE_VECTOR (table);
533 else
534 {
535 SCM_ASSERT (scm_is_simple_vector (table), table,
536 SCM_ARG1, "hash_fn_remove_x");
537 buckets = table;
538 }
539 if (SCM_SIMPLE_VECTOR_LENGTH (table) == 0)
540 return SCM_EOL;
541
542 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
543 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
544 scm_out_of_range ("hash_fn_remove_x", scm_from_ulong (k));
545 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
546 if (scm_is_true (h))
547 {
548 SCM_SIMPLE_VECTOR_SET
549 (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
550 if (!scm_is_eq (table, buckets))
551 {
552 SCM_HASHTABLE_DECREMENT (table);
553 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
554 scm_i_rehash (table, hash_fn, closure, "scm_hash_fn_remove_x");
555 }
556 }
557 return h;
558 }
559
560 SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
561 (SCM table),
562 "Remove all items from @var{table} (without triggering a resize).")
563 #define FUNC_NAME s_scm_hash_clear_x
564 {
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);
572 return SCM_UNSPECIFIED;
573 }
574 #undef FUNC_NAME
575
576 \f
577
578 SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
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.")
584 #define FUNC_NAME s_scm_hashq_get_handle
585 {
586 return scm_hash_fn_get_handle (table, key, scm_ihashq, scm_sloppy_assq, 0);
587 }
588 #undef FUNC_NAME
589
590
591 SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
592 (SCM table, SCM key, SCM init),
593 "This function looks up @var{key} in @var{table} and returns its handle.\n"
594 "If @var{key} is not already present, a new handle is created which\n"
595 "associates @var{key} with @var{init}.")
596 #define FUNC_NAME s_scm_hashq_create_handle_x
597 {
598 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashq, scm_sloppy_assq, 0);
599 }
600 #undef FUNC_NAME
601
602
603 SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
604 (SCM table, SCM key, SCM dflt),
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"
607 "return @var{default} (or @code{#f} if no @var{default} argument\n"
608 "is supplied). Uses @code{eq?} for equality testing.")
609 #define FUNC_NAME s_scm_hashq_ref
610 {
611 if (SCM_UNBNDP (dflt))
612 dflt = SCM_BOOL_F;
613 return scm_hash_fn_ref (table, key, dflt, scm_ihashq, scm_sloppy_assq, 0);
614 }
615 #undef FUNC_NAME
616
617
618
619 SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
620 (SCM table, SCM key, SCM val),
621 "Find the entry in @var{table} associated with @var{key}, and\n"
622 "store @var{value} there. Uses @code{eq?} for equality testing.")
623 #define FUNC_NAME s_scm_hashq_set_x
624 {
625 return scm_hash_fn_set_x (table, key, val, scm_ihashq, scm_sloppy_assq, 0);
626 }
627 #undef FUNC_NAME
628
629
630
631 SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
632 (SCM table, SCM key),
633 "Remove @var{key} (and any value associated with it) from\n"
634 "@var{table}. Uses @code{eq?} for equality tests.")
635 #define FUNC_NAME s_scm_hashq_remove_x
636 {
637 return scm_hash_fn_remove_x (table, key, scm_ihashq, scm_sloppy_assq, 0);
638 }
639 #undef FUNC_NAME
640
641
642 \f
643
644 SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
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.")
650 #define FUNC_NAME s_scm_hashv_get_handle
651 {
652 return scm_hash_fn_get_handle (table, key, scm_ihashv, scm_sloppy_assv, 0);
653 }
654 #undef FUNC_NAME
655
656
657 SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
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}.")
662 #define FUNC_NAME s_scm_hashv_create_handle_x
663 {
664 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashv,
665 scm_sloppy_assv, 0);
666 }
667 #undef FUNC_NAME
668
669
670 SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
671 (SCM table, SCM key, SCM dflt),
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"
674 "return @var{default} (or @code{#f} if no @var{default} argument\n"
675 "is supplied). Uses @code{eqv?} for equality testing.")
676 #define FUNC_NAME s_scm_hashv_ref
677 {
678 if (SCM_UNBNDP (dflt))
679 dflt = SCM_BOOL_F;
680 return scm_hash_fn_ref (table, key, dflt, scm_ihashv, scm_sloppy_assv, 0);
681 }
682 #undef FUNC_NAME
683
684
685
686 SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
687 (SCM table, SCM key, SCM val),
688 "Find the entry in @var{table} associated with @var{key}, and\n"
689 "store @var{value} there. Uses @code{eqv?} for equality testing.")
690 #define FUNC_NAME s_scm_hashv_set_x
691 {
692 return scm_hash_fn_set_x (table, key, val, scm_ihashv, scm_sloppy_assv, 0);
693 }
694 #undef FUNC_NAME
695
696
697 SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
698 (SCM table, SCM key),
699 "Remove @var{key} (and any value associated with it) from\n"
700 "@var{table}. Uses @code{eqv?} for equality tests.")
701 #define FUNC_NAME s_scm_hashv_remove_x
702 {
703 return scm_hash_fn_remove_x (table, key, scm_ihashv, scm_sloppy_assv, 0);
704 }
705 #undef FUNC_NAME
706
707 \f
708
709 SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
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.")
715 #define FUNC_NAME s_scm_hash_get_handle
716 {
717 return scm_hash_fn_get_handle (table, key, scm_ihash, scm_sloppy_assoc, 0);
718 }
719 #undef FUNC_NAME
720
721
722 SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
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}.")
727 #define FUNC_NAME s_scm_hash_create_handle_x
728 {
729 return scm_hash_fn_create_handle_x (table, key, init, scm_ihash, scm_sloppy_assoc, 0);
730 }
731 #undef FUNC_NAME
732
733
734 SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
735 (SCM table, SCM key, SCM dflt),
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"
738 "return @var{default} (or @code{#f} if no @var{default} argument\n"
739 "is supplied). Uses @code{equal?} for equality testing.")
740 #define FUNC_NAME s_scm_hash_ref
741 {
742 if (SCM_UNBNDP (dflt))
743 dflt = SCM_BOOL_F;
744 return scm_hash_fn_ref (table, key, dflt, scm_ihash, scm_sloppy_assoc, 0);
745 }
746 #undef FUNC_NAME
747
748
749
750 SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
751 (SCM table, SCM key, SCM val),
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.")
755 #define FUNC_NAME s_scm_hash_set_x
756 {
757 return scm_hash_fn_set_x (table, key, val, scm_ihash, scm_sloppy_assoc, 0);
758 }
759 #undef FUNC_NAME
760
761
762
763 SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
764 (SCM table, SCM key),
765 "Remove @var{key} (and any value associated with it) from\n"
766 "@var{table}. Uses @code{equal?} for equality tests.")
767 #define FUNC_NAME s_scm_hash_remove_x
768 {
769 return scm_hash_fn_remove_x (table, key, scm_ihash, scm_sloppy_assoc, 0);
770 }
771 #undef FUNC_NAME
772
773 \f
774
775
776 typedef struct scm_t_ihashx_closure
777 {
778 SCM hash;
779 SCM assoc;
780 } scm_t_ihashx_closure;
781
782
783
784 static unsigned long
785 scm_ihashx (SCM obj, unsigned long n, scm_t_ihashx_closure *closure)
786 {
787 SCM answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
788 return scm_to_ulong (answer);
789 }
790
791
792
793 static SCM
794 scm_sloppy_assx (SCM obj, SCM alist, scm_t_ihashx_closure *closure)
795 {
796 return scm_call_2 (closure->assoc, obj, alist);
797 }
798
799
800 SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
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"
806 "table size. @code{assoc} must be an associator function, like\n"
807 "@code{assoc}, @code{assq} or @code{assv}.")
808 #define FUNC_NAME s_scm_hashx_get_handle
809 {
810 scm_t_ihashx_closure closure;
811 closure.hash = hash;
812 closure.assoc = assoc;
813 return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
814 (void *) &closure);
815 }
816 #undef FUNC_NAME
817
818
819 SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
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"
825 "table size. @code{assoc} must be an associator function, like\n"
826 "@code{assoc}, @code{assq} or @code{assv}.")
827 #define FUNC_NAME s_scm_hashx_create_handle_x
828 {
829 scm_t_ihashx_closure closure;
830 closure.hash = hash;
831 closure.assoc = assoc;
832 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
833 scm_sloppy_assx, (void *)&closure);
834 }
835 #undef FUNC_NAME
836
837
838
839 SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
840 (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
841 "This behaves the same way as the corresponding @code{ref}\n"
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}.")
850 #define FUNC_NAME s_scm_hashx_ref
851 {
852 scm_t_ihashx_closure closure;
853 if (SCM_UNBNDP (dflt))
854 dflt = SCM_BOOL_F;
855 closure.hash = hash;
856 closure.assoc = assoc;
857 return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
858 (void *)&closure);
859 }
860 #undef FUNC_NAME
861
862
863
864
865 SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
866 (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
867 "This behaves the same way as the corresponding @code{set!}\n"
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}.")
876 #define FUNC_NAME s_scm_hashx_set_x
877 {
878 scm_t_ihashx_closure closure;
879 closure.hash = hash;
880 closure.assoc = assoc;
881 return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
882 (void *)&closure);
883 }
884 #undef FUNC_NAME
885
886 SCM_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
898 {
899 scm_t_ihashx_closure closure;
900 closure.hash = hash;
901 closure.assoc = assoc;
902 return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
903 (void *) &closure);
904 }
905 #undef FUNC_NAME
906
907 /* Hash table iterators */
908
909 static const char s_scm_hash_fold[];
910
911 SCM
912 scm_internal_hash_fold (SCM (*fn) (), void *closure, SCM init, SCM table)
913 {
914 long i, n;
915 SCM buckets, result = init;
916
917 if (SCM_HASHTABLE_P (table))
918 buckets = SCM_HASHTABLE_VECTOR (table);
919 else
920 buckets = table;
921
922 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
923 for (i = 0; i < n; ++i)
924 {
925 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
926 while (!scm_is_null (ls))
927 {
928 if (!scm_is_pair (ls))
929 scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
930 handle = SCM_CAR (ls);
931 if (!scm_is_pair (handle))
932 scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
933 result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
934 ls = SCM_CDR (ls);
935 }
936 }
937
938 return result;
939 }
940
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
947 static const char s_scm_hash_for_each[];
948
949 void
950 scm_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
960 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
961 for (i = 0; i < n; ++i)
962 {
963 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
964 while (!scm_is_null (ls))
965 {
966 if (!scm_is_pair (ls))
967 scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
968 handle = SCM_CAR (ls);
969 if (!scm_is_pair (handle))
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
977 SCM_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
996 static SCM
997 for_each_proc (void *proc, SCM handle)
998 {
999 return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
1000 }
1001
1002 SCM_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);
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
1021 SCM_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);
1035 return SCM_UNSPECIFIED;
1036 }
1037 #undef FUNC_NAME
1038
1039 static SCM
1040 map_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
1045 SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
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.")
1051 #define FUNC_NAME s_scm_hash_map_to_list
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
1063 \f
1064
1065
1066 void
1067 scm_hashtab_prehistory ()
1068 {
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);
1073 scm_c_hook_add (&scm_after_gc_c_hook, rehash_after_gc, 0, 0);
1074 }
1075
1076 void
1077 scm_init_hashtab ()
1078 {
1079 #include "libguile/hashtab.x"
1080 }
1081
1082 /*
1083 Local Variables:
1084 c-file-style: "gnu"
1085 End:
1086 */