1 // Copyright 2017 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_OBJECTS_HASH_TABLE_H_ 6 #define V8_OBJECTS_HASH_TABLE_H_ 7 8 #include "src/base/compiler-specific.h" 9 #include "src/globals.h" 10 #include "src/objects/fixed-array.h" 11 12 // Has to be the last include (doesn't have include guards): 13 #include "src/objects/object-macros.h" 14 15 namespace v8 { 16 namespace internal { 17 18 // HashTable is a subclass of FixedArray that implements a hash table 19 // that uses open addressing and quadratic probing. 20 // 21 // In order for the quadratic probing to work, elements that have not 22 // yet been used and elements that have been deleted are 23 // distinguished. Probing continues when deleted elements are 24 // encountered and stops when unused elements are encountered. 25 // 26 // - Elements with key == undefined have not been used yet. 27 // - Elements with key == the_hole have been deleted. 28 // 29 // The hash table class is parameterized with a Shape. 30 // Shape must be a class with the following interface: 31 // class ExampleShape { 32 // public: 33 // // Tells whether key matches other. 34 // static bool IsMatch(Key key, Object* other); 35 // // Returns the hash value for key. 36 // static uint32_t Hash(Isolate* isolate, Key key); 37 // // Returns the hash value for object. 38 // static uint32_t HashForObject(Isolate* isolate, Object* object); 39 // // Convert key to an object. 40 // static inline Handle<Object> AsHandle(Isolate* isolate, Key key); 41 // // The prefix size indicates number of elements in the beginning 42 // // of the backing storage. 43 // static const int kPrefixSize = ..; 44 // // The Element size indicates number of elements per entry. 45 // static const int kEntrySize = ..; 46 // // Indicates whether IsMatch can deal with other being the_hole (a 47 // // deleted entry). 48 // static const bool kNeedsHoleCheck = ..; 49 // }; 50 // The prefix size indicates an amount of memory in the 51 // beginning of the backing storage that can be used for non-element 52 // information by subclasses. 53 54 template <typename KeyT> 55 class BaseShape { 56 public: 57 typedef KeyT Key; 58 static inline int GetMapRootIndex(); 59 static const bool kNeedsHoleCheck = true; Unwrap(Object * key)60 static Object* Unwrap(Object* key) { return key; } 61 static inline bool IsKey(ReadOnlyRoots roots, Object* key); 62 static inline bool IsLive(ReadOnlyRoots roots, Object* key); 63 }; 64 NON_EXPORTED_BASE(FixedArray)65 class V8_EXPORT_PRIVATE HashTableBase : public NON_EXPORTED_BASE(FixedArray) { 66 public: 67 // Returns the number of elements in the hash table. 68 inline int NumberOfElements() const; 69 70 // Returns the number of deleted elements in the hash table. 71 inline int NumberOfDeletedElements() const; 72 73 // Returns the capacity of the hash table. 74 inline int Capacity() const; 75 76 // ElementAdded should be called whenever an element is added to a 77 // hash table. 78 inline void ElementAdded(); 79 80 // ElementRemoved should be called whenever an element is removed from 81 // a hash table. 82 inline void ElementRemoved(); 83 inline void ElementsRemoved(int n); 84 85 // Computes the required capacity for a table holding the given 86 // number of elements. May be more than HashTable::kMaxCapacity. 87 static inline int ComputeCapacity(int at_least_space_for); 88 89 // Compute the probe offset (quadratic probing). 90 V8_INLINE static uint32_t GetProbeOffset(uint32_t n) { 91 return (n + n * n) >> 1; 92 } 93 94 static const int kNumberOfElementsIndex = 0; 95 static const int kNumberOfDeletedElementsIndex = 1; 96 static const int kCapacityIndex = 2; 97 static const int kPrefixStartIndex = 3; 98 99 // Constant used for denoting a absent entry. 100 static const int kNotFound = -1; 101 102 // Minimum capacity for newly created hash tables. 103 static const int kMinCapacity = 4; 104 105 protected: 106 // Update the number of elements in the hash table. 107 inline void SetNumberOfElements(int nof); 108 109 // Update the number of deleted elements in the hash table. 110 inline void SetNumberOfDeletedElements(int nod); 111 112 // Returns probe entry. 113 static uint32_t GetProbe(uint32_t hash, uint32_t number, uint32_t size) { 114 DCHECK(base::bits::IsPowerOfTwo(size)); 115 return (hash + GetProbeOffset(number)) & (size - 1); 116 } 117 118 inline static uint32_t FirstProbe(uint32_t hash, uint32_t size) { 119 return hash & (size - 1); 120 } 121 122 inline static uint32_t NextProbe(uint32_t last, uint32_t number, 123 uint32_t size) { 124 return (last + number) & (size - 1); 125 } 126 }; 127 128 template <typename Derived, typename Shape> 129 class HashTable : public HashTableBase { 130 public: 131 typedef Shape ShapeT; 132 typedef typename Shape::Key Key; 133 134 // Returns a new HashTable object. 135 V8_WARN_UNUSED_RESULT static Handle<Derived> New( 136 Isolate* isolate, int at_least_space_for, 137 PretenureFlag pretenure = NOT_TENURED, 138 MinimumCapacity capacity_option = USE_DEFAULT_MINIMUM_CAPACITY); 139 140 DECL_CAST(HashTable) 141 142 // Garbage collection support. 143 void IteratePrefix(ObjectVisitor* visitor); 144 void IterateElements(ObjectVisitor* visitor); 145 146 // Find entry for key otherwise return kNotFound. 147 inline int FindEntry(ReadOnlyRoots roots, Key key, int32_t hash); 148 int FindEntry(Isolate* isolate, Key key); 149 150 // Rehashes the table in-place. 151 void Rehash(Isolate* isolate); 152 153 // Tells whether k is a real key. The hole and undefined are not allowed 154 // as keys and can be used to indicate missing or deleted elements. 155 static bool IsKey(ReadOnlyRoots roots, Object* k); 156 157 inline bool ToKey(ReadOnlyRoots roots, int entry, Object** out_k); 158 159 // Returns the key at entry. KeyAt(int entry)160 Object* KeyAt(int entry) { return get(EntryToIndex(entry) + kEntryKeyIndex); } 161 162 static const int kElementsStartIndex = kPrefixStartIndex + Shape::kPrefixSize; 163 static const int kEntrySize = Shape::kEntrySize; 164 STATIC_ASSERT(kEntrySize > 0); 165 static const int kEntryKeyIndex = 0; 166 static const int kElementsStartOffset = 167 kHeaderSize + kElementsStartIndex * kPointerSize; 168 // Maximal capacity of HashTable. Based on maximal length of underlying 169 // FixedArray. Staying below kMaxCapacity also ensures that EntryToIndex 170 // cannot overflow. 171 static const int kMaxCapacity = 172 (FixedArray::kMaxLength - kElementsStartIndex) / kEntrySize; 173 174 // Don't shrink a HashTable below this capacity. 175 static const int kMinShrinkCapacity = 16; 176 177 // Maximum length to create a regular HashTable (aka. non large object). 178 static const int kMaxRegularCapacity = 16384; 179 180 // Returns the index for an entry (of the key) EntryToIndex(int entry)181 static constexpr inline int EntryToIndex(int entry) { 182 return (entry * kEntrySize) + kElementsStartIndex; 183 } 184 185 // Ensure enough space for n additional elements. 186 V8_WARN_UNUSED_RESULT static Handle<Derived> EnsureCapacity( 187 Isolate* isolate, Handle<Derived> table, int n, 188 PretenureFlag pretenure = NOT_TENURED); 189 190 // Returns true if this table has sufficient capacity for adding n elements. 191 bool HasSufficientCapacityToAdd(int number_of_additional_elements); 192 193 protected: 194 friend class ObjectHashTable; 195 196 V8_WARN_UNUSED_RESULT static Handle<Derived> NewInternal( 197 Isolate* isolate, int capacity, PretenureFlag pretenure); 198 199 // Find the entry at which to insert element with the given key that 200 // has the given hash value. 201 uint32_t FindInsertionEntry(uint32_t hash); 202 203 // Attempt to shrink hash table after removal of key. 204 V8_WARN_UNUSED_RESULT static Handle<Derived> Shrink( 205 Isolate* isolate, Handle<Derived> table, int additionalCapacity = 0); 206 207 private: 208 // Ensure that kMaxRegularCapacity yields a non-large object dictionary. 209 STATIC_ASSERT(EntryToIndex(kMaxRegularCapacity) < kMaxRegularLength); 210 STATIC_ASSERT(v8::base::bits::IsPowerOfTwo(kMaxRegularCapacity)); 211 static const int kMaxRegularEntry = kMaxRegularCapacity / kEntrySize; 212 static const int kMaxRegularIndex = EntryToIndex(kMaxRegularEntry); 213 STATIC_ASSERT(OffsetOfElementAt(kMaxRegularIndex) < 214 kMaxRegularHeapObjectSize); 215 216 // Sets the capacity of the hash table. SetCapacity(int capacity)217 void SetCapacity(int capacity) { 218 // To scale a computed hash code to fit within the hash table, we 219 // use bit-wise AND with a mask, so the capacity must be positive 220 // and non-zero. 221 DCHECK_GT(capacity, 0); 222 DCHECK_LE(capacity, kMaxCapacity); 223 set(kCapacityIndex, Smi::FromInt(capacity)); 224 } 225 226 // Returns _expected_ if one of entries given by the first _probe_ probes is 227 // equal to _expected_. Otherwise, returns the entry given by the probe 228 // number _probe_. 229 uint32_t EntryForProbe(Isolate* isolate, Object* k, int probe, 230 uint32_t expected); 231 232 void Swap(uint32_t entry1, uint32_t entry2, WriteBarrierMode mode); 233 234 // Rehashes this hash-table into the new table. 235 void Rehash(Isolate* isolate, Derived* new_table); 236 }; 237 238 // HashTableKey is an abstract superclass for virtual key behavior. 239 class HashTableKey { 240 public: HashTableKey(uint32_t hash)241 explicit HashTableKey(uint32_t hash) : hash_(hash) {} 242 243 // Returns whether the other object matches this key. 244 virtual bool IsMatch(Object* other) = 0; 245 // Returns the hash value for this key. 246 // Required. ~HashTableKey()247 virtual ~HashTableKey() {} 248 Hash()249 uint32_t Hash() const { return hash_; } 250 251 protected: set_hash(uint32_t hash)252 void set_hash(uint32_t hash) { 253 DCHECK_EQ(0, hash_); 254 hash_ = hash; 255 } 256 257 private: 258 uint32_t hash_ = 0; 259 }; 260 261 class ObjectHashTableShape : public BaseShape<Handle<Object>> { 262 public: 263 static inline bool IsMatch(Handle<Object> key, Object* other); 264 static inline uint32_t Hash(Isolate* isolate, Handle<Object> key); 265 static inline uint32_t HashForObject(Isolate* isolate, Object* object); 266 static inline Handle<Object> AsHandle(Handle<Object> key); 267 static const int kPrefixSize = 0; 268 static const int kEntryValueIndex = 1; 269 static const int kEntrySize = 2; 270 static const bool kNeedsHoleCheck = false; 271 }; 272 273 template <typename Derived, typename Shape> 274 class ObjectHashTableBase : public HashTable<Derived, Shape> { 275 public: 276 // Looks up the value associated with the given key. The hole value is 277 // returned in case the key is not present. 278 Object* Lookup(Handle<Object> key); 279 Object* Lookup(Handle<Object> key, int32_t hash); 280 Object* Lookup(ReadOnlyRoots roots, Handle<Object> key, int32_t hash); 281 282 // Returns the value at entry. 283 Object* ValueAt(int entry); 284 285 // Overwrite all keys and values with the hole value. 286 static void FillEntriesWithHoles(Handle<Derived>); 287 288 // Adds (or overwrites) the value associated with the given key. 289 static Handle<Derived> Put(Handle<Derived> table, Handle<Object> key, 290 Handle<Object> value); 291 static Handle<Derived> Put(Isolate* isolate, Handle<Derived> table, 292 Handle<Object> key, Handle<Object> value, 293 int32_t hash); 294 295 // Returns an ObjectHashTable (possibly |table|) where |key| has been removed. 296 static Handle<Derived> Remove(Isolate* isolate, Handle<Derived> table, 297 Handle<Object> key, bool* was_present); 298 static Handle<Derived> Remove(Isolate* isolate, Handle<Derived> table, 299 Handle<Object> key, bool* was_present, 300 int32_t hash); 301 302 // Returns the index to the value of an entry. EntryToValueIndex(int entry)303 static inline int EntryToValueIndex(int entry) { 304 return HashTable<Derived, Shape>::EntryToIndex(entry) + 305 Shape::kEntryValueIndex; 306 } 307 308 protected: 309 void AddEntry(int entry, Object* key, Object* value); 310 void RemoveEntry(int entry); 311 }; 312 313 // ObjectHashTable maps keys that are arbitrary objects to object values by 314 // using the identity hash of the key for hashing purposes. 315 class ObjectHashTable 316 : public ObjectHashTableBase<ObjectHashTable, ObjectHashTableShape> { 317 public: 318 DECL_CAST(ObjectHashTable) 319 DECL_PRINTER(ObjectHashTable) 320 }; 321 322 class EphemeronHashTableShape : public ObjectHashTableShape { 323 public: 324 static inline int GetMapRootIndex(); 325 }; 326 327 // EphemeronHashTable is similar to ObjectHashTable but gets special treatment 328 // by the GC. The GC treats its entries as ephemerons: both key and value are 329 // weak references, however if the key is strongly reachable its corresponding 330 // value is also kept alive. 331 class EphemeronHashTable 332 : public ObjectHashTableBase<EphemeronHashTable, EphemeronHashTableShape> { 333 public: 334 DECL_CAST(EphemeronHashTable) 335 DECL_PRINTER(EphemeronHashTable) 336 337 protected: 338 friend class MarkCompactCollector; 339 }; 340 341 class ObjectHashSetShape : public ObjectHashTableShape { 342 public: 343 static const int kPrefixSize = 0; 344 static const int kEntrySize = 1; 345 }; 346 347 class ObjectHashSet : public HashTable<ObjectHashSet, ObjectHashSetShape> { 348 public: 349 static Handle<ObjectHashSet> Add(Isolate* isolate, Handle<ObjectHashSet> set, 350 Handle<Object> key); 351 352 inline bool Has(Isolate* isolate, Handle<Object> key, int32_t hash); 353 inline bool Has(Isolate* isolate, Handle<Object> key); 354 355 DECL_CAST(ObjectHashSet) 356 }; 357 358 } // namespace internal 359 } // namespace v8 360 361 #include "src/objects/object-macros-undef.h" 362 363 #endif // V8_OBJECTS_HASH_TABLE_H_ 364