1 // Copyright 2008 the V8 project authors. All rights reserved.
2 // 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 #include "../include/v8stdint.h"
29 #include "globals.h"
30 #include "checks.h"
31 #include "utils.h"
32 #include "allocation.h"
33
34 #include "hashmap.h"
35
36 namespace v8 {
37 namespace internal {
38
39 Allocator HashMap::DefaultAllocator;
40
41
HashMap()42 HashMap::HashMap() {
43 allocator_ = NULL;
44 match_ = NULL;
45 }
46
47
HashMap(MatchFun match,Allocator * allocator,uint32_t initial_capacity)48 HashMap::HashMap(MatchFun match,
49 Allocator* allocator,
50 uint32_t initial_capacity) {
51 allocator_ = allocator;
52 match_ = match;
53 Initialize(initial_capacity);
54 }
55
56
~HashMap()57 HashMap::~HashMap() {
58 if (allocator_) {
59 allocator_->Delete(map_);
60 }
61 }
62
63
Lookup(void * key,uint32_t hash,bool insert)64 HashMap::Entry* HashMap::Lookup(void* key, uint32_t hash, bool insert) {
65 // Find a matching entry.
66 Entry* p = Probe(key, hash);
67 if (p->key != NULL) {
68 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
Remove(void * key,uint32_t hash)92 void HashMap::Remove(void* key, uint32_t hash) {
93 // Lookup the entry for the key to remove.
94 Entry* p = Probe(key, hash);
95 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
Clear()151 void 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
Start() const161 HashMap::Entry* HashMap::Start() const {
162 return Next(map_ - 1);
163 }
164
165
Next(Entry * p) const166 HashMap::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
Probe(void * key,uint32_t hash)178 HashMap::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
186 ASSERT(occupancy_ < capacity_); // Guarantees loop termination.
187 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
Initialize(uint32_t capacity)198 void HashMap::Initialize(uint32_t capacity) {
199 ASSERT(IsPowerOf2(capacity));
200 map_ = reinterpret_cast<Entry*>(allocator_->New(capacity * sizeof(Entry)));
201 if (map_ == NULL) {
202 v8::internal::FatalProcessOutOfMemory("HashMap::Initialize");
203 return;
204 }
205 capacity_ = capacity;
206 Clear();
207 }
208
209
Resize()210 void 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
231