1 /*
  2  * Copyright (c) 2012, 2023, 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_UTILITIES_RESOURCEHASH_HPP
 26 #define SHARE_UTILITIES_RESOURCEHASH_HPP
 27 
 28 #include "memory/allocation.hpp"
 29 #include "utilities/globalDefinitions.hpp"
 30 #include "utilities/numberSeq.hpp"
 31 #include "utilities/tableStatistics.hpp"
 32 
 33 #include <type_traits>
 34 
 35 template<typename K, typename V>
 36 class ResourceHashtableNode : public AnyObj {
 37 public:
 38   unsigned _hash;
 39   K _key;
 40   V _value;
 41   ResourceHashtableNode* _next;
 42 
 43   ResourceHashtableNode(unsigned hash, K const& key, V const& value,
 44                         ResourceHashtableNode* next = nullptr) :
 45     _hash(hash), _key(key), _value(value), _next(next) {}
 46 
 47   // Create a node with a default-constructed value.
 48   ResourceHashtableNode(unsigned hash, K const& key,
 49                         ResourceHashtableNode* next = nullptr) :
 50     _hash(hash), _key(key), _value(), _next(next) {}
 51 };
 52 
 53 template<
 54     class STORAGE,
 55     typename K, typename V,
 56     AnyObj::allocation_type ALLOC_TYPE,
 57     MEMFLAGS MEM_TYPE,
 58     unsigned (*HASH)  (K const&),
 59     bool     (*EQUALS)(K const&, K const&)
 60     >
 61 class ResourceHashtableBase : public STORAGE {
 62   static_assert(ALLOC_TYPE == AnyObj::C_HEAP || std::is_trivially_destructible<K>::value,
 63                 "Destructor for K is only called with C_HEAP");
 64   static_assert(ALLOC_TYPE == AnyObj::C_HEAP || std::is_trivially_destructible<V>::value,
 65                 "Destructor for V is only called with C_HEAP");
 66   using Node = ResourceHashtableNode<K, V>;
 67  private:
 68   int _number_of_entries;
 69 
 70   Node** bucket_at(unsigned index) {
 71     Node** t = table();
 72     return &t[index];
 73   }
 74 
 75   const Node* const* bucket_at(unsigned index) const {
 76     Node** t = table();
 77     return &t[index];
 78   }
 79 
 80   // Returns a pointer to where the node where the value would reside if
 81   // it's in the table.
 82   Node** lookup_node(unsigned hash, K const& key) {
 83     unsigned index = hash % table_size();
 84     Node** ptr = bucket_at(index);
 85     while (*ptr != nullptr) {
 86       Node* node = *ptr;
 87       if (node->_hash == hash && EQUALS(key, node->_key)) {
 88         break;
 89       }
 90       ptr = &(node->_next);
 91     }
 92     return ptr;
 93   }
 94 
 95   Node const** lookup_node(unsigned hash, K const& key) const {
 96     return const_cast<Node const**>(
 97         const_cast<ResourceHashtableBase*>(this)->lookup_node(hash, key));
 98   }
 99 
100  protected:
101   Node** table() const { return STORAGE::table(); }
102 
103   ResourceHashtableBase() : STORAGE(), _number_of_entries(0) {}
104   ResourceHashtableBase(unsigned size) : STORAGE(size), _number_of_entries(0) {}
105   NONCOPYABLE(ResourceHashtableBase);
106 
107   ~ResourceHashtableBase() {
108     if (ALLOC_TYPE == AnyObj::C_HEAP) {
109       unlink_all();
110     }
111   }
112 
113  public:
114   unsigned table_size() const { return STORAGE::table_size(); }
115   int number_of_entries() const { return _number_of_entries; }
116 
117   bool contains(K const& key) const {
118     return get(key) != nullptr;
119   }
120 
121   V* get(K const& key) const {
122     unsigned hv = HASH(key);
123     Node const** ptr = lookup_node(hv, key);
124     if (*ptr != nullptr) {
125       return const_cast<V*>(&(*ptr)->_value);
126     } else {
127       return nullptr;
128     }
129   }
130 
131  /**
132   * Inserts a value in the front of the table, assuming that
133   * the entry is absent.
134   * The table must be locked for the get or test that the entry
135   * is absent, and for this operation.
136   * This is a faster variant of put_if_absent because it adds to the
137   * head of the bucket, and doesn't search the bucket.
138   * @return: true: a new item is always added
139   */
140   bool put_when_absent(K const& key, V const& value) {
141     unsigned hv = HASH(key);
142     unsigned index = hv % table_size();
143     assert(*lookup_node(hv, key) == nullptr, "use put_if_absent");
144     Node** ptr = bucket_at(index);
145     if (ALLOC_TYPE == AnyObj::C_HEAP) {
146       *ptr = new (MEM_TYPE) Node(hv, key, value, *ptr);
147     } else {
148       *ptr = new Node(hv, key, value, *ptr);
149     }
150     _number_of_entries ++;
151     return true;
152   }
153 
154  /**
155   * Inserts or replaces a value in the table.
156   * @return: true:  if a new item is added
157   *          false: if the item already existed and the value is updated
158   */
159   bool put(K const& key, V const& value) {
160     unsigned hv = HASH(key);
161     Node** ptr = lookup_node(hv, key);
162     if (*ptr != nullptr) {
163       (*ptr)->_value = value;
164       return false;
165     } else {
166       if (ALLOC_TYPE == AnyObj::C_HEAP) {
167         *ptr = new (MEM_TYPE) Node(hv, key, value);
168       } else {
169         *ptr = new Node(hv, key, value);
170       }
171       _number_of_entries ++;
172       return true;
173     }
174   }
175 
176   // Look up the key.
177   // If an entry for the key exists, leave map unchanged and return a pointer to its value.
178   // If no entry for the key exists, create a new entry from key and a default-created value
179   //  and return a pointer to the value.
180   // *p_created is true if entry was created, false if entry pre-existed.
181   V* put_if_absent(K const& key, bool* p_created) {
182     unsigned hv = HASH(key);
183     Node** ptr = lookup_node(hv, key);
184     if (*ptr == nullptr) {
185       if (ALLOC_TYPE == AnyObj::C_HEAP) {
186         *ptr = new (MEM_TYPE) Node(hv, key);
187       } else {
188         *ptr = new Node(hv, key);
189       }
190       *p_created = true;
191       _number_of_entries ++;
192     } else {
193       *p_created = false;
194     }
195     return &(*ptr)->_value;
196   }
197 
198   // Look up the key.
199   // If an entry for the key exists, leave map unchanged and return a pointer to its value.
200   // If no entry for the key exists, create a new entry from key and value and return a
201   //  pointer to the value.
202   // *p_created is true if entry was created, false if entry pre-existed.
203   V* put_if_absent(K const& key, V const& value, bool* p_created) {
204     unsigned hv = HASH(key);
205     Node** ptr = lookup_node(hv, key);
206     if (*ptr == nullptr) {
207       if (ALLOC_TYPE == AnyObj::C_HEAP) {
208         *ptr = new (MEM_TYPE) Node(hv, key, value);
209       } else {
210         *ptr = new Node(hv, key, value);
211       }
212       *p_created = true;
213       _number_of_entries ++;
214     } else {
215       *p_created = false;
216     }
217     return &(*ptr)->_value;
218   }
219 
220   template<typename Function>
221   bool remove(K const& key, Function function) {
222     unsigned hv = HASH(key);
223     Node** ptr = lookup_node(hv, key);
224 
225     Node* node = *ptr;
226     if (node != nullptr) {
227       *ptr = node->_next;
228       function(node->_key, node->_value);
229       if (ALLOC_TYPE == AnyObj::C_HEAP) {
230         delete node;
231       }
232       _number_of_entries --;
233       return true;
234     }
235     return false;
236   }
237 
238   bool remove(K const& key) {
239     auto dummy = [&] (K& k, V& v) { };
240     return remove(key, dummy);
241   }
242 
243   // ITER contains bool do_entry(K const&, V const&), which will be
244   // called for each entry in the table.  If do_entry() returns false,
245   // the iteration is cancelled.
246   template<class ITER>
247   void iterate(ITER* iter) const {
248     auto function = [&] (K& k, V& v) {
249       return iter->do_entry(k, v);
250     };
251     iterate(function);
252   }
253 
254   template<typename Function>
255   void iterate(Function function) const { // lambda enabled API
256     Node* const* bucket = table();
257     const unsigned sz = table_size();
258     int cnt = _number_of_entries;
259 
260     while (cnt > 0 && bucket < bucket_at(sz)) {
261       Node* node = *bucket;
262       while (node != nullptr) {
263         bool cont = function(node->_key, node->_value);
264         if (!cont) { return; }
265         node = node->_next;
266         --cnt;
267       }
268       ++bucket;
269     }
270   }
271 
272   // same as above, but unconditionally iterate all entries
273   template<typename Function>
274   void iterate_all(Function function) const { // lambda enabled API
275     auto wrapper = [&] (K& k, V& v) {
276       function(k, v);
277       return true;
278     };
279     iterate(wrapper);
280   }
281 
282   // ITER contains bool do_entry(K const&, V const&), which will be
283   // called for each entry in the table.  If do_entry() returns true,
284   // the entry is deleted.
285   template<class ITER>
286   void unlink(ITER* iter) {
287     const unsigned sz = table_size();
288     int cnt = _number_of_entries;
289 
290     for (unsigned index = 0; cnt > 0 && index < sz; ++index) {
291       Node** ptr = bucket_at(index);
292 
293       while (*ptr != nullptr) {
294         Node* node = *ptr;
295         // do_entry must clean up the key and value in Node.
296         bool clean = iter->do_entry(node->_key, node->_value);
297         if (clean) {
298           *ptr = node->_next;
299           if (ALLOC_TYPE == AnyObj::C_HEAP) {
300             delete node;
301           }
302           _number_of_entries --;
303         } else {
304           ptr = &(node->_next);
305         }
306         if (--cnt <= 0) return;
307       }
308     }
309   }
310 
311   // unlink_all() is a specialized version of unlink() when we decide to remove all elements.
312   // It can not replace unlink(ITER* iter) if user-provided iter releases key/value
313   void unlink_all() {
314     Node** bucket = table();
315     const unsigned sz = table_size();
316 
317     while (_number_of_entries > 0 && bucket < bucket_at(sz)) {
318       Node* node = *bucket;
319       int n = 0;
320 
321       while (node != NULL) {
322         Node* cur = node;
323         node = node->_next;
324         if (ALLOC_TYPE == AnyObj::C_HEAP) {
325           delete cur;
326         }
327         n++;
328       }
329 
330       if (n > 0) {
331         *bucket = nullptr;
332         _number_of_entries -= n;
333       }
334       bucket++;
335     }
336 
337     assert(_number_of_entries == 0, "sanity check");
338   }
339 
340   template<typename Function>
341   TableStatistics statistics_calculate(Function size_function) const {
342     NumberSeq summary;
343     size_t literal_bytes = 0;
344     Node* const* bucket = table();
345     const unsigned sz = table_size();
346     while (bucket < bucket_at(sz)) {
347       Node* node = *bucket;
348       int count = 0;
349       while (node != nullptr) {
350         literal_bytes += size_function(node->_key, node->_value);
351         count++;
352         node = node->_next;
353       }
354       summary.add((double)count);
355       ++bucket;
356     }
357     return TableStatistics(summary, literal_bytes, sizeof(Node*), sizeof(Node));
358   }
359 
360   // This method calculates the "shallow" size. If you want the recursive size, use statistics_calculate.
361   size_t mem_size() const {
362     return sizeof(*this) +
363       table_size() * sizeof(Node*) +
364       number_of_entries() * sizeof(Node);
365   }
366 };
367 
368 template<unsigned TABLE_SIZE, typename K, typename V>
369 class FixedResourceHashtableStorage : public AnyObj {
370   using Node = ResourceHashtableNode<K, V>;
371 
372   Node* _table[TABLE_SIZE];
373 protected:
374   FixedResourceHashtableStorage() { memset(_table, 0, sizeof(_table)); }
375   ~FixedResourceHashtableStorage() = default;
376 
377   constexpr unsigned table_size() const {
378     return TABLE_SIZE;
379   }
380 
381   Node** table() const {
382     return const_cast<Node**>(_table);
383   }
384 };
385 
386 template<
387     typename K, typename V,
388     unsigned SIZE = 256,
389     AnyObj::allocation_type ALLOC_TYPE = AnyObj::RESOURCE_AREA,
390     MEMFLAGS MEM_TYPE = mtInternal,
391     unsigned (*HASH)  (K const&)           = primitive_hash<K>,
392     bool     (*EQUALS)(K const&, K const&) = primitive_equals<K>
393     >
394 class ResourceHashtable : public ResourceHashtableBase<
395   FixedResourceHashtableStorage<SIZE, K, V>,
396     K, V, ALLOC_TYPE, MEM_TYPE, HASH, EQUALS> {
397   NONCOPYABLE(ResourceHashtable);
398 public:
399   ResourceHashtable() : ResourceHashtableBase<FixedResourceHashtableStorage<SIZE, K, V>,
400                                               K, V, ALLOC_TYPE, MEM_TYPE, HASH, EQUALS>() {}
401 };
402 
403 #endif // SHARE_UTILITIES_RESOURCEHASH_HPP