1 /*
2  * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 
25 #ifndef SHARE_JFR_UTILITIES_JFRHASHTABLE_HPP
26 #define SHARE_JFR_UTILITIES_JFRHASHTABLE_HPP
27 
28 #include "jfr/utilities/jfrAllocation.hpp"
29 #include "runtime/orderAccess.hpp"
30 #include "utilities/debug.hpp"
31 #include "utilities/macros.hpp"
32 
33 template <typename T>
34 class JfrBasicHashtableEntry : public JfrCHeapObj {
35  private:
36   typedef JfrBasicHashtableEntry<T> Entry;
37   Entry* _next;
38   T _literal;          // ref to item in table.
39   uintptr_t _hash;
40 
41  public:
JfrBasicHashtableEntry(uintptr_t hash,const T & data)42   JfrBasicHashtableEntry(uintptr_t hash, const T& data) : _next(NULL), _literal(data), _hash(hash) {}
hash() const43   uintptr_t hash() const { return _hash; }
literal() const44   T literal() const { return _literal; }
literal_addr()45   T* literal_addr() { return &_literal; }
set_literal(T s)46   void set_literal(T s) { _literal = s; }
set_next(Entry * next)47   void set_next(Entry* next) { _next = next; }
next() const48   Entry* next() const { return _next; }
next_addr()49   Entry** next_addr() { return &_next; }
50 };
51 
52 template <typename T>
53 class JfrHashtableBucket : public CHeapObj<mtTracing> {
54   template <typename>
55   friend class JfrBasicHashtable;
56  private:
57   typedef JfrBasicHashtableEntry<T> TableEntry;
58   TableEntry* _entry;
59 
get_entry() const60   TableEntry* get_entry() const {
61     return (TableEntry*)OrderAccess::load_acquire(&_entry);
62   }
set_entry(TableEntry * entry)63   void set_entry(TableEntry* entry) { OrderAccess::release_store(&_entry, entry);}
entry_addr()64   TableEntry** entry_addr() { return &_entry; }
65 };
66 
67 template <typename T>
68 class JfrBasicHashtable : public CHeapObj<mtTracing> {
69  private:
70   typedef JfrHashtableBucket<T> Bucket;
71   typedef JfrBasicHashtableEntry<T> TableEntry;
72   Bucket* _buckets;
73   uintptr_t _table_size;
74   const size_t _entry_size;
75   size_t _number_of_entries;
76 
77  protected:
JfrBasicHashtable(uintptr_t table_size,size_t entry_size)78   JfrBasicHashtable(uintptr_t table_size, size_t entry_size) :
79     _buckets(NULL), _table_size(table_size), _entry_size(entry_size), _number_of_entries(0) {
80     _buckets = NEW_C_HEAP_ARRAY2(Bucket, table_size, mtTracing, CURRENT_PC);
81     memset((void*)_buckets, 0, table_size * sizeof(Bucket));
82   }
83 
hash_to_index(uintptr_t full_hash) const84   size_t hash_to_index(uintptr_t full_hash) const {
85     const uintptr_t h = full_hash % _table_size;
86     assert(h >= 0 && h < _table_size, "Illegal hash value");
87     return (size_t)h;
88   }
entry_size() const89   size_t entry_size() const { return _entry_size; }
unlink_entry(TableEntry * entry)90   void unlink_entry(TableEntry* entry) {
91     entry->set_next(NULL);
92     --_number_of_entries;
93   }
free_buckets()94   void free_buckets() {
95     FREE_C_HEAP_ARRAY(Bucket, _buckets);
96   }
bucket(size_t i)97   TableEntry* bucket(size_t i) { return _buckets[i].get_entry();}
bucket_addr(size_t i)98   TableEntry** bucket_addr(size_t i) { return _buckets[i].entry_addr(); }
table_size() const99   uintptr_t table_size() const { return _table_size; }
number_of_entries() const100   size_t number_of_entries() const { return _number_of_entries; }
add_entry(size_t index,TableEntry * entry)101   void add_entry(size_t index, TableEntry* entry) {
102     assert(entry != NULL, "invariant");
103     entry->set_next(bucket(index));
104     _buckets[index].set_entry(entry);
105     ++_number_of_entries;
106   }
107 };
108 
109 template <typename IdType, typename Entry, typename T>
110 class AscendingId : public JfrCHeapObj  {
111  private:
112   IdType _id;
113  public:
AscendingId()114   AscendingId() : _id(0) {}
115   // callbacks
on_link(Entry * entry)116   void on_link(Entry* entry) {
117     assert(entry != NULL, "invariant");
118     assert(entry->id() == 0, "invariant");
119     entry->set_id(++_id);
120   }
on_equals(uintptr_t hash,const Entry * entry)121   bool on_equals(uintptr_t hash, const Entry* entry) {
122     assert(entry->hash() == hash, "invariant");
123     return true;
124   }
125 };
126 
127 // IdType must be scalar
128 template <typename T, typename IdType>
129 class JfrHashtableEntry : public JfrBasicHashtableEntry<T> {
130  public:
JfrHashtableEntry(uintptr_t hash,const T & data)131   JfrHashtableEntry(uintptr_t hash, const T& data) : JfrBasicHashtableEntry<T>(hash, data), _id(0) {}
132   typedef IdType ID;
id() const133   ID id() const { return _id; }
set_id(ID id) const134   void set_id(ID id) const { _id = id; }
value() const135   T& value() const { return *const_cast<JfrHashtableEntry*>(this)->literal_addr();}
value_addr() const136   const T* value_addr() const { return const_cast<JfrHashtableEntry*>(this)->literal_addr(); }
137  private:
138   mutable ID _id;
139 };
140 
141 template <typename T, typename IdType, template <typename, typename> class Entry,
142           typename Callback = AscendingId<IdType, Entry<T, IdType>, T> ,
143           size_t TABLE_SIZE = 1009>
144 class HashTableHost : public JfrBasicHashtable<T> {
145  public:
146   typedef Entry<T, IdType> HashEntry;
HashTableHost(size_t size=0)147   HashTableHost(size_t size = 0) : JfrBasicHashtable<T>(size == 0 ? TABLE_SIZE : size, sizeof(HashEntry)), _callback(new Callback()) {}
HashTableHost(Callback * cb,size_t size=0)148   HashTableHost(Callback* cb, size_t size = 0) : JfrBasicHashtable<T>(size == 0 ? TABLE_SIZE : size, sizeof(HashEntry)), _callback(cb) {}
~HashTableHost()149   ~HashTableHost() {
150     this->clear_entries();
151     this->free_buckets();
152   }
153 
154   // direct insert assumes non-existing entry
155   HashEntry& put(uintptr_t hash, const T& data);
156 
157   // lookup entry, will put if not found
lookup_put(uintptr_t hash,const T & data)158   HashEntry& lookup_put(uintptr_t hash, const T& data) {
159     HashEntry* entry = lookup_only(hash);
160     return entry == NULL ? put(hash, data) : *entry;
161   }
162 
163   HashEntry* lookup_only(uintptr_t hash);
164 
165   // id retrieval
id(uintptr_t hash,const T & data)166   IdType id(uintptr_t hash, const T& data) {
167     assert(data != NULL, "invariant");
168     const HashEntry& entry = lookup_put(hash, data);
169     assert(entry.id() > 0, "invariant");
170     return entry.id();
171   }
172 
173   template <typename Functor>
174   void iterate_value(Functor& f);
175 
176   template <typename Functor>
177   void iterate_entry(Functor& f);
178 
cardinality() const179   size_t cardinality() const { return this->number_of_entries(); }
has_entries() const180   bool has_entries() const { return this->cardinality() > 0; }
181   void clear_entries();
182 
183   // removal and deallocation
free_entry(HashEntry * entry)184   void free_entry(HashEntry* entry) {
185     assert(entry != NULL, "invariant");
186     JfrBasicHashtable<T>::unlink_entry(entry);
187     _callback->on_unlink(entry);
188     delete entry;
189   }
190 
191  private:
192   Callback* _callback;
index_for(uintptr_t hash)193   size_t index_for(uintptr_t hash) { return this->hash_to_index(hash); }
194   HashEntry* new_entry(uintptr_t hash, const T& data);
add_entry(size_t index,HashEntry * new_entry)195   void add_entry(size_t index, HashEntry* new_entry) {
196     assert(new_entry != NULL, "invariant");
197     _callback->on_link(new_entry);
198     assert(new_entry->id() > 0, "invariant");
199     JfrBasicHashtable<T>::add_entry(index, new_entry);
200   }
201 };
202 
203 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
put(uintptr_t hash,const T & data)204 Entry<T, IdType>& HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::put(uintptr_t hash, const T& data) {
205   assert(lookup_only(hash) == NULL, "use lookup_put()");
206   HashEntry* const entry = new_entry(hash, data);
207   add_entry(index_for(hash), entry);
208   return *entry;
209 }
210 
211 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
lookup_only(uintptr_t hash)212 Entry<T, IdType>* HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::lookup_only(uintptr_t hash) {
213   HashEntry* entry = (HashEntry*)this->bucket(index_for(hash));
214   while (entry != NULL) {
215     if (entry->hash() == hash && _callback->on_equals(hash, entry)) {
216       return entry;
217     }
218     entry = (HashEntry*)entry->next();
219   }
220   return NULL;
221 }
222 
223 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
224 template <typename Functor>
iterate_value(Functor & f)225 void HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::iterate_value(Functor& f) {
226   for (size_t i = 0; i < this->table_size(); ++i) {
227     const HashEntry* entry = (const HashEntry*)this->bucket(i);
228     while (entry != NULL) {
229       if (!f(entry->value())) {
230         break;
231       }
232       entry = (HashEntry*)entry->next();
233     }
234   }
235 }
236 
237 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
238 template <typename Functor>
iterate_entry(Functor & f)239 void HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::iterate_entry(Functor& f) {
240   for (size_t i = 0; i < this->table_size(); ++i) {
241     const HashEntry* entry = (const HashEntry*)this->bucket(i);
242     while (entry != NULL) {
243       if (!f(entry)) {
244         break;
245       }
246       entry = (const HashEntry*)entry->next();
247     }
248   }
249 }
250 
251 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
clear_entries()252 void HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::clear_entries() {
253   for (size_t i = 0; i < this->table_size(); ++i) {
254     HashEntry** bucket = (HashEntry**)this->bucket_addr(i);
255     HashEntry* entry = *bucket;
256     while (entry != NULL) {
257       HashEntry* entry_to_remove = entry;
258       entry = (HashEntry*)entry->next();
259       this->free_entry(entry_to_remove);
260     }
261     *bucket = NULL;
262   }
263   assert(this->number_of_entries() == 0, "should have removed all entries");
264 }
265 
266 template <typename T, typename IdType, template <typename, typename> class Entry, typename Callback, size_t TABLE_SIZE>
new_entry(uintptr_t hash,const T & data)267 Entry<T, IdType>* HashTableHost<T, IdType, Entry, Callback, TABLE_SIZE>::new_entry(uintptr_t hash, const T& data) {
268   assert(sizeof(HashEntry) == this->entry_size(), "invariant");
269   HashEntry* const entry = new HashEntry(hash, data);
270   assert(entry != NULL, "invariant");
271   assert(0 == entry->id(), "invariant");
272   return entry;
273 }
274 
275 #endif // SHARE_JFR_UTILITIES_JFRHASHTABLE_HPP
276