Home | History | Annotate | Download | only in src
      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