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