123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893 |
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- #include <assert.h>
- #include "libguile/_scm.h"
- #include "libguile/hash.h"
- #include "libguile/eval.h"
- #include "libguile/ports.h"
- #include "libguile/bdw-gc.h"
- #include "libguile/validate.h"
- #include "libguile/weak-set.h"
- typedef struct {
- unsigned long hash;
- scm_t_bits key;
- } scm_t_weak_entry;
- struct weak_entry_data {
- scm_t_weak_entry *in;
- scm_t_weak_entry *out;
- };
-
- static void*
- do_copy_weak_entry (void *data)
- {
- struct weak_entry_data *e = data;
- e->out->hash = e->in->hash;
- e->out->key = e->in->key;
- return NULL;
- }
- static void
- copy_weak_entry (scm_t_weak_entry *src, scm_t_weak_entry *dst)
- {
- struct weak_entry_data data;
- data.in = src;
- data.out = dst;
-
- GC_call_with_alloc_lock (do_copy_weak_entry, &data);
- }
-
- typedef struct {
- scm_t_weak_entry *entries;
- scm_i_pthread_mutex_t lock;
- unsigned long size;
- unsigned long n_items;
- unsigned long lower;
- unsigned long upper;
- int size_index;
- int min_size_index;
- } scm_t_weak_set;
- #define SCM_WEAK_SET_P(x) (SCM_HAS_TYP7 (x, scm_tc7_weak_set))
- #define SCM_VALIDATE_WEAK_SET(pos, arg) \
- SCM_MAKE_VALIDATE_MSG (pos, arg, WEAK_SET_P, "weak-set")
- #define SCM_WEAK_SET(x) ((scm_t_weak_set *) SCM_CELL_WORD_1 (x))
- static unsigned long
- hash_to_index (unsigned long hash, unsigned long size)
- {
- return (hash >> 1) % size;
- }
- static unsigned long
- entry_distance (unsigned long hash, unsigned long k, unsigned long size)
- {
- unsigned long origin = hash_to_index (hash, size);
- if (k >= origin)
- return k - origin;
- else
-
- return size - origin + k;
- }
- #ifndef HAVE_GC_MOVE_DISAPPEARING_LINK
- static void
- GC_move_disappearing_link (void **from, void **to)
- {
- GC_unregister_disappearing_link (from);
- SCM_I_REGISTER_DISAPPEARING_LINK (to, *to);
- }
- #endif
- static void
- move_weak_entry (scm_t_weak_entry *from, scm_t_weak_entry *to)
- {
- if (from->hash)
- {
- scm_t_weak_entry copy;
-
- copy_weak_entry (from, ©);
- to->hash = copy.hash;
- to->key = copy.key;
- if (copy.key && SCM_HEAP_OBJECT_P (SCM_PACK (copy.key)))
- GC_move_disappearing_link ((void **) &from->key, (void **) &to->key);
- }
- else
- {
- to->hash = 0;
- to->key = 0;
- }
- }
- static void
- rob_from_rich (scm_t_weak_set *set, unsigned long k)
- {
- unsigned long empty, size;
- size = set->size;
-
- assert (set->n_items < size);
-
- empty = k;
- do
- empty = (empty + 1) % size;
-
- while (set->entries[empty].key);
- do
- {
- unsigned long last = empty ? (empty - 1) : (size - 1);
- move_weak_entry (&set->entries[last], &set->entries[empty]);
- empty = last;
- }
- while (empty != k);
-
- set->entries[empty].hash = 0;
- set->entries[empty].key = 0;
- }
- static void
- give_to_poor (scm_t_weak_set *set, unsigned long k)
- {
-
- unsigned long size = set->size;
- while (1)
- {
- unsigned long next = (k + 1) % size;
- unsigned long hash;
- scm_t_weak_entry copy;
- hash = set->entries[next].hash;
- if (!hash || hash_to_index (hash, size) == next)
- break;
- copy_weak_entry (&set->entries[next], ©);
- if (!copy.key)
-
- {
- give_to_poor (set, next);
- set->n_items--;
- continue;
- }
- move_weak_entry (&set->entries[next], &set->entries[k]);
- k = next;
- }
-
- set->entries[k].hash = 0;
- set->entries[k].key = 0;
- }
- static unsigned long hashset_size[] = {
- 31, 61, 113, 223, 443, 883, 1759, 3517, 7027, 14051, 28099, 56197, 112363,
- 224717, 449419, 898823, 1797641, 3595271, 7190537, 14381041, 28762081,
- 57524111, 115048217, 230096423
- };
- #define HASHSET_SIZE_N (sizeof(hashset_size)/sizeof(unsigned long))
- static int
- compute_size_index (scm_t_weak_set *set)
- {
- int i = set->size_index;
- if (set->n_items < set->lower)
- {
-
- do
- --i;
- while (i > set->min_size_index
- && set->n_items < hashset_size[i] / 5);
- }
- else if (set->n_items > set->upper)
- {
- ++i;
- if (i >= HASHSET_SIZE_N)
-
- abort ();
- }
- return i;
- }
- static int
- is_acceptable_size_index (scm_t_weak_set *set, int size_index)
- {
- int computed = compute_size_index (set);
- if (size_index == computed)
-
- return 1;
- if (size_index == computed + 1)
- {
-
- unsigned long new_lower = hashset_size[size_index] / 5;
- return set->size > new_lower;
- }
- if (size_index == computed - 1)
- {
-
- return 0;
- }
-
- return 0;
- }
- static void
- resize_set (scm_t_weak_set *set)
- {
- scm_t_weak_entry *old_entries, *new_entries;
- int new_size_index;
- unsigned long old_size, new_size, old_k;
- do
- {
- new_size_index = compute_size_index (set);
- if (new_size_index == set->size_index)
- return;
- new_size = hashset_size[new_size_index];
- new_entries = scm_gc_malloc_pointerless (new_size * sizeof(scm_t_weak_entry),
- "weak set");
- }
- while (!is_acceptable_size_index (set, new_size_index));
- old_entries = set->entries;
- old_size = set->size;
- memset (new_entries, 0, new_size * sizeof(scm_t_weak_entry));
- set->size_index = new_size_index;
- set->size = new_size;
- if (new_size_index <= set->min_size_index)
- set->lower = 0;
- else
- set->lower = new_size / 5;
- set->upper = 9 * new_size / 10;
- set->n_items = 0;
- set->entries = new_entries;
- for (old_k = 0; old_k < old_size; old_k++)
- {
- scm_t_weak_entry copy;
- unsigned long new_k, distance;
- if (!old_entries[old_k].hash)
- continue;
-
- copy_weak_entry (&old_entries[old_k], ©);
-
- if (!copy.key)
- continue;
-
- new_k = hash_to_index (copy.hash, new_size);
- for (distance = 0; ; distance++, new_k = (new_k + 1) % new_size)
- {
- unsigned long other_hash = new_entries[new_k].hash;
- if (!other_hash)
-
- break;
-
- if (entry_distance (other_hash, new_k, new_size) < distance)
- {
- rob_from_rich (set, new_k);
- break;
- }
- }
-
- set->n_items++;
- new_entries[new_k].hash = copy.hash;
- new_entries[new_k].key = copy.key;
- if (SCM_HEAP_OBJECT_P (SCM_PACK (copy.key)))
- SCM_I_REGISTER_DISAPPEARING_LINK ((void **) &new_entries[new_k].key,
- (void *) new_entries[new_k].key);
- }
- }
- static void
- vacuum_weak_set (scm_t_weak_set *set)
- {
- scm_t_weak_entry *entries = set->entries;
- unsigned long size = set->size;
- unsigned long k;
- for (k = 0; k < size; k++)
- {
- unsigned long hash = entries[k].hash;
-
- if (hash)
- {
- scm_t_weak_entry copy;
- copy_weak_entry (&entries[k], ©);
- if (!copy.key)
-
- {
- give_to_poor (set, k);
- set->n_items--;
- }
- }
- }
- if (set->n_items < set->lower)
- resize_set (set);
- }
- static SCM
- weak_set_lookup (scm_t_weak_set *set, unsigned long hash,
- scm_t_set_predicate_fn pred, void *closure,
- SCM dflt)
- {
- unsigned long k, distance, size;
- scm_t_weak_entry *entries;
-
- size = set->size;
- entries = set->entries;
- hash = (hash << 1) | 0x1;
- k = hash_to_index (hash, size);
-
- for (distance = 0; distance < size; distance++, k = (k + 1) % size)
- {
- unsigned long other_hash;
- retry:
- other_hash = entries[k].hash;
- if (!other_hash)
-
- return dflt;
- if (hash == other_hash)
- {
- scm_t_weak_entry copy;
-
- copy_weak_entry (&entries[k], ©);
- if (!copy.key)
-
- {
- give_to_poor (set, k);
- set->n_items--;
- goto retry;
- }
- if (pred (SCM_PACK (copy.key), closure))
-
- return SCM_PACK (copy.key);
- }
-
- if (entry_distance (other_hash, k, size) < distance)
- return dflt;
- }
-
- return dflt;
- }
- static SCM
- weak_set_add_x (scm_t_weak_set *set, unsigned long hash,
- scm_t_set_predicate_fn pred, void *closure,
- SCM obj)
- {
- unsigned long k, distance, size;
- scm_t_weak_entry *entries;
-
- size = set->size;
- entries = set->entries;
- hash = (hash << 1) | 0x1;
- k = hash_to_index (hash, size);
- for (distance = 0; ; distance++, k = (k + 1) % size)
- {
- unsigned long other_hash;
- retry:
- other_hash = entries[k].hash;
- if (!other_hash)
-
- break;
- if (other_hash == hash)
- {
- scm_t_weak_entry copy;
- copy_weak_entry (&entries[k], ©);
-
- if (!copy.key)
-
- {
- give_to_poor (set, k);
- set->n_items--;
- goto retry;
- }
- if (pred (SCM_PACK (copy.key), closure))
-
- return SCM_PACK (copy.key);
- }
- if (set->n_items > set->upper)
-
- {
- resize_set (set);
- return weak_set_add_x (set, hash >> 1, pred, closure, obj);
- }
-
- if (entry_distance (other_hash, k, size) < distance)
- {
- rob_from_rich (set, k);
- break;
- }
- }
-
- set->n_items++;
- entries[k].hash = hash;
- entries[k].key = SCM_UNPACK (obj);
- if (SCM_HEAP_OBJECT_P (obj))
- SCM_I_REGISTER_DISAPPEARING_LINK ((void **) &entries[k].key,
- (void *) SCM2PTR (obj));
- return obj;
- }
- static void
- weak_set_remove_x (scm_t_weak_set *set, unsigned long hash,
- scm_t_set_predicate_fn pred, void *closure)
- {
- unsigned long k, distance, size;
- scm_t_weak_entry *entries;
-
- size = set->size;
- entries = set->entries;
- hash = (hash << 1) | 0x1;
- k = hash_to_index (hash, size);
- for (distance = 0; distance < size; distance++, k = (k + 1) % size)
- {
- unsigned long other_hash;
- retry:
- other_hash = entries[k].hash;
- if (!other_hash)
-
- return;
- if (other_hash == hash)
- {
- scm_t_weak_entry copy;
-
- copy_weak_entry (&entries[k], ©);
-
- if (!copy.key)
-
- {
- give_to_poor (set, k);
- set->n_items--;
- goto retry;
- }
- if (pred (SCM_PACK (copy.key), closure))
-
- {
- entries[k].hash = 0;
- entries[k].key = 0;
- if (SCM_HEAP_OBJECT_P (SCM_PACK (copy.key)))
- GC_unregister_disappearing_link ((void **) &entries[k].key);
- if (--set->n_items < set->lower)
- resize_set (set);
- else
- give_to_poor (set, k);
- return;
- }
- }
-
- if (entry_distance (other_hash, k, size) < distance)
- return;
- }
- }
- static SCM
- make_weak_set (unsigned long k)
- {
- scm_t_weak_set *set;
- int i = 0, n = k ? k : 31;
- while (i + 1 < HASHSET_SIZE_N && n > hashset_size[i])
- ++i;
- n = hashset_size[i];
- set = scm_gc_malloc (sizeof (*set), "weak-set");
- set->entries = scm_gc_malloc_pointerless (n * sizeof(scm_t_weak_entry),
- "weak-set");
- memset (set->entries, 0, n * sizeof(scm_t_weak_entry));
- set->n_items = 0;
- set->size = n;
- set->lower = 0;
- set->upper = 9 * n / 10;
- set->size_index = i;
- set->min_size_index = i;
- scm_i_pthread_mutex_init (&set->lock, NULL);
- return scm_cell (scm_tc7_weak_set, (scm_t_bits)set);
- }
- void
- scm_i_weak_set_print (SCM exp, SCM port, scm_print_state *pstate)
- {
- scm_puts_unlocked ("#<", port);
- scm_puts_unlocked ("weak-set ", port);
- scm_uintprint (SCM_WEAK_SET (exp)->n_items, 10, port);
- scm_putc_unlocked ('/', port);
- scm_uintprint (SCM_WEAK_SET (exp)->size, 10, port);
- scm_puts_unlocked (">", port);
- }
- static void
- do_vacuum_weak_set (SCM set)
- {
- scm_t_weak_set *s;
- s = SCM_WEAK_SET (set);
-
- scm_i_pthread_mutex_lock (&s->lock);
- vacuum_weak_set (s);
- scm_i_pthread_mutex_unlock (&s->lock);
- }
- SCM
- scm_c_make_weak_set (unsigned long k)
- {
- SCM ret;
- ret = make_weak_set (k);
- scm_i_register_weak_gc_callback (ret, do_vacuum_weak_set);
- return ret;
- }
- SCM
- scm_weak_set_p (SCM obj)
- {
- return scm_from_bool (SCM_WEAK_SET_P (obj));
- }
- SCM
- scm_weak_set_clear_x (SCM set)
- {
- scm_t_weak_set *s = SCM_WEAK_SET (set);
- scm_i_pthread_mutex_lock (&s->lock);
- memset (s->entries, 0, sizeof (scm_t_weak_entry) * s->size);
- s->n_items = 0;
- scm_i_pthread_mutex_unlock (&s->lock);
- return SCM_UNSPECIFIED;
- }
- SCM
- scm_c_weak_set_lookup (SCM set, unsigned long raw_hash,
- scm_t_set_predicate_fn pred,
- void *closure, SCM dflt)
- {
- SCM ret;
- scm_t_weak_set *s = SCM_WEAK_SET (set);
- scm_i_pthread_mutex_lock (&s->lock);
- ret = weak_set_lookup (s, raw_hash, pred, closure, dflt);
- scm_i_pthread_mutex_unlock (&s->lock);
- return ret;
- }
- SCM
- scm_c_weak_set_add_x (SCM set, unsigned long raw_hash,
- scm_t_set_predicate_fn pred,
- void *closure, SCM obj)
- {
- SCM ret;
- scm_t_weak_set *s = SCM_WEAK_SET (set);
- scm_i_pthread_mutex_lock (&s->lock);
- ret = weak_set_add_x (s, raw_hash, pred, closure, obj);
- scm_i_pthread_mutex_unlock (&s->lock);
- return ret;
- }
- void
- scm_c_weak_set_remove_x (SCM set, unsigned long raw_hash,
- scm_t_set_predicate_fn pred,
- void *closure)
- {
- scm_t_weak_set *s = SCM_WEAK_SET (set);
- scm_i_pthread_mutex_lock (&s->lock);
- weak_set_remove_x (s, raw_hash, pred, closure);
- scm_i_pthread_mutex_unlock (&s->lock);
- }
- static int
- eq_predicate (SCM x, void *closure)
- {
- return scm_is_eq (x, SCM_PACK_POINTER (closure));
- }
- SCM
- scm_weak_set_add_x (SCM set, SCM obj)
- {
- return scm_c_weak_set_add_x (set, scm_ihashq (obj, -1),
- eq_predicate, SCM_UNPACK_POINTER (obj), obj);
- }
- SCM
- scm_weak_set_remove_x (SCM set, SCM obj)
- {
- scm_c_weak_set_remove_x (set, scm_ihashq (obj, -1),
- eq_predicate, SCM_UNPACK_POINTER (obj));
- return SCM_UNSPECIFIED;
- }
- SCM
- scm_c_weak_set_fold (scm_t_set_fold_fn proc, void *closure,
- SCM init, SCM set)
- {
- scm_t_weak_set *s;
- scm_t_weak_entry *entries;
- unsigned long k, size;
- s = SCM_WEAK_SET (set);
- scm_i_pthread_mutex_lock (&s->lock);
- size = s->size;
- entries = s->entries;
- for (k = 0; k < size; k++)
- {
- if (entries[k].hash)
- {
- scm_t_weak_entry copy;
-
- copy_weak_entry (&entries[k], ©);
-
- if (copy.key)
- {
-
- scm_i_pthread_mutex_unlock (&s->lock);
- init = proc (closure, SCM_PACK (copy.key), init);
- scm_i_pthread_mutex_lock (&s->lock);
- }
- }
- }
-
- scm_i_pthread_mutex_unlock (&s->lock);
-
- return init;
- }
- static SCM
- fold_trampoline (void *closure, SCM item, SCM init)
- {
- return scm_call_2 (SCM_PACK_POINTER (closure), item, init);
- }
- SCM
- scm_weak_set_fold (SCM proc, SCM init, SCM set)
- {
- return scm_c_weak_set_fold (fold_trampoline, SCM_UNPACK_POINTER (proc), init, set);
- }
- static SCM
- for_each_trampoline (void *closure, SCM item, SCM seed)
- {
- scm_call_1 (SCM_PACK_POINTER (closure), item);
- return seed;
- }
- SCM
- scm_weak_set_for_each (SCM proc, SCM set)
- {
- scm_c_weak_set_fold (for_each_trampoline, SCM_UNPACK_POINTER (proc), SCM_BOOL_F, set);
- return SCM_UNSPECIFIED;
- }
- static SCM
- map_trampoline (void *closure, SCM item, SCM seed)
- {
- return scm_cons (scm_call_1 (SCM_PACK_POINTER (closure), item), seed);
- }
- SCM
- scm_weak_set_map_to_list (SCM proc, SCM set)
- {
- return scm_c_weak_set_fold (map_trampoline, SCM_UNPACK_POINTER (proc), SCM_EOL, set);
- }
- void
- scm_init_weak_set ()
- {
- #include "libguile/weak-set.x"
- }
|