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 Node* const* bucket = table(); 110 const unsigned sz = table_size(); 111 while (bucket < bucket_at(sz)) { 112 Node* node = *bucket; 113 while (node != nullptr) { 114 Node* cur = node; 115 node = node->_next; 116 delete cur; 117 } 118 ++bucket; 119 } 120 } 121 } 122 123 public: 124 unsigned table_size() const { return STORAGE::table_size(); } 125 int number_of_entries() const { return _number_of_entries; } 126 127 bool contains(K const& key) const { 128 return get(key) != nullptr; 129 } 130 131 V* get(K const& key) const { 132 unsigned hv = HASH(key); 133 Node const** ptr = lookup_node(hv, key); 134 if (*ptr != nullptr) { 135 return const_cast<V*>(&(*ptr)->_value); 136 } else { 137 return nullptr; 138 } 139 } 140 141 /** 142 * Inserts a value in the front of the table, assuming that 143 * the entry is absent. 144 * The table must be locked for the get or test that the entry 145 * is absent, and for this operation. 146 * This is a faster variant of put_if_absent because it adds to the 147 * head of the bucket, and doesn't search the bucket. 148 * @return: true: a new item is always added 149 */ 150 bool put_when_absent(K const& key, V const& value) { 151 unsigned hv = HASH(key); 152 unsigned index = hv % table_size(); 153 assert(*lookup_node(hv, key) == nullptr, "use put_if_absent"); 154 Node** ptr = bucket_at(index); 155 if (ALLOC_TYPE == AnyObj::C_HEAP) { 156 *ptr = new (MEM_TYPE) Node(hv, key, value, *ptr); 157 } else { 158 *ptr = new Node(hv, key, value, *ptr); 159 } 160 _number_of_entries ++; 161 return true; 162 } 163 164 /** 165 * Inserts or replaces a value in the table. 166 * @return: true: if a new item is added 167 * false: if the item already existed and the value is updated 168 */ 169 bool put(K const& key, V const& value) { 170 unsigned hv = HASH(key); 171 Node** ptr = lookup_node(hv, key); 172 if (*ptr != nullptr) { 173 (*ptr)->_value = value; 174 return false; 175 } else { 176 if (ALLOC_TYPE == AnyObj::C_HEAP) { 177 *ptr = new (MEM_TYPE) Node(hv, key, value); 178 } else { 179 *ptr = new Node(hv, key, value); 180 } 181 _number_of_entries ++; 182 return true; 183 } 184 } 185 186 // Look up the key. 187 // If an entry for the key exists, leave map unchanged and return a pointer to its value. 188 // If no entry for the key exists, create a new entry from key and a default-created value 189 // and return a pointer to the value. 190 // *p_created is true if entry was created, false if entry pre-existed. 191 V* put_if_absent(K const& key, bool* p_created) { 192 unsigned hv = HASH(key); 193 Node** ptr = lookup_node(hv, key); 194 if (*ptr == nullptr) { 195 if (ALLOC_TYPE == AnyObj::C_HEAP) { 196 *ptr = new (MEM_TYPE) Node(hv, key); 197 } else { 198 *ptr = new Node(hv, key); 199 } 200 *p_created = true; 201 _number_of_entries ++; 202 } else { 203 *p_created = false; 204 } 205 return &(*ptr)->_value; 206 } 207 208 // Look up the key. 209 // If an entry for the key exists, leave map unchanged and return a pointer to its value. 210 // If no entry for the key exists, create a new entry from key and value and return a 211 // pointer to the value. 212 // *p_created is true if entry was created, false if entry pre-existed. 213 V* put_if_absent(K const& key, V const& value, bool* p_created) { 214 unsigned hv = HASH(key); 215 Node** ptr = lookup_node(hv, key); 216 if (*ptr == nullptr) { 217 if (ALLOC_TYPE == AnyObj::C_HEAP) { 218 *ptr = new (MEM_TYPE) Node(hv, key, value); 219 } else { 220 *ptr = new Node(hv, key, value); 221 } 222 *p_created = true; 223 _number_of_entries ++; 224 } else { 225 *p_created = false; 226 } 227 return &(*ptr)->_value; 228 } 229 230 template<typename Function> 231 bool remove(K const& key, Function function) { 232 unsigned hv = HASH(key); 233 Node** ptr = lookup_node(hv, key); 234 235 Node* node = *ptr; 236 if (node != nullptr) { 237 *ptr = node->_next; 238 function(node->_key, node->_value); 239 if (ALLOC_TYPE == AnyObj::C_HEAP) { 240 delete node; 241 } 242 _number_of_entries --; 243 return true; 244 } 245 return false; 246 } 247 248 bool remove(K const& key) { 249 auto dummy = [&] (K& k, V& v) { }; 250 return remove(key, dummy); 251 } 252 253 // ITER contains bool do_entry(K const&, V const&), which will be 254 // called for each entry in the table. If do_entry() returns false, 255 // the iteration is cancelled. 256 template<class ITER> 257 void iterate(ITER* iter) const { 258 auto function = [&] (K& k, V& v) { 259 return iter->do_entry(k, v); 260 }; 261 iterate(function); 262 } 263 264 template<typename Function> 265 void iterate(Function function) const { // lambda enabled API 266 Node* const* bucket = table(); 267 const unsigned sz = table_size(); 268 int cnt = _number_of_entries; 269 270 while (cnt > 0 && bucket < bucket_at(sz)) { 271 Node* node = *bucket; 272 while (node != nullptr) { 273 bool cont = function(node->_key, node->_value); 274 if (!cont) { return; } 275 node = node->_next; 276 --cnt; 277 } 278 ++bucket; 279 } 280 } 281 282 // same as above, but unconditionally iterate all entries 283 template<typename Function> 284 void iterate_all(Function function) const { // lambda enabled API 285 auto wrapper = [&] (K& k, V& v) { 286 function(k, v); 287 return true; 288 }; 289 iterate(wrapper); 290 } 291 292 // ITER contains bool do_entry(K const&, V const&), which will be 293 // called for each entry in the table. If do_entry() returns true, 294 // the entry is deleted. 295 template<class ITER> 296 void unlink(ITER* iter) { 297 const unsigned sz = table_size(); 298 for (unsigned index = 0; index < sz; index++) { 299 Node** ptr = bucket_at(index); 300 while (*ptr != nullptr) { 301 Node* node = *ptr; 302 // do_entry must clean up the key and value in Node. 303 bool clean = iter->do_entry(node->_key, node->_value); 304 if (clean) { 305 *ptr = node->_next; 306 if (ALLOC_TYPE == AnyObj::C_HEAP) { 307 delete node; 308 } 309 _number_of_entries --; 310 } else { 311 ptr = &(node->_next); 312 } 313 } 314 } 315 } 316 317 template<typename Function> 318 TableStatistics statistics_calculate(Function size_function) const { 319 NumberSeq summary; 320 size_t literal_bytes = 0; 321 Node* const* bucket = table(); 322 const unsigned sz = table_size(); 323 while (bucket < bucket_at(sz)) { 324 Node* node = *bucket; 325 int count = 0; 326 while (node != nullptr) { 327 literal_bytes += size_function(node->_key, node->_value); 328 count++; 329 node = node->_next; 330 } 331 summary.add((double)count); 332 ++bucket; 333 } 334 return TableStatistics(summary, literal_bytes, sizeof(Node*), sizeof(Node)); 335 } 336 337 // This method calculates the "shallow" size. If you want the recursive size, use statistics_calculate. 338 size_t mem_size() const { 339 return sizeof(*this) + 340 table_size() * sizeof(Node*) + 341 number_of_entries() * sizeof(Node); 342 } 343 }; 344 345 template<unsigned TABLE_SIZE, typename K, typename V> 346 class FixedResourceHashtableStorage : public AnyObj { 347 using Node = ResourceHashtableNode<K, V>; 348 349 Node* _table[TABLE_SIZE]; 350 protected: 351 FixedResourceHashtableStorage() { memset(_table, 0, sizeof(_table)); } 352 ~FixedResourceHashtableStorage() = default; 353 354 constexpr unsigned table_size() const { 355 return TABLE_SIZE; 356 } 357 358 Node** table() const { 359 return const_cast<Node**>(_table); 360 } 361 }; 362 363 template< 364 typename K, typename V, 365 unsigned SIZE = 256, 366 AnyObj::allocation_type ALLOC_TYPE = AnyObj::RESOURCE_AREA, 367 MEMFLAGS MEM_TYPE = mtInternal, 368 unsigned (*HASH) (K const&) = primitive_hash<K>, 369 bool (*EQUALS)(K const&, K const&) = primitive_equals<K> 370 > 371 class ResourceHashtable : public ResourceHashtableBase< 372 FixedResourceHashtableStorage<SIZE, K, V>, 373 K, V, ALLOC_TYPE, MEM_TYPE, HASH, EQUALS> { 374 NONCOPYABLE(ResourceHashtable); 375 public: 376 ResourceHashtable() : ResourceHashtableBase<FixedResourceHashtableStorage<SIZE, K, V>, 377 K, V, ALLOC_TYPE, MEM_TYPE, HASH, EQUALS>() {} 378 }; 379 380 #endif // SHARE_UTILITIES_RESOURCEHASH_HPP