Merge remote-tracking branch 'origin/stable-2.0'
[bpt/guile.git] / libguile / hashtab.c
1 /* Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2003, 2004, 2006,
2 * 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public License
6 * as published by the Free Software Foundation; either version 3 of
7 * the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA
18 */
19
20
21 \f
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <alloca.h>
27 #include <stdio.h>
28 #include <assert.h>
29
30 #include "libguile/_scm.h"
31 #include "libguile/alist.h"
32 #include "libguile/hash.h"
33 #include "libguile/eval.h"
34 #include "libguile/root.h"
35 #include "libguile/vectors.h"
36 #include "libguile/ports.h"
37 #include "libguile/bdw-gc.h"
38
39 #include "libguile/validate.h"
40 #include "libguile/hashtab.h"
41
42
43 \f
44
45 /* A hash table is a cell containing a vector of association lists.
46 *
47 * Growing or shrinking, with following rehashing, is triggered when
48 * the load factor
49 *
50 * L = N / S (N: number of items in table, S: bucket vector length)
51 *
52 * passes an upper limit of 0.9 or a lower limit of 0.25.
53 *
54 * The implementation stores the upper and lower number of items which
55 * trigger a resize in the hashtable object.
56 *
57 * Possible hash table sizes (primes) are stored in the array
58 * hashtable_size.
59 */
60
61 static unsigned long hashtable_size[] = {
62 31, 61, 113, 223, 443, 883, 1759, 3517, 7027, 14051, 28099, 56197, 112363,
63 224717, 449419, 898823, 1797641, 3595271, 7190537, 14381041
64 #if SIZEOF_SCM_T_BITS > 4
65 /* vector lengths are stored in the first word of vectors, shifted by
66 8 bits for the tc8, so for 32-bit we only get 2^24-1 = 16777215
67 elements. But we allow a few more sizes for 64-bit. */
68 , 28762081, 57524111, 115048217, 230096423, 460192829
69 #endif
70 };
71
72 #define HASHTABLE_SIZE_N (sizeof(hashtable_size)/sizeof(unsigned long))
73
74 static char *s_hashtable = "hashtable";
75
76 static SCM
77 make_hash_table (unsigned long k, const char *func_name)
78 {
79 SCM vector;
80 scm_t_hashtable *t;
81 int i = 0, n = k ? k : 31;
82 while (i + 1 < HASHTABLE_SIZE_N && n > hashtable_size[i])
83 ++i;
84 n = hashtable_size[i];
85
86 vector = scm_c_make_vector (n, SCM_EOL);
87
88 t = scm_gc_malloc_pointerless (sizeof (*t), s_hashtable);
89 t->min_size_index = t->size_index = i;
90 t->n_items = 0;
91 t->lower = 0;
92 t->upper = 9 * n / 10;
93
94 /* FIXME: we just need two words of storage, not three */
95 return scm_double_cell (scm_tc7_hashtable, SCM_UNPACK (vector),
96 (scm_t_bits)t, 0);
97 }
98
99 void
100 scm_i_rehash (SCM table,
101 scm_t_hash_fn hash_fn,
102 void *closure,
103 const char* func_name)
104 {
105 SCM buckets, new_buckets;
106 int i;
107 unsigned long old_size;
108 unsigned long new_size;
109
110 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
111 {
112 /* rehashing is not triggered when i <= min_size */
113 i = SCM_HASHTABLE (table)->size_index;
114 do
115 --i;
116 while (i > SCM_HASHTABLE (table)->min_size_index
117 && SCM_HASHTABLE_N_ITEMS (table) < hashtable_size[i] / 4);
118 }
119 else
120 {
121 i = SCM_HASHTABLE (table)->size_index + 1;
122 if (i >= HASHTABLE_SIZE_N)
123 /* don't rehash */
124 return;
125 }
126 SCM_HASHTABLE (table)->size_index = i;
127
128 new_size = hashtable_size[i];
129 if (i <= SCM_HASHTABLE (table)->min_size_index)
130 SCM_HASHTABLE (table)->lower = 0;
131 else
132 SCM_HASHTABLE (table)->lower = new_size / 4;
133 SCM_HASHTABLE (table)->upper = 9 * new_size / 10;
134 buckets = SCM_HASHTABLE_VECTOR (table);
135
136 new_buckets = scm_c_make_vector (new_size, SCM_EOL);
137
138 SCM_SET_HASHTABLE_VECTOR (table, new_buckets);
139 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
140
141 old_size = SCM_SIMPLE_VECTOR_LENGTH (buckets);
142 for (i = 0; i < old_size; ++i)
143 {
144 SCM ls, cell, handle;
145
146 ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
147 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_EOL);
148
149 while (scm_is_pair (ls))
150 {
151 unsigned long h;
152
153 cell = ls;
154 handle = SCM_CAR (cell);
155 ls = SCM_CDR (ls);
156
157 h = hash_fn (SCM_CAR (handle), new_size, closure);
158 if (h >= new_size)
159 scm_out_of_range (func_name, scm_from_ulong (h));
160 SCM_SETCDR (cell, SCM_SIMPLE_VECTOR_REF (new_buckets, h));
161 SCM_SIMPLE_VECTOR_SET (new_buckets, h, cell);
162 SCM_HASHTABLE_INCREMENT (table);
163 }
164 }
165 }
166
167
168 void
169 scm_i_hashtable_print (SCM exp, SCM port, scm_print_state *pstate)
170 {
171 scm_puts_unlocked ("#<hash-table ", port);
172 scm_uintprint (SCM_UNPACK (exp), 16, port);
173 scm_putc (' ', port);
174 scm_uintprint (SCM_HASHTABLE_N_ITEMS (exp), 10, port);
175 scm_putc_unlocked ('/', port);
176 scm_uintprint (SCM_SIMPLE_VECTOR_LENGTH (SCM_HASHTABLE_VECTOR (exp)),
177 10, port);
178 scm_puts_unlocked (">", port);
179 }
180
181
182 SCM
183 scm_c_make_hash_table (unsigned long k)
184 {
185 return make_hash_table (k, "scm_c_make_hash_table");
186 }
187
188 SCM_DEFINE (scm_make_hash_table, "make-hash-table", 0, 1, 0,
189 (SCM n),
190 "Make a new abstract hash table object with minimum number of buckets @var{n}\n")
191 #define FUNC_NAME s_scm_make_hash_table
192 {
193 return make_hash_table (SCM_UNBNDP (n) ? 0 : scm_to_ulong (n), FUNC_NAME);
194 }
195 #undef FUNC_NAME
196
197 #define SCM_WEAK_TABLE_P(x) (scm_is_true (scm_weak_table_p (x)))
198
199 SCM_DEFINE (scm_hash_table_p, "hash-table?", 1, 0, 0,
200 (SCM obj),
201 "Return @code{#t} if @var{obj} is an abstract hash table object.")
202 #define FUNC_NAME s_scm_hash_table_p
203 {
204 return scm_from_bool (SCM_HASHTABLE_P (obj) || SCM_WEAK_TABLE_P (obj));
205 }
206 #undef FUNC_NAME
207
208
209 \f
210 /* Accessing hash table entries. */
211
212 SCM
213 scm_hash_fn_get_handle (SCM table, SCM obj,
214 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
215 void * closure)
216 #define FUNC_NAME "scm_hash_fn_get_handle"
217 {
218 unsigned long k;
219 SCM buckets, h;
220
221 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
222 buckets = SCM_HASHTABLE_VECTOR (table);
223
224 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
225 return SCM_BOOL_F;
226 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
227 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
228 scm_out_of_range (FUNC_NAME, scm_from_ulong (k));
229
230 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
231
232 return h;
233 }
234 #undef FUNC_NAME
235
236
237 SCM
238 scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init,
239 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
240 void * closure)
241 #define FUNC_NAME "scm_hash_fn_create_handle_x"
242 {
243 unsigned long k;
244 SCM buckets, it;
245
246 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
247 buckets = SCM_HASHTABLE_VECTOR (table);
248
249 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
250 SCM_MISC_ERROR ("void hashtable", SCM_EOL);
251
252 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
253 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
254 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
255
256 it = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
257
258 if (scm_is_pair (it))
259 return it;
260 else if (scm_is_true (it))
261 scm_wrong_type_arg_msg (NULL, 0, it, "a pair");
262 else
263 {
264 SCM handle, new_bucket;
265
266 handle = scm_cons (obj, init);
267 new_bucket = scm_cons (handle, SCM_EOL);
268
269 if (!scm_is_eq (SCM_HASHTABLE_VECTOR (table), buckets))
270 {
271 buckets = SCM_HASHTABLE_VECTOR (table);
272 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
273 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
274 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
275 }
276 SCM_SETCDR (new_bucket, SCM_SIMPLE_VECTOR_REF (buckets, k));
277 SCM_SIMPLE_VECTOR_SET (buckets, k, new_bucket);
278 SCM_HASHTABLE_INCREMENT (table);
279
280 /* Maybe rehash the table. */
281 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table)
282 || SCM_HASHTABLE_N_ITEMS (table) > SCM_HASHTABLE_UPPER (table))
283 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
284 return SCM_CAR (new_bucket);
285 }
286 }
287 #undef FUNC_NAME
288
289
290 SCM
291 scm_hash_fn_ref (SCM table, SCM obj, SCM dflt,
292 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
293 void *closure)
294 {
295 SCM it = scm_hash_fn_get_handle (table, obj, hash_fn, assoc_fn, closure);
296 if (scm_is_pair (it))
297 return SCM_CDR (it);
298 else
299 return dflt;
300 }
301
302 SCM
303 scm_hash_fn_set_x (SCM table, SCM obj, SCM val,
304 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
305 void *closure)
306 {
307 SCM pair;
308
309 pair = scm_hash_fn_create_handle_x (table, obj, val,
310 hash_fn, assoc_fn, closure);
311
312 if (!scm_is_eq (SCM_CDR (pair), val))
313 SCM_SETCDR (pair, val);
314
315 return val;
316 }
317
318
319 SCM
320 scm_hash_fn_remove_x (SCM table, SCM obj,
321 scm_t_hash_fn hash_fn,
322 scm_t_assoc_fn assoc_fn,
323 void *closure)
324 #define FUNC_NAME "hash_fn_remove_x"
325 {
326 unsigned long k;
327 SCM buckets, h;
328
329 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
330
331 buckets = SCM_HASHTABLE_VECTOR (table);
332
333 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
334 return SCM_EOL;
335
336 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
337 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
338 scm_out_of_range (FUNC_NAME, scm_from_ulong (k));
339
340 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
341
342 if (scm_is_true (h))
343 {
344 SCM_SIMPLE_VECTOR_SET
345 (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
346 SCM_HASHTABLE_DECREMENT (table);
347 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
348 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
349 }
350 return h;
351 }
352 #undef FUNC_NAME
353
354 SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
355 (SCM table),
356 "Remove all items from @var{table} (without triggering a resize).")
357 #define FUNC_NAME s_scm_hash_clear_x
358 {
359 if (SCM_WEAK_TABLE_P (table))
360 {
361 scm_weak_table_clear_x (table);
362 return SCM_UNSPECIFIED;
363 }
364
365 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
366
367 scm_vector_fill_x (SCM_HASHTABLE_VECTOR (table), SCM_EOL);
368 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
369
370 return SCM_UNSPECIFIED;
371 }
372 #undef FUNC_NAME
373
374 \f
375
376 SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
377 (SCM table, SCM key),
378 "This procedure returns the @code{(key . value)} pair from the\n"
379 "hash table @var{table}. If @var{table} does not hold an\n"
380 "associated value for @var{key}, @code{#f} is returned.\n"
381 "Uses @code{eq?} for equality testing.")
382 #define FUNC_NAME s_scm_hashq_get_handle
383 {
384 return scm_hash_fn_get_handle (table, key,
385 (scm_t_hash_fn) scm_ihashq,
386 (scm_t_assoc_fn) scm_sloppy_assq,
387 0);
388 }
389 #undef FUNC_NAME
390
391
392 SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
393 (SCM table, SCM key, SCM init),
394 "This function looks up @var{key} in @var{table} and returns its handle.\n"
395 "If @var{key} is not already present, a new handle is created which\n"
396 "associates @var{key} with @var{init}.")
397 #define FUNC_NAME s_scm_hashq_create_handle_x
398 {
399 return scm_hash_fn_create_handle_x (table, key, init,
400 (scm_t_hash_fn) scm_ihashq,
401 (scm_t_assoc_fn) scm_sloppy_assq,
402 0);
403 }
404 #undef FUNC_NAME
405
406
407 SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
408 (SCM table, SCM key, SCM dflt),
409 "Look up @var{key} in the hash table @var{table}, and return the\n"
410 "value (if any) associated with it. If @var{key} is not found,\n"
411 "return @var{dflt} (or @code{#f} if no @var{dflt} argument\n"
412 "is supplied). Uses @code{eq?} for equality testing.")
413 #define FUNC_NAME s_scm_hashq_ref
414 {
415 if (SCM_UNBNDP (dflt))
416 dflt = SCM_BOOL_F;
417
418 if (SCM_WEAK_TABLE_P (table))
419 return scm_weak_table_refq (table, key, dflt);
420
421 return scm_hash_fn_ref (table, key, dflt,
422 (scm_t_hash_fn) scm_ihashq,
423 (scm_t_assoc_fn) scm_sloppy_assq,
424 0);
425 }
426 #undef FUNC_NAME
427
428
429
430 SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
431 (SCM table, SCM key, SCM val),
432 "Find the entry in @var{table} associated with @var{key}, and\n"
433 "store @var{val} there. Uses @code{eq?} for equality testing.")
434 #define FUNC_NAME s_scm_hashq_set_x
435 {
436 if (SCM_WEAK_TABLE_P (table))
437 {
438 scm_weak_table_putq_x (table, key, val);
439 return val;
440 }
441
442 return scm_hash_fn_set_x (table, key, val,
443 (scm_t_hash_fn) scm_ihashq,
444 (scm_t_assoc_fn) scm_sloppy_assq,
445 0);
446 }
447 #undef FUNC_NAME
448
449
450
451 SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
452 (SCM table, SCM key),
453 "Remove @var{key} (and any value associated with it) from\n"
454 "@var{table}. Uses @code{eq?} for equality tests.")
455 #define FUNC_NAME s_scm_hashq_remove_x
456 {
457 if (SCM_WEAK_TABLE_P (table))
458 {
459 scm_weak_table_remq_x (table, key);
460 /* This return value is for historical compatibility with
461 hash-remove!, which returns either the "handle" corresponding
462 to the entry, or #f. Since weak tables don't have handles, we
463 have to return #f. */
464 return SCM_BOOL_F;
465 }
466
467 return scm_hash_fn_remove_x (table, key,
468 (scm_t_hash_fn) scm_ihashq,
469 (scm_t_assoc_fn) scm_sloppy_assq,
470 0);
471 }
472 #undef FUNC_NAME
473
474
475 \f
476
477 SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
478 (SCM table, SCM key),
479 "This procedure returns the @code{(key . value)} pair from the\n"
480 "hash table @var{table}. If @var{table} does not hold an\n"
481 "associated value for @var{key}, @code{#f} is returned.\n"
482 "Uses @code{eqv?} for equality testing.")
483 #define FUNC_NAME s_scm_hashv_get_handle
484 {
485 return scm_hash_fn_get_handle (table, key,
486 (scm_t_hash_fn) scm_ihashv,
487 (scm_t_assoc_fn) scm_sloppy_assv,
488 0);
489 }
490 #undef FUNC_NAME
491
492
493 SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
494 (SCM table, SCM key, SCM init),
495 "This function looks up @var{key} in @var{table} and returns its handle.\n"
496 "If @var{key} is not already present, a new handle is created which\n"
497 "associates @var{key} with @var{init}.")
498 #define FUNC_NAME s_scm_hashv_create_handle_x
499 {
500 return scm_hash_fn_create_handle_x (table, key, init,
501 (scm_t_hash_fn) scm_ihashv,
502 (scm_t_assoc_fn) scm_sloppy_assv,
503 0);
504 }
505 #undef FUNC_NAME
506
507
508 static int
509 assv_predicate (SCM k, SCM v, void *closure)
510 {
511 return scm_is_true (scm_eqv_p (k, SCM_PACK_POINTER (closure)));
512 }
513
514 SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
515 (SCM table, SCM key, SCM dflt),
516 "Look up @var{key} in the hash table @var{table}, and return the\n"
517 "value (if any) associated with it. If @var{key} is not found,\n"
518 "return @var{dflt} (or @code{#f} if no @var{dflt} argument\n"
519 "is supplied). Uses @code{eqv?} for equality testing.")
520 #define FUNC_NAME s_scm_hashv_ref
521 {
522 if (SCM_UNBNDP (dflt))
523 dflt = SCM_BOOL_F;
524
525 if (SCM_WEAK_TABLE_P (table))
526 return scm_c_weak_table_ref (table, scm_ihashv (key, -1),
527 assv_predicate, SCM_PACK (key), dflt);
528
529 return scm_hash_fn_ref (table, key, dflt,
530 (scm_t_hash_fn) scm_ihashv,
531 (scm_t_assoc_fn) scm_sloppy_assv,
532 0);
533 }
534 #undef FUNC_NAME
535
536
537
538 SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
539 (SCM table, SCM key, SCM val),
540 "Find the entry in @var{table} associated with @var{key}, and\n"
541 "store @var{value} there. Uses @code{eqv?} for equality testing.")
542 #define FUNC_NAME s_scm_hashv_set_x
543 {
544 if (SCM_WEAK_TABLE_P (table))
545 {
546 scm_c_weak_table_put_x (table, scm_ihashv (key, -1),
547 assv_predicate, SCM_PACK (key),
548 key, val);
549 return val;
550 }
551
552 return scm_hash_fn_set_x (table, key, val,
553 (scm_t_hash_fn) scm_ihashv,
554 (scm_t_assoc_fn) scm_sloppy_assv,
555 0);
556 }
557 #undef FUNC_NAME
558
559
560 SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
561 (SCM table, SCM key),
562 "Remove @var{key} (and any value associated with it) from\n"
563 "@var{table}. Uses @code{eqv?} for equality tests.")
564 #define FUNC_NAME s_scm_hashv_remove_x
565 {
566 if (SCM_WEAK_TABLE_P (table))
567 {
568 scm_c_weak_table_remove_x (table, scm_ihashv (key, -1),
569 assv_predicate, SCM_PACK (key));
570 /* See note in hashq-remove!. */
571 return SCM_BOOL_F;
572 }
573
574 return scm_hash_fn_remove_x (table, key,
575 (scm_t_hash_fn) scm_ihashv,
576 (scm_t_assoc_fn) scm_sloppy_assv,
577 0);
578 }
579 #undef FUNC_NAME
580
581 \f
582
583 SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
584 (SCM table, SCM key),
585 "This procedure returns the @code{(key . value)} pair from the\n"
586 "hash table @var{table}. If @var{table} does not hold an\n"
587 "associated value for @var{key}, @code{#f} is returned.\n"
588 "Uses @code{equal?} for equality testing.")
589 #define FUNC_NAME s_scm_hash_get_handle
590 {
591 return scm_hash_fn_get_handle (table, key,
592 (scm_t_hash_fn) scm_ihash,
593 (scm_t_assoc_fn) scm_sloppy_assoc,
594 0);
595 }
596 #undef FUNC_NAME
597
598
599 SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
600 (SCM table, SCM key, SCM init),
601 "This function looks up @var{key} in @var{table} and returns its handle.\n"
602 "If @var{key} is not already present, a new handle is created which\n"
603 "associates @var{key} with @var{init}.")
604 #define FUNC_NAME s_scm_hash_create_handle_x
605 {
606 return scm_hash_fn_create_handle_x (table, key, init,
607 (scm_t_hash_fn) scm_ihash,
608 (scm_t_assoc_fn) scm_sloppy_assoc,
609 0);
610 }
611 #undef FUNC_NAME
612
613
614 static int
615 assoc_predicate (SCM k, SCM v, void *closure)
616 {
617 return scm_is_true (scm_equal_p (k, SCM_PACK_POINTER (closure)));
618 }
619
620 SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
621 (SCM table, SCM key, SCM dflt),
622 "Look up @var{key} in the hash table @var{table}, and return the\n"
623 "value (if any) associated with it. If @var{key} is not found,\n"
624 "return @var{dflt} (or @code{#f} if no @var{dflt} argument\n"
625 "is supplied). Uses @code{equal?} for equality testing.")
626 #define FUNC_NAME s_scm_hash_ref
627 {
628 if (SCM_UNBNDP (dflt))
629 dflt = SCM_BOOL_F;
630
631 if (SCM_WEAK_TABLE_P (table))
632 return scm_c_weak_table_ref (table, scm_ihash (key, -1),
633 assoc_predicate, SCM_PACK (key), dflt);
634
635 return scm_hash_fn_ref (table, key, dflt,
636 (scm_t_hash_fn) scm_ihash,
637 (scm_t_assoc_fn) scm_sloppy_assoc,
638 0);
639 }
640 #undef FUNC_NAME
641
642
643
644 SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
645 (SCM table, SCM key, SCM val),
646 "Find the entry in @var{table} associated with @var{key}, and\n"
647 "store @var{val} there. Uses @code{equal?} for equality\n"
648 "testing.")
649 #define FUNC_NAME s_scm_hash_set_x
650 {
651 if (SCM_WEAK_TABLE_P (table))
652 {
653 scm_c_weak_table_put_x (table, scm_ihash (key, -1),
654 assoc_predicate, SCM_PACK (key),
655 key, val);
656 return val;
657 }
658
659 return scm_hash_fn_set_x (table, key, val,
660 (scm_t_hash_fn) scm_ihash,
661 (scm_t_assoc_fn) scm_sloppy_assoc,
662 0);
663 }
664 #undef FUNC_NAME
665
666
667
668 SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
669 (SCM table, SCM key),
670 "Remove @var{key} (and any value associated with it) from\n"
671 "@var{table}. Uses @code{equal?} for equality tests.")
672 #define FUNC_NAME s_scm_hash_remove_x
673 {
674 if (SCM_WEAK_TABLE_P (table))
675 {
676 scm_c_weak_table_remove_x (table, scm_ihash (key, -1),
677 assoc_predicate, SCM_PACK (key));
678 /* See note in hashq-remove!. */
679 return SCM_BOOL_F;
680 }
681
682 return scm_hash_fn_remove_x (table, key,
683 (scm_t_hash_fn) scm_ihash,
684 (scm_t_assoc_fn) scm_sloppy_assoc,
685 0);
686 }
687 #undef FUNC_NAME
688
689 \f
690
691
692 typedef struct scm_t_ihashx_closure
693 {
694 SCM hash;
695 SCM assoc;
696 SCM key;
697 } scm_t_ihashx_closure;
698
699 static unsigned long
700 scm_ihashx (SCM obj, unsigned long n, void *arg)
701 {
702 SCM answer;
703 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
704 answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
705 return scm_to_ulong (answer);
706 }
707
708 static SCM
709 scm_sloppy_assx (SCM obj, SCM alist, void *arg)
710 {
711 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
712 return scm_call_2 (closure->assoc, obj, alist);
713 }
714
715 static int
716 assx_predicate (SCM k, SCM v, void *closure)
717 {
718 scm_t_ihashx_closure *c = (scm_t_ihashx_closure *) closure;
719
720 /* FIXME: The hashx interface is crazy. Hash tables have nothing to
721 do with alists in principle. Instead of getting an assoc proc,
722 hashx functions should use an equality predicate. Perhaps we can
723 change this before 2.2, but until then, add a terrible, terrible
724 hack. */
725
726 return scm_is_true (scm_call_2 (c->assoc, c->key, scm_acons (k, v, SCM_EOL)));
727 }
728
729
730 SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
731 (SCM hash, SCM assoc, SCM table, SCM key),
732 "This behaves the same way as the corresponding\n"
733 "@code{-get-handle} function, but uses @var{hash} as a hash\n"
734 "function and @var{assoc} to compare keys. @code{hash} must be\n"
735 "a function that takes two arguments, a key to be hashed and a\n"
736 "table size. @code{assoc} must be an associator function, like\n"
737 "@code{assoc}, @code{assq} or @code{assv}.")
738 #define FUNC_NAME s_scm_hashx_get_handle
739 {
740 scm_t_ihashx_closure closure;
741 closure.hash = hash;
742 closure.assoc = assoc;
743 closure.key = key;
744
745 return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
746 (void *) &closure);
747 }
748 #undef FUNC_NAME
749
750
751 SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
752 (SCM hash, SCM assoc, SCM table, SCM key, SCM init),
753 "This behaves the same way as the corresponding\n"
754 "@code{-create-handle} function, but uses @var{hash} as a hash\n"
755 "function and @var{assoc} to compare keys. @code{hash} must be\n"
756 "a function that takes two arguments, a key to be hashed and a\n"
757 "table size. @code{assoc} must be an associator function, like\n"
758 "@code{assoc}, @code{assq} or @code{assv}.")
759 #define FUNC_NAME s_scm_hashx_create_handle_x
760 {
761 scm_t_ihashx_closure closure;
762 closure.hash = hash;
763 closure.assoc = assoc;
764 closure.key = key;
765
766 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
767 scm_sloppy_assx, (void *)&closure);
768 }
769 #undef FUNC_NAME
770
771
772
773 SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
774 (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
775 "This behaves the same way as the corresponding @code{ref}\n"
776 "function, but uses @var{hash} as a hash function and\n"
777 "@var{assoc} to compare keys. @code{hash} must be a function\n"
778 "that takes two arguments, a key to be hashed and a table size.\n"
779 "@code{assoc} must be an associator function, like @code{assoc},\n"
780 "@code{assq} or @code{assv}.\n"
781 "\n"
782 "By way of illustration, @code{hashq-ref table key} is\n"
783 "equivalent to @code{hashx-ref hashq assq table key}.")
784 #define FUNC_NAME s_scm_hashx_ref
785 {
786 scm_t_ihashx_closure closure;
787 if (SCM_UNBNDP (dflt))
788 dflt = SCM_BOOL_F;
789 closure.hash = hash;
790 closure.assoc = assoc;
791 closure.key = key;
792
793 if (SCM_WEAK_TABLE_P (table))
794 {
795 unsigned long h = scm_to_ulong (scm_call_2 (hash, key,
796 scm_from_ulong (-1)));
797 return scm_c_weak_table_ref (table, h, assx_predicate, &closure, dflt);
798 }
799
800 return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
801 (void *)&closure);
802 }
803 #undef FUNC_NAME
804
805
806
807
808 SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
809 (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
810 "This behaves the same way as the corresponding @code{set!}\n"
811 "function, but uses @var{hash} as a hash function and\n"
812 "@var{assoc} to compare keys. @code{hash} must be a function\n"
813 "that takes two arguments, a key to be hashed and a table size.\n"
814 "@code{assoc} must be an associator function, like @code{assoc},\n"
815 "@code{assq} or @code{assv}.\n"
816 "\n"
817 " By way of illustration, @code{hashq-set! table key} is\n"
818 "equivalent to @code{hashx-set! hashq assq table key}.")
819 #define FUNC_NAME s_scm_hashx_set_x
820 {
821 scm_t_ihashx_closure closure;
822 closure.hash = hash;
823 closure.assoc = assoc;
824 closure.key = key;
825
826 if (SCM_WEAK_TABLE_P (table))
827 {
828 unsigned long h = scm_to_ulong (scm_call_2 (hash, key,
829 scm_from_ulong (-1)));
830 scm_c_weak_table_put_x (table, h, assx_predicate, &closure, key, val);
831 return val;
832 }
833
834 return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
835 (void *)&closure);
836 }
837 #undef FUNC_NAME
838
839 SCM_DEFINE (scm_hashx_remove_x, "hashx-remove!", 4, 0, 0,
840 (SCM hash, SCM assoc, SCM table, SCM obj),
841 "This behaves the same way as the corresponding @code{remove!}\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-remove! table key} is\n"
849 "equivalent to @code{hashx-remove! hashq assq #f table key}.")
850 #define FUNC_NAME s_scm_hashx_remove_x
851 {
852 scm_t_ihashx_closure closure;
853 closure.hash = hash;
854 closure.assoc = assoc;
855 closure.key = obj;
856
857 if (SCM_WEAK_TABLE_P (table))
858 {
859 unsigned long h = scm_to_ulong (scm_call_2 (hash, obj,
860 scm_from_ulong (-1)));
861 scm_c_weak_table_remove_x (table, h, assx_predicate, &closure);
862 /* See note in hashq-remove!. */
863 return SCM_BOOL_F;
864 }
865
866 return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
867 (void *) &closure);
868 }
869 #undef FUNC_NAME
870
871 /* Hash table iterators */
872
873 SCM_DEFINE (scm_hash_fold, "hash-fold", 3, 0, 0,
874 (SCM proc, SCM init, SCM table),
875 "An iterator over hash-table elements.\n"
876 "Accumulates and returns a result by applying PROC successively.\n"
877 "The arguments to PROC are \"(key value prior-result)\" where key\n"
878 "and value are successive pairs from the hash table TABLE, and\n"
879 "prior-result is either INIT (for the first application of PROC)\n"
880 "or the return value of the previous application of PROC.\n"
881 "For example, @code{(hash-fold acons '() tab)} will convert a hash\n"
882 "table into an a-list of key-value pairs.")
883 #define FUNC_NAME s_scm_hash_fold
884 {
885 SCM_VALIDATE_PROC (1, proc);
886
887 if (SCM_WEAK_TABLE_P (table))
888 return scm_weak_table_fold (proc, init, table);
889
890 SCM_VALIDATE_HASHTABLE (3, table);
891 return scm_internal_hash_fold ((scm_t_hash_fold_fn) scm_call_3,
892 (void *) SCM_UNPACK (proc), init, table);
893 }
894 #undef FUNC_NAME
895
896 static SCM
897 for_each_proc (void *proc, SCM handle)
898 {
899 return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
900 }
901
902 SCM_DEFINE (scm_hash_for_each, "hash-for-each", 2, 0, 0,
903 (SCM proc, SCM table),
904 "An iterator over hash-table elements.\n"
905 "Applies PROC successively on all hash table items.\n"
906 "The arguments to PROC are \"(key value)\" where key\n"
907 "and value are successive pairs from the hash table TABLE.")
908 #define FUNC_NAME s_scm_hash_for_each
909 {
910 SCM_VALIDATE_PROC (1, proc);
911
912 if (SCM_WEAK_TABLE_P (table))
913 {
914 scm_weak_table_for_each (proc, table);
915 return SCM_UNSPECIFIED;
916 }
917
918 SCM_VALIDATE_HASHTABLE (2, table);
919
920 scm_internal_hash_for_each_handle (for_each_proc,
921 (void *) SCM_UNPACK (proc),
922 table);
923 return SCM_UNSPECIFIED;
924 }
925 #undef FUNC_NAME
926
927 SCM_DEFINE (scm_hash_for_each_handle, "hash-for-each-handle", 2, 0, 0,
928 (SCM proc, SCM table),
929 "An iterator over hash-table elements.\n"
930 "Applies PROC successively on all hash table handles.")
931 #define FUNC_NAME s_scm_hash_for_each_handle
932 {
933 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)), proc, 1, FUNC_NAME);
934 SCM_VALIDATE_HASHTABLE (2, table);
935
936 scm_internal_hash_for_each_handle ((scm_t_hash_handle_fn) scm_call_1,
937 (void *) SCM_UNPACK (proc),
938 table);
939 return SCM_UNSPECIFIED;
940 }
941 #undef FUNC_NAME
942
943 static SCM
944 map_proc (void *proc, SCM key, SCM data, SCM value)
945 {
946 return scm_cons (scm_call_2 (SCM_PACK (proc), key, data), value);
947 }
948
949 SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
950 (SCM proc, SCM table),
951 "An iterator over hash-table elements.\n"
952 "Accumulates and returns as a list the results of applying PROC successively.\n"
953 "The arguments to PROC are \"(key value)\" where key\n"
954 "and value are successive pairs from the hash table TABLE.")
955 #define FUNC_NAME s_scm_hash_map_to_list
956 {
957 SCM_VALIDATE_PROC (1, proc);
958
959 if (SCM_WEAK_TABLE_P (table))
960 return scm_weak_table_map_to_list (proc, table);
961
962 SCM_VALIDATE_HASHTABLE (2, table);
963 return scm_internal_hash_fold (map_proc,
964 (void *) SCM_UNPACK (proc),
965 SCM_EOL,
966 table);
967 }
968 #undef FUNC_NAME
969
970 static SCM
971 count_proc (void *pred, SCM key, SCM data, SCM value)
972 {
973 if (scm_is_false (scm_call_2 (SCM_PACK (pred), key, data)))
974 return value;
975 else
976 return scm_oneplus(value);
977 }
978
979 SCM_DEFINE (scm_hash_count, "hash-count", 2, 0, 0,
980 (SCM pred, SCM table),
981 "Return the number of elements in the given hash TABLE that\n"
982 "cause `(PRED KEY VALUE)' to return true. To quickly determine\n"
983 "the total number of elements, use `(const #t)' for PRED.")
984 #define FUNC_NAME s_scm_hash_count
985 {
986 SCM init;
987
988 SCM_VALIDATE_PROC (1, pred);
989 SCM_VALIDATE_HASHTABLE (2, table);
990
991 init = scm_from_int (0);
992 return scm_internal_hash_fold ((scm_t_hash_fold_fn) count_proc,
993 (void *) SCM_UNPACK (pred), init, table);
994 }
995 #undef FUNC_NAME
996
997 \f
998
999 SCM
1000 scm_internal_hash_fold (scm_t_hash_fold_fn fn, void *closure,
1001 SCM init, SCM table)
1002 #define FUNC_NAME s_scm_hash_fold
1003 {
1004 long i, n;
1005 SCM buckets, result = init;
1006
1007 if (SCM_WEAK_TABLE_P (table))
1008 return scm_c_weak_table_fold (fn, closure, init, table);
1009
1010 SCM_VALIDATE_HASHTABLE (0, table);
1011 buckets = SCM_HASHTABLE_VECTOR (table);
1012
1013 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
1014 for (i = 0; i < n; ++i)
1015 {
1016 SCM ls, handle;
1017
1018 for (ls = SCM_SIMPLE_VECTOR_REF (buckets, i); !scm_is_null (ls);
1019 ls = SCM_CDR (ls))
1020 {
1021 handle = SCM_CAR (ls);
1022 result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
1023 }
1024 }
1025
1026 return result;
1027 }
1028 #undef FUNC_NAME
1029
1030 /* The following redundant code is here in order to be able to support
1031 hash-for-each-handle. An alternative would have been to replace
1032 this code and scm_internal_hash_fold above with a single
1033 scm_internal_hash_fold_handles, but we don't want to promote such
1034 an API. */
1035
1036 void
1037 scm_internal_hash_for_each_handle (scm_t_hash_handle_fn fn, void *closure,
1038 SCM table)
1039 #define FUNC_NAME s_scm_hash_for_each
1040 {
1041 long i, n;
1042 SCM buckets;
1043
1044 SCM_VALIDATE_HASHTABLE (0, table);
1045 buckets = SCM_HASHTABLE_VECTOR (table);
1046 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
1047
1048 for (i = 0; i < n; ++i)
1049 {
1050 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
1051 while (!scm_is_null (ls))
1052 {
1053 if (!scm_is_pair (ls))
1054 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
1055 handle = SCM_CAR (ls);
1056 if (!scm_is_pair (handle))
1057 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
1058 fn (closure, handle);
1059 ls = SCM_CDR (ls);
1060 }
1061 }
1062 }
1063 #undef FUNC_NAME
1064
1065 \f
1066
1067
1068 void
1069 scm_init_hashtab ()
1070 {
1071 #include "libguile/hashtab.x"
1072 }
1073
1074 /*
1075 Local Variables:
1076 c-file-style: "gnu"
1077 End:
1078 */