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 42 HashMap::HashMap() { 43 allocator_ = NULL; 44 match_ = NULL; 45 } 46 47 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 57 HashMap::~HashMap() { 58 if (allocator_) { 59 allocator_->Delete(map_); 60 } 61 } 62 63 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 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 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 161 HashMap::Entry* HashMap::Start() const { 162 return Next(map_ - 1); 163 } 164 165 166 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 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 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 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