• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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 // The reason we write our own hash map instead of using unordered_map in STL,
6 // is that STL containers use a mutex pool on debug build, which will lead to
7 // deadlock when we are using async signal handler.
8 
9 #ifndef V8_BASE_HASHMAP_H_
10 #define V8_BASE_HASHMAP_H_
11 
12 #include <stdlib.h>
13 
14 #include "src/base/bits.h"
15 #include "src/base/hashmap-entry.h"
16 #include "src/base/logging.h"
17 
18 namespace v8 {
19 namespace base {
20 
21 class DefaultAllocationPolicy {
22  public:
New(size_t size)23   V8_INLINE void* New(size_t size) { return malloc(size); }
Delete(void * p)24   V8_INLINE static void Delete(void* p) { free(p); }
25 };
26 
27 template <typename Key, typename Value, class MatchFun, class AllocationPolicy>
28 class TemplateHashMapImpl {
29  public:
30   typedef TemplateHashMapEntry<Key, Value> Entry;
31 
32   // The default capacity.  This is used by the call sites which want
33   // to pass in a non-default AllocationPolicy but want to use the
34   // default value of capacity specified by the implementation.
35   static const uint32_t kDefaultHashMapCapacity = 8;
36 
37   // initial_capacity is the size of the initial hash map;
38   // it must be a power of 2 (and thus must not be 0).
39   TemplateHashMapImpl(uint32_t capacity = kDefaultHashMapCapacity,
40                       MatchFun match = MatchFun(),
41                       AllocationPolicy allocator = AllocationPolicy());
42 
43   ~TemplateHashMapImpl();
44 
45   // If an entry with matching key is found, returns that entry.
46   // Otherwise, nullptr is returned.
47   Entry* Lookup(const Key& key, uint32_t hash) const;
48 
49   // If an entry with matching key is found, returns that entry.
50   // If no matching entry is found, a new entry is inserted with
51   // corresponding key, key hash, and default initialized value.
52   Entry* LookupOrInsert(const Key& key, uint32_t hash,
53                         AllocationPolicy allocator = AllocationPolicy());
54 
55   // If an entry with matching key is found, returns that entry.
56   // If no matching entry is found, a new entry is inserted with
57   // corresponding key, key hash, and value created by func.
58   template <typename Func>
59   Entry* LookupOrInsert(const Key& key, uint32_t hash, const Func& value_func,
60                         AllocationPolicy allocator = AllocationPolicy());
61 
62   Entry* InsertNew(const Key& key, uint32_t hash,
63                    AllocationPolicy allocator = AllocationPolicy());
64 
65   // Removes the entry with matching key.
66   // It returns the value of the deleted entry
67   // or null if there is no value for such key.
68   Value Remove(const Key& key, uint32_t hash);
69 
70   // Empties the hash map (occupancy() == 0).
71   void Clear();
72 
73   // The number of (non-empty) entries in the table.
occupancy()74   uint32_t occupancy() const { return occupancy_; }
75 
76   // The capacity of the table. The implementation
77   // makes sure that occupancy is at most 80% of
78   // the table capacity.
capacity()79   uint32_t capacity() const { return capacity_; }
80 
81   // Iteration
82   //
83   // for (Entry* p = map.Start(); p != nullptr; p = map.Next(p)) {
84   //   ...
85   // }
86   //
87   // If entries are inserted during iteration, the effect of
88   // calling Next() is undefined.
89   Entry* Start() const;
90   Entry* Next(Entry* entry) const;
91 
92  private:
93   Entry* map_;
94   uint32_t capacity_;
95   uint32_t occupancy_;
96   // TODO(leszeks): This takes up space even if it has no state, maybe replace
97   // with something that does the empty base optimisation e.g. std::tuple
98   MatchFun match_;
99 
map_end()100   Entry* map_end() const { return map_ + capacity_; }
101   Entry* Probe(const Key& key, uint32_t hash) const;
102   Entry* FillEmptyEntry(Entry* entry, const Key& key, const Value& value,
103                         uint32_t hash,
104                         AllocationPolicy allocator = AllocationPolicy());
105   void Initialize(uint32_t capacity, AllocationPolicy allocator);
106   void Resize(AllocationPolicy allocator);
107 };
108 template <typename Key, typename Value, typename MatchFun,
109           class AllocationPolicy>
110 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::
TemplateHashMapImpl(uint32_t initial_capacity,MatchFun match,AllocationPolicy allocator)111     TemplateHashMapImpl(uint32_t initial_capacity, MatchFun match,
112                         AllocationPolicy allocator)
113     : match_(match) {
114   Initialize(initial_capacity, allocator);
115 }
116 
117 template <typename Key, typename Value, typename MatchFun,
118           class AllocationPolicy>
119 TemplateHashMapImpl<Key, Value, MatchFun,
~TemplateHashMapImpl()120                     AllocationPolicy>::~TemplateHashMapImpl() {
121   AllocationPolicy::Delete(map_);
122 }
123 
124 template <typename Key, typename Value, typename MatchFun,
125           class AllocationPolicy>
126 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
Lookup(const Key & key,uint32_t hash)127 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Lookup(
128     const Key& key, uint32_t hash) const {
129   Entry* entry = Probe(key, hash);
130   return entry->exists() ? entry : nullptr;
131 }
132 
133 template <typename Key, typename Value, typename MatchFun,
134           class AllocationPolicy>
135 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
LookupOrInsert(const Key & key,uint32_t hash,AllocationPolicy allocator)136 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert(
137     const Key& key, uint32_t hash, AllocationPolicy allocator) {
138   return LookupOrInsert(key, hash, []() { return Value(); }, allocator);
139 }
140 
141 template <typename Key, typename Value, typename MatchFun,
142           class AllocationPolicy>
143 template <typename Func>
144 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
LookupOrInsert(const Key & key,uint32_t hash,const Func & value_func,AllocationPolicy allocator)145 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert(
146     const Key& key, uint32_t hash, const Func& value_func,
147     AllocationPolicy allocator) {
148   // Find a matching entry.
149   Entry* entry = Probe(key, hash);
150   if (entry->exists()) {
151     return entry;
152   }
153 
154   return FillEmptyEntry(entry, key, value_func(), hash, allocator);
155 }
156 
157 template <typename Key, typename Value, typename MatchFun,
158           class AllocationPolicy>
159 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
InsertNew(const Key & key,uint32_t hash,AllocationPolicy allocator)160 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::InsertNew(
161     const Key& key, uint32_t hash, AllocationPolicy allocator) {
162   Entry* entry = Probe(key, hash);
163   return FillEmptyEntry(entry, key, Value(), hash, allocator);
164 }
165 
166 template <typename Key, typename Value, typename MatchFun,
167           class AllocationPolicy>
Remove(const Key & key,uint32_t hash)168 Value TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Remove(
169     const Key& key, uint32_t hash) {
170   // Lookup the entry for the key to remove.
171   Entry* p = Probe(key, hash);
172   if (!p->exists()) {
173     // Key not found nothing to remove.
174     return nullptr;
175   }
176 
177   Value value = p->value;
178   // To remove an entry we need to ensure that it does not create an empty
179   // entry that will cause the search for another entry to stop too soon. If all
180   // the entries between the entry to remove and the next empty slot have their
181   // initial position inside this interval, clearing the entry to remove will
182   // not break the search. If, while searching for the next empty entry, an
183   // entry is encountered which does not have its initial position between the
184   // entry to remove and the position looked at, then this entry can be moved to
185   // the place of the entry to remove without breaking the search for it. The
186   // entry made vacant by this move is now the entry to remove and the process
187   // starts over.
188   // Algorithm from http://en.wikipedia.org/wiki/Open_addressing.
189 
190   // This guarantees loop termination as there is at least one empty entry so
191   // eventually the removed entry will have an empty entry after it.
192   DCHECK(occupancy_ < capacity_);
193 
194   // p is the candidate entry to clear. q is used to scan forwards.
195   Entry* q = p;  // Start at the entry to remove.
196   while (true) {
197     // Move q to the next entry.
198     q = q + 1;
199     if (q == map_end()) {
200       q = map_;
201     }
202 
203     // All entries between p and q have their initial position between p and q
204     // and the entry p can be cleared without breaking the search for these
205     // entries.
206     if (!q->exists()) {
207       break;
208     }
209 
210     // Find the initial position for the entry at position q.
211     Entry* r = map_ + (q->hash & (capacity_ - 1));
212 
213     // If the entry at position q has its initial position outside the range
214     // between p and q it can be moved forward to position p and will still be
215     // found. There is now a new candidate entry for clearing.
216     if ((q > p && (r <= p || r > q)) || (q < p && (r <= p && r > q))) {
217       *p = *q;
218       p = q;
219     }
220   }
221 
222   // Clear the entry which is allowed to en emptied.
223   p->clear();
224   occupancy_--;
225   return value;
226 }
227 
228 template <typename Key, typename Value, typename MatchFun,
229           class AllocationPolicy>
Clear()230 void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Clear() {
231   // Mark all entries as empty.
232   for (size_t i = 0; i < capacity_; ++i) {
233     map_[i].clear();
234   }
235   occupancy_ = 0;
236 }
237 
238 template <typename Key, typename Value, typename MatchFun,
239           class AllocationPolicy>
240 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
Start()241 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Start() const {
242   return Next(map_ - 1);
243 }
244 
245 template <typename Key, typename Value, typename MatchFun,
246           class AllocationPolicy>
247 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
Next(Entry * entry)248 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Next(
249     Entry* entry) const {
250   const Entry* end = map_end();
251   DCHECK(map_ - 1 <= entry && entry < end);
252   for (entry++; entry < end; entry++) {
253     if (entry->exists()) {
254       return entry;
255     }
256   }
257   return nullptr;
258 }
259 
260 template <typename Key, typename Value, typename MatchFun,
261           class AllocationPolicy>
262 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
Probe(const Key & key,uint32_t hash)263 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Probe(
264     const Key& key, uint32_t hash) const {
265   DCHECK(base::bits::IsPowerOfTwo32(capacity_));
266   size_t i = hash & (capacity_ - 1);
267   DCHECK(i < capacity_);
268 
269   DCHECK(occupancy_ < capacity_);  // Guarantees loop termination.
270   while (map_[i].exists() && !match_(hash, map_[i].hash, key, map_[i].key)) {
271     i = (i + 1) & (capacity_ - 1);
272   }
273 
274   return &map_[i];
275 }
276 
277 template <typename Key, typename Value, typename MatchFun,
278           class AllocationPolicy>
279 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
FillEmptyEntry(Entry * entry,const Key & key,const Value & value,uint32_t hash,AllocationPolicy allocator)280 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::FillEmptyEntry(
281     Entry* entry, const Key& key, const Value& value, uint32_t hash,
282     AllocationPolicy allocator) {
283   DCHECK(!entry->exists());
284 
285   new (entry) Entry(key, value, hash);
286   occupancy_++;
287 
288   // Grow the map if we reached >= 80% occupancy.
289   if (occupancy_ + occupancy_ / 4 >= capacity_) {
290     Resize(allocator);
291     entry = Probe(key, hash);
292   }
293 
294   return entry;
295 }
296 
297 template <typename Key, typename Value, typename MatchFun,
298           class AllocationPolicy>
Initialize(uint32_t capacity,AllocationPolicy allocator)299 void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Initialize(
300     uint32_t capacity, AllocationPolicy allocator) {
301   DCHECK(base::bits::IsPowerOfTwo32(capacity));
302   map_ = reinterpret_cast<Entry*>(allocator.New(capacity * sizeof(Entry)));
303   if (map_ == nullptr) {
304     FATAL("Out of memory: HashMap::Initialize");
305     return;
306   }
307   capacity_ = capacity;
308   Clear();
309 }
310 
311 template <typename Key, typename Value, typename MatchFun,
312           class AllocationPolicy>
Resize(AllocationPolicy allocator)313 void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Resize(
314     AllocationPolicy allocator) {
315   Entry* map = map_;
316   uint32_t n = occupancy_;
317 
318   // Allocate larger map.
319   Initialize(capacity_ * 2, allocator);
320 
321   // Rehash all current entries.
322   for (Entry* entry = map; n > 0; entry++) {
323     if (entry->exists()) {
324       Entry* new_entry = Probe(entry->key, entry->hash);
325       new_entry = FillEmptyEntry(new_entry, entry->key, entry->value,
326                                  entry->hash, allocator);
327       n--;
328     }
329   }
330 
331   // Delete old map.
332   AllocationPolicy::Delete(map);
333 }
334 
335 // Match function which compares hashes before executing a (potentially
336 // expensive) key comparison.
337 template <typename Key, typename MatchFun>
338 struct HashEqualityThenKeyMatcher {
HashEqualityThenKeyMatcherHashEqualityThenKeyMatcher339   explicit HashEqualityThenKeyMatcher(MatchFun match) : match_(match) {}
340 
operatorHashEqualityThenKeyMatcher341   bool operator()(uint32_t hash1, uint32_t hash2, const Key& key1,
342                   const Key& key2) const {
343     return hash1 == hash2 && match_(key1, key2);
344   }
345 
346  private:
347   MatchFun match_;
348 };
349 
350 // Hashmap<void*, void*> which takes a custom key comparison function pointer.
351 template <typename AllocationPolicy>
352 class CustomMatcherTemplateHashMapImpl
353     : public TemplateHashMapImpl<
354           void*, void*,
355           HashEqualityThenKeyMatcher<void*, bool (*)(void*, void*)>,
356           AllocationPolicy> {
357   typedef TemplateHashMapImpl<
358       void*, void*, HashEqualityThenKeyMatcher<void*, bool (*)(void*, void*)>,
359       AllocationPolicy>
360       Base;
361 
362  public:
363   typedef bool (*MatchFun)(void*, void*);
364 
365   CustomMatcherTemplateHashMapImpl(
366       MatchFun match, uint32_t capacity = Base::kDefaultHashMapCapacity,
367       AllocationPolicy allocator = AllocationPolicy())
Base(capacity,HashEqualityThenKeyMatcher<void *,MatchFun> (match),allocator)368       : Base(capacity, HashEqualityThenKeyMatcher<void*, MatchFun>(match),
369              allocator) {}
370 };
371 
372 typedef CustomMatcherTemplateHashMapImpl<DefaultAllocationPolicy>
373     CustomMatcherHashMap;
374 
375 // Match function which compares keys directly by equality.
376 template <typename Key>
377 struct KeyEqualityMatcher {
operatorKeyEqualityMatcher378   bool operator()(uint32_t hash1, uint32_t hash2, const Key& key1,
379                   const Key& key2) const {
380     return key1 == key2;
381   }
382 };
383 
384 // Hashmap<void*, void*> which compares the key pointers directly.
385 template <typename AllocationPolicy>
386 class PointerTemplateHashMapImpl
387     : public TemplateHashMapImpl<void*, void*, KeyEqualityMatcher<void*>,
388                                  AllocationPolicy> {
389   typedef TemplateHashMapImpl<void*, void*, KeyEqualityMatcher<void*>,
390                               AllocationPolicy>
391       Base;
392 
393  public:
394   PointerTemplateHashMapImpl(uint32_t capacity = Base::kDefaultHashMapCapacity,
395                              AllocationPolicy allocator = AllocationPolicy())
Base(capacity,KeyEqualityMatcher<void * > (),allocator)396       : Base(capacity, KeyEqualityMatcher<void*>(), allocator) {}
397 };
398 
399 typedef PointerTemplateHashMapImpl<DefaultAllocationPolicy> HashMap;
400 
401 // A hash map for pointer keys and values with an STL-like interface.
402 template <class Key, class Value, class MatchFun, class AllocationPolicy>
403 class TemplateHashMap
404     : private TemplateHashMapImpl<void*, void*,
405                                   HashEqualityThenKeyMatcher<void*, MatchFun>,
406                                   AllocationPolicy> {
407   typedef TemplateHashMapImpl<void*, void*,
408                               HashEqualityThenKeyMatcher<void*, MatchFun>,
409                               AllocationPolicy>
410       Base;
411 
412  public:
413   STATIC_ASSERT(sizeof(Key*) == sizeof(void*));    // NOLINT
414   STATIC_ASSERT(sizeof(Value*) == sizeof(void*));  // NOLINT
415   struct value_type {
416     Key* first;
417     Value* second;
418   };
419 
420   class Iterator {
421    public:
422     Iterator& operator++() {
423       entry_ = map_->Next(entry_);
424       return *this;
425     }
426 
427     value_type* operator->() { return reinterpret_cast<value_type*>(entry_); }
428     bool operator!=(const Iterator& other) { return entry_ != other.entry_; }
429 
430    private:
Iterator(const Base * map,typename Base::Entry * entry)431     Iterator(const Base* map, typename Base::Entry* entry)
432         : map_(map), entry_(entry) {}
433 
434     const Base* map_;
435     typename Base::Entry* entry_;
436 
437     friend class TemplateHashMap;
438   };
439 
440   TemplateHashMap(MatchFun match,
441                   AllocationPolicy allocator = AllocationPolicy())
Base(Base::kDefaultHashMapCapacity,HashEqualityThenKeyMatcher<void *,MatchFun> (match),allocator)442       : Base(Base::kDefaultHashMapCapacity,
443              HashEqualityThenKeyMatcher<void*, MatchFun>(match), allocator) {}
444 
begin()445   Iterator begin() const { return Iterator(this, this->Start()); }
end()446   Iterator end() const { return Iterator(this, nullptr); }
447   Iterator find(Key* key, bool insert = false,
448                 AllocationPolicy allocator = AllocationPolicy()) {
449     if (insert) {
450       return Iterator(this, this->LookupOrInsert(key, key->Hash(), allocator));
451     }
452     return Iterator(this, this->Lookup(key, key->Hash()));
453   }
454 };
455 
456 }  // namespace base
457 }  // namespace v8
458 
459 #endif  // V8_BASE_HASHMAP_H_
460