src/Hash.cpp
author Jens Alfke <jens@mooseyard.com>
Thu Sep 24 10:28:50 2009 -0700 (2009-09-24)
changeset 3 8e3ae153e2c9
parent 0 31a43d94cc26
child 8 21a6c17f4e3e
permissions -rw-r--r--
Fixed a nasty bug in HashDictionary that could cause heap corruption after removing a value. Added a test case to catch that bug. A few tweaks to Hash.
     1 /*
     2  *  Hash.cpp
     3  *  Ottoman
     4  *
     5  *  Created by Jens Alfke on 8/20/09.
     6  *  Copyright 2009 Jens Alfke. All rights reserved.
     7  *  BSD-Licensed: See the file "LICENSE.txt" for details.
     8  */
     9 
    10 #include "Hash.h"
    11 #include "Dictionary.h"
    12 #include <algorithm>
    13 
    14 namespace Mooseyard {
    15     
    16     class Hash::IndexEntry {
    17     public:
    18         IndexEntry (Key key, Hash::Value value =NULL)
    19             :_key(key), 
    20              _value(value) 
    21         { }
    22         
    23         const Key& key() const                      {return _key;}
    24         HashCode hash() const                       {return _key.hash;}
    25         Hash::Value value() const                   {return _key.bytes ?_value :NULL;}
    26         void setValue (Hash::Value value)           {_value = value;}
    27         
    28         bool isAvailable() const                    {return _value == NULL;}    // false if deleted
    29         bool hasValue() const                       {return _key.bytes != NULL;}// false if deleted
    30         bool operator==(const IndexEntry &e) const  {return _key.hash==e._key.hash && _key.equals(e._key);}
    31         bool operator!=(const IndexEntry &e) const  {return ! (*this==e);}
    32         
    33         void markDeleted()                          {_key.bytes = NULL; _value = (Hash::Value)-1;}
    34         void markEmpty()                            {_key.bytes = NULL; _value = NULL;}
    35         
    36     private:
    37         Key _key;                       // bytes will be NULL if empty or deleted
    38         Hash::Value _value;             // NULL if empty, -1 if deleted
    39     };
    40         
    41             
    42     Hash::Hash (int capacity)
    43         :_count(0),
    44          _tableSize( Dictionary::choosePowerOfTwoSize(capacity) ),
    45          _table( (IndexEntry*) calloc(_tableSize,sizeof(IndexEntry)) )
    46     { }
    47     
    48     Hash::~Hash() {
    49         free(_table);
    50     }
    51     
    52     Hash::IndexEntry& Hash::_find (Key key) const {
    53         IndexEntry newEntry(key);
    54         int index = newEntry.hash() % _tableSize;
    55         IndexEntry *found = &_table[index];
    56         int probe = 0;
    57         while (!found->isAvailable() && (!found->hasValue() || *found != newEntry)) {
    58             index = (index + ++probe) % _tableSize;
    59             found = &_table[index];
    60         }
    61         return *found;
    62     }
    63     
    64     Hash::IndexEntry& Hash::_findForPut (const Hash::IndexEntry &newEntry) {
    65         int index = newEntry.hash() % _tableSize;
    66         IndexEntry *found = &_table[index];
    67         int probe = 0;
    68         while (found->hasValue() && *found != newEntry) {
    69             index = (index + ++probe) % _tableSize;
    70             found = &_table[index];
    71         }
    72         return *found;
    73     }
    74     
    75     Hash::Value Hash::get(Key key) const {
    76         return _find(key).value();
    77     }
    78     
    79     void Hash::put(Key key, Hash::Value value) {
    80         IndexEntry newEntry = IndexEntry(key,value);
    81         IndexEntry &found = _findForPut(newEntry);
    82         if (found.hasValue())
    83             found.setValue(value);
    84         else {
    85             found = newEntry;
    86             _count++;
    87             if (_count/(float)_tableSize > Dictionary::kMaxLoadFactor)
    88                 _resize(2*_tableSize);
    89         }
    90     }
    91     
    92     Hash::Value Hash::remove(Key key) {
    93         IndexEntry &entry = _find(key);
    94         Value formerValue = entry.value();
    95         if (!formerValue)
    96             return NULL;
    97         entry.markDeleted();
    98         _count--;
    99         if (_count/(float)_tableSize < Dictionary::kMinLoadFactor)
   100             _resize(_tableSize/2);
   101         return formerValue;
   102     }
   103         
   104     void Hash::removeAll() {
   105         memset(_table, 0, _tableSize*sizeof(*_table));
   106         _count = 0;
   107     }
   108     
   109     void Hash::dump() const {
   110         for (int index=0; index<_tableSize; index++)
   111             if (_table[index].hasValue()) {
   112                 const Key &key = _table[index].key();
   113                 printf("%3i: %*s -> %p\n", 
   114                        index, (int)key.length,(const char*)key.bytes, _table[index].value());
   115             }
   116     }
   117     
   118     
   119     void Hash::_resize(int newSize) {
   120         newSize = std::max(newSize, Dictionary::kMinSize);
   121         if (newSize != _tableSize) {
   122             IndexEntry* oldTable = _table;
   123             int oldTableSize = _tableSize;
   124             _tableSize = newSize;
   125             _table = (IndexEntry*) calloc(_tableSize,sizeof(IndexEntry));
   126             
   127             // Add existing entries to new table:
   128             for (int index=0; index<oldTableSize; index++) {
   129                 IndexEntry &entry = oldTable[index];
   130                 if (entry.value())
   131                     _findForPut(entry) = entry;
   132             }
   133             free(oldTable);
   134         }
   135     }
   136     
   137     Hash::Iterator::Iterator (const Hash *hash)
   138         :_entry(hash->_table-1), _end(hash->_table+hash->_tableSize)
   139     {
   140         next();
   141     }
   142 
   143     Hash::Iterator::Iterator (const Hash &hash)
   144         :_entry(hash._table-1), _end(hash._table+hash._tableSize)
   145     {
   146         next();
   147     }
   148 
   149     bool Hash::Iterator::next() {
   150         while (++_entry < _end) {
   151             if (_entry->hasValue())
   152                 return true;
   153         }
   154         return false;
   155     }
   156 
   157     Key Hash::Iterator::key() const                 {return _entry->key();}
   158     Hash::Value Hash::Iterator::value() const       {return _entry->value();}
   159     
   160 }