]> www.ginac.de Git - cln.git/blob - src/base/hash/cl_hash1weak.h
Initial revision
[cln.git] / src / base / hash / cl_hash1weak.h
1 // Weak hash tables with 1 key and a value
2
3 #ifndef _CL_HASH1WEAK_H
4 #define _CL_HASH1WEAK_H
5
6 #include "cl_hash1.h"
7
8 // This is a hash table in which an entry can be removed when a user-defined
9 // condition is fulfilled (e.g. the value is not referenced any more).
10 // We don't remove unused entries immediately, only when the hash table
11 // wants to grow. This way the hash table also serves as a cache.
12
13 // Requirements:
14 // - same as for hash1,
15 // - key1_type must be a subclass of cl_gc[object|pointer],
16 // - value_type must be a subclass of cl_[gc|rc][object|pointer],
17 // - function  maygc_htentry(const cl_htentry1<key1_type,value_type>&);
18 //   must be filled in at runtime.
19
20 template <class key1_type, class value_type>
21 struct cl_heap_weak_hashtable_1 : public cl_heap_hashtable_1 <key1_type,value_type> {
22         // Allocation.
23         void* operator new (size_t size) { return cl_malloc_hook(size); }
24         // Deallocation.
25         void operator delete (void* ptr) { cl_free_hook(ptr); }
26 public:
27         // Function which tells when an unused entry may be garbage collected.
28         cl_boolean (* const _maygc_htentry) (const cl_htentry1<key1_type,value_type>&);
29         // Constructor.
30         cl_heap_weak_hashtable_1 (cl_boolean (*maygc_htentry) (const cl_htentry1<key1_type,value_type>&))
31                 : cl_heap_hashtable_1 <key1_type,value_type> (),
32                   _maygc_htentry (maygc_htentry)
33         {
34                 _garcol_fun = garcol;
35         }
36 private:
37         // Garbage collection.
38         // Before growing the table, we check whether we can remove unused
39         // entries.
40         static cl_boolean garcol (cl_heap* _ht)
41         {
42                 var cl_heap_weak_hashtable_1* ht = (cl_heap_weak_hashtable_1*)_ht;
43                 // Now ht->_garcol_fun = garcol.
44                 // It is not worth doing a garbage collection if the table
45                 // is small, say, has fewer than 100 entries.
46                 if (ht->_count < 100)
47                         return cl_false;
48                 // Do a garbage collection.
49                 var long removed = 0;
50                 for (long i = 0; i < ht->_size; i++)
51                     if (ht->_entries[i].next >= 0) {
52                         var cl_htentry1<key1_type,value_type>& entry = ht->_entries[i].entry;
53                         if (ht->_maygc_htentry(entry)) {
54                                 // This is hairy. We remove the entry and
55                                 // free the value after its refcount has
56                                 // dropped to zero. But in order to protect
57                                 // against too early destruction
58                                 // we have to temporarily increase the refcount.
59                                 if (entry.val.pointer_p())
60                                         entry.val.inc_pointer_refcount();
61                                 ht->remove(entry.key);
62                                 if (entry.val.pointer_p()) {
63                                         var cl_heap* p = entry.val.heappointer;
64                                         if (!(--p->refcount == 0)) cl_abort();
65                                         cl_free_heap_object(p);
66                                 }
67                                 removed++;
68                         }
69                     }
70                 if (removed == 0)
71                         // Unsuccessful. Let the table grow immediately.
72                         return cl_false;
73                 else if (2*removed < ht->_count) {
74                         // Table shrank by less than a factor of 1/1.5.
75                         // Don't expand the table now, but expand it next time.
76                         ht->_garcol_fun = garcol_nexttime;
77                         return cl_true;
78                 } else {
79                         // Table shrank much. Don't expand the table now,
80                         // and try a GC next time.
81                         return cl_true;
82                 }
83         }
84         static cl_boolean garcol_nexttime (cl_heap* _ht)
85         {
86                 var cl_heap_weak_hashtable_1* ht = (cl_heap_weak_hashtable_1*)_ht;
87                 // Now ht->_garcol_fun = garcol_nexttime.
88                 ht->_garcol_fun = garcol;
89                 return cl_false;
90         }
91 };
92
93 #endif /* _CL_HASH1WEAK_H */