blob: 6f76e9f7f48966ee70bfb96dde2f4b34bf646519 [file] [log] [blame]
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +00001// Copyright 2012 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#ifndef V8_HASHMAP_H_
29#define V8_HASHMAP_H_
30
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +000031#include "allocation.h"
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +000032#include "checks.h"
33#include "utils.h"
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +000034
kasperl@chromium.org71affb52009-05-26 05:44:31 +000035namespace v8 {
36namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000037
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +000038template<class AllocationPolicy>
rossberg@chromium.org2c067b12012-03-19 11:01:52 +000039class TemplateHashMapImpl {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000040 public:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000041 typedef bool (*MatchFun) (void* key1, void* key2);
42
rossberg@chromium.org400388e2012-06-06 09:29:22 +000043 // The default capacity. This is used by the call sites which want
44 // to pass in a non-default AllocationPolicy but want to use the
45 // default value of capacity specified by the implementation.
46 static const uint32_t kDefaultHashMapCapacity = 8;
47
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000048 // initial_capacity is the size of the initial hash map;
49 // it must be a power of 2 (and thus must not be 0).
rossberg@chromium.org400388e2012-06-06 09:29:22 +000050 TemplateHashMapImpl(MatchFun match,
51 uint32_t capacity = kDefaultHashMapCapacity,
52 AllocationPolicy allocator = AllocationPolicy());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
rossberg@chromium.org2c067b12012-03-19 11:01:52 +000054 ~TemplateHashMapImpl();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000055
ager@chromium.org32912102009-01-16 10:38:43 +000056 // HashMap entries are (key, value, hash) triplets.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000057 // Some clients may not need to use the value slot
58 // (e.g. implementers of sets, where the key is the value).
59 struct Entry {
60 void* key;
61 void* value;
62 uint32_t hash; // the full hash value for key
63 };
64
65 // If an entry with matching key is found, Lookup()
66 // returns that entry. If no matching entry is found,
67 // but insert is set, a new entry is inserted with
68 // corresponding key, key hash, and NULL value.
69 // Otherwise, NULL is returned.
rossberg@chromium.org400388e2012-06-06 09:29:22 +000070 Entry* Lookup(void* key, uint32_t hash, bool insert,
71 AllocationPolicy allocator = AllocationPolicy());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000072
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +000073 // Removes the entry with matching key.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +000074 // It returns the value of the deleted entry
75 // or null if there is no value for such key.
76 void* Remove(void* key, uint32_t hash);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +000077
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000078 // Empties the hash map (occupancy() == 0).
79 void Clear();
80
81 // The number of (non-empty) entries in the table.
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +000082 uint32_t occupancy() const { return occupancy_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000083
84 // The capacity of the table. The implementation
85 // makes sure that occupancy is at most 80% of
86 // the table capacity.
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +000087 uint32_t capacity() const { return capacity_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000088
89 // Iteration
90 //
91 // for (Entry* p = map.Start(); p != NULL; p = map.Next(p)) {
92 // ...
93 // }
94 //
95 // If entries are inserted during iteration, the effect of
96 // calling Next() is undefined.
97 Entry* Start() const;
98 Entry* Next(Entry* p) const;
99
100 private:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000101 MatchFun match_;
102 Entry* map_;
103 uint32_t capacity_;
104 uint32_t occupancy_;
105
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +0000106 Entry* map_end() const { return map_ + capacity_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000107 Entry* Probe(void* key, uint32_t hash);
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000108 void Initialize(uint32_t capacity, AllocationPolicy allocator);
109 void Resize(AllocationPolicy allocator);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000110};
111
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000112typedef TemplateHashMapImpl<FreeStoreAllocationPolicy> HashMap;
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000113
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000114template<class AllocationPolicy>
115TemplateHashMapImpl<AllocationPolicy>::TemplateHashMapImpl(
116 MatchFun match, uint32_t initial_capacity, AllocationPolicy allocator) {
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000117 match_ = match;
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000118 Initialize(initial_capacity, allocator);
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000119}
120
121
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000122template<class AllocationPolicy>
123TemplateHashMapImpl<AllocationPolicy>::~TemplateHashMapImpl() {
124 AllocationPolicy::Delete(map_);
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000125}
126
127
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000128template<class AllocationPolicy>
129typename TemplateHashMapImpl<AllocationPolicy>::Entry*
130TemplateHashMapImpl<AllocationPolicy>::Lookup(
131 void* key, uint32_t hash, bool insert, AllocationPolicy allocator) {
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000132 // Find a matching entry.
133 Entry* p = Probe(key, hash);
134 if (p->key != NULL) {
135 return p;
136 }
137
138 // No entry found; insert one if necessary.
139 if (insert) {
140 p->key = key;
141 p->value = NULL;
142 p->hash = hash;
143 occupancy_++;
144
145 // Grow the map if we reached >= 80% occupancy.
146 if (occupancy_ + occupancy_/4 >= capacity_) {
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000147 Resize(allocator);
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000148 p = Probe(key, hash);
149 }
150
151 return p;
152 }
153
154 // No entry found and none inserted.
155 return NULL;
156}
157
158
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000159template<class AllocationPolicy>
160void* TemplateHashMapImpl<AllocationPolicy>::Remove(void* key, uint32_t hash) {
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000161 // Lookup the entry for the key to remove.
162 Entry* p = Probe(key, hash);
163 if (p->key == NULL) {
164 // Key not found nothing to remove.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000165 return NULL;
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000166 }
167
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000168 void* value = p->value;
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000169 // To remove an entry we need to ensure that it does not create an empty
170 // entry that will cause the search for another entry to stop too soon. If all
171 // the entries between the entry to remove and the next empty slot have their
172 // initial position inside this interval, clearing the entry to remove will
173 // not break the search. If, while searching for the next empty entry, an
174 // entry is encountered which does not have its initial position between the
175 // entry to remove and the position looked at, then this entry can be moved to
176 // the place of the entry to remove without breaking the search for it. The
177 // entry made vacant by this move is now the entry to remove and the process
178 // starts over.
179 // Algorithm from http://en.wikipedia.org/wiki/Open_addressing.
180
181 // This guarantees loop termination as there is at least one empty entry so
182 // eventually the removed entry will have an empty entry after it.
183 ASSERT(occupancy_ < capacity_);
184
185 // p is the candidate entry to clear. q is used to scan forwards.
186 Entry* q = p; // Start at the entry to remove.
187 while (true) {
188 // Move q to the next entry.
189 q = q + 1;
190 if (q == map_end()) {
191 q = map_;
192 }
193
194 // All entries between p and q have their initial position between p and q
195 // and the entry p can be cleared without breaking the search for these
196 // entries.
197 if (q->key == NULL) {
198 break;
199 }
200
201 // Find the initial position for the entry at position q.
202 Entry* r = map_ + (q->hash & (capacity_ - 1));
203
204 // If the entry at position q has its initial position outside the range
205 // between p and q it can be moved forward to position p and will still be
206 // found. There is now a new candidate entry for clearing.
207 if ((q > p && (r <= p || r > q)) ||
208 (q < p && (r <= p && r > q))) {
209 *p = *q;
210 p = q;
211 }
212 }
213
214 // Clear the entry which is allowed to en emptied.
215 p->key = NULL;
216 occupancy_--;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +0000217 return value;
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000218}
219
220
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000221template<class AllocationPolicy>
222void TemplateHashMapImpl<AllocationPolicy>::Clear() {
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000223 // Mark all entries as empty.
224 const Entry* end = map_end();
225 for (Entry* p = map_; p < end; p++) {
226 p->key = NULL;
227 }
228 occupancy_ = 0;
229}
230
231
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000232template<class AllocationPolicy>
233typename TemplateHashMapImpl<AllocationPolicy>::Entry*
234 TemplateHashMapImpl<AllocationPolicy>::Start() const {
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000235 return Next(map_ - 1);
236}
237
238
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000239template<class AllocationPolicy>
240typename TemplateHashMapImpl<AllocationPolicy>::Entry*
241 TemplateHashMapImpl<AllocationPolicy>::Next(Entry* p) const {
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000242 const Entry* end = map_end();
243 ASSERT(map_ - 1 <= p && p < end);
244 for (p++; p < end; p++) {
245 if (p->key != NULL) {
246 return p;
247 }
248 }
249 return NULL;
250}
251
252
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000253template<class AllocationPolicy>
254typename TemplateHashMapImpl<AllocationPolicy>::Entry*
255 TemplateHashMapImpl<AllocationPolicy>::Probe(void* key, uint32_t hash) {
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000256 ASSERT(key != NULL);
257
258 ASSERT(IsPowerOf2(capacity_));
259 Entry* p = map_ + (hash & (capacity_ - 1));
260 const Entry* end = map_end();
261 ASSERT(map_ <= p && p < end);
262
263 ASSERT(occupancy_ < capacity_); // Guarantees loop termination.
264 while (p->key != NULL && (hash != p->hash || !match_(key, p->key))) {
265 p++;
266 if (p >= end) {
267 p = map_;
268 }
269 }
270
271 return p;
272}
273
274
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000275template<class AllocationPolicy>
276void TemplateHashMapImpl<AllocationPolicy>::Initialize(
277 uint32_t capacity, AllocationPolicy allocator) {
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000278 ASSERT(IsPowerOf2(capacity));
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000279 map_ = reinterpret_cast<Entry*>(allocator.New(capacity * sizeof(Entry)));
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000280 if (map_ == NULL) {
281 v8::internal::FatalProcessOutOfMemory("HashMap::Initialize");
282 return;
283 }
284 capacity_ = capacity;
285 Clear();
286}
287
288
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000289template<class AllocationPolicy>
290void TemplateHashMapImpl<AllocationPolicy>::Resize(AllocationPolicy allocator) {
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000291 Entry* map = map_;
292 uint32_t n = occupancy_;
293
294 // Allocate larger map.
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000295 Initialize(capacity_ * 2, allocator);
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000296
297 // Rehash all current entries.
298 for (Entry* p = map; n > 0; p++) {
299 if (p->key != NULL) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000300 Lookup(p->key, p->hash, true, allocator)->value = p->value;
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000301 n--;
302 }
303 }
304
305 // Delete old map.
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000306 AllocationPolicy::Delete(map);
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000307}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000308
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000309
310// A hash map for pointer keys and values with an STL-like interface.
311template<class Key, class Value, class AllocationPolicy>
312class TemplateHashMap: private TemplateHashMapImpl<AllocationPolicy> {
313 public:
314 STATIC_ASSERT(sizeof(Key*) == sizeof(void*)); // NOLINT
315 STATIC_ASSERT(sizeof(Value*) == sizeof(void*)); // NOLINT
316 struct value_type {
317 Key* first;
318 Value* second;
319 };
320
321 class Iterator {
322 public:
323 Iterator& operator++() {
324 entry_ = map_->Next(entry_);
325 return *this;
326 }
327
328 value_type* operator->() { return reinterpret_cast<value_type*>(entry_); }
329 bool operator!=(const Iterator& other) { return entry_ != other.entry_; }
330
331 private:
332 Iterator(const TemplateHashMapImpl<AllocationPolicy>* map,
333 typename TemplateHashMapImpl<AllocationPolicy>::Entry* entry) :
334 map_(map), entry_(entry) { }
335
336 const TemplateHashMapImpl<AllocationPolicy>* map_;
337 typename TemplateHashMapImpl<AllocationPolicy>::Entry* entry_;
338
339 friend class TemplateHashMap;
340 };
341
342 TemplateHashMap(
rossberg@chromium.org400388e2012-06-06 09:29:22 +0000343 typename TemplateHashMapImpl<AllocationPolicy>::MatchFun match,
344 AllocationPolicy allocator = AllocationPolicy())
345 : TemplateHashMapImpl<AllocationPolicy>(
346 match,
347 TemplateHashMapImpl<AllocationPolicy>::kDefaultHashMapCapacity,
348 allocator) { }
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000349
350 Iterator begin() const { return Iterator(this, this->Start()); }
351 Iterator end() const { return Iterator(this, NULL); }
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000352 Iterator find(Key* key, bool insert = false,
353 AllocationPolicy allocator = AllocationPolicy()) {
354 return Iterator(this, this->Lookup(key, key->Hash(), insert, allocator));
rossberg@chromium.org2c067b12012-03-19 11:01:52 +0000355 }
356};
357
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000358} } // namespace v8::internal
359
360#endif // V8_HASHMAP_H_