1 /*
   2  * Copyright (c) 2012, 2018, 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_VM_UTILITIES_RESOURCEHASH_HPP
  26 #define SHARE_VM_UTILITIES_RESOURCEHASH_HPP
  27 
  28 #include "memory/allocation.hpp"
  29 
  30 template<typename K> struct ResourceHashtableFns {
  31     typedef unsigned (*hash_fn)(K const&);
  32     typedef bool (*equals_fn)(K const&, K const&);
  33 };
  34 
  35 template<typename K> unsigned primitive_hash(const K& k) {
  36   unsigned hash = (unsigned)((uintptr_t)k);
  37   return hash ^ (hash >> 3); // just in case we're dealing with aligned ptrs
  38 }
  39 
  40 template<typename K> bool primitive_equals(const K& k0, const K& k1) {
  41   return k0 == k1;
  42 }
  43 
  44 enum {
  45   CONFIGURABLE_SIZE = 0
  46 };
  47 
  48 template<
  49     typename K, typename V,
  50     // xlC does not compile this:
  51     // http://stackoverflow.com/questions/8532961/template-argument-of-type-that-is-defined-by-inner-typedef-from-other-template-c
  52     //typename ResourceHashtableFns<K>::hash_fn   HASH   = primitive_hash<K>,
  53     //typename ResourceHashtableFns<K>::equals_fn EQUALS = primitive_equals<K>,
  54     unsigned (*HASH)  (K const&)           = primitive_hash<K>,
  55     bool     (*EQUALS)(K const&, K const&) = primitive_equals<K>,
  56     unsigned SIZE = 256,
  57     ResourceObj::allocation_type ALLOC_TYPE = ResourceObj::RESOURCE_AREA,
  58     MEMFLAGS MEM_TYPE = mtInternal
  59     >
  60 class ResourceHashtable : public ResourceObj {
  61  private:
  62 
  63   class Node : public ResourceObj {
  64    public:
  65     unsigned _hash;
  66     K _key;
  67     V _value;
  68     Node* _next;
  69 
  70     Node(unsigned hash, K const& key, V const& value) :
  71         _hash(hash), _key(key), _value(value), _next(NULL) {}
  72   };
  73 
  74   Node* _static_table[SIZE == 0 ? 1 : SIZE];
  75   unsigned _configured_table_size;
  76   Node** _configured_table;
  77 
  78   // Returns a pointer to where the node where the value would reside if
  79   // it's in the table.
  80   Node** lookup_node(unsigned hash, K const& key) {
  81     unsigned index = hash % size();
  82     Node** table = get_table();
  83     Node** ptr = &table[index];
  84     while (*ptr != NULL) {
  85       Node* node = *ptr;
  86       if (node->_hash == hash && EQUALS(key, node->_key)) {
  87         break;
  88       }
  89       ptr = &(node->_next);
  90     }
  91     return ptr;
  92   }
  93 
  94   Node const** lookup_node(unsigned hash, K const& key) const {
  95     return const_cast<Node const**>(
  96         const_cast<ResourceHashtable*>(this)->lookup_node(hash, key));
  97   }
  98 
  99  public:
 100   ResourceHashtable() {
 101     assert(SIZE != CONFIGURABLE_SIZE, "must be");
 102     memset(_static_table, 0, SIZE * sizeof(Node*));
 103     _configured_table = NULL;
 104   }
 105 
 106   ResourceHashtable(unsigned configured_table_size) : _configured_table_size(configured_table_size) {
 107     assert(SIZE == CONFIGURABLE_SIZE, "must be");
 108     _configured_table = (Node**)operator new(size() * sizeof(Node*), ALLOC_TYPE, MEM_TYPE);
 109     memset(_configured_table, 0, size() * sizeof(Node*));
 110   }
 111 
 112   ALWAYSINLINE unsigned size() const {
 113     if (SIZE != CONFIGURABLE_SIZE) {
 114       return SIZE;
 115     } else {
 116       return _configured_table_size;
 117     }
 118   }
 119 
 120   ALWAYSINLINE Node** get_table() const {
 121     if (SIZE != CONFIGURABLE_SIZE) {
 122       return (Node**)(&_static_table[0]);
 123     } else {
 124       return _configured_table;
 125     }
 126   }
 127 
 128   ~ResourceHashtable() {
 129     if (ALLOC_TYPE == C_HEAP) {
 130       Node** table = get_table();
 131       Node* const* bucket = table;
 132       while (bucket < &table[size()]) {
 133         Node* node = *bucket;
 134         while (node != NULL) {
 135           Node* cur = node;
 136           node = node->_next;
 137           delete cur;
 138         }
 139         ++bucket;
 140       }
 141       if (_configured_table != NULL) {
 142         FreeHeap((void*)_configured_table);
 143       }
 144     }
 145   }
 146 
 147   bool contains(K const& key) const {
 148     return get(key) != NULL;
 149   }
 150 
 151   V* get(K const& key) const {
 152     unsigned hv = HASH(key);
 153     Node const** ptr = lookup_node(hv, key);
 154     if (*ptr != NULL) {
 155       return const_cast<V*>(&(*ptr)->_value);
 156     } else {
 157       return NULL;
 158     }
 159   }
 160 
 161  /**
 162   * Inserts or replaces a value in the table.
 163   * @return: true:  if a new item is added
 164   *          false: if the item already existed and the value is updated
 165   */
 166   bool put(K const& key, V const& value) {
 167     unsigned hv = HASH(key);
 168     Node** ptr = lookup_node(hv, key);
 169     if (*ptr != NULL) {
 170       (*ptr)->_value = value;
 171       return false;
 172     } else {
 173       *ptr = new (ALLOC_TYPE, MEM_TYPE) Node(hv, key, value);
 174       return true;
 175     }
 176   }
 177 
 178   bool remove(K const& key) {
 179     unsigned hv = HASH(key);
 180     Node** ptr = lookup_node(hv, key);
 181 
 182     Node* node = *ptr;
 183     if (node != NULL) {
 184       *ptr = node->_next;
 185       if (ALLOC_TYPE == C_HEAP) {
 186         delete node;
 187       }
 188       return true;
 189     }
 190     return false;
 191   }
 192 
 193   // ITER contains bool do_entry(K const&, V const&), which will be
 194   // called for each entry in the table.  If do_entry() returns false,
 195   // the iteration is cancelled.
 196   template<class ITER>
 197   void iterate(ITER* iter) const {
 198     Node** table = get_table();
 199     Node* const* bucket = table;
 200     while (bucket < &table[size()]) {
 201       Node* node = *bucket;
 202       while (node != NULL) {
 203         bool cont = iter->do_entry(node->_key, node->_value);
 204         if (!cont) { return; }
 205         node = node->_next;
 206       }
 207       ++bucket;
 208     }
 209   }
 210 
 211   static size_t node_size() {
 212     return sizeof(Node);
 213   }
 214 };
 215 
 216 
 217 #endif // SHARE_VM_UTILITIES_RESOURCEHASH_HPP