• 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:
23   template <typename T, typename TypeTag = T[]>
NewArray(size_t length)24   V8_INLINE T* NewArray(size_t length) {
25     return static_cast<T*>(malloc(length * sizeof(T)));
26   }
27   template <typename T, typename TypeTag = T[]>
DeleteArray(T * p,size_t length)28   V8_INLINE void DeleteArray(T* p, size_t length) {
29     free(p);
30   }
31 };
32 
33 template <typename Key, typename Value, class MatchFun, class AllocationPolicy>
34 class TemplateHashMapImpl {
35  public:
36   using Entry = TemplateHashMapEntry<Key, Value>;
37 
38   // The default capacity.  This is used by the call sites which want
39   // to pass in a non-default AllocationPolicy but want to use the
40   // default value of capacity specified by the implementation.
41   static const uint32_t kDefaultHashMapCapacity = 8;
42 
43   // initial_capacity is the size of the initial hash map;
44   // it must be a power of 2 (and thus must not be 0).
45   explicit TemplateHashMapImpl(uint32_t capacity = kDefaultHashMapCapacity,
46                                MatchFun match = MatchFun(),
47                                AllocationPolicy allocator = AllocationPolicy());
48 
49   TemplateHashMapImpl(const TemplateHashMapImpl&) = delete;
50   TemplateHashMapImpl& operator=(const TemplateHashMapImpl&) = delete;
51 
52   // Clones the given hashmap and creates a copy with the same entries.
53   explicit TemplateHashMapImpl(const TemplateHashMapImpl* original,
54                                AllocationPolicy allocator = AllocationPolicy());
55 
56   TemplateHashMapImpl(TemplateHashMapImpl&& other) V8_NOEXCEPT = default;
57 
58   ~TemplateHashMapImpl();
59 
60   TemplateHashMapImpl& operator=(TemplateHashMapImpl&& other)
61       V8_NOEXCEPT = default;
62 
63   // If an entry with matching key is found, returns that entry.
64   // Otherwise, nullptr is returned.
65   Entry* Lookup(const Key& key, uint32_t hash) const;
66 
67   // If an entry with matching key is found, returns that entry.
68   // If no matching entry is found, a new entry is inserted with
69   // corresponding key, key hash, and default initialized value.
70   Entry* LookupOrInsert(const Key& key, uint32_t hash);
71 
72   // If an entry with matching key is found, returns that entry.
73   // If no matching entry is found, a new entry is inserted with
74   // corresponding key, key hash, and value created by func.
75   template <typename Func>
76   Entry* LookupOrInsert(const Key& key, uint32_t hash, const Func& value_func);
77 
78   // Heterogeneous version of LookupOrInsert, which allows a
79   // different lookup key type than the hashmap's key type.
80   // The requirement is that MatchFun has an overload:
81   //
82   //   operator()(const LookupKey& lookup_key, const Key& entry_key)
83   //
84   // If an entry with matching key is found, returns that entry.
85   // If no matching entry is found, a new entry is inserted with
86   // a key created by key_func, key hash, and value created by
87   // value_func.
88   template <typename LookupKey, typename KeyFunc, typename ValueFunc>
89   Entry* LookupOrInsert(const LookupKey& lookup_key, uint32_t hash,
90                         const KeyFunc& key_func, const ValueFunc& value_func);
91 
92   Entry* InsertNew(const Key& key, uint32_t hash);
93 
94   // Removes the entry with matching key.
95   // It returns the value of the deleted entry
96   // or null if there is no value for such key.
97   Value Remove(const Key& key, uint32_t hash);
98 
99   // Empties the hash map (occupancy() == 0).
100   void Clear();
101 
102   // Empties the map and makes it unusable for allocation.
Invalidate()103   void Invalidate() {
104     DCHECK_NOT_NULL(impl_.map_);
105     impl_.allocator().DeleteArray(impl_.map_, capacity());
106     impl_ = Impl(impl_.match(), AllocationPolicy());
107   }
108 
109   // The number of (non-empty) entries in the table.
occupancy()110   uint32_t occupancy() const { return impl_.occupancy_; }
111 
112   // The capacity of the table. The implementation
113   // makes sure that occupancy is at most 80% of
114   // the table capacity.
capacity()115   uint32_t capacity() const { return impl_.capacity_; }
116 
117   // Iteration
118   //
119   // for (Entry* p = map.Start(); p != nullptr; p = map.Next(p)) {
120   //   ...
121   // }
122   //
123   // If entries are inserted during iteration, the effect of
124   // calling Next() is undefined.
125   Entry* Start() const;
126   Entry* Next(Entry* entry) const;
127 
allocator()128   AllocationPolicy allocator() const { return impl_.allocator(); }
129 
130  protected:
131   void Initialize(uint32_t capacity);
132 
133  private:
map_end()134   Entry* map_end() const { return impl_.map_ + impl_.capacity_; }
135   template <typename LookupKey>
136   Entry* Probe(const LookupKey& key, uint32_t hash) const;
137   Entry* FillEmptyEntry(Entry* entry, const Key& key, const Value& value,
138                         uint32_t hash);
139   void Resize();
140 
141   // To support matcher and allocator that may not be possible to
142   // default-construct, we have to store their instances. Using this to store
143   // all internal state of the hash map and using private inheritance to store
144   // matcher and allocator lets us take advantage of an empty base class
145   // optimization to avoid extra space in the common case when MatchFun and
146   // AllocationPolicy have no state.
147   // TODO(ishell): Once we reach C++20, consider removing the Impl struct and
148   // adding match and allocator as [[no_unique_address]] fields.
149   struct Impl : private MatchFun, private AllocationPolicy {
ImplImpl150     Impl(MatchFun match, AllocationPolicy allocator)
151         : MatchFun(std::move(match)), AllocationPolicy(std::move(allocator)) {}
152 
153     Impl() = default;
154     Impl(const Impl&) V8_NOEXCEPT = default;
ImplImpl155     Impl(Impl&& other) V8_NOEXCEPT { *this = std::move(other); }
156 
157     Impl& operator=(const Impl& other) V8_NOEXCEPT = default;
158     Impl& operator=(Impl&& other) V8_NOEXCEPT {
159       MatchFun::operator=(std::move(other));
160       AllocationPolicy::operator=(std::move(other));
161       map_ = other.map_;
162       capacity_ = other.capacity_;
163       occupancy_ = other.occupancy_;
164 
165       other.map_ = nullptr;
166       other.capacity_ = 0;
167       other.occupancy_ = 0;
168       return *this;
169     }
170 
matchImpl171     const MatchFun& match() const { return *this; }
matchImpl172     MatchFun& match() { return *this; }
173 
allocatorImpl174     const AllocationPolicy& allocator() const { return *this; }
allocatorImpl175     AllocationPolicy& allocator() { return *this; }
176 
177     Entry* map_ = nullptr;
178     uint32_t capacity_ = 0;
179     uint32_t occupancy_ = 0;
180   } impl_;
181 };
182 template <typename Key, typename Value, typename MatchFun,
183           class AllocationPolicy>
184 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::
TemplateHashMapImpl(uint32_t initial_capacity,MatchFun match,AllocationPolicy allocator)185     TemplateHashMapImpl(uint32_t initial_capacity, MatchFun match,
186                         AllocationPolicy allocator)
187     : impl_(std::move(match), std::move(allocator)) {
188   Initialize(initial_capacity);
189 }
190 
191 template <typename Key, typename Value, typename MatchFun,
192           class AllocationPolicy>
193 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::
TemplateHashMapImpl(const TemplateHashMapImpl * original,AllocationPolicy allocator)194     TemplateHashMapImpl(const TemplateHashMapImpl* original,
195                         AllocationPolicy allocator)
196     : impl_(original->impl_.match(), std::move(allocator)) {
197   impl_.capacity_ = original->capacity();
198   impl_.occupancy_ = original->occupancy();
199   impl_.map_ = impl_.allocator().template NewArray<Entry>(capacity());
200   memcpy(impl_.map_, original->impl_.map_, capacity() * sizeof(Entry));
201 }
202 
203 template <typename Key, typename Value, typename MatchFun,
204           class AllocationPolicy>
205 TemplateHashMapImpl<Key, Value, MatchFun,
~TemplateHashMapImpl()206                     AllocationPolicy>::~TemplateHashMapImpl() {
207   if (impl_.map_) impl_.allocator().DeleteArray(impl_.map_, capacity());
208 }
209 
210 template <typename Key, typename Value, typename MatchFun,
211           class AllocationPolicy>
212 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
Lookup(const Key & key,uint32_t hash)213 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Lookup(
214     const Key& key, uint32_t hash) const {
215   Entry* entry = Probe(key, hash);
216   return entry->exists() ? entry : nullptr;
217 }
218 
219 template <typename Key, typename Value, typename MatchFun,
220           class AllocationPolicy>
221 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
LookupOrInsert(const Key & key,uint32_t hash)222 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert(
223     const Key& key, uint32_t hash) {
224   return LookupOrInsert(key, hash, []() { return Value(); });
225 }
226 
227 template <typename Key, typename Value, typename MatchFun,
228           class AllocationPolicy>
229 template <typename Func>
230 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
LookupOrInsert(const Key & key,uint32_t hash,const Func & value_func)231 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert(
232     const Key& key, uint32_t hash, const Func& value_func) {
233   return LookupOrInsert(
234       key, hash, [&key]() { return key; }, value_func);
235 }
236 
237 template <typename Key, typename Value, typename MatchFun,
238           class AllocationPolicy>
239 template <typename LookupKey, typename KeyFunc, typename ValueFunc>
240 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
LookupOrInsert(const LookupKey & lookup_key,uint32_t hash,const KeyFunc & key_func,const ValueFunc & value_func)241 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert(
242     const LookupKey& lookup_key, uint32_t hash, const KeyFunc& key_func,
243     const ValueFunc& value_func) {
244   // Find a matching entry.
245   Entry* entry = Probe(lookup_key, hash);
246   if (entry->exists()) {
247     return entry;
248   }
249 
250   return FillEmptyEntry(entry, key_func(), value_func(), hash);
251 }
252 
253 template <typename Key, typename Value, typename MatchFun,
254           class AllocationPolicy>
255 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
InsertNew(const Key & key,uint32_t hash)256 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::InsertNew(
257     const Key& key, uint32_t hash) {
258   Entry* entry = Probe(key, hash);
259   return FillEmptyEntry(entry, key, Value(), hash);
260 }
261 
262 template <typename Key, typename Value, typename MatchFun,
263           class AllocationPolicy>
Remove(const Key & key,uint32_t hash)264 Value TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Remove(
265     const Key& key, uint32_t hash) {
266   // Lookup the entry for the key to remove.
267   Entry* p = Probe(key, hash);
268   if (!p->exists()) {
269     // Key not found nothing to remove.
270     return nullptr;
271   }
272 
273   Value value = p->value;
274   // To remove an entry we need to ensure that it does not create an empty
275   // entry that will cause the search for another entry to stop too soon. If all
276   // the entries between the entry to remove and the next empty slot have their
277   // initial position inside this interval, clearing the entry to remove will
278   // not break the search. If, while searching for the next empty entry, an
279   // entry is encountered which does not have its initial position between the
280   // entry to remove and the position looked at, then this entry can be moved to
281   // the place of the entry to remove without breaking the search for it. The
282   // entry made vacant by this move is now the entry to remove and the process
283   // starts over.
284   // Algorithm from http://en.wikipedia.org/wiki/Open_addressing.
285 
286   // This guarantees loop termination as there is at least one empty entry so
287   // eventually the removed entry will have an empty entry after it.
288   DCHECK(occupancy() < capacity());
289 
290   // p is the candidate entry to clear. q is used to scan forwards.
291   Entry* q = p;  // Start at the entry to remove.
292   while (true) {
293     // Move q to the next entry.
294     q = q + 1;
295     if (q == map_end()) {
296       q = impl_.map_;
297     }
298 
299     // All entries between p and q have their initial position between p and q
300     // and the entry p can be cleared without breaking the search for these
301     // entries.
302     if (!q->exists()) {
303       break;
304     }
305 
306     // Find the initial position for the entry at position q.
307     Entry* r = impl_.map_ + (q->hash & (capacity() - 1));
308 
309     // If the entry at position q has its initial position outside the range
310     // between p and q it can be moved forward to position p and will still be
311     // found. There is now a new candidate entry for clearing.
312     if ((q > p && (r <= p || r > q)) || (q < p && (r <= p && r > q))) {
313       *p = *q;
314       p = q;
315     }
316   }
317 
318   // Clear the entry which is allowed to en emptied.
319   p->clear();
320   impl_.occupancy_--;
321   return value;
322 }
323 
324 template <typename Key, typename Value, typename MatchFun,
325           class AllocationPolicy>
Clear()326 void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Clear() {
327   // Mark all entries as empty.
328   for (size_t i = 0; i < capacity(); ++i) {
329     impl_.map_[i].clear();
330   }
331   impl_.occupancy_ = 0;
332 }
333 
334 template <typename Key, typename Value, typename MatchFun,
335           class AllocationPolicy>
336 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
Start()337 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Start() const {
338   return Next(impl_.map_ - 1);
339 }
340 
341 template <typename Key, typename Value, typename MatchFun,
342           class AllocationPolicy>
343 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
Next(Entry * entry)344 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Next(
345     Entry* entry) const {
346   const Entry* end = map_end();
347   DCHECK(impl_.map_ - 1 <= entry && entry < end);
348   for (entry++; entry < end; entry++) {
349     if (entry->exists()) {
350       return entry;
351     }
352   }
353   return nullptr;
354 }
355 
356 template <typename Key, typename Value, typename MatchFun,
357           class AllocationPolicy>
358 template <typename LookupKey>
359 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
Probe(const LookupKey & key,uint32_t hash)360 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Probe(
361     const LookupKey& key, uint32_t hash) const {
362   DCHECK(base::bits::IsPowerOfTwo(capacity()));
363   size_t i = hash & (capacity() - 1);
364   DCHECK(i < capacity());
365 
366   DCHECK(occupancy() < capacity());  // Guarantees loop termination.
367   Entry* map = impl_.map_;
368   while (map[i].exists() &&
369          !impl_.match()(hash, map[i].hash, key, map[i].key)) {
370     i = (i + 1) & (capacity() - 1);
371   }
372 
373   return &map[i];
374 }
375 
376 template <typename Key, typename Value, typename MatchFun,
377           class AllocationPolicy>
378 typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
FillEmptyEntry(Entry * entry,const Key & key,const Value & value,uint32_t hash)379 TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::FillEmptyEntry(
380     Entry* entry, const Key& key, const Value& value, uint32_t hash) {
381   DCHECK(!entry->exists());
382 
383   new (entry) Entry(key, value, hash);
384   impl_.occupancy_++;
385 
386   // Grow the map if we reached >= 80% occupancy.
387   if (occupancy() + occupancy() / 4 >= capacity()) {
388     Resize();
389     entry = Probe(key, hash);
390   }
391 
392   return entry;
393 }
394 
395 template <typename Key, typename Value, typename MatchFun,
396           class AllocationPolicy>
Initialize(uint32_t capacity)397 void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Initialize(
398     uint32_t capacity) {
399   DCHECK(base::bits::IsPowerOfTwo(capacity));
400   impl_.map_ = impl_.allocator().template NewArray<Entry>(capacity);
401   if (impl_.map_ == nullptr) {
402     FATAL("Out of memory: HashMap::Initialize");
403     return;
404   }
405   impl_.capacity_ = capacity;
406   Clear();
407 }
408 
409 template <typename Key, typename Value, typename MatchFun,
410           class AllocationPolicy>
Resize()411 void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Resize() {
412   Entry* old_map = impl_.map_;
413   uint32_t old_capacity = capacity();
414   uint32_t n = occupancy();
415 
416   // Allocate larger map.
417   Initialize(capacity() * 2);
418 
419   // Rehash all current entries.
420   for (Entry* entry = old_map; n > 0; entry++) {
421     if (entry->exists()) {
422       Entry* new_entry = Probe(entry->key, entry->hash);
423       new_entry =
424           FillEmptyEntry(new_entry, entry->key, entry->value, entry->hash);
425       n--;
426     }
427   }
428 
429   // Delete old map.
430   impl_.allocator().DeleteArray(old_map, old_capacity);
431 }
432 
433 // Match function which compares hashes before executing a (potentially
434 // expensive) key comparison.
435 template <typename Key, typename MatchFun>
436 struct HashEqualityThenKeyMatcher {
HashEqualityThenKeyMatcherHashEqualityThenKeyMatcher437   explicit HashEqualityThenKeyMatcher(MatchFun match) : match_(match) {}
438 
operatorHashEqualityThenKeyMatcher439   bool operator()(uint32_t hash1, uint32_t hash2, const Key& key1,
440                   const Key& key2) const {
441     return hash1 == hash2 && match_(key1, key2);
442   }
443 
444  private:
445   MatchFun match_;
446 };
447 
448 // Hashmap<void*, void*> which takes a custom key comparison function pointer.
449 template <typename AllocationPolicy>
450 class CustomMatcherTemplateHashMapImpl
451     : public TemplateHashMapImpl<
452           void*, void*,
453           HashEqualityThenKeyMatcher<void*, bool (*)(void*, void*)>,
454           AllocationPolicy> {
455   using Base = TemplateHashMapImpl<
456       void*, void*, HashEqualityThenKeyMatcher<void*, bool (*)(void*, void*)>,
457       AllocationPolicy>;
458 
459  public:
460   using MatchFun = bool (*)(void*, void*);
461 
462   explicit CustomMatcherTemplateHashMapImpl(
463       MatchFun match, uint32_t capacity = Base::kDefaultHashMapCapacity,
464       AllocationPolicy allocator = AllocationPolicy())
Base(capacity,HashEqualityThenKeyMatcher<void *,MatchFun> (match),allocator)465       : Base(capacity, HashEqualityThenKeyMatcher<void*, MatchFun>(match),
466              allocator) {}
467 
468   explicit CustomMatcherTemplateHashMapImpl(
469       const CustomMatcherTemplateHashMapImpl* original,
470       AllocationPolicy allocator = AllocationPolicy())
Base(original,allocator)471       : Base(original, allocator) {}
472 
473   CustomMatcherTemplateHashMapImpl(const CustomMatcherTemplateHashMapImpl&) =
474       delete;
475   CustomMatcherTemplateHashMapImpl& operator=(
476       const CustomMatcherTemplateHashMapImpl&) = delete;
477 };
478 
479 using CustomMatcherHashMap =
480     CustomMatcherTemplateHashMapImpl<DefaultAllocationPolicy>;
481 
482 // Match function which compares keys directly by equality.
483 template <typename Key>
484 struct KeyEqualityMatcher {
operatorKeyEqualityMatcher485   bool operator()(uint32_t hash1, uint32_t hash2, const Key& key1,
486                   const Key& key2) const {
487     return key1 == key2;
488   }
489 };
490 
491 // Hashmap<void*, void*> which compares the key pointers directly.
492 template <typename AllocationPolicy>
493 class PointerTemplateHashMapImpl
494     : public TemplateHashMapImpl<void*, void*, KeyEqualityMatcher<void*>,
495                                  AllocationPolicy> {
496   using Base = TemplateHashMapImpl<void*, void*, KeyEqualityMatcher<void*>,
497                                    AllocationPolicy>;
498 
499  public:
500   explicit PointerTemplateHashMapImpl(
501       uint32_t capacity = Base::kDefaultHashMapCapacity,
502       AllocationPolicy allocator = AllocationPolicy())
Base(capacity,KeyEqualityMatcher<void * > (),allocator)503       : Base(capacity, KeyEqualityMatcher<void*>(), allocator) {}
504 
505   PointerTemplateHashMapImpl(const PointerTemplateHashMapImpl& other,
506                              AllocationPolicy allocator = AllocationPolicy())
507       : Base(&other, allocator) {}
508 
PointerTemplateHashMapImpl(PointerTemplateHashMapImpl && other)509   PointerTemplateHashMapImpl(PointerTemplateHashMapImpl&& other) V8_NOEXCEPT
510       : Base(std::move(other)) {}
511 
512   PointerTemplateHashMapImpl& operator=(PointerTemplateHashMapImpl&& other)
513       V8_NOEXCEPT {
514     static_cast<Base&>(*this) = std::move(other);
515     return *this;
516   }
517 };
518 
519 using HashMap = PointerTemplateHashMapImpl<DefaultAllocationPolicy>;
520 
521 // A hash map for pointer keys and values with an STL-like interface.
522 template <class Key, class Value, class MatchFun, class AllocationPolicy>
523 class TemplateHashMap
524     : private TemplateHashMapImpl<void*, void*,
525                                   HashEqualityThenKeyMatcher<void*, MatchFun>,
526                                   AllocationPolicy> {
527   using Base = TemplateHashMapImpl<void*, void*,
528                                    HashEqualityThenKeyMatcher<void*, MatchFun>,
529                                    AllocationPolicy>;
530 
531  public:
532   STATIC_ASSERT(sizeof(Key*) == sizeof(void*));    // NOLINT
533   STATIC_ASSERT(sizeof(Value*) == sizeof(void*));  // NOLINT
534   struct value_type {
535     Key* first;
536     Value* second;
537   };
538 
539   class Iterator {
540    public:
541     Iterator& operator++() {
542       entry_ = map_->Next(entry_);
543       return *this;
544     }
545 
546     value_type* operator->() { return reinterpret_cast<value_type*>(entry_); }
547     bool operator!=(const Iterator& other) { return entry_ != other.entry_; }
548 
549    private:
Iterator(const Base * map,typename Base::Entry * entry)550     Iterator(const Base* map, typename Base::Entry* entry)
551         : map_(map), entry_(entry) {}
552 
553     const Base* map_;
554     typename Base::Entry* entry_;
555 
556     friend class TemplateHashMap;
557   };
558 
559   explicit TemplateHashMap(MatchFun match,
560                            AllocationPolicy allocator = AllocationPolicy())
Base(Base::kDefaultHashMapCapacity,HashEqualityThenKeyMatcher<void *,MatchFun> (match),allocator)561       : Base(Base::kDefaultHashMapCapacity,
562              HashEqualityThenKeyMatcher<void*, MatchFun>(match), allocator) {}
563 
begin()564   Iterator begin() const { return Iterator(this, this->Start()); }
end()565   Iterator end() const { return Iterator(this, nullptr); }
566   Iterator find(Key* key, bool insert = false) {
567     if (insert) {
568       return Iterator(this, this->LookupOrInsert(key, key->Hash()));
569     }
570     return Iterator(this, this->Lookup(key, key->Hash()));
571   }
572 };
573 
574 }  // namespace base
575 }  // namespace v8
576 
577 #endif  // V8_BASE_HASHMAP_H_
578