Fixes in weak hash tables handling, notably in `hash-fold'.
[bpt/guile.git] / libguile / hashtab.c
CommitLineData
2b829bbb 1/* Copyright (C) 1995,1996,1998,1999,2000,2001, 2003, 2004, 2006 Free Software Foundation, Inc.
0f2d19dd 2 *
73be1d9e
MV
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.
0f2d19dd 7 *
73be1d9e
MV
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.
0f2d19dd 12 *
73be1d9e
MV
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
92205699 15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
73be1d9e 16 */
1bbd0b84 17
1bbd0b84 18
0f2d19dd
JB
19\f
20
06c1d900
MV
21#include <stdio.h>
22
a0599745
MD
23#include "libguile/_scm.h"
24#include "libguile/alist.h"
25#include "libguile/hash.h"
26#include "libguile/eval.h"
fdc28395 27#include "libguile/root.h"
a0599745 28#include "libguile/vectors.h"
f59a096e 29#include "libguile/ports.h"
a0599745
MD
30
31#include "libguile/validate.h"
32#include "libguile/hashtab.h"
e4d21e6b
LC
33
34#include <gc/gc.h>
35#include <gc/gc_typed.h>
36
37
0f2d19dd
JB
38\f
39
c35738c1
MD
40/* NOTES
41 *
42 * 1. The current hash table implementation uses weak alist vectors
43 * (implementation in weaks.c) internally, but we do the scanning
44 * ourselves (in scan_weak_hashtables) because we need to update the
45 * hash table structure when items are dropped during GC.
46 *
47 * 2. All hash table operations still work on alist vectors.
48 *
f59a096e
MD
49 */
50
c35738c1
MD
51/* Hash tables are either vectors of association lists or smobs
52 * containing such vectors. Currently, the vector version represents
53 * constant size tables while those wrapped in a smob represents
54 * resizing tables.
55 *
56 * Growing or shrinking, with following rehashing, is triggered when
57 * the load factor
58 *
59 * L = N / S (N: number of items in table, S: bucket vector length)
60 *
61 * passes an upper limit of 0.9 or a lower limit of 0.25.
62 *
63 * The implementation stores the upper and lower number of items which
64 * trigger a resize in the hashtable object.
65 *
66 * Possible hash table sizes (primes) are stored in the array
67 * hashtable_size.
f59a096e
MD
68 */
69
f59a096e
MD
70scm_t_bits scm_tc16_hashtable;
71
0a4c1355
MD
72static unsigned long hashtable_size[] = {
73 31, 61, 113, 223, 443, 883, 1759, 3517, 7027, 14051, 28099, 56197, 112363,
93777082
MV
74 224717, 449419, 898823, 1797641, 3595271, 7190537, 14381041
75#if 0
76 /* vectors are currently restricted to 2^24-1 = 16777215 elements. */
77 28762081, 57524111, 115048217, 230096423, 460192829
78 /* larger values can't be represented as INUMs */
79#endif
f59a096e
MD
80};
81
93777082
MV
82#define HASHTABLE_SIZE_N (sizeof(hashtable_size)/sizeof(unsigned long))
83
f59a096e
MD
84static char *s_hashtable = "hashtable";
85
c35738c1
MD
86SCM weak_hashtables = SCM_EOL;
87
e4d21e6b 88
3a2de079 89\f
e4d21e6b
LC
90/* Weak cells for use in weak alist vectors (aka. weak hash tables).
91
92 We have weal-car cells, weak-cdr cells, and doubly weak cells. In weak
93 cells, the weak component(s) are not scanned for pointers and are
94 registered as disapperaring links; therefore, the weak component may be
95 set to NULL by the garbage collector when no other reference to that word
96 exist. Thus, we use `scm_fixup_weak_alist ()' to check for nullified weak
97 cells and remove them. */
98
99
100/* Type descriptors for weak-c[ad]r cells. */
101static GC_descr wcar_cell_descr, wcdr_cell_descr;
102
3a2de079
LC
103
104static SCM
105scm_weak_car_cell (SCM car, SCM cdr)
106{
e4d21e6b
LC
107 scm_t_cell *cell;
108
109 cell = (scm_t_cell *)GC_malloc_explicitly_typed (sizeof (*cell),
110 wcar_cell_descr);
3a2de079
LC
111
112 cell->word_0 = car;
113 cell->word_1 = cdr;
114
115 if (SCM_NIMP (car))
116 {
117 /* Weak car cells make sense iff the car is non-immediate. */
118 GC_GENERAL_REGISTER_DISAPPEARING_LINK ((GC_PTR)&cell->word_0,
119 (GC_PTR)SCM_UNPACK (car));
120 }
121
122 return (SCM_PACK (cell));
123}
124
125static SCM
126scm_weak_cdr_cell (SCM car, SCM cdr)
127{
e4d21e6b
LC
128 scm_t_cell *cell;
129
130 cell = (scm_t_cell *)GC_malloc_explicitly_typed (sizeof (*cell),
131 wcdr_cell_descr);
3a2de079
LC
132
133 cell->word_0 = car;
134 cell->word_1 = cdr;
135
136 if (SCM_NIMP (cdr))
137 {
138 /* Weak cdr cells make sense iff the cdr is non-immediate. */
139 GC_GENERAL_REGISTER_DISAPPEARING_LINK ((GC_PTR)&cell->word_1,
140 (GC_PTR)SCM_UNPACK (cdr));
141 }
142
143 return (SCM_PACK (cell));
144}
145
146static SCM
147scm_doubly_weak_cell (SCM car, SCM cdr)
148{
e4d21e6b 149 /* Doubly weak cells shall not be scanned at all for pointers. */
3a2de079
LC
150 scm_t_cell *cell = (scm_t_cell *)scm_gc_malloc_pointerless (sizeof (*cell),
151 "weak cell");
152
153 cell->word_0 = car;
154 cell->word_1 = cdr;
155
156 if (SCM_NIMP (car))
157 {
158 GC_GENERAL_REGISTER_DISAPPEARING_LINK ((GC_PTR)&cell->word_0,
159 (GC_PTR)SCM_UNPACK (car));
160 }
161 if (SCM_NIMP (cdr))
162 {
163 GC_GENERAL_REGISTER_DISAPPEARING_LINK ((GC_PTR)&cell->word_1,
164 (GC_PTR)SCM_UNPACK (cdr));
165 }
166
167 return (SCM_PACK (cell));
168}
169
170/* Testing the weak component(s) of a cell for reachability. */
171#define SCM_WEAK_CELL_WORD_DELETED_P(_cell, _word) \
e4d21e6b 172 (SCM_CELL_OBJECT ((_cell), (_word)) == SCM_PACK (NULL))
3a2de079
LC
173#define SCM_WEAK_CELL_CAR_DELETED_P(_cell) \
174 (SCM_WEAK_CELL_WORD_DELETED_P ((_cell), 0))
175#define SCM_WEAK_CELL_CDR_DELETED_P(_cell) \
176 (SCM_WEAK_CELL_WORD_DELETED_P ((_cell), 1))
177
178/* Accessing the components of a weak cell. */
179#define SCM_WEAK_CELL_WORD(_cell, _word) \
180 ((SCM_WEAK_CELL_WORD_DELETED_P ((_cell), (_word))) \
181 ? SCM_BOOL_F : SCM_CAR (pair))
182#define SCM_WEAK_CELL_CAR(_cell) (SCM_WEAK_CELL_WORD ((_cell), 0))
183#define SCM_WEAK_CELL_CDR(_cell) (SCM_WEAK_CELL_WORD ((_cell), 1))
184
185
186/* Return a ``usable'' version of ALIST, an alist of weak pairs. By
d9c82e20
LC
187 ``usable'', we mean that it contains only valid Scheme objects. On
188 return, REMOVE_ITEMS is set to the number of pairs that have been
189 deleted. */
3a2de079 190static SCM
d9c82e20 191scm_fixup_weak_alist (SCM alist, size_t *removed_items)
3a2de079
LC
192{
193 SCM result;
194 SCM prev = SCM_EOL;
195
d9c82e20 196 *removed_items = 0;
3a2de079
LC
197 for (result = alist;
198 scm_is_pair (alist);
199 prev = alist, alist = SCM_CDR (alist))
200 {
201 SCM pair = SCM_CAR (alist);
202
203 if (scm_is_pair (pair))
204 {
205 if ((SCM_WEAK_CELL_CAR_DELETED_P (pair))
206 || (SCM_WEAK_CELL_CDR_DELETED_P (pair)))
207 {
208 /* Remove from ALIST weak pair PAIR whose car/cdr has been
209 nullified by the GC. */
210 if (prev == SCM_EOL)
d9c82e20 211 result = SCM_CDR (alist);
3a2de079
LC
212 else
213 SCM_SETCDR (prev, SCM_CDR (alist));
214
d9c82e20 215 (*removed_items)++;
3a2de079
LC
216 continue;
217 }
218 }
219 }
220
221 return result;
222}
223
d9c82e20
LC
224
225/* Helper macros. */
226
227/* Return true if OBJ is either a weak hash table or a weak alist vector (as
228 defined in `weaks.[ch]').
741e83fc
LC
229 FIXME: We should eventually keep only weah hash tables. Actually, the
230 procs in `weaks.c' already no longer return vectors. */
231/* XXX: We assume that if OBJ is a vector, then it's a _weak_ alist vector. */
d9c82e20
LC
232#define IS_WEAK_THING(_obj) \
233 ((SCM_HASHTABLE_P (table) && (SCM_HASHTABLE_WEAK_P (table))) \
234 || (SCM_I_IS_VECTOR (table)))
235
236/* Fixup BUCKET, an alist part of weak hash table OBJ. BUCKETS is the full
237 bucket vector for OBJ and IDX is the index of BUCKET within this
741e83fc 238 vector. See also `scm_internal_hash_fold ()'. */
d9c82e20
LC
239#define START_WEAK_BUCKET_FIXUP(_obj, _buckets, _idx, _bucket) \
240do \
241 { \
242 size_t _removed; \
243 \
741e83fc 244 /* Disable the GC so that BUCKET remains valid until ASSOC_FN has \
d9c82e20
LC
245 returned. */ \
246 /* FIXME: We could maybe trigger a rehash here depending on whether \
247 `scm_fixup_weak_alist ()' noticed some change. */ \
248 GC_disable (); \
249 (_bucket) = scm_fixup_weak_alist ((_bucket), &_removed); \
250 SCM_SIMPLE_VECTOR_SET ((_buckets), (_idx), (_bucket)); \
251 \
252 if ((_removed) && (SCM_HASHTABLE_P (_obj))) \
253 SCM_SET_HASHTABLE_N_ITEMS ((_obj), \
254 SCM_HASHTABLE_N_ITEMS (_obj) - _removed); \
255 } \
256while (0)
257
258/* Terminate a weak bucket fixup phase. */
259#define END_WEAK_BUCKET_FIXUP(_obj, _buckets, _idx, _bucket) \
260 do { GC_enable (); } while (0)
261
262
3a2de079 263\f
c35738c1 264static SCM
a9cf5c71
MV
265make_hash_table (int flags, unsigned long k, const char *func_name)
266{
c35738c1 267 SCM table, vector;
9358af6a 268 scm_t_hashtable *t;
110beb83
MD
269 int i = 0, n = k ? k : 31;
270 while (i < HASHTABLE_SIZE_N && n > hashtable_size[i])
271 ++i;
272 n = hashtable_size[i];
3a2de079
LC
273
274 /* In both cases, i.e., regardless of whether we are creating a weak hash
275 table, we return a non-weak vector. This is because the vector itself
276 is not weak in the case of a weak hash table: the alist pairs are. */
277 vector = scm_c_make_vector (n, SCM_EOL);
278
279 t = scm_gc_malloc_pointerless (sizeof (*t), s_hashtable);
c35738c1 280 t->min_size_index = t->size_index = i;
f59a096e 281 t->n_items = 0;
c35738c1 282 t->lower = 0;
110beb83 283 t->upper = 9 * n / 10;
c35738c1 284 t->flags = flags;
d3a80924 285 t->hash_fn = NULL;
c35738c1
MD
286 if (flags)
287 {
3a2de079 288 /* FIXME: We should eventually remove WEAK_HASHTABLES. */
c35738c1
MD
289 SCM_NEWSMOB3 (table, scm_tc16_hashtable, vector, t, weak_hashtables);
290 weak_hashtables = table;
291 }
292 else
293 SCM_NEWSMOB3 (table, scm_tc16_hashtable, vector, t, SCM_EOL);
f59a096e
MD
294 return table;
295}
296
c35738c1
MD
297void
298scm_i_rehash (SCM table,
299 unsigned long (*hash_fn)(),
300 void *closure,
301 const char* func_name)
302{
303 SCM buckets, new_buckets;
304 int i;
305 unsigned long old_size;
306 unsigned long new_size;
307
741e83fc
LC
308 if (SCM_HASHTABLE_P (table) && SCM_HASHTABLE_WEAK_P (table))
309 /* FIXME: We don't currently support weak hash table rehashing. In order
310 to support it, we need to pay attention to NULL pairs, as in
311 `scm_internal_hash_fold ()', `START_WEAK_BUCKET_FIXUP ()', et al. */
312 return;
313
c35738c1
MD
314 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
315 {
316 /* rehashing is not triggered when i <= min_size */
317 i = SCM_HASHTABLE (table)->size_index;
318 do
319 --i;
320 while (i > SCM_HASHTABLE (table)->min_size_index
321 && SCM_HASHTABLE_N_ITEMS (table) < hashtable_size[i] / 4);
322 }
323 else
324 {
325 i = SCM_HASHTABLE (table)->size_index + 1;
326 if (i >= HASHTABLE_SIZE_N)
327 /* don't rehash */
328 return;
d3a80924
MV
329
330 /* Remember HASH_FN for rehash_after_gc, but only when CLOSURE
331 is not needed since CLOSURE can not be guaranteed to be valid
332 after this function returns.
333 */
334 if (closure == NULL)
335 SCM_HASHTABLE (table)->hash_fn = hash_fn;
c35738c1
MD
336 }
337 SCM_HASHTABLE (table)->size_index = i;
76da80e7 338
c35738c1
MD
339 new_size = hashtable_size[i];
340 if (i <= SCM_HASHTABLE (table)->min_size_index)
341 SCM_HASHTABLE (table)->lower = 0;
342 else
343 SCM_HASHTABLE (table)->lower = new_size / 4;
344 SCM_HASHTABLE (table)->upper = 9 * new_size / 10;
345 buckets = SCM_HASHTABLE_VECTOR (table);
3a2de079
LC
346
347 new_buckets = scm_c_make_vector (new_size, SCM_EOL);
c35738c1 348
bc6580eb
MV
349 /* When this is a weak hashtable, running the GC might change it.
350 We need to cope with this while rehashing its elements. We do
06c1d900
MV
351 this by first installing the new, empty bucket vector. Then we
352 remove the elements from the old bucket vector and insert them
353 into the new one.
bc6580eb
MV
354 */
355
356 SCM_SET_HASHTABLE_VECTOR (table, new_buckets);
357 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
bc6580eb 358
3ebc1832 359 old_size = SCM_SIMPLE_VECTOR_LENGTH (buckets);
c35738c1
MD
360 for (i = 0; i < old_size; ++i)
361 {
bc6580eb
MV
362 SCM ls, cell, handle;
363
364 ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
c2f21af5
MV
365 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_EOL);
366
bc6580eb 367 while (scm_is_pair (ls))
c35738c1
MD
368 {
369 unsigned long h;
bc6580eb
MV
370 cell = ls;
371 handle = SCM_CAR (cell);
372 ls = SCM_CDR (ls);
c35738c1
MD
373 h = hash_fn (SCM_CAR (handle), new_size, closure);
374 if (h >= new_size)
b9bd8526 375 scm_out_of_range (func_name, scm_from_ulong (h));
bc6580eb
MV
376 SCM_SETCDR (cell, SCM_SIMPLE_VECTOR_REF (new_buckets, h));
377 SCM_SIMPLE_VECTOR_SET (new_buckets, h, cell);
378 SCM_HASHTABLE_INCREMENT (table);
c35738c1
MD
379 }
380 }
c35738c1
MD
381}
382
383
f59a096e
MD
384static int
385hashtable_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
386{
c35738c1
MD
387 scm_puts ("#<", port);
388 if (SCM_HASHTABLE_WEAK_KEY_P (exp))
389 scm_puts ("weak-key-", port);
390 else if (SCM_HASHTABLE_WEAK_VALUE_P (exp))
391 scm_puts ("weak-value-", port);
392 else if (SCM_HASHTABLE_DOUBLY_WEAK_P (exp))
393 scm_puts ("doubly-weak-", port);
394 scm_puts ("hash-table ", port);
06c1d900 395 scm_uintprint (SCM_HASHTABLE_N_ITEMS (exp), 10, port);
f59a096e 396 scm_putc ('/', port);
3ebc1832
MV
397 scm_uintprint (SCM_SIMPLE_VECTOR_LENGTH (SCM_HASHTABLE_VECTOR (exp)),
398 10, port);
f59a096e
MD
399 scm_puts (">", port);
400 return 1;
401}
402
26224b3f
LC
403/* FIXME */
404#define UNMARKED_CELL_P(x) 0 /* (SCM_NIMP(x) && !SCM_GC_MARK_P (x)) */
c35738c1
MD
405
406/* keep track of hash tables that need to shrink after scan */
407static SCM to_rehash = SCM_EOL;
408
06c1d900
MV
409/* scan hash tables and update hash tables item count */
410void
411scm_i_scan_weak_hashtables ()
c35738c1 412{
26224b3f 413#if 0 /* FIXME */
c35738c1
MD
414 SCM *next = &weak_hashtables;
415 SCM h = *next;
d2e53ed6 416 while (!scm_is_null (h))
c35738c1
MD
417 {
418 if (!SCM_GC_MARK_P (h))
419 *next = h = SCM_HASHTABLE_NEXT (h);
420 else
421 {
06c1d900
MV
422 SCM vec = SCM_HASHTABLE_VECTOR (h);
423 size_t delta = SCM_I_WVECT_DELTA (vec);
424 SCM_I_SET_WVECT_DELTA (vec, 0);
425 SCM_SET_HASHTABLE_N_ITEMS (h, SCM_HASHTABLE_N_ITEMS (h) - delta);
426
427 if (SCM_HASHTABLE_N_ITEMS (h) < SCM_HASHTABLE_LOWER (h))
c35738c1
MD
428 {
429 SCM tmp = SCM_HASHTABLE_NEXT (h);
430 /* temporarily move table from weak_hashtables to to_rehash */
431 SCM_SET_HASHTABLE_NEXT (h, to_rehash);
432 to_rehash = h;
433 *next = h = tmp;
434 }
435 else
436 {
437 next = SCM_HASHTABLE_NEXTLOC (h);
438 h = SCM_HASHTABLE_NEXT (h);
439 }
440 }
441 }
26224b3f 442#endif
c35738c1
MD
443}
444
445static void *
446rehash_after_gc (void *dummy1 SCM_UNUSED,
447 void *dummy2 SCM_UNUSED,
448 void *dummy3 SCM_UNUSED)
449{
d2e53ed6 450 if (!scm_is_null (to_rehash))
c35738c1 451 {
e0245b20 452 SCM first = to_rehash, last, h;
15635be5
MD
453 /* important to clear to_rehash here so that we don't get stuck
454 in an infinite loop if scm_i_rehash causes GC */
455 to_rehash = SCM_EOL;
e0245b20 456 h = first;
c35738c1
MD
457 do
458 {
d3a80924
MV
459 /* Rehash only when we have a hash_fn.
460 */
461 if (SCM_HASHTABLE (h)->hash_fn)
462 scm_i_rehash (h, SCM_HASHTABLE (h)->hash_fn, NULL,
463 "rehash_after_gc");
c35738c1
MD
464 last = h;
465 h = SCM_HASHTABLE_NEXT (h);
d2e53ed6 466 } while (!scm_is_null (h));
c35738c1
MD
467 /* move tables back to weak_hashtables */
468 SCM_SET_HASHTABLE_NEXT (last, weak_hashtables);
e0245b20 469 weak_hashtables = first;
c35738c1
MD
470 }
471 return 0;
472}
473
f59a096e
MD
474static size_t
475hashtable_free (SCM obj)
476{
477 scm_gc_free (SCM_HASHTABLE (obj), sizeof (scm_t_hashtable), s_hashtable);
478 return 0;
479}
480
481
00ffa0e7 482SCM
c014a02e 483scm_c_make_hash_table (unsigned long k)
00ffa0e7 484{
c35738c1 485 return make_hash_table (0, k, "scm_c_make_hash_table");
f59a096e
MD
486}
487
488SCM_DEFINE (scm_make_hash_table, "make-hash-table", 0, 1, 0,
489 (SCM n),
a9cf5c71 490 "Make a new abstract hash table object with minimum number of buckets @var{n}\n")
f59a096e
MD
491#define FUNC_NAME s_scm_make_hash_table
492{
493 if (SCM_UNBNDP (n))
c35738c1 494 return make_hash_table (0, 0, FUNC_NAME);
f59a096e 495 else
a55c2b68 496 return make_hash_table (0, scm_to_ulong (n), FUNC_NAME);
f59a096e
MD
497}
498#undef FUNC_NAME
499
c35738c1
MD
500SCM_DEFINE (scm_make_weak_key_hash_table, "make-weak-key-hash-table", 0, 1, 0,
501 (SCM n),
502 "@deffnx {Scheme Procedure} make-weak-value-hash-table size\n"
503 "@deffnx {Scheme Procedure} make-doubly-weak-hash-table size\n"
a9cf5c71 504 "Return a weak hash table with @var{size} buckets.\n"
c35738c1
MD
505 "\n"
506 "You can modify weak hash tables in exactly the same way you\n"
507 "would modify regular hash tables. (@pxref{Hash Tables})")
508#define FUNC_NAME s_scm_make_weak_key_hash_table
f59a096e 509{
c35738c1
MD
510 if (SCM_UNBNDP (n))
511 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR, 0, FUNC_NAME);
f59a096e 512 else
a55c2b68
MV
513 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR,
514 scm_to_ulong (n), FUNC_NAME);
c35738c1
MD
515}
516#undef FUNC_NAME
517
518
519SCM_DEFINE (scm_make_weak_value_hash_table, "make-weak-value-hash-table", 0, 1, 0,
520 (SCM n),
521 "Return a hash table with weak values with @var{size} buckets.\n"
522 "(@pxref{Hash Tables})")
523#define FUNC_NAME s_scm_make_weak_value_hash_table
524{
525 if (SCM_UNBNDP (n))
526 return make_hash_table (SCM_HASHTABLEF_WEAK_CDR, 0, FUNC_NAME);
f59a096e 527 else
c35738c1 528 {
a55c2b68
MV
529 return make_hash_table (SCM_HASHTABLEF_WEAK_CDR,
530 scm_to_ulong (n), FUNC_NAME);
f59a096e 531 }
c35738c1
MD
532}
533#undef FUNC_NAME
f59a096e 534
c35738c1
MD
535
536SCM_DEFINE (scm_make_doubly_weak_hash_table, "make-doubly-weak-hash-table", 1, 0, 0,
537 (SCM n),
538 "Return a hash table with weak keys and values with @var{size}\n"
539 "buckets. (@pxref{Hash Tables})")
540#define FUNC_NAME s_scm_make_doubly_weak_hash_table
541{
542 if (SCM_UNBNDP (n))
543 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
544 0,
545 FUNC_NAME);
546 else
f59a096e 547 {
c35738c1 548 return make_hash_table (SCM_HASHTABLEF_WEAK_CAR | SCM_HASHTABLEF_WEAK_CDR,
a55c2b68 549 scm_to_ulong (n),
c35738c1 550 FUNC_NAME);
f59a096e 551 }
f59a096e 552}
c35738c1
MD
553#undef FUNC_NAME
554
555
556SCM_DEFINE (scm_hash_table_p, "hash-table?", 1, 0, 0,
557 (SCM obj),
a9cf5c71 558 "Return @code{#t} if @var{obj} is an abstract hash table object.")
c35738c1
MD
559#define FUNC_NAME s_scm_hash_table_p
560{
7888309b 561 return scm_from_bool (SCM_HASHTABLE_P (obj));
c35738c1
MD
562}
563#undef FUNC_NAME
564
565
566SCM_DEFINE (scm_weak_key_hash_table_p, "weak-key-hash-table?", 1, 0, 0,
567 (SCM obj),
568 "@deffnx {Scheme Procedure} weak-value-hash-table? obj\n"
569 "@deffnx {Scheme Procedure} doubly-weak-hash-table? obj\n"
570 "Return @code{#t} if @var{obj} is the specified weak hash\n"
571 "table. Note that a doubly weak hash table is neither a weak key\n"
572 "nor a weak value hash table.")
573#define FUNC_NAME s_scm_weak_key_hash_table_p
574{
7888309b 575 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_KEY_P (obj));
c35738c1
MD
576}
577#undef FUNC_NAME
578
579
580SCM_DEFINE (scm_weak_value_hash_table_p, "weak-value-hash-table?", 1, 0, 0,
581 (SCM obj),
582 "Return @code{#t} if @var{obj} is a weak value hash table.")
583#define FUNC_NAME s_scm_weak_value_hash_table_p
584{
7888309b 585 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_WEAK_VALUE_P (obj));
c35738c1
MD
586}
587#undef FUNC_NAME
588
589
590SCM_DEFINE (scm_doubly_weak_hash_table_p, "doubly-weak-hash-table?", 1, 0, 0,
591 (SCM obj),
592 "Return @code{#t} if @var{obj} is a doubly weak hash table.")
593#define FUNC_NAME s_scm_doubly_weak_hash_table_p
594{
7888309b 595 return scm_from_bool (SCM_HASHTABLE_P (obj) && SCM_HASHTABLE_DOUBLY_WEAK_P (obj));
c35738c1
MD
596}
597#undef FUNC_NAME
598
22a52da1 599
0f2d19dd 600SCM
34d19ef6 601scm_hash_fn_get_handle (SCM table, SCM obj, unsigned long (*hash_fn)(), SCM (*assoc_fn)(), void * closure)
22a52da1 602#define FUNC_NAME "scm_hash_fn_get_handle"
0f2d19dd 603{
e4d21e6b 604 int weak = 0;
c014a02e 605 unsigned long k;
d9c82e20 606 SCM buckets, alist, h;
0f2d19dd 607
f59a096e 608 if (SCM_HASHTABLE_P (table))
d9c82e20 609 buckets = SCM_HASHTABLE_VECTOR (table);
f59a096e 610 else
d9c82e20
LC
611 {
612 SCM_VALIDATE_VECTOR (1, table);
613 buckets = table;
614 }
615
616 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
22a52da1 617 return SCM_BOOL_F;
d9c82e20
LC
618 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
619 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
b9bd8526 620 scm_out_of_range ("hash_fn_get_handle", scm_from_ulong (k));
3a2de079 621
d9c82e20
LC
622 weak = IS_WEAK_THING (table);
623 alist = SCM_SIMPLE_VECTOR_REF (buckets, k);
3a2de079 624
d9c82e20
LC
625 if (weak)
626 START_WEAK_BUCKET_FIXUP (table, buckets, k, alist);
3a2de079
LC
627
628 h = assoc_fn (obj, alist, closure);
e4d21e6b 629 if (weak)
d9c82e20 630 END_WEAK_BUCKET_FIXUP (table, buckets, k, alist);
e4d21e6b 631
0f2d19dd
JB
632 return h;
633}
22a52da1 634#undef FUNC_NAME
0f2d19dd
JB
635
636
0f2d19dd 637SCM
34d19ef6
HWN
638scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init, unsigned long (*hash_fn)(),
639 SCM (*assoc_fn)(), void * closure)
cbaadf02 640#define FUNC_NAME "scm_hash_fn_create_handle_x"
0f2d19dd 641{
e4d21e6b 642 int weak = 0;
c014a02e 643 unsigned long k;
3a2de079 644 SCM buckets, alist, it;
0f2d19dd 645
f59a096e 646 if (SCM_HASHTABLE_P (table))
0a4c1355 647 buckets = SCM_HASHTABLE_VECTOR (table);
f59a096e
MD
648 else
649 {
3ebc1832 650 SCM_ASSERT (scm_is_simple_vector (table),
f59a096e
MD
651 table, SCM_ARG1, "hash_fn_create_handle_x");
652 buckets = table;
f59a096e 653 }
3ebc1832 654 if (SCM_SIMPLE_VECTOR_LENGTH (buckets) == 0)
cbaadf02
DH
655 SCM_MISC_ERROR ("void hashtable", SCM_EOL);
656
3ebc1832
MV
657 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
658 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
b9bd8526 659 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
3a2de079 660
d9c82e20 661 weak = IS_WEAK_THING (table);
3a2de079 662 alist = SCM_SIMPLE_VECTOR_REF (buckets, k);
d9c82e20
LC
663 if (weak)
664 START_WEAK_BUCKET_FIXUP (table, buckets, k, alist);
3a2de079
LC
665
666 it = assoc_fn (obj, alist, closure);
e4d21e6b 667 if (weak)
d9c82e20 668 END_WEAK_BUCKET_FIXUP (table, buckets, k, alist);
e4d21e6b 669
7888309b 670 if (scm_is_true (it))
0a4c1355 671 return it;
ee083ac2
DH
672 else
673 {
5b582466
MV
674 /* When this is a weak hashtable, running the GC can change it.
675 Thus, we must allocate the new cells first and can only then
676 access BUCKETS. Also, we need to fetch the bucket vector
677 again since the hashtable might have been rehashed. This
678 necessitates a new hash value as well.
bc6580eb 679 */
3a2de079
LC
680 SCM handle, new_bucket;
681
741e83fc 682 if ((SCM_HASHTABLE_P (table)) && (SCM_HASHTABLE_WEAK_P (table)))
3a2de079 683 {
741e83fc 684 /* FIXME: We don't support weak alist vectors. */
3a2de079
LC
685 /* Use a weak cell. */
686 if (SCM_HASHTABLE_DOUBLY_WEAK_P (table))
687 handle = scm_doubly_weak_cell (obj, init);
688 else if (SCM_HASHTABLE_WEAK_KEY_P (table))
689 handle = scm_weak_car_cell (obj, init);
690 else
691 handle = scm_weak_cdr_cell (obj, init);
692 }
693 else
694 /* Use a regular, non-weak cell. */
695 handle = scm_cons (obj, init);
696
697 new_bucket = scm_cons (handle, SCM_EOL);
698
5b582466
MV
699 if (!scm_is_eq (table, buckets)
700 && !scm_is_eq (SCM_HASHTABLE_VECTOR (table), buckets))
701 {
702 buckets = SCM_HASHTABLE_VECTOR (table);
703 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
704 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
705 scm_out_of_range ("hash_fn_create_handle_x", scm_from_ulong (k));
706 }
bc6580eb 707 SCM_SETCDR (new_bucket, SCM_SIMPLE_VECTOR_REF (buckets, k));
3ebc1832 708 SCM_SIMPLE_VECTOR_SET (buckets, k, new_bucket);
a41666e5 709 if (!scm_is_eq (table, buckets))
f59a096e 710 {
d3a80924
MV
711 /* Update element count and maybe rehash the table. The
712 table might have too few entries here since weak hash
713 tables used with the hashx_* functions can not be
714 rehashed after GC.
715 */
f59a096e 716 SCM_HASHTABLE_INCREMENT (table);
d3a80924
MV
717 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table)
718 || SCM_HASHTABLE_N_ITEMS (table) > SCM_HASHTABLE_UPPER (table))
c35738c1 719 scm_i_rehash (table, hash_fn, closure, FUNC_NAME);
f59a096e 720 }
ee083ac2
DH
721 return SCM_CAR (new_bucket);
722 }
0f2d19dd 723}
cbaadf02 724#undef FUNC_NAME
0f2d19dd 725
1cc91f1b 726
0f2d19dd 727SCM
34d19ef6
HWN
728scm_hash_fn_ref (SCM table, SCM obj, SCM dflt, unsigned long (*hash_fn)(),
729 SCM (*assoc_fn)(), void * closure)
0f2d19dd 730{
22a52da1 731 SCM it = scm_hash_fn_get_handle (table, obj, hash_fn, assoc_fn, closure);
d2e53ed6 732 if (scm_is_pair (it))
0f2d19dd 733 return SCM_CDR (it);
22a52da1
DH
734 else
735 return dflt;
0f2d19dd
JB
736}
737
738
739
1cc91f1b 740
0f2d19dd 741SCM
34d19ef6
HWN
742scm_hash_fn_set_x (SCM table, SCM obj, SCM val, unsigned long (*hash_fn)(),
743 SCM (*assoc_fn)(), void * closure)
0f2d19dd
JB
744{
745 SCM it;
746
747 it = scm_hash_fn_create_handle_x (table, obj, SCM_BOOL_F, hash_fn, assoc_fn, closure);
748 SCM_SETCDR (it, val);
749 return val;
750}
751
752
d9c82e20 753SCM
a9cf5c71
MV
754scm_hash_fn_remove_x (SCM table, SCM obj,
755 unsigned long (*hash_fn)(),
756 SCM (*assoc_fn)(),
757 void *closure)
0f2d19dd 758{
e4d21e6b 759 int weak = 0;
c014a02e 760 unsigned long k;
3a2de079 761 SCM buckets, alist, h;
0f2d19dd 762
87ca11ff 763 if (SCM_HASHTABLE_P (table))
0a4c1355 764 buckets = SCM_HASHTABLE_VECTOR (table);
87ca11ff
MD
765 else
766 {
3ebc1832
MV
767 SCM_ASSERT (scm_is_simple_vector (table), table,
768 SCM_ARG1, "hash_fn_remove_x");
87ca11ff 769 buckets = table;
87ca11ff 770 }
3ebc1832 771 if (SCM_SIMPLE_VECTOR_LENGTH (table) == 0)
0f2d19dd 772 return SCM_EOL;
87ca11ff 773
3ebc1832
MV
774 k = hash_fn (obj, SCM_SIMPLE_VECTOR_LENGTH (buckets), closure);
775 if (k >= SCM_SIMPLE_VECTOR_LENGTH (buckets))
b9bd8526 776 scm_out_of_range ("hash_fn_remove_x", scm_from_ulong (k));
3a2de079 777
d9c82e20 778 weak = IS_WEAK_THING (table);
3a2de079 779 alist = SCM_SIMPLE_VECTOR_REF (buckets, k);
d9c82e20
LC
780 if (weak)
781 START_WEAK_BUCKET_FIXUP (table, buckets, k, alist);
3a2de079
LC
782
783 h = assoc_fn (obj, alist, closure);
e4d21e6b 784 if (weak)
d9c82e20 785 END_WEAK_BUCKET_FIXUP (table, buckets, k, alist);
e4d21e6b 786
7888309b 787 if (scm_is_true (h))
87ca11ff 788 {
3ebc1832 789 SCM_SIMPLE_VECTOR_SET
a9cf5c71 790 (buckets, k, scm_delq_x (h, SCM_SIMPLE_VECTOR_REF (buckets, k)));
a41666e5 791 if (!scm_is_eq (table, buckets))
87ca11ff
MD
792 {
793 SCM_HASHTABLE_DECREMENT (table);
794 if (SCM_HASHTABLE_N_ITEMS (table) < SCM_HASHTABLE_LOWER (table))
c35738c1 795 scm_i_rehash (table, hash_fn, closure, "scm_hash_fn_remove_x");
87ca11ff
MD
796 }
797 }
0f2d19dd
JB
798 return h;
799}
800
c35738c1
MD
801SCM_DEFINE (scm_hash_clear_x, "hash-clear!", 1, 0, 0,
802 (SCM table),
a9cf5c71 803 "Remove all items from @var{table} (without triggering a resize).")
c35738c1
MD
804#define FUNC_NAME s_scm_hash_clear_x
805{
a9cf5c71
MV
806 if (SCM_HASHTABLE_P (table))
807 {
808 scm_vector_fill_x (SCM_HASHTABLE_VECTOR (table), SCM_EOL);
809 SCM_SET_HASHTABLE_N_ITEMS (table, 0);
810 }
811 else
812 scm_vector_fill_x (table, SCM_EOL);
c35738c1
MD
813 return SCM_UNSPECIFIED;
814}
815#undef FUNC_NAME
0f2d19dd
JB
816
817\f
818
a1ec6916 819SCM_DEFINE (scm_hashq_get_handle, "hashq-get-handle", 2, 0, 0,
22a52da1
DH
820 (SCM table, SCM key),
821 "This procedure returns the @code{(key . value)} pair from the\n"
822 "hash table @var{table}. If @var{table} does not hold an\n"
823 "associated value for @var{key}, @code{#f} is returned.\n"
824 "Uses @code{eq?} for equality testing.")
1bbd0b84 825#define FUNC_NAME s_scm_hashq_get_handle
0f2d19dd 826{
22a52da1 827 return scm_hash_fn_get_handle (table, key, scm_ihashq, scm_sloppy_assq, 0);
0f2d19dd 828}
1bbd0b84 829#undef FUNC_NAME
0f2d19dd
JB
830
831
a1ec6916 832SCM_DEFINE (scm_hashq_create_handle_x, "hashq-create-handle!", 3, 0, 0,
d550d22a
GB
833 (SCM table, SCM key, SCM init),
834 "This function looks up @var{key} in @var{table} and returns its handle.\n"
b380b885
MD
835 "If @var{key} is not already present, a new handle is created which\n"
836 "associates @var{key} with @var{init}.")
1bbd0b84 837#define FUNC_NAME s_scm_hashq_create_handle_x
0f2d19dd 838{
d550d22a 839 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashq, scm_sloppy_assq, 0);
0f2d19dd 840}
1bbd0b84 841#undef FUNC_NAME
0f2d19dd
JB
842
843
a1ec6916 844SCM_DEFINE (scm_hashq_ref, "hashq-ref", 2, 1, 0,
1e6808ea 845 (SCM table, SCM key, SCM dflt),
b380b885
MD
846 "Look up @var{key} in the hash table @var{table}, and return the\n"
847 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
848 "return @var{default} (or @code{#f} if no @var{default} argument\n"
849 "is supplied). Uses @code{eq?} for equality testing.")
1bbd0b84 850#define FUNC_NAME s_scm_hashq_ref
0f2d19dd 851{
54778cd3 852 if (SCM_UNBNDP (dflt))
0f2d19dd 853 dflt = SCM_BOOL_F;
1e6808ea 854 return scm_hash_fn_ref (table, key, dflt, scm_ihashq, scm_sloppy_assq, 0);
0f2d19dd 855}
1bbd0b84 856#undef FUNC_NAME
0f2d19dd
JB
857
858
859
a1ec6916 860SCM_DEFINE (scm_hashq_set_x, "hashq-set!", 3, 0, 0,
1e6808ea 861 (SCM table, SCM key, SCM val),
5352393c
MG
862 "Find the entry in @var{table} associated with @var{key}, and\n"
863 "store @var{value} there. Uses @code{eq?} for equality testing.")
1bbd0b84 864#define FUNC_NAME s_scm_hashq_set_x
0f2d19dd 865{
1e6808ea 866 return scm_hash_fn_set_x (table, key, val, scm_ihashq, scm_sloppy_assq, 0);
0f2d19dd 867}
1bbd0b84 868#undef FUNC_NAME
0f2d19dd
JB
869
870
871
a1ec6916 872SCM_DEFINE (scm_hashq_remove_x, "hashq-remove!", 2, 0, 0,
1e6808ea 873 (SCM table, SCM key),
5352393c
MG
874 "Remove @var{key} (and any value associated with it) from\n"
875 "@var{table}. Uses @code{eq?} for equality tests.")
1bbd0b84 876#define FUNC_NAME s_scm_hashq_remove_x
0f2d19dd 877{
a9cf5c71 878 return scm_hash_fn_remove_x (table, key, scm_ihashq, scm_sloppy_assq, 0);
0f2d19dd 879}
1bbd0b84 880#undef FUNC_NAME
0f2d19dd
JB
881
882
883\f
884
a1ec6916 885SCM_DEFINE (scm_hashv_get_handle, "hashv-get-handle", 2, 0, 0,
22a52da1
DH
886 (SCM table, SCM key),
887 "This procedure returns the @code{(key . value)} pair from the\n"
888 "hash table @var{table}. If @var{table} does not hold an\n"
889 "associated value for @var{key}, @code{#f} is returned.\n"
890 "Uses @code{eqv?} for equality testing.")
1bbd0b84 891#define FUNC_NAME s_scm_hashv_get_handle
0f2d19dd 892{
22a52da1 893 return scm_hash_fn_get_handle (table, key, scm_ihashv, scm_sloppy_assv, 0);
0f2d19dd 894}
1bbd0b84 895#undef FUNC_NAME
0f2d19dd
JB
896
897
a1ec6916 898SCM_DEFINE (scm_hashv_create_handle_x, "hashv-create-handle!", 3, 0, 0,
d550d22a
GB
899 (SCM table, SCM key, SCM init),
900 "This function looks up @var{key} in @var{table} and returns its handle.\n"
901 "If @var{key} is not already present, a new handle is created which\n"
902 "associates @var{key} with @var{init}.")
1bbd0b84 903#define FUNC_NAME s_scm_hashv_create_handle_x
0f2d19dd 904{
1e6808ea
MG
905 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashv,
906 scm_sloppy_assv, 0);
0f2d19dd 907}
1bbd0b84 908#undef FUNC_NAME
0f2d19dd
JB
909
910
a1ec6916 911SCM_DEFINE (scm_hashv_ref, "hashv-ref", 2, 1, 0,
1e6808ea 912 (SCM table, SCM key, SCM dflt),
d550d22a
GB
913 "Look up @var{key} in the hash table @var{table}, and return the\n"
914 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
915 "return @var{default} (or @code{#f} if no @var{default} argument\n"
916 "is supplied). Uses @code{eqv?} for equality testing.")
1bbd0b84 917#define FUNC_NAME s_scm_hashv_ref
0f2d19dd 918{
54778cd3 919 if (SCM_UNBNDP (dflt))
0f2d19dd 920 dflt = SCM_BOOL_F;
1e6808ea 921 return scm_hash_fn_ref (table, key, dflt, scm_ihashv, scm_sloppy_assv, 0);
0f2d19dd 922}
1bbd0b84 923#undef FUNC_NAME
0f2d19dd
JB
924
925
926
a1ec6916 927SCM_DEFINE (scm_hashv_set_x, "hashv-set!", 3, 0, 0,
1e6808ea 928 (SCM table, SCM key, SCM val),
5352393c
MG
929 "Find the entry in @var{table} associated with @var{key}, and\n"
930 "store @var{value} there. Uses @code{eqv?} for equality testing.")
1bbd0b84 931#define FUNC_NAME s_scm_hashv_set_x
0f2d19dd 932{
1e6808ea 933 return scm_hash_fn_set_x (table, key, val, scm_ihashv, scm_sloppy_assv, 0);
0f2d19dd 934}
1bbd0b84 935#undef FUNC_NAME
0f2d19dd
JB
936
937
a1ec6916 938SCM_DEFINE (scm_hashv_remove_x, "hashv-remove!", 2, 0, 0,
1e6808ea 939 (SCM table, SCM key),
5352393c
MG
940 "Remove @var{key} (and any value associated with it) from\n"
941 "@var{table}. Uses @code{eqv?} for equality tests.")
1bbd0b84 942#define FUNC_NAME s_scm_hashv_remove_x
0f2d19dd 943{
a9cf5c71 944 return scm_hash_fn_remove_x (table, key, scm_ihashv, scm_sloppy_assv, 0);
0f2d19dd 945}
1bbd0b84 946#undef FUNC_NAME
0f2d19dd
JB
947
948\f
949
a1ec6916 950SCM_DEFINE (scm_hash_get_handle, "hash-get-handle", 2, 0, 0,
22a52da1
DH
951 (SCM table, SCM key),
952 "This procedure returns the @code{(key . value)} pair from the\n"
953 "hash table @var{table}. If @var{table} does not hold an\n"
954 "associated value for @var{key}, @code{#f} is returned.\n"
955 "Uses @code{equal?} for equality testing.")
1bbd0b84 956#define FUNC_NAME s_scm_hash_get_handle
0f2d19dd 957{
22a52da1 958 return scm_hash_fn_get_handle (table, key, scm_ihash, scm_sloppy_assoc, 0);
0f2d19dd 959}
1bbd0b84 960#undef FUNC_NAME
0f2d19dd
JB
961
962
a1ec6916 963SCM_DEFINE (scm_hash_create_handle_x, "hash-create-handle!", 3, 0, 0,
d550d22a
GB
964 (SCM table, SCM key, SCM init),
965 "This function looks up @var{key} in @var{table} and returns its handle.\n"
966 "If @var{key} is not already present, a new handle is created which\n"
967 "associates @var{key} with @var{init}.")
1bbd0b84 968#define FUNC_NAME s_scm_hash_create_handle_x
0f2d19dd 969{
d550d22a 970 return scm_hash_fn_create_handle_x (table, key, init, scm_ihash, scm_sloppy_assoc, 0);
0f2d19dd 971}
1bbd0b84 972#undef FUNC_NAME
0f2d19dd
JB
973
974
a1ec6916 975SCM_DEFINE (scm_hash_ref, "hash-ref", 2, 1, 0,
1e6808ea 976 (SCM table, SCM key, SCM dflt),
d550d22a
GB
977 "Look up @var{key} in the hash table @var{table}, and return the\n"
978 "value (if any) associated with it. If @var{key} is not found,\n"
5352393c
MG
979 "return @var{default} (or @code{#f} if no @var{default} argument\n"
980 "is supplied). Uses @code{equal?} for equality testing.")
1bbd0b84 981#define FUNC_NAME s_scm_hash_ref
0f2d19dd 982{
54778cd3 983 if (SCM_UNBNDP (dflt))
0f2d19dd 984 dflt = SCM_BOOL_F;
1e6808ea 985 return scm_hash_fn_ref (table, key, dflt, scm_ihash, scm_sloppy_assoc, 0);
0f2d19dd 986}
1bbd0b84 987#undef FUNC_NAME
0f2d19dd
JB
988
989
990
a1ec6916 991SCM_DEFINE (scm_hash_set_x, "hash-set!", 3, 0, 0,
1e6808ea 992 (SCM table, SCM key, SCM val),
5352393c
MG
993 "Find the entry in @var{table} associated with @var{key}, and\n"
994 "store @var{value} there. Uses @code{equal?} for equality\n"
995 "testing.")
1bbd0b84 996#define FUNC_NAME s_scm_hash_set_x
0f2d19dd 997{
1e6808ea 998 return scm_hash_fn_set_x (table, key, val, scm_ihash, scm_sloppy_assoc, 0);
0f2d19dd 999}
1bbd0b84 1000#undef FUNC_NAME
0f2d19dd
JB
1001
1002
1003
a1ec6916 1004SCM_DEFINE (scm_hash_remove_x, "hash-remove!", 2, 0, 0,
1e6808ea 1005 (SCM table, SCM key),
5352393c
MG
1006 "Remove @var{key} (and any value associated with it) from\n"
1007 "@var{table}. Uses @code{equal?} for equality tests.")
1bbd0b84 1008#define FUNC_NAME s_scm_hash_remove_x
0f2d19dd 1009{
a9cf5c71 1010 return scm_hash_fn_remove_x (table, key, scm_ihash, scm_sloppy_assoc, 0);
0f2d19dd 1011}
1bbd0b84 1012#undef FUNC_NAME
0f2d19dd
JB
1013
1014\f
1015
1016
92c2555f 1017typedef struct scm_t_ihashx_closure
0f2d19dd
JB
1018{
1019 SCM hash;
1020 SCM assoc;
92c2555f 1021} scm_t_ihashx_closure;
0f2d19dd
JB
1022
1023
1cc91f1b 1024
c014a02e 1025static unsigned long
92c2555f 1026scm_ihashx (SCM obj, unsigned long n, scm_t_ihashx_closure *closure)
0f2d19dd 1027{
b9bd8526 1028 SCM answer = scm_call_2 (closure->hash, obj, scm_from_ulong (n));
a55c2b68 1029 return scm_to_ulong (answer);
0f2d19dd
JB
1030}
1031
1032
1cc91f1b 1033
0f2d19dd 1034static SCM
92c2555f 1035scm_sloppy_assx (SCM obj, SCM alist, scm_t_ihashx_closure *closure)
0f2d19dd 1036{
87ca11ff 1037 return scm_call_2 (closure->assoc, obj, alist);
0f2d19dd
JB
1038}
1039
1040
a1ec6916 1041SCM_DEFINE (scm_hashx_get_handle, "hashx-get-handle", 4, 0, 0,
1e6808ea
MG
1042 (SCM hash, SCM assoc, SCM table, SCM key),
1043 "This behaves the same way as the corresponding\n"
1044 "@code{-get-handle} function, but uses @var{hash} as a hash\n"
1045 "function and @var{assoc} to compare keys. @code{hash} must be\n"
1046 "a function that takes two arguments, a key to be hashed and a\n"
d550d22a
GB
1047 "table size. @code{assoc} must be an associator function, like\n"
1048 "@code{assoc}, @code{assq} or @code{assv}.")
1bbd0b84 1049#define FUNC_NAME s_scm_hashx_get_handle
0f2d19dd 1050{
92c2555f 1051 scm_t_ihashx_closure closure;
0f2d19dd
JB
1052 closure.hash = hash;
1053 closure.assoc = assoc;
1e6808ea 1054 return scm_hash_fn_get_handle (table, key, scm_ihashx, scm_sloppy_assx,
87ca11ff 1055 (void *) &closure);
0f2d19dd 1056}
1bbd0b84 1057#undef FUNC_NAME
0f2d19dd
JB
1058
1059
a1ec6916 1060SCM_DEFINE (scm_hashx_create_handle_x, "hashx-create-handle!", 5, 0, 0,
1e6808ea
MG
1061 (SCM hash, SCM assoc, SCM table, SCM key, SCM init),
1062 "This behaves the same way as the corresponding\n"
1063 "@code{-create-handle} function, but uses @var{hash} as a hash\n"
1064 "function and @var{assoc} to compare keys. @code{hash} must be\n"
1065 "a function that takes two arguments, a key to be hashed and a\n"
d550d22a
GB
1066 "table size. @code{assoc} must be an associator function, like\n"
1067 "@code{assoc}, @code{assq} or @code{assv}.")
1bbd0b84 1068#define FUNC_NAME s_scm_hashx_create_handle_x
0f2d19dd 1069{
92c2555f 1070 scm_t_ihashx_closure closure;
0f2d19dd
JB
1071 closure.hash = hash;
1072 closure.assoc = assoc;
1e6808ea
MG
1073 return scm_hash_fn_create_handle_x (table, key, init, scm_ihashx,
1074 scm_sloppy_assx, (void *)&closure);
0f2d19dd 1075}
1bbd0b84 1076#undef FUNC_NAME
0f2d19dd
JB
1077
1078
1079
a1ec6916 1080SCM_DEFINE (scm_hashx_ref, "hashx-ref", 4, 1, 0,
1e6808ea 1081 (SCM hash, SCM assoc, SCM table, SCM key, SCM dflt),
d550d22a 1082 "This behaves the same way as the corresponding @code{ref}\n"
1e6808ea
MG
1083 "function, but uses @var{hash} as a hash function and\n"
1084 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1085 "that takes two arguments, a key to be hashed and a table size.\n"
1086 "@code{assoc} must be an associator function, like @code{assoc},\n"
1087 "@code{assq} or @code{assv}.\n"
1088 "\n"
1089 "By way of illustration, @code{hashq-ref table key} is\n"
1090 "equivalent to @code{hashx-ref hashq assq table key}.")
1bbd0b84 1091#define FUNC_NAME s_scm_hashx_ref
0f2d19dd 1092{
92c2555f 1093 scm_t_ihashx_closure closure;
54778cd3 1094 if (SCM_UNBNDP (dflt))
0f2d19dd
JB
1095 dflt = SCM_BOOL_F;
1096 closure.hash = hash;
1097 closure.assoc = assoc;
1e6808ea
MG
1098 return scm_hash_fn_ref (table, key, dflt, scm_ihashx, scm_sloppy_assx,
1099 (void *)&closure);
0f2d19dd 1100}
1bbd0b84 1101#undef FUNC_NAME
0f2d19dd
JB
1102
1103
1104
1105
a1ec6916 1106SCM_DEFINE (scm_hashx_set_x, "hashx-set!", 5, 0, 0,
1e6808ea 1107 (SCM hash, SCM assoc, SCM table, SCM key, SCM val),
d550d22a 1108 "This behaves the same way as the corresponding @code{set!}\n"
1e6808ea
MG
1109 "function, but uses @var{hash} as a hash function and\n"
1110 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1111 "that takes two arguments, a key to be hashed and a table size.\n"
1112 "@code{assoc} must be an associator function, like @code{assoc},\n"
1113 "@code{assq} or @code{assv}.\n"
1114 "\n"
1115 " By way of illustration, @code{hashq-set! table key} is\n"
1116 "equivalent to @code{hashx-set! hashq assq table key}.")
1bbd0b84 1117#define FUNC_NAME s_scm_hashx_set_x
0f2d19dd 1118{
92c2555f 1119 scm_t_ihashx_closure closure;
0f2d19dd
JB
1120 closure.hash = hash;
1121 closure.assoc = assoc;
1e6808ea
MG
1122 return scm_hash_fn_set_x (table, key, val, scm_ihashx, scm_sloppy_assx,
1123 (void *)&closure);
0f2d19dd 1124}
1bbd0b84 1125#undef FUNC_NAME
0f2d19dd 1126
a9cf5c71
MV
1127SCM_DEFINE (scm_hashx_remove_x, "hashx-remove!", 4, 0, 0,
1128 (SCM hash, SCM assoc, SCM table, SCM obj),
1129 "This behaves the same way as the corresponding @code{remove!}\n"
1130 "function, but uses @var{hash} as a hash function and\n"
1131 "@var{assoc} to compare keys. @code{hash} must be a function\n"
1132 "that takes two arguments, a key to be hashed and a table size.\n"
1133 "@code{assoc} must be an associator function, like @code{assoc},\n"
1134 "@code{assq} or @code{assv}.\n"
1135 "\n"
1136 " By way of illustration, @code{hashq-remove! table key} is\n"
1137 "equivalent to @code{hashx-remove! hashq assq #f table key}.")
1138#define FUNC_NAME s_scm_hashx_remove_x
0f2d19dd 1139{
92c2555f 1140 scm_t_ihashx_closure closure;
0f2d19dd
JB
1141 closure.hash = hash;
1142 closure.assoc = assoc;
4cff503f
KR
1143 return scm_hash_fn_remove_x (table, obj, scm_ihashx, scm_sloppy_assx,
1144 (void *) &closure);
0f2d19dd 1145}
a9cf5c71 1146#undef FUNC_NAME
0f2d19dd 1147
711a9fd7 1148/* Hash table iterators */
b94903c2 1149
711a9fd7 1150static const char s_scm_hash_fold[];
c7df61cd
MD
1151
1152SCM
8cd5191b 1153scm_internal_hash_fold (SCM (*fn) (), void *closure, SCM init, SCM table)
c7df61cd 1154{
87ca11ff
MD
1155 long i, n;
1156 SCM buckets, result = init;
87ca11ff
MD
1157
1158 if (SCM_HASHTABLE_P (table))
0a4c1355 1159 buckets = SCM_HASHTABLE_VECTOR (table);
87ca11ff 1160 else
741e83fc 1161 /* Weak alist vector. */
0a4c1355
MD
1162 buckets = table;
1163
3ebc1832 1164 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
c7df61cd
MD
1165 for (i = 0; i < n; ++i)
1166 {
741e83fc
LC
1167 SCM prev, ls;
1168
1169 for (prev = SCM_BOOL_F, ls = SCM_SIMPLE_VECTOR_REF (buckets, i);
1170 !scm_is_null (ls);
1171 prev = ls, ls = SCM_CDR (ls))
c7df61cd 1172 {
741e83fc
LC
1173 SCM handle;
1174
d2e53ed6 1175 if (!scm_is_pair (ls))
0a4c1355 1176 scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
741e83fc 1177
c7df61cd 1178 handle = SCM_CAR (ls);
d2e53ed6 1179 if (!scm_is_pair (handle))
0a4c1355 1180 scm_wrong_type_arg (s_scm_hash_fold, SCM_ARG3, buckets);
741e83fc
LC
1181
1182 if (IS_WEAK_THING (table))
1183 {
1184 if ((SCM_CAR (handle) == SCM_PACK (NULL))
1185 || (SCM_CDR (handle) == SCM_PACK (NULL)))
1186 {
1187 /* We hit a weak pair whose car/cdr has become
1188 unreachable: unlink it from the bucket. */
1189 if (prev != SCM_BOOL_F)
1190 SCM_SETCDR (prev, SCM_CDR (ls));
1191 else
1192 SCM_SIMPLE_VECTOR_SET (buckets, i, SCM_CDR (ls));
1193
1194 if (SCM_HASHTABLE_P (table))
1195 {
1196 /* Update the item count. */
1197 unsigned long items = SCM_HASHTABLE_N_ITEMS (table);
1198
1199 if (items <= 0)
1200 abort ();
1201 SCM_SET_HASHTABLE_N_ITEMS (table, items - 1);
1202 }
1203
1204 continue;
1205 }
1206 }
1207
c7df61cd 1208 result = fn (closure, SCM_CAR (handle), SCM_CDR (handle), result);
c7df61cd
MD
1209 }
1210 }
87ca11ff 1211
c7df61cd
MD
1212 return result;
1213}
1214
711a9fd7
MD
1215/* The following redundant code is here in order to be able to support
1216 hash-for-each-handle. An alternative would have been to replace
1217 this code and scm_internal_hash_fold above with a single
1218 scm_internal_hash_fold_handles, but we don't want to promote such
1219 an API. */
1220
1221static const char s_scm_hash_for_each[];
1222
1223void
1224scm_internal_hash_for_each_handle (SCM (*fn) (), void *closure, SCM table)
1225{
1226 long i, n;
1227 SCM buckets;
1228
1229 if (SCM_HASHTABLE_P (table))
1230 buckets = SCM_HASHTABLE_VECTOR (table);
1231 else
1232 buckets = table;
1233
3ebc1832 1234 n = SCM_SIMPLE_VECTOR_LENGTH (buckets);
711a9fd7
MD
1235 for (i = 0; i < n; ++i)
1236 {
3ebc1832 1237 SCM ls = SCM_SIMPLE_VECTOR_REF (buckets, i), handle;
d2e53ed6 1238 while (!scm_is_null (ls))
711a9fd7 1239 {
d2e53ed6 1240 if (!scm_is_pair (ls))
711a9fd7
MD
1241 scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
1242 handle = SCM_CAR (ls);
d2e53ed6 1243 if (!scm_is_pair (handle))
711a9fd7
MD
1244 scm_wrong_type_arg (s_scm_hash_for_each, SCM_ARG3, buckets);
1245 fn (closure, handle);
1246 ls = SCM_CDR (ls);
1247 }
1248 }
1249}
1250
1251SCM_DEFINE (scm_hash_fold, "hash-fold", 3, 0, 0,
1252 (SCM proc, SCM init, SCM table),
1253 "An iterator over hash-table elements.\n"
1254 "Accumulates and returns a result by applying PROC successively.\n"
1255 "The arguments to PROC are \"(key value prior-result)\" where key\n"
1256 "and value are successive pairs from the hash table TABLE, and\n"
1257 "prior-result is either INIT (for the first application of PROC)\n"
1258 "or the return value of the previous application of PROC.\n"
1259 "For example, @code{(hash-fold acons '() tab)} will convert a hash\n"
1260 "table into an a-list of key-value pairs.")
1261#define FUNC_NAME s_scm_hash_fold
1262{
1263 SCM_VALIDATE_PROC (1, proc);
1264 if (!SCM_HASHTABLE_P (table))
1265 SCM_VALIDATE_VECTOR (3, table);
1266 return scm_internal_hash_fold (scm_call_3, (void *) SCM_UNPACK (proc), init, table);
1267}
1268#undef FUNC_NAME
1269
c35738c1 1270static SCM
711a9fd7 1271for_each_proc (void *proc, SCM handle)
c35738c1 1272{
711a9fd7 1273 return scm_call_2 (SCM_PACK (proc), SCM_CAR (handle), SCM_CDR (handle));
c35738c1
MD
1274}
1275
1276SCM_DEFINE (scm_hash_for_each, "hash-for-each", 2, 0, 0,
1277 (SCM proc, SCM table),
1278 "An iterator over hash-table elements.\n"
1279 "Applies PROC successively on all hash table items.\n"
1280 "The arguments to PROC are \"(key value)\" where key\n"
1281 "and value are successive pairs from the hash table TABLE.")
1282#define FUNC_NAME s_scm_hash_for_each
1283{
1284 SCM_VALIDATE_PROC (1, proc);
1285 if (!SCM_HASHTABLE_P (table))
1286 SCM_VALIDATE_VECTOR (2, table);
711a9fd7
MD
1287
1288 scm_internal_hash_for_each_handle (for_each_proc,
1289 (void *) SCM_UNPACK (proc),
1290 table);
1291 return SCM_UNSPECIFIED;
1292}
1293#undef FUNC_NAME
1294
1295SCM_DEFINE (scm_hash_for_each_handle, "hash-for-each-handle", 2, 0, 0,
1296 (SCM proc, SCM table),
1297 "An iterator over hash-table elements.\n"
1298 "Applies PROC successively on all hash table handles.")
1299#define FUNC_NAME s_scm_hash_for_each_handle
1300{
1301 scm_t_trampoline_1 call = scm_trampoline_1 (proc);
1302 SCM_ASSERT (call, proc, 1, FUNC_NAME);
1303 if (!SCM_HASHTABLE_P (table))
1304 SCM_VALIDATE_VECTOR (2, table);
1305
1306 scm_internal_hash_for_each_handle (call,
1307 (void *) SCM_UNPACK (proc),
1308 table);
c35738c1
MD
1309 return SCM_UNSPECIFIED;
1310}
1311#undef FUNC_NAME
1312
1313static SCM
1314map_proc (void *proc, SCM key, SCM data, SCM value)
1315{
1316 return scm_cons (scm_call_2 (SCM_PACK (proc), key, data), value);
1317}
1318
711a9fd7 1319SCM_DEFINE (scm_hash_map_to_list, "hash-map->list", 2, 0, 0,
c35738c1
MD
1320 (SCM proc, SCM table),
1321 "An iterator over hash-table elements.\n"
1322 "Accumulates and returns as a list the results of applying PROC successively.\n"
1323 "The arguments to PROC are \"(key value)\" where key\n"
1324 "and value are successive pairs from the hash table TABLE.")
711a9fd7 1325#define FUNC_NAME s_scm_hash_map_to_list
c35738c1
MD
1326{
1327 SCM_VALIDATE_PROC (1, proc);
1328 if (!SCM_HASHTABLE_P (table))
1329 SCM_VALIDATE_VECTOR (2, table);
1330 return scm_internal_hash_fold (map_proc,
1331 (void *) SCM_UNPACK (proc),
1332 SCM_EOL,
1333 table);
1334}
1335#undef FUNC_NAME
1336
0f2d19dd
JB
1337\f
1338
1cc91f1b 1339
0f2d19dd 1340void
c35738c1 1341scm_hashtab_prehistory ()
0f2d19dd 1342{
e4d21e6b
LC
1343 /* Initialize weak cells. */
1344 GC_word wcar_cell_bitmap[GC_BITMAP_SIZE (scm_t_cell)] = { 0 };
1345 GC_word wcdr_cell_bitmap[GC_BITMAP_SIZE (scm_t_cell)] = { 0 };
1346
1347 /* In a weak-car cell, only the second word must be scanned for
1348 pointers. */
1349 GC_set_bit (wcar_cell_bitmap, GC_WORD_OFFSET (scm_t_cell, word_1));
1350 wcar_cell_descr = GC_make_descriptor (wcar_cell_bitmap,
1351 GC_WORD_LEN (scm_t_cell));
1352
1353 /* Conversely, in a weak-cdr cell, only the first word must be scanned for
1354 pointers. */
1355 GC_set_bit (wcdr_cell_bitmap, GC_WORD_OFFSET (scm_t_cell, word_0));
1356 wcdr_cell_descr = GC_make_descriptor (wcdr_cell_bitmap,
1357 GC_WORD_LEN (scm_t_cell));
1358
1359
1360 /* Initialize the hashtab SMOB type. */
f59a096e
MD
1361 scm_tc16_hashtable = scm_make_smob_type (s_hashtable, 0);
1362 scm_set_smob_mark (scm_tc16_hashtable, scm_markcdr);
1363 scm_set_smob_print (scm_tc16_hashtable, hashtable_print);
1364 scm_set_smob_free (scm_tc16_hashtable, hashtable_free);
c35738c1
MD
1365 scm_c_hook_add (&scm_after_gc_c_hook, rehash_after_gc, 0, 0);
1366}
1367
1368void
1369scm_init_hashtab ()
1370{
a0599745 1371#include "libguile/hashtab.x"
0f2d19dd 1372}
89e00824
ML
1373
1374/*
1375 Local Variables:
1376 c-file-style: "gnu"
1377 End:
1378*/