85e4bb0abb342e8ec566046c8c611483aa70dcb0
[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 #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_pair (it))
458 return it;
459 else if (scm_is_true (it))
460 scm_wrong_type_arg_msg (NULL, 0, it, "a pair");
461 else
462 {
463 /* When this is a weak hashtable, running the GC can change it.
464 Thus, we must allocate the new cells first and can only then
465 access BUCKETS. Also, we need to fetch the bucket vector
466 again since the hashtable might have been rehashed. This
467 necessitates a new hash value as well.
468 */
469 SCM new_bucket = scm_acons (obj, init, SCM_EOL);
470 if (!scm_is_eq (table, buckets)
471 && !scm_is_eq (SCM_HASHTABLE_VECTOR (table), buckets))
472 {
473 buckets = SCM_HASHTABLE_VECTOR (table);
474 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
475 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
476 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
477 }
478 SCM_SETCDR (new_bucket, SCM_SIMPLE_VECTOR_REF (buckets, k));
479 SCM_SIMPLE_VECTOR_SET (buckets, k, new_bucket);
480 if (!scm_is_eq (table, buckets))
481 {
482 /* Update element count and maybe rehash the table. The
483 table might have too few entries here since weak hash
484 tables used with the hashx_* functions can not be
485 rehashed after GC.
486 */
487 SCM_HASHTABLE_INCREMENT (table);
488 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table)
489 || SCM_HASHTABLE_N_ITEMS (table) > SCM_HASHTABLE_UPPER (table))
490 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
491 }
492 return SCM_CAR (new_bucket);
493 }
494 }
495 #undef FUNC_NAME
496
497
498 SCM
499 scm_hash_fn_ref (SCM table, SCM obj, SCM dflt, unsigned long (*hash_fn)(),
500 SCM (*assoc_fn)(), void * closure)
501 {
502 SCM it = scm_hash_fn_get_handle (table, obj, hash_fn, assoc_fn, closure);
503 if (scm_is_pair (it))
504 return SCM_CDR (it);
505 else
506 return dflt;
507 }
508
509
510
511
512 SCM
513 scm_hash_fn_set_x (SCM table, SCM obj, SCM val, unsigned long (*hash_fn)(),
514 SCM (*assoc_fn)(), void * closure)
515 {
516 SCM it;
517
518 it = scm_hash_fn_create_handle_x (table, obj, SCM_BOOL_F, hash_fn, assoc_fn, closure);
519 SCM_SETCDR (it, val);
520 return val;
521 }
522
523
524 SCM
525 scm_hash_fn_remove_x (SCM table, SCM obj,
526 unsigned long (*hash_fn)(),
527 SCM (*assoc_fn)(),
528 void *closure)
529 {
530 unsigned long k;
531 SCM buckets, h;
532
533 if (SCM_HASHTABLE_P (table))
534 buckets = SCM_HASHTABLE_VECTOR (table);
535 else
536 {
537 SCM_ASSERT (scm_is_simple_vector (table), table,
538 SCM_ARG1, "hash_fn_remove_x");
539 buckets = table;
540 }
541 if (SCM_SIMPLE_VECTOR_LENGTH (table) == 0)
542 return SCM_EOL;
543
544 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
545 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
546 scm_out_of_range ("hash_fn_remove_x", scm_from_ulong (k));
547 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
548 if (scm_is_true (h))
549 {
550 SCM_SIMPLE_VECTOR_SET
551 (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
552 if (!scm_is_eq (table, buckets))
553 {
554 SCM_HASHTABLE_DECREMENT (table);
555 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
556 scm_i_rehash (table, hash_fn, closure, "scm_hash_fn_remove_x");
557 }
558 }
559 return h;
560 }
561
562 SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
563 (SCM table),
564 "Remove all items from @var{table} (without triggering a resize).")
565 #define FUNC_NAME s_scm_hash_clear_x
566 {
567 if (SCM_HASHTABLE_P (table))
568 {
569 scm_vector_fill_x (SCM_HASHTABLE_VECTOR (table), SCM_EOL);
570 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
571 }
572 else
573 scm_vector_fill_x (table, SCM_EOL);
574 return SCM_UNSPECIFIED;
575 }
576 #undef FUNC_NAME
577
578 \f
579
580 SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
581 (SCM table, SCM key),
582 "This procedure returns the @code{(key . value)} pair from the\n"
583 "hash table @var{table}. If @var{table} does not hold an\n"
584 "associated value for @var{key}, @code{#f} is returned.\n"
585 "Uses @code{eq?} for equality testing.")
586 #define FUNC_NAME s_scm_hashq_get_handle
587 {
588 return scm_hash_fn_get_handle (table, key, scm_ihashq, scm_sloppy_assq, 0);
589 }
590 #undef FUNC_NAME
591
592
593 SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
594 (SCM table, SCM key, SCM init),
595 "This function looks up @var{key} in @var{table} and returns its handle.\n"
596 "If @var{key} is not already present, a new handle is created which\n"
597 "associates @var{key} with @var{init}.")
598 #define FUNC_NAME s_scm_hashq_create_handle_x
599 {
600 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashq, scm_sloppy_assq, 0);
601 }
602 #undef FUNC_NAME
603
604
605 SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
606 (SCM table, SCM key, SCM dflt),
607 "Look up @var{key} in the hash table @var{table}, and return the\n"
608 "value (if any) associated with it. If @var{key} is not found,\n"
609 "return @var{default} (or @code{#f} if no @var{default} argument\n"
610 "is supplied). Uses @code{eq?} for equality testing.")
611 #define FUNC_NAME s_scm_hashq_ref
612 {
613 if (SCM_UNBNDP (dflt))
614 dflt = SCM_BOOL_F;
615 return scm_hash_fn_ref (table, key, dflt, scm_ihashq, scm_sloppy_assq, 0);
616 }
617 #undef FUNC_NAME
618
619
620
621 SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
622 (SCM table, SCM key, SCM val),
623 "Find the entry in @var{table} associated with @var{key}, and\n"
624 "store @var{value} there. Uses @code{eq?} for equality testing.")
625 #define FUNC_NAME s_scm_hashq_set_x
626 {
627 return scm_hash_fn_set_x (table, key, val, scm_ihashq, scm_sloppy_assq, 0);
628 }
629 #undef FUNC_NAME
630
631
632
633 SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
634 (SCM table, SCM key),
635 "Remove @var{key} (and any value associated with it) from\n"
636 "@var{table}. Uses @code{eq?} for equality tests.")
637 #define FUNC_NAME s_scm_hashq_remove_x
638 {
639 return scm_hash_fn_remove_x (table, key, scm_ihashq, scm_sloppy_assq, 0);
640 }
641 #undef FUNC_NAME
642
643
644 \f
645
646 SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
647 (SCM table, SCM key),
648 "This procedure returns the @code{(key . value)} pair from the\n"
649 "hash table @var{table}. If @var{table} does not hold an\n"
650 "associated value for @var{key}, @code{#f} is returned.\n"
651 "Uses @code{eqv?} for equality testing.")
652 #define FUNC_NAME s_scm_hashv_get_handle
653 {
654 return scm_hash_fn_get_handle (table, key, scm_ihashv, scm_sloppy_assv, 0);
655 }
656 #undef FUNC_NAME
657
658
659 SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
660 (SCM table, SCM key, SCM init),
661 "This function looks up @var{key} in @var{table} and returns its handle.\n"
662 "If @var{key} is not already present, a new handle is created which\n"
663 "associates @var{key} with @var{init}.")
664 #define FUNC_NAME s_scm_hashv_create_handle_x
665 {
666 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashv,
667 scm_sloppy_assv, 0);
668 }
669 #undef FUNC_NAME
670
671
672 SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
673 (SCM table, SCM key, SCM dflt),
674 "Look up @var{key} in the hash table @var{table}, and return the\n"
675 "value (if any) associated with it. If @var{key} is not found,\n"
676 "return @var{default} (or @code{#f} if no @var{default} argument\n"
677 "is supplied). Uses @code{eqv?} for equality testing.")
678 #define FUNC_NAME s_scm_hashv_ref
679 {
680 if (SCM_UNBNDP (dflt))
681 dflt = SCM_BOOL_F;
682 return scm_hash_fn_ref (table, key, dflt, scm_ihashv, scm_sloppy_assv, 0);
683 }
684 #undef FUNC_NAME
685
686
687
688 SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
689 (SCM table, SCM key, SCM val),
690 "Find the entry in @var{table} associated with @var{key}, and\n"
691 "store @var{value} there. Uses @code{eqv?} for equality testing.")
692 #define FUNC_NAME s_scm_hashv_set_x
693 {
694 return scm_hash_fn_set_x (table, key, val, scm_ihashv, scm_sloppy_assv, 0);
695 }
696 #undef FUNC_NAME
697
698
699 SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
700 (SCM table, SCM key),
701 "Remove @var{key} (and any value associated with it) from\n"
702 "@var{table}. Uses @code{eqv?} for equality tests.")
703 #define FUNC_NAME s_scm_hashv_remove_x
704 {
705 return scm_hash_fn_remove_x (table, key, scm_ihashv, scm_sloppy_assv, 0);
706 }
707 #undef FUNC_NAME
708
709 \f
710
711 SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
712 (SCM table, SCM key),
713 "This procedure returns the @code{(key . value)} pair from the\n"
714 "hash table @var{table}. If @var{table} does not hold an\n"
715 "associated value for @var{key}, @code{#f} is returned.\n"
716 "Uses @code{equal?} for equality testing.")
717 #define FUNC_NAME s_scm_hash_get_handle
718 {
719 return scm_hash_fn_get_handle (table, key, scm_ihash, scm_sloppy_assoc, 0);
720 }
721 #undef FUNC_NAME
722
723
724 SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
725 (SCM table, SCM key, SCM init),
726 "This function looks up @var{key} in @var{table} and returns its handle.\n"
727 "If @var{key} is not already present, a new handle is created which\n"
728 "associates @var{key} with @var{init}.")
729 #define FUNC_NAME s_scm_hash_create_handle_x
730 {
731 return scm_hash_fn_create_handle_x (table, key, init, scm_ihash, scm_sloppy_assoc, 0);
732 }
733 #undef FUNC_NAME
734
735
736 SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
737 (SCM table, SCM key, SCM dflt),
738 "Look up @var{key} in the hash table @var{table}, and return the\n"
739 "value (if any) associated with it. If @var{key} is not found,\n"
740 "return @var{default} (or @code{#f} if no @var{default} argument\n"
741 "is supplied). Uses @code{equal?} for equality testing.")
742 #define FUNC_NAME s_scm_hash_ref
743 {
744 if (SCM_UNBNDP (dflt))
745 dflt = SCM_BOOL_F;
746 return scm_hash_fn_ref (table, key, dflt, scm_ihash, scm_sloppy_assoc, 0);
747 }
748 #undef FUNC_NAME
749
750
751
752 SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
753 (SCM table, SCM key, SCM val),
754 "Find the entry in @var{table} associated with @var{key}, and\n"
755 "store @var{value} there. Uses @code{equal?} for equality\n"
756 "testing.")
757 #define FUNC_NAME s_scm_hash_set_x
758 {
759 return scm_hash_fn_set_x (table, key, val, scm_ihash, scm_sloppy_assoc, 0);
760 }
761 #undef FUNC_NAME
762
763
764
765 SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
766 (SCM table, SCM key),
767 "Remove @var{key} (and any value associated with it) from\n"
768 "@var{table}. Uses @code{equal?} for equality tests.")
769 #define FUNC_NAME s_scm_hash_remove_x
770 {
771 return scm_hash_fn_remove_x (table, key, scm_ihash, scm_sloppy_assoc, 0);
772 }
773 #undef FUNC_NAME
774
775 \f
776
777
778 typedef struct scm_t_ihashx_closure
779 {
780 SCM hash;
781 SCM assoc;
782 } scm_t_ihashx_closure;
783
784
785
786 static unsigned long
787 scm_ihashx (SCM obj, unsigned long n, scm_t_ihashx_closure *closure)
788 {
789 SCM answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
790 return scm_to_ulong (answer);
791 }
792
793
794
795 static SCM
796 scm_sloppy_assx (SCM obj, SCM alist, scm_t_ihashx_closure *closure)
797 {
798 return scm_call_2 (closure->assoc, obj, alist);
799 }
800
801
802 SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
803 (SCM hash, SCM assoc, SCM table, SCM key),
804 "This behaves the same way as the corresponding\n"
805 "@code{-get-handle} function, but uses @var{hash} as a hash\n"
806 "function and @var{assoc} to compare keys. @code{hash} must be\n"
807 "a function that takes two arguments, a key to be hashed and a\n"
808 "table size. @code{assoc} must be an associator function, like\n"
809 "@code{assoc}, @code{assq} or @code{assv}.")
810 #define FUNC_NAME s_scm_hashx_get_handle
811 {
812 scm_t_ihashx_closure closure;
813 closure.hash = hash;
814 closure.assoc = assoc;
815 return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
816 (void *) &closure);
817 }
818 #undef FUNC_NAME
819
820
821 SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
822 (SCM hash, SCM assoc, SCM table, SCM key, SCM init),
823 "This behaves the same way as the corresponding\n"
824 "@code{-create-handle} function, but uses @var{hash} as a hash\n"
825 "function and @var{assoc} to compare keys. @code{hash} must be\n"
826 "a function that takes two arguments, a key to be hashed and a\n"
827 "table size. @code{assoc} must be an associator function, like\n"
828 "@code{assoc}, @code{assq} or @code{assv}.")
829 #define FUNC_NAME s_scm_hashx_create_handle_x
830 {
831 scm_t_ihashx_closure closure;
832 closure.hash = hash;
833 closure.assoc = assoc;
834 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
835 scm_sloppy_assx, (void *)&closure);
836 }
837 #undef FUNC_NAME
838
839
840
841 SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
842 (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
843 "This behaves the same way as the corresponding @code{ref}\n"
844 "function, but uses @var{hash} as a hash function and\n"
845 "@var{assoc} to compare keys. @code{hash} must be a function\n"
846 "that takes two arguments, a key to be hashed and a table size.\n"
847 "@code{assoc} must be an associator function, like @code{assoc},\n"
848 "@code{assq} or @code{assv}.\n"
849 "\n"
850 "By way of illustration, @code{hashq-ref table key} is\n"
851 "equivalent to @code{hashx-ref hashq assq table key}.")
852 #define FUNC_NAME s_scm_hashx_ref
853 {
854 scm_t_ihashx_closure closure;
855 if (SCM_UNBNDP (dflt))
856 dflt = SCM_BOOL_F;
857 closure.hash = hash;
858 closure.assoc = assoc;
859 return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
860 (void *)&closure);
861 }
862 #undef FUNC_NAME
863
864
865
866
867 SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
868 (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
869 "This behaves the same way as the corresponding @code{set!}\n"
870 "function, but uses @var{hash} as a hash function and\n"
871 "@var{assoc} to compare keys. @code{hash} must be a function\n"
872 "that takes two arguments, a key to be hashed and a table size.\n"
873 "@code{assoc} must be an associator function, like @code{assoc},\n"
874 "@code{assq} or @code{assv}.\n"
875 "\n"
876 " By way of illustration, @code{hashq-set! table key} is\n"
877 "equivalent to @code{hashx-set! hashq assq table key}.")
878 #define FUNC_NAME s_scm_hashx_set_x
879 {
880 scm_t_ihashx_closure closure;
881 closure.hash = hash;
882 closure.assoc = assoc;
883 return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
884 (void *)&closure);
885 }
886 #undef FUNC_NAME
887
888 SCM_DEFINE (scm_hashx_remove_x, "hashx-remove!", 4, 0, 0,
889 (SCM hash, SCM assoc, SCM table, SCM obj),
890 "This behaves the same way as the corresponding @code{remove!}\n"
891 "function, but uses @var{hash} as a hash function and\n"
892 "@var{assoc} to compare keys. @code{hash} must be a function\n"
893 "that takes two arguments, a key to be hashed and a table size.\n"
894 "@code{assoc} must be an associator function, like @code{assoc},\n"
895 "@code{assq} or @code{assv}.\n"
896 "\n"
897 " By way of illustration, @code{hashq-remove! table key} is\n"
898 "equivalent to @code{hashx-remove! hashq assq #f table key}.")
899 #define FUNC_NAME s_scm_hashx_remove_x
900 {
901 scm_t_ihashx_closure closure;
902 closure.hash = hash;
903 closure.assoc = assoc;
904 return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
905 (void *) &closure);
906 }
907 #undef FUNC_NAME
908
909 /* Hash table iterators */
910
911 static const char s_scm_hash_fold[];
912
913 SCM
914 scm_internal_hash_fold (SCM (*fn) (), void *closure, SCM init, SCM table)
915 {
916 long i, n;
917 SCM buckets, result = init;
918
919 if (SCM_HASHTABLE_P (table))
920 buckets = SCM_HASHTABLE_VECTOR (table);
921 else
922 buckets = table;
923
924 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
925 for (i = 0; i < n; ++i)
926 {
927 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
928 while (!scm_is_null (ls))
929 {
930 if (!scm_is_pair (ls))
931 scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
932 handle = SCM_CAR (ls);
933 if (!scm_is_pair (handle))
934 scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
935 result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
936 ls = SCM_CDR (ls);
937 }
938 }
939
940 return result;
941 }
942
943 /* The following redundant code is here in order to be able to support
944 hash-for-each-handle. An alternative would have been to replace
945 this code and scm_internal_hash_fold above with a single
946 scm_internal_hash_fold_handles, but we don't want to promote such
947 an API. */
948
949 static const char s_scm_hash_for_each[];
950
951 void
952 scm_internal_hash_for_each_handle (SCM (*fn) (), void *closure, SCM table)
953 {
954 long i, n;
955 SCM buckets;
956
957 if (SCM_HASHTABLE_P (table))
958 buckets = SCM_HASHTABLE_VECTOR (table);
959 else
960 buckets = table;
961
962 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
963 for (i = 0; i < n; ++i)
964 {
965 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
966 while (!scm_is_null (ls))
967 {
968 if (!scm_is_pair (ls))
969 scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
970 handle = SCM_CAR (ls);
971 if (!scm_is_pair (handle))
972 scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
973 fn (closure, handle);
974 ls = SCM_CDR (ls);
975 }
976 }
977 }
978
979 SCM_DEFINE (scm_hash_fold, "hash-fold", 3, 0, 0,
980 (SCM proc, SCM init, SCM table),
981 "An iterator over hash-table elements.\n"
982 "Accumulates and returns a result by applying PROC successively.\n"
983 "The arguments to PROC are \"(key value prior-result)\" where key\n"
984 "and value are successive pairs from the hash table TABLE, and\n"
985 "prior-result is either INIT (for the first application of PROC)\n"
986 "or the return value of the previous application of PROC.\n"
987 "For example, @code{(hash-fold acons '() tab)} will convert a hash\n"
988 "table into an a-list of key-value pairs.")
989 #define FUNC_NAME s_scm_hash_fold
990 {
991 SCM_VALIDATE_PROC (1, proc);
992 if (!SCM_HASHTABLE_P (table))
993 SCM_VALIDATE_VECTOR (3, table);
994 return scm_internal_hash_fold (scm_call_3, (void *) SCM_UNPACK (proc), init, table);
995 }
996 #undef FUNC_NAME
997
998 static SCM
999 for_each_proc (void *proc, SCM handle)
1000 {
1001 return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
1002 }
1003
1004 SCM_DEFINE (scm_hash_for_each, "hash-for-each", 2, 0, 0,
1005 (SCM proc, SCM table),
1006 "An iterator over hash-table elements.\n"
1007 "Applies PROC successively on all hash table items.\n"
1008 "The arguments to PROC are \"(key value)\" where key\n"
1009 "and value are successive pairs from the hash table TABLE.")
1010 #define FUNC_NAME s_scm_hash_for_each
1011 {
1012 SCM_VALIDATE_PROC (1, proc);
1013 if (!SCM_HASHTABLE_P (table))
1014 SCM_VALIDATE_VECTOR (2, table);
1015
1016 scm_internal_hash_for_each_handle (for_each_proc,
1017 (void *) SCM_UNPACK (proc),
1018 table);
1019 return SCM_UNSPECIFIED;
1020 }
1021 #undef FUNC_NAME
1022
1023 SCM_DEFINE (scm_hash_for_each_handle, "hash-for-each-handle", 2, 0, 0,
1024 (SCM proc, SCM table),
1025 "An iterator over hash-table elements.\n"
1026 "Applies PROC successively on all hash table handles.")
1027 #define FUNC_NAME s_scm_hash_for_each_handle
1028 {
1029 scm_t_trampoline_1 call = scm_trampoline_1 (proc);
1030 SCM_ASSERT (call, proc, 1, FUNC_NAME);
1031 if (!SCM_HASHTABLE_P (table))
1032 SCM_VALIDATE_VECTOR (2, table);
1033
1034 scm_internal_hash_for_each_handle (call,
1035 (void *) SCM_UNPACK (proc),
1036 table);
1037 return SCM_UNSPECIFIED;
1038 }
1039 #undef FUNC_NAME
1040
1041 static SCM
1042 map_proc (void *proc, SCM key, SCM data, SCM value)
1043 {
1044 return scm_cons (scm_call_2 (SCM_PACK (proc), key, data), value);
1045 }
1046
1047 SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
1048 (SCM proc, SCM table),
1049 "An iterator over hash-table elements.\n"
1050 "Accumulates and returns as a list the results of applying PROC successively.\n"
1051 "The arguments to PROC are \"(key value)\" where key\n"
1052 "and value are successive pairs from the hash table TABLE.")
1053 #define FUNC_NAME s_scm_hash_map_to_list
1054 {
1055 SCM_VALIDATE_PROC (1, proc);
1056 if (!SCM_HASHTABLE_P (table))
1057 SCM_VALIDATE_VECTOR (2, table);
1058 return scm_internal_hash_fold (map_proc,
1059 (void *) SCM_UNPACK (proc),
1060 SCM_EOL,
1061 table);
1062 }
1063 #undef FUNC_NAME
1064
1065 \f
1066
1067
1068 void
1069 scm_hashtab_prehistory ()
1070 {
1071 scm_tc16_hashtable = scm_make_smob_type (s_hashtable, 0);
1072 scm_set_smob_mark (scm_tc16_hashtable, scm_markcdr);
1073 scm_set_smob_print (scm_tc16_hashtable, hashtable_print);
1074 scm_set_smob_free (scm_tc16_hashtable, hashtable_free);
1075 scm_c_hook_add (&scm_after_gc_c_hook, rehash_after_gc, 0, 0);
1076 }
1077
1078 void
1079 scm_init_hashtab ()
1080 {
1081 #include "libguile/hashtab.x"
1082 }
1083
1084 /*
1085 Local Variables:
1086 c-file-style: "gnu"
1087 End:
1088 */