Remove unused macro UNMARKED_CELL_P()
[bpt/guile.git] / libguile / hashtab.c
1 /* Copyright (C) 1995,1996,1998,1999,2000,2001, 2003, 2004, 2006, 2008 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 /* keep track of hash tables that need to shrink after scan */
219 static SCM to_rehash = SCM_EOL;
220
221 /* scan hash tables and update hash tables item count */
222 void
223 scm_i_scan_weak_hashtables ()
224 {
225 SCM *next = &weak_hashtables;
226 SCM h = *next;
227 while (!scm_is_null (h))
228 {
229 if (!SCM_GC_MARK_P (h))
230 *next = h = SCM_HASHTABLE_NEXT (h);
231 else
232 {
233 SCM vec = SCM_HASHTABLE_VECTOR (h);
234 size_t delta = SCM_I_WVECT_DELTA (vec);
235 SCM_I_SET_WVECT_DELTA (vec, 0);
236 SCM_SET_HASHTABLE_N_ITEMS (h, SCM_HASHTABLE_N_ITEMS (h) - delta);
237
238 if (SCM_HASHTABLE_N_ITEMS (h) < SCM_HASHTABLE_LOWER (h))
239 {
240 SCM tmp = SCM_HASHTABLE_NEXT (h);
241 /* temporarily move table from weak_hashtables to to_rehash */
242 SCM_SET_HASHTABLE_NEXT (h, to_rehash);
243 to_rehash = h;
244 *next = h = tmp;
245 }
246 else
247 {
248 next = SCM_HASHTABLE_NEXTLOC (h);
249 h = SCM_HASHTABLE_NEXT (h);
250 }
251 }
252 }
253 }
254
255 static void *
256 rehash_after_gc (void *dummy1 SCM_UNUSED,
257 void *dummy2 SCM_UNUSED,
258 void *dummy3 SCM_UNUSED)
259 {
260 if (!scm_is_null (to_rehash))
261 {
262 SCM first = to_rehash, last, h;
263 /* important to clear to_rehash here so that we don't get stuck
264 in an infinite loop if scm_i_rehash causes GC */
265 to_rehash = SCM_EOL;
266 h = first;
267 do
268 {
269 /* Rehash only when we have a hash_fn.
270 */
271 if (SCM_HASHTABLE (h)->hash_fn)
272 scm_i_rehash (h, SCM_HASHTABLE (h)->hash_fn, NULL,
273 "rehash_after_gc");
274 last = h;
275 h = SCM_HASHTABLE_NEXT (h);
276 } while (!scm_is_null (h));
277 /* move tables back to weak_hashtables */
278 SCM_SET_HASHTABLE_NEXT (last, weak_hashtables);
279 weak_hashtables = first;
280 }
281 return 0;
282 }
283
284 static size_t
285 hashtable_free (SCM obj)
286 {
287 scm_gc_free (SCM_HASHTABLE (obj), sizeof (scm_t_hashtable), s_hashtable);
288 return 0;
289 }
290
291
292 SCM
293 scm_c_make_hash_table (unsigned long k)
294 {
295 return make_hash_table (0, k, "scm_c_make_hash_table");
296 }
297
298 SCM_DEFINE (scm_make_hash_table, "make-hash-table", 0, 1, 0,
299 (SCM n),
300 "Make a new abstract hash table object with minimum number of buckets @var{n}\n")
301 #define FUNC_NAME s_scm_make_hash_table
302 {
303 if (SCM_UNBNDP (n))
304 return make_hash_table (0, 0, FUNC_NAME);
305 else
306 return make_hash_table (0, scm_to_ulong (n), FUNC_NAME);
307 }
308 #undef FUNC_NAME
309
310 SCM_DEFINE (scm_make_weak_key_hash_table, "make-weak-key-hash-table", 0, 1, 0,
311 (SCM n),
312 "@deffnx {Scheme Procedure} make-weak-value-hash-table size\n"
313 "@deffnx {Scheme Procedure} make-doubly-weak-hash-table size\n"
314 "Return a weak hash table with @var{size} buckets.\n"
315 "\n"
316 "You can modify weak hash tables in exactly the same way you\n"
317 "would modify regular hash tables. (@pxref{Hash Tables})")
318 #define FUNC_NAME s_scm_make_weak_key_hash_table
319 {
320 if (SCM_UNBNDP (n))
321 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR, 0, FUNC_NAME);
322 else
323 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR,
324 scm_to_ulong (n), FUNC_NAME);
325 }
326 #undef FUNC_NAME
327
328
329 SCM_DEFINE (scm_make_weak_value_hash_table, "make-weak-value-hash-table", 0, 1, 0,
330 (SCM n),
331 "Return a hash table with weak values with @var{size} buckets.\n"
332 "(@pxref{Hash Tables})")
333 #define FUNC_NAME s_scm_make_weak_value_hash_table
334 {
335 if (SCM_UNBNDP (n))
336 return make_hash_table (SCM_HASHTABLEF_WEAK_CDR, 0, FUNC_NAME);
337 else
338 {
339 return make_hash_table (SCM_HASHTABLEF_WEAK_CDR,
340 scm_to_ulong (n), FUNC_NAME);
341 }
342 }
343 #undef FUNC_NAME
344
345
346 SCM_DEFINE (scm_make_doubly_weak_hash_table, "make-doubly-weak-hash-table", 1, 0, 0,
347 (SCM n),
348 "Return a hash table with weak keys and values with @var{size}\n"
349 "buckets. (@pxref{Hash Tables})")
350 #define FUNC_NAME s_scm_make_doubly_weak_hash_table
351 {
352 if (SCM_UNBNDP (n))
353 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
354 0,
355 FUNC_NAME);
356 else
357 {
358 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
359 scm_to_ulong (n),
360 FUNC_NAME);
361 }
362 }
363 #undef FUNC_NAME
364
365
366 SCM_DEFINE (scm_hash_table_p, "hash-table?", 1, 0, 0,
367 (SCM obj),
368 "Return @code{#t} if @var{obj} is an abstract hash table object.")
369 #define FUNC_NAME s_scm_hash_table_p
370 {
371 return scm_from_bool (SCM_HASHTABLE_P (obj));
372 }
373 #undef FUNC_NAME
374
375
376 SCM_DEFINE (scm_weak_key_hash_table_p, "weak-key-hash-table?", 1, 0, 0,
377 (SCM obj),
378 "@deffnx {Scheme Procedure} weak-value-hash-table? obj\n"
379 "@deffnx {Scheme Procedure} doubly-weak-hash-table? obj\n"
380 "Return @code{#t} if @var{obj} is the specified weak hash\n"
381 "table. Note that a doubly weak hash table is neither a weak key\n"
382 "nor a weak value hash table.")
383 #define FUNC_NAME s_scm_weak_key_hash_table_p
384 {
385 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_KEY_P (obj));
386 }
387 #undef FUNC_NAME
388
389
390 SCM_DEFINE (scm_weak_value_hash_table_p, "weak-value-hash-table?", 1, 0, 0,
391 (SCM obj),
392 "Return @code{#t} if @var{obj} is a weak value hash table.")
393 #define FUNC_NAME s_scm_weak_value_hash_table_p
394 {
395 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_VALUE_P (obj));
396 }
397 #undef FUNC_NAME
398
399
400 SCM_DEFINE (scm_doubly_weak_hash_table_p, "doubly-weak-hash-table?", 1, 0, 0,
401 (SCM obj),
402 "Return @code{#t} if @var{obj} is a doubly weak hash table.")
403 #define FUNC_NAME s_scm_doubly_weak_hash_table_p
404 {
405 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_DOUBLY_WEAK_P (obj));
406 }
407 #undef FUNC_NAME
408
409
410 SCM
411 scm_hash_fn_get_handle (SCM table, SCM obj, unsigned long (*hash_fn)(), SCM (*assoc_fn)(), void * closure)
412 #define FUNC_NAME "scm_hash_fn_get_handle"
413 {
414 unsigned long k;
415 SCM h;
416
417 if (SCM_HASHTABLE_P (table))
418 table = SCM_HASHTABLE_VECTOR (table);
419 else
420 SCM_VALIDATE_VECTOR (1, table);
421 if (SCM_SIMPLE_VECTOR_LENGTH (table) == 0)
422 return SCM_BOOL_F;
423 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (table), closure);
424 if (k >= SCM_SIMPLE_VECTOR_LENGTH (table))
425 scm_out_of_range ("hash_fn_get_handle", scm_from_ulong (k));
426 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (table, k), closure);
427 return h;
428 }
429 #undef FUNC_NAME
430
431
432 SCM
433 scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init, unsigned long (*hash_fn)(),
434 SCM (*assoc_fn)(), void * closure)
435 #define FUNC_NAME "scm_hash_fn_create_handle_x"
436 {
437 unsigned long k;
438 SCM buckets, it;
439
440 if (SCM_HASHTABLE_P (table))
441 buckets = SCM_HASHTABLE_VECTOR (table);
442 else
443 {
444 SCM_ASSERT (scm_is_simple_vector (table),
445 table, SCM_ARG1, "hash_fn_create_handle_x");
446 buckets = table;
447 }
448 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
449 SCM_MISC_ERROR ("void hashtable", SCM_EOL);
450
451 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
452 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
453 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
454 it = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
455 if (scm_is_pair (it))
456 return it;
457 else if (scm_is_true (it))
458 scm_wrong_type_arg_msg (NULL, 0, it, "a pair");
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 */