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