blob: 1422afdc7e9f73b34e1690785af4dd1a71b45267 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2008 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
ager@chromium.orgbeb25712010-11-29 08:02:25 +000028#include "../include/v8stdint.h"
29#include "globals.h"
30#include "checks.h"
31#include "utils.h"
32#include "allocation.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000033
34#include "hashmap.h"
35
kasperl@chromium.org71affb52009-05-26 05:44:31 +000036namespace v8 {
37namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000038
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039Allocator HashMap::DefaultAllocator;
40
41
42HashMap::HashMap() {
43 allocator_ = NULL;
44 match_ = NULL;
45}
46
47
48HashMap::HashMap(MatchFun match,
49 Allocator* allocator,
50 uint32_t initial_capacity) {
51 allocator_ = allocator;
52 match_ = match;
53 Initialize(initial_capacity);
54}
55
56
57HashMap::~HashMap() {
58 if (allocator_) {
59 allocator_->Delete(map_);
60 }
61}
62
63
64HashMap::Entry* HashMap::Lookup(void* key, uint32_t hash, bool insert) {
65 // Find a matching entry.
66 Entry* p = Probe(key, hash);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +000067 if (p->key != NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000068 return p;
69 }
70
71 // No entry found; insert one if necessary.
72 if (insert) {
73 p->key = key;
74 p->value = NULL;
75 p->hash = hash;
76 occupancy_++;
77
78 // Grow the map if we reached >= 80% occupancy.
79 if (occupancy_ + occupancy_/4 >= capacity_) {
80 Resize();
81 p = Probe(key, hash);
82 }
83
84 return p;
85 }
86
87 // No entry found and none inserted.
88 return NULL;
89}
90
91
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +000092void HashMap::Remove(void* key, uint32_t hash) {
93 // Lookup the entry for the key to remove.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +000094 Entry* p = Probe(key, hash);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +000095 if (p->key == NULL) {
96 // Key not found nothing to remove.
97 return;
98 }
99
100 // To remove an entry we need to ensure that it does not create an empty
101 // entry that will cause the search for another entry to stop too soon. If all
102 // the entries between the entry to remove and the next empty slot have their
103 // initial position inside this interval, clearing the entry to remove will
104 // not break the search. If, while searching for the next empty entry, an
105 // entry is encountered which does not have its initial position between the
106 // entry to remove and the position looked at, then this entry can be moved to
107 // the place of the entry to remove without breaking the search for it. The
108 // entry made vacant by this move is now the entry to remove and the process
109 // starts over.
110 // Algorithm from http://en.wikipedia.org/wiki/Open_addressing.
111
112 // This guarantees loop termination as there is at least one empty entry so
113 // eventually the removed entry will have an empty entry after it.
114 ASSERT(occupancy_ < capacity_);
115
116 // p is the candidate entry to clear. q is used to scan forwards.
117 Entry* q = p; // Start at the entry to remove.
118 while (true) {
119 // Move q to the next entry.
120 q = q + 1;
121 if (q == map_end()) {
122 q = map_;
123 }
124
125 // All entries between p and q have their initial position between p and q
126 // and the entry p can be cleared without breaking the search for these
127 // entries.
128 if (q->key == NULL) {
129 break;
130 }
131
132 // Find the initial position for the entry at position q.
133 Entry* r = map_ + (q->hash & (capacity_ - 1));
134
135 // If the entry at position q has its initial position outside the range
136 // between p and q it can be moved forward to position p and will still be
137 // found. There is now a new candidate entry for clearing.
138 if ((q > p && (r <= p || r > q)) ||
139 (q < p && (r <= p && r > q))) {
140 *p = *q;
141 p = q;
142 }
143 }
144
145 // Clear the entry which is allowed to en emptied.
146 p->key = NULL;
147 occupancy_--;
148}
149
150
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000151void HashMap::Clear() {
152 // Mark all entries as empty.
153 const Entry* end = map_end();
154 for (Entry* p = map_; p < end; p++) {
155 p->key = NULL;
156 }
157 occupancy_ = 0;
158}
159
160
161HashMap::Entry* HashMap::Start() const {
162 return Next(map_ - 1);
163}
164
165
166HashMap::Entry* HashMap::Next(Entry* p) const {
167 const Entry* end = map_end();
168 ASSERT(map_ - 1 <= p && p < end);
169 for (p++; p < end; p++) {
170 if (p->key != NULL) {
171 return p;
172 }
173 }
174 return NULL;
175}
176
177
178HashMap::Entry* HashMap::Probe(void* key, uint32_t hash) {
179 ASSERT(key != NULL);
180
181 ASSERT(IsPowerOf2(capacity_));
182 Entry* p = map_ + (hash & (capacity_ - 1));
183 const Entry* end = map_end();
184 ASSERT(map_ <= p && p < end);
185
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000186 ASSERT(occupancy_ < capacity_); // Guarantees loop termination.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000187 while (p->key != NULL && (hash != p->hash || !match_(key, p->key))) {
188 p++;
189 if (p >= end) {
190 p = map_;
191 }
192 }
193
194 return p;
195}
196
197
198void HashMap::Initialize(uint32_t capacity) {
199 ASSERT(IsPowerOf2(capacity));
200 map_ = reinterpret_cast<Entry*>(allocator_->New(capacity * sizeof(Entry)));
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000201 if (map_ == NULL) {
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000202 v8::internal::FatalProcessOutOfMemory("HashMap::Initialize");
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000203 return;
204 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000205 capacity_ = capacity;
206 Clear();
207}
208
209
210void HashMap::Resize() {
211 Entry* map = map_;
212 uint32_t n = occupancy_;
213
214 // Allocate larger map.
215 Initialize(capacity_ * 2);
216
217 // Rehash all current entries.
218 for (Entry* p = map; n > 0; p++) {
219 if (p->key != NULL) {
220 Lookup(p->key, p->hash, true)->value = p->value;
221 n--;
222 }
223 }
224
225 // Delete old map.
226 allocator_->Delete(map);
227}
228
229
230} } // namespace v8::internal