• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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