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 \f
209 /* Accessing hash table entries. */
210
211 SCM
212 scm_hash_fn_get_handle (SCM table, SCM obj,
213 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
214 void * closure)
215 #define FUNC_NAME "scm_hash_fn_get_handle"
216 {
217 unsigned long k;
218 SCM buckets, h;
219
220 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
221 buckets = SCM_HASHTABLE_VECTOR (table);
222
223 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
224 return SCM_BOOL_F;
225 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
226 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
227 scm_out_of_range (FUNC_NAME, scm_from_ulong (k));
228
229 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
230
231 return h;
232 }
233 #undef FUNC_NAME
234
235
236 SCM
237 scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init,
238 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
239 void * closure)
240 #define FUNC_NAME "scm_hash_fn_create_handle_x"
241 {
242 unsigned long k;
243 SCM buckets, it;
244
245 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
246 buckets = SCM_HASHTABLE_VECTOR (table);
247
248 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
249 SCM_MISC_ERROR ("void hashtable", SCM_EOL);
250
251 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
252 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
253 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
254
255 it = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
256
257 if (scm_is_pair (it))
258 return it;
259 else if (scm_is_true (it))
260 scm_wrong_type_arg_msg (NULL, 0, it, "a pair");
261 else
262 {
263 SCM handle, new_bucket;
264
265 handle = scm_cons (obj, init);
266 new_bucket = scm_cons (handle, SCM_EOL);
267
268 if (!scm_is_eq (SCM_HASHTABLE_VECTOR (table), buckets))
269 {
270 buckets = SCM_HASHTABLE_VECTOR (table);
271 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
272 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
273 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
274 }
275 SCM_SETCDR (new_bucket, SCM_SIMPLE_VECTOR_REF (buckets, k));
276 SCM_SIMPLE_VECTOR_SET (buckets, k, new_bucket);
277 SCM_HASHTABLE_INCREMENT (table);
278
279 /* Maybe rehash the table. */
280 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table)
281 || SCM_HASHTABLE_N_ITEMS (table) > SCM_HASHTABLE_UPPER (table))
282 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
283 return SCM_CAR (new_bucket);
284 }
285 }
286 #undef FUNC_NAME
287
288
289 SCM
290 scm_hash_fn_ref (SCM table, SCM obj, SCM dflt,
291 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
292 void *closure)
293 {
294 SCM it = scm_hash_fn_get_handle (table, obj, hash_fn, assoc_fn, closure);
295 if (scm_is_pair (it))
296 return SCM_CDR (it);
297 else
298 return dflt;
299 }
300
301 SCM
302 scm_hash_fn_set_x (SCM table, SCM obj, SCM val,
303 scm_t_hash_fn hash_fn, scm_t_assoc_fn assoc_fn,
304 void *closure)
305 {
306 SCM pair;
307
308 pair = scm_hash_fn_create_handle_x (table, obj, val,
309 hash_fn, assoc_fn, closure);
310
311 if (!scm_is_eq (SCM_CDR (pair), val))
312 SCM_SETCDR (pair, val);
313
314 return val;
315 }
316
317
318 SCM
319 scm_hash_fn_remove_x (SCM table, SCM obj,
320 scm_t_hash_fn hash_fn,
321 scm_t_assoc_fn assoc_fn,
322 void *closure)
323 #define FUNC_NAME "hash_fn_remove_x"
324 {
325 unsigned long k;
326 SCM buckets, h;
327
328 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
329
330 buckets = SCM_HASHTABLE_VECTOR (table);
331
332 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
333 return SCM_EOL;
334
335 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
336 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
337 scm_out_of_range (FUNC_NAME, scm_from_ulong (k));
338
339 h = assoc_fn (obj, SCM_SIMPLE_VECTOR_REF (buckets, k), closure);
340
341 if (scm_is_true (h))
342 {
343 SCM_SIMPLE_VECTOR_SET
344 (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
345 SCM_HASHTABLE_DECREMENT (table);
346 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
347 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
348 }
349 return h;
350 }
351 #undef FUNC_NAME
352
353 SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
354 (SCM table),
355 "Remove all items from @var{table} (without triggering a resize).")
356 #define FUNC_NAME s_scm_hash_clear_x
357 {
358 if (SCM_WEAK_TABLE_P (table))
359 {
360 scm_weak_table_clear_x (table);
361 return SCM_UNSPECIFIED;
362 }
363
364 SCM_VALIDATE_HASHTABLE (SCM_ARG1, table);
365
366 scm_vector_fill_x (SCM_HASHTABLE_VECTOR (table), SCM_EOL);
367 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
368
369 return SCM_UNSPECIFIED;
370 }
371 #undef FUNC_NAME
372
373 \f
374
375 SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
376 (SCM table, SCM key),
377 "This procedure returns the @code{(key . value)} pair from the\n"
378 "hash table @var{table}. If @var{table} does not hold an\n"
379 "associated value for @var{key}, @code{#f} is returned.\n"
380 "Uses @code{eq?} for equality testing.")
381 #define FUNC_NAME s_scm_hashq_get_handle
382 {
383 return scm_hash_fn_get_handle (table, key,
384 (scm_t_hash_fn) scm_ihashq,
385 (scm_t_assoc_fn) scm_sloppy_assq,
386 0);
387 }
388 #undef FUNC_NAME
389
390
391 SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
392 (SCM table, SCM key, SCM init),
393 "This function looks up @var{key} in @var{table} and returns its handle.\n"
394 "If @var{key} is not already present, a new handle is created which\n"
395 "associates @var{key} with @var{init}.")
396 #define FUNC_NAME s_scm_hashq_create_handle_x
397 {
398 return scm_hash_fn_create_handle_x (table, key, init,
399 (scm_t_hash_fn) scm_ihashq,
400 (scm_t_assoc_fn) scm_sloppy_assq,
401 0);
402 }
403 #undef FUNC_NAME
404
405
406 SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
407 (SCM table, SCM key, SCM dflt),
408 "Look up @var{key} in the hash table @var{table}, and return the\n"
409 "value (if any) associated with it. If @var{key} is not found,\n"
410 "return @var{dflt} (or @code{#f} if no @var{dflt} argument\n"
411 "is supplied). Uses @code{eq?} for equality testing.")
412 #define FUNC_NAME s_scm_hashq_ref
413 {
414 if (SCM_UNBNDP (dflt))
415 dflt = SCM_BOOL_F;
416
417 if (SCM_WEAK_TABLE_P (table))
418 return scm_weak_table_refq (table, key, dflt);
419
420 return scm_hash_fn_ref (table, key, dflt,
421 (scm_t_hash_fn) scm_ihashq,
422 (scm_t_assoc_fn) scm_sloppy_assq,
423 0);
424 }
425 #undef FUNC_NAME
426
427
428
429 SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
430 (SCM table, SCM key, SCM val),
431 "Find the entry in @var{table} associated with @var{key}, and\n"
432 "store @var{val} there. Uses @code{eq?} for equality testing.")
433 #define FUNC_NAME s_scm_hashq_set_x
434 {
435 if (SCM_WEAK_TABLE_P (table))
436 {
437 scm_weak_table_putq_x (table, key, val);
438 return val;
439 }
440
441 return scm_hash_fn_set_x (table, key, val,
442 (scm_t_hash_fn) scm_ihashq,
443 (scm_t_assoc_fn) scm_sloppy_assq,
444 0);
445 }
446 #undef FUNC_NAME
447
448
449
450 SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
451 (SCM table, SCM key),
452 "Remove @var{key} (and any value associated with it) from\n"
453 "@var{table}. Uses @code{eq?} for equality tests.")
454 #define FUNC_NAME s_scm_hashq_remove_x
455 {
456 if (SCM_WEAK_TABLE_P (table))
457 {
458 scm_weak_table_remq_x (table, key);
459 /* This return value is for historical compatibility with
460 hash-remove!, which returns either the "handle" corresponding
461 to the entry, or #f. Since weak tables don't have handles, we
462 have to return #f. */
463 return SCM_BOOL_F;
464 }
465
466 return scm_hash_fn_remove_x (table, key,
467 (scm_t_hash_fn) scm_ihashq,
468 (scm_t_assoc_fn) scm_sloppy_assq,
469 0);
470 }
471 #undef FUNC_NAME
472
473
474 \f
475
476 SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
477 (SCM table, SCM key),
478 "This procedure returns the @code{(key . value)} pair from the\n"
479 "hash table @var{table}. If @var{table} does not hold an\n"
480 "associated value for @var{key}, @code{#f} is returned.\n"
481 "Uses @code{eqv?} for equality testing.")
482 #define FUNC_NAME s_scm_hashv_get_handle
483 {
484 return scm_hash_fn_get_handle (table, key,
485 (scm_t_hash_fn) scm_ihashv,
486 (scm_t_assoc_fn) scm_sloppy_assv,
487 0);
488 }
489 #undef FUNC_NAME
490
491
492 SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
493 (SCM table, SCM key, SCM init),
494 "This function looks up @var{key} in @var{table} and returns its handle.\n"
495 "If @var{key} is not already present, a new handle is created which\n"
496 "associates @var{key} with @var{init}.")
497 #define FUNC_NAME s_scm_hashv_create_handle_x
498 {
499 return scm_hash_fn_create_handle_x (table, key, init,
500 (scm_t_hash_fn) scm_ihashv,
501 (scm_t_assoc_fn) scm_sloppy_assv,
502 0);
503 }
504 #undef FUNC_NAME
505
506
507 static int
508 assv_predicate (SCM k, SCM v, void *closure)
509 {
510 return scm_is_true (scm_eqv_p (k, SCM_PACK_POINTER (closure)));
511 }
512
513 SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
514 (SCM table, SCM key, SCM dflt),
515 "Look up @var{key} in the hash table @var{table}, and return the\n"
516 "value (if any) associated with it. If @var{key} is not found,\n"
517 "return @var{dflt} (or @code{#f} if no @var{dflt} argument\n"
518 "is supplied). Uses @code{eqv?} for equality testing.")
519 #define FUNC_NAME s_scm_hashv_ref
520 {
521 if (SCM_UNBNDP (dflt))
522 dflt = SCM_BOOL_F;
523
524 if (SCM_WEAK_TABLE_P (table))
525 return scm_c_weak_table_ref (table, scm_ihashv (key, -1),
526 assv_predicate, SCM_PACK (key), dflt);
527
528 return scm_hash_fn_ref (table, key, dflt,
529 (scm_t_hash_fn) scm_ihashv,
530 (scm_t_assoc_fn) scm_sloppy_assv,
531 0);
532 }
533 #undef FUNC_NAME
534
535
536
537 SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
538 (SCM table, SCM key, SCM val),
539 "Find the entry in @var{table} associated with @var{key}, and\n"
540 "store @var{value} there. Uses @code{eqv?} for equality testing.")
541 #define FUNC_NAME s_scm_hashv_set_x
542 {
543 if (SCM_WEAK_TABLE_P (table))
544 {
545 scm_c_weak_table_put_x (table, scm_ihashv (key, -1),
546 assv_predicate, SCM_PACK (key),
547 key, val);
548 return val;
549 }
550
551 return scm_hash_fn_set_x (table, key, val,
552 (scm_t_hash_fn) scm_ihashv,
553 (scm_t_assoc_fn) scm_sloppy_assv,
554 0);
555 }
556 #undef FUNC_NAME
557
558
559 SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
560 (SCM table, SCM key),
561 "Remove @var{key} (and any value associated with it) from\n"
562 "@var{table}. Uses @code{eqv?} for equality tests.")
563 #define FUNC_NAME s_scm_hashv_remove_x
564 {
565 if (SCM_WEAK_TABLE_P (table))
566 {
567 scm_c_weak_table_remove_x (table, scm_ihashv (key, -1),
568 assv_predicate, SCM_PACK (key));
569 /* See note in hashq-remove!. */
570 return SCM_BOOL_F;
571 }
572
573 return scm_hash_fn_remove_x (table, key,
574 (scm_t_hash_fn) scm_ihashv,
575 (scm_t_assoc_fn) scm_sloppy_assv,
576 0);
577 }
578 #undef FUNC_NAME
579
580 \f
581
582 SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
583 (SCM table, SCM key),
584 "This procedure returns the @code{(key . value)} pair from the\n"
585 "hash table @var{table}. If @var{table} does not hold an\n"
586 "associated value for @var{key}, @code{#f} is returned.\n"
587 "Uses @code{equal?} for equality testing.")
588 #define FUNC_NAME s_scm_hash_get_handle
589 {
590 return scm_hash_fn_get_handle (table, key,
591 (scm_t_hash_fn) scm_ihash,
592 (scm_t_assoc_fn) scm_sloppy_assoc,
593 0);
594 }
595 #undef FUNC_NAME
596
597
598 SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
599 (SCM table, SCM key, SCM init),
600 "This function looks up @var{key} in @var{table} and returns its handle.\n"
601 "If @var{key} is not already present, a new handle is created which\n"
602 "associates @var{key} with @var{init}.")
603 #define FUNC_NAME s_scm_hash_create_handle_x
604 {
605 return scm_hash_fn_create_handle_x (table, key, init,
606 (scm_t_hash_fn) scm_ihash,
607 (scm_t_assoc_fn) scm_sloppy_assoc,
608 0);
609 }
610 #undef FUNC_NAME
611
612
613 static int
614 assoc_predicate (SCM k, SCM v, void *closure)
615 {
616 return scm_is_true (scm_equal_p (k, SCM_PACK_POINTER (closure)));
617 }
618
619 SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
620 (SCM table, SCM key, SCM dflt),
621 "Look up @var{key} in the hash table @var{table}, and return the\n"
622 "value (if any) associated with it. If @var{key} is not found,\n"
623 "return @var{dflt} (or @code{#f} if no @var{dflt} argument\n"
624 "is supplied). Uses @code{equal?} for equality testing.")
625 #define FUNC_NAME s_scm_hash_ref
626 {
627 if (SCM_UNBNDP (dflt))
628 dflt = SCM_BOOL_F;
629
630 if (SCM_WEAK_TABLE_P (table))
631 return scm_c_weak_table_ref (table, scm_ihash (key, -1),
632 assoc_predicate, SCM_PACK (key), dflt);
633
634 return scm_hash_fn_ref (table, key, dflt,
635 (scm_t_hash_fn) scm_ihash,
636 (scm_t_assoc_fn) scm_sloppy_assoc,
637 0);
638 }
639 #undef FUNC_NAME
640
641
642
643 SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
644 (SCM table, SCM key, SCM val),
645 "Find the entry in @var{table} associated with @var{key}, and\n"
646 "store @var{val} there. Uses @code{equal?} for equality\n"
647 "testing.")
648 #define FUNC_NAME s_scm_hash_set_x
649 {
650 if (SCM_WEAK_TABLE_P (table))
651 {
652 scm_c_weak_table_put_x (table, scm_ihash (key, -1),
653 assoc_predicate, SCM_PACK (key),
654 key, val);
655 return val;
656 }
657
658 return scm_hash_fn_set_x (table, key, val,
659 (scm_t_hash_fn) scm_ihash,
660 (scm_t_assoc_fn) scm_sloppy_assoc,
661 0);
662 }
663 #undef FUNC_NAME
664
665
666
667 SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
668 (SCM table, SCM key),
669 "Remove @var{key} (and any value associated with it) from\n"
670 "@var{table}. Uses @code{equal?} for equality tests.")
671 #define FUNC_NAME s_scm_hash_remove_x
672 {
673 if (SCM_WEAK_TABLE_P (table))
674 {
675 scm_c_weak_table_remove_x (table, scm_ihash (key, -1),
676 assoc_predicate, SCM_PACK (key));
677 /* See note in hashq-remove!. */
678 return SCM_BOOL_F;
679 }
680
681 return scm_hash_fn_remove_x (table, key,
682 (scm_t_hash_fn) scm_ihash,
683 (scm_t_assoc_fn) scm_sloppy_assoc,
684 0);
685 }
686 #undef FUNC_NAME
687
688 \f
689
690
691 typedef struct scm_t_ihashx_closure
692 {
693 SCM hash;
694 SCM assoc;
695 SCM key;
696 } scm_t_ihashx_closure;
697
698 static unsigned long
699 scm_ihashx (SCM obj, unsigned long n, void *arg)
700 {
701 SCM answer;
702 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
703 answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
704 return scm_to_ulong (answer);
705 }
706
707 static SCM
708 scm_sloppy_assx (SCM obj, SCM alist, void *arg)
709 {
710 scm_t_ihashx_closure *closure = (scm_t_ihashx_closure *) arg;
711 return scm_call_2 (closure->assoc, obj, alist);
712 }
713
714 static int
715 assx_predicate (SCM k, SCM v, void *closure)
716 {
717 scm_t_ihashx_closure *c = (scm_t_ihashx_closure *) closure;
718
719 /* FIXME: The hashx interface is crazy. Hash tables have nothing to
720 do with alists in principle. Instead of getting an assoc proc,
721 hashx functions should use an equality predicate. Perhaps we can
722 change this before 2.2, but until then, add a terrible, terrible
723 hack. */
724
725 return scm_is_true (scm_call_2 (c->assoc, c->key, scm_acons (k, v, SCM_EOL)));
726 }
727
728
729 SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
730 (SCM hash, SCM assoc, SCM table, SCM key),
731 "This behaves the same way as the corresponding\n"
732 "@code{-get-handle} function, but uses @var{hash} as a hash\n"
733 "function and @var{assoc} to compare keys. @code{hash} must be\n"
734 "a function that takes two arguments, a key to be hashed and a\n"
735 "table size. @code{assoc} must be an associator function, like\n"
736 "@code{assoc}, @code{assq} or @code{assv}.")
737 #define FUNC_NAME s_scm_hashx_get_handle
738 {
739 scm_t_ihashx_closure closure;
740 closure.hash = hash;
741 closure.assoc = assoc;
742 closure.key = key;
743
744 return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
745 (void *) &closure);
746 }
747 #undef FUNC_NAME
748
749
750 SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
751 (SCM hash, SCM assoc, SCM table, SCM key, SCM init),
752 "This behaves the same way as the corresponding\n"
753 "@code{-create-handle} function, but uses @var{hash} as a hash\n"
754 "function and @var{assoc} to compare keys. @code{hash} must be\n"
755 "a function that takes two arguments, a key to be hashed and a\n"
756 "table size. @code{assoc} must be an associator function, like\n"
757 "@code{assoc}, @code{assq} or @code{assv}.")
758 #define FUNC_NAME s_scm_hashx_create_handle_x
759 {
760 scm_t_ihashx_closure closure;
761 closure.hash = hash;
762 closure.assoc = assoc;
763 closure.key = key;
764
765 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
766 scm_sloppy_assx, (void *)&closure);
767 }
768 #undef FUNC_NAME
769
770
771
772 SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
773 (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
774 "This behaves the same way as the corresponding @code{ref}\n"
775 "function, but uses @var{hash} as a hash function and\n"
776 "@var{assoc} to compare keys. @code{hash} must be a function\n"
777 "that takes two arguments, a key to be hashed and a table size.\n"
778 "@code{assoc} must be an associator function, like @code{assoc},\n"
779 "@code{assq} or @code{assv}.\n"
780 "\n"
781 "By way of illustration, @code{hashq-ref table key} is\n"
782 "equivalent to @code{hashx-ref hashq assq table key}.")
783 #define FUNC_NAME s_scm_hashx_ref
784 {
785 scm_t_ihashx_closure closure;
786 if (SCM_UNBNDP (dflt))
787 dflt = SCM_BOOL_F;
788 closure.hash = hash;
789 closure.assoc = assoc;
790 closure.key = key;
791
792 if (SCM_WEAK_TABLE_P (table))
793 {
794 unsigned long h = scm_to_ulong (scm_call_2 (hash, key,
795 scm_from_ulong (-1)));
796 return scm_c_weak_table_ref (table, h, assx_predicate, &closure, dflt);
797 }
798
799 return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
800 (void *)&closure);
801 }
802 #undef FUNC_NAME
803
804
805
806
807 SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
808 (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
809 "This behaves the same way as the corresponding @code{set!}\n"
810 "function, but uses @var{hash} as a hash function and\n"
811 "@var{assoc} to compare keys. @code{hash} must be a function\n"
812 "that takes two arguments, a key to be hashed and a table size.\n"
813 "@code{assoc} must be an associator function, like @code{assoc},\n"
814 "@code{assq} or @code{assv}.\n"
815 "\n"
816 " By way of illustration, @code{hashq-set! table key} is\n"
817 "equivalent to @code{hashx-set! hashq assq table key}.")
818 #define FUNC_NAME s_scm_hashx_set_x
819 {
820 scm_t_ihashx_closure closure;
821 closure.hash = hash;
822 closure.assoc = assoc;
823 closure.key = key;
824
825 if (SCM_WEAK_TABLE_P (table))
826 {
827 unsigned long h = scm_to_ulong (scm_call_2 (hash, key,
828 scm_from_ulong (-1)));
829 scm_c_weak_table_put_x (table, h, assx_predicate, &closure, key, val);
830 return val;
831 }
832
833 return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
834 (void *)&closure);
835 }
836 #undef FUNC_NAME
837
838 SCM_DEFINE (scm_hashx_remove_x, "hashx-remove!", 4, 0, 0,
839 (SCM hash, SCM assoc, SCM table, SCM obj),
840 "This behaves the same way as the corresponding @code{remove!}\n"
841 "function, but uses @var{hash} as a hash function and\n"
842 "@var{assoc} to compare keys. @code{hash} must be a function\n"
843 "that takes two arguments, a key to be hashed and a table size.\n"
844 "@code{assoc} must be an associator function, like @code{assoc},\n"
845 "@code{assq} or @code{assv}.\n"
846 "\n"
847 " By way of illustration, @code{hashq-remove! table key} is\n"
848 "equivalent to @code{hashx-remove! hashq assq #f table key}.")
849 #define FUNC_NAME s_scm_hashx_remove_x
850 {
851 scm_t_ihashx_closure closure;
852 closure.hash = hash;
853 closure.assoc = assoc;
854 closure.key = obj;
855
856 if (SCM_WEAK_TABLE_P (table))
857 {
858 unsigned long h = scm_to_ulong (scm_call_2 (hash, obj,
859 scm_from_ulong (-1)));
860 scm_c_weak_table_remove_x (table, h, assx_predicate, &closure);
861 /* See note in hashq-remove!. */
862 return SCM_BOOL_F;
863 }
864
865 return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
866 (void *) &closure);
867 }
868 #undef FUNC_NAME
869
870 /* Hash table iterators */
871
872 SCM_DEFINE (scm_hash_fold, "hash-fold", 3, 0, 0,
873 (SCM proc, SCM init, SCM table),
874 "An iterator over hash-table elements.\n"
875 "Accumulates and returns a result by applying PROC successively.\n"
876 "The arguments to PROC are \"(key value prior-result)\" where key\n"
877 "and value are successive pairs from the hash table TABLE, and\n"
878 "prior-result is either INIT (for the first application of PROC)\n"
879 "or the return value of the previous application of PROC.\n"
880 "For example, @code{(hash-fold acons '() tab)} will convert a hash\n"
881 "table into an a-list of key-value pairs.")
882 #define FUNC_NAME s_scm_hash_fold
883 {
884 SCM_VALIDATE_PROC (1, proc);
885
886 if (SCM_WEAK_TABLE_P (table))
887 return scm_weak_table_fold (proc, init, table);
888
889 SCM_VALIDATE_HASHTABLE (3, table);
890 return scm_internal_hash_fold ((scm_t_hash_fold_fn) scm_call_3,
891 (void *) SCM_UNPACK (proc), init, table);
892 }
893 #undef FUNC_NAME
894
895 static SCM
896 for_each_proc (void *proc, SCM handle)
897 {
898 return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
899 }
900
901 SCM_DEFINE (scm_hash_for_each, "hash-for-each", 2, 0, 0,
902 (SCM proc, SCM table),
903 "An iterator over hash-table elements.\n"
904 "Applies PROC successively on all hash table items.\n"
905 "The arguments to PROC are \"(key value)\" where key\n"
906 "and value are successive pairs from the hash table TABLE.")
907 #define FUNC_NAME s_scm_hash_for_each
908 {
909 SCM_VALIDATE_PROC (1, proc);
910
911 if (SCM_WEAK_TABLE_P (table))
912 {
913 scm_weak_table_for_each (proc, table);
914 return SCM_UNSPECIFIED;
915 }
916
917 SCM_VALIDATE_HASHTABLE (2, table);
918
919 scm_internal_hash_for_each_handle (for_each_proc,
920 (void *) SCM_UNPACK (proc),
921 table);
922 return SCM_UNSPECIFIED;
923 }
924 #undef FUNC_NAME
925
926 SCM_DEFINE (scm_hash_for_each_handle, "hash-for-each-handle", 2, 0, 0,
927 (SCM proc, SCM table),
928 "An iterator over hash-table elements.\n"
929 "Applies PROC successively on all hash table handles.")
930 #define FUNC_NAME s_scm_hash_for_each_handle
931 {
932 SCM_ASSERT (scm_is_true (scm_procedure_p (proc)), proc, 1, FUNC_NAME);
933 SCM_VALIDATE_HASHTABLE (2, table);
934
935 scm_internal_hash_for_each_handle ((scm_t_hash_handle_fn) scm_call_1,
936 (void *) SCM_UNPACK (proc),
937 table);
938 return SCM_UNSPECIFIED;
939 }
940 #undef FUNC_NAME
941
942 static SCM
943 map_proc (void *proc, SCM key, SCM data, SCM value)
944 {
945 return scm_cons (scm_call_2 (SCM_PACK (proc), key, data), value);
946 }
947
948 SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
949 (SCM proc, SCM table),
950 "An iterator over hash-table elements.\n"
951 "Accumulates and returns as a list the results of applying PROC successively.\n"
952 "The arguments to PROC are \"(key value)\" where key\n"
953 "and value are successive pairs from the hash table TABLE.")
954 #define FUNC_NAME s_scm_hash_map_to_list
955 {
956 SCM_VALIDATE_PROC (1, proc);
957
958 if (SCM_WEAK_TABLE_P (table))
959 return scm_weak_table_map_to_list (proc, table);
960
961 SCM_VALIDATE_HASHTABLE (2, table);
962 return scm_internal_hash_fold (map_proc,
963 (void *) SCM_UNPACK (proc),
964 SCM_EOL,
965 table);
966 }
967 #undef FUNC_NAME
968
969 \f
970
971 SCM
972 scm_internal_hash_fold (scm_t_hash_fold_fn fn, void *closure,
973 SCM init, SCM table)
974 #define FUNC_NAME s_scm_hash_fold
975 {
976 long i, n;
977 SCM buckets, result = init;
978
979 if (SCM_WEAK_TABLE_P (table))
980 return scm_c_weak_table_fold (fn, closure, init, table);
981
982 SCM_VALIDATE_HASHTABLE (0, table);
983 buckets = SCM_HASHTABLE_VECTOR (table);
984
985 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
986 for (i = 0; i < n; ++i)
987 {
988 SCM ls, handle;
989
990 for (ls = SCM_SIMPLE_VECTOR_REF (buckets, i); !scm_is_null (ls);
991 ls = SCM_CDR (ls))
992 {
993 handle = SCM_CAR (ls);
994 result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
995 }
996 }
997
998 return result;
999 }
1000 #undef FUNC_NAME
1001
1002 /* The following redundant code is here in order to be able to support
1003 hash-for-each-handle. An alternative would have been to replace
1004 this code and scm_internal_hash_fold above with a single
1005 scm_internal_hash_fold_handles, but we don't want to promote such
1006 an API. */
1007
1008 void
1009 scm_internal_hash_for_each_handle (scm_t_hash_handle_fn fn, void *closure,
1010 SCM table)
1011 #define FUNC_NAME s_scm_hash_for_each
1012 {
1013 long i, n;
1014 SCM buckets;
1015
1016 SCM_VALIDATE_HASHTABLE (0, table);
1017 buckets = SCM_HASHTABLE_VECTOR (table);
1018 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
1019
1020 for (i = 0; i < n; ++i)
1021 {
1022 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
1023 while (!scm_is_null (ls))
1024 {
1025 if (!scm_is_pair (ls))
1026 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
1027 handle = SCM_CAR (ls);
1028 if (!scm_is_pair (handle))
1029 SCM_WRONG_TYPE_ARG (SCM_ARG3, buckets);
1030 fn (closure, handle);
1031 ls = SCM_CDR (ls);
1032 }
1033 }
1034 }
1035 #undef FUNC_NAME
1036
1037 \f
1038
1039
1040 void
1041 scm_init_hashtab ()
1042 {
1043 #include "libguile/hashtab.x"
1044 }
1045
1046 /*
1047 Local Variables:
1048 c-file-style: "gnu"
1049 End:
1050 */