src/Hash.cpp
author Jens Alfke <jens@mooseyard.com>
Sun Sep 20 15:14:12 2009 -0700 (2009-09-20)
changeset 0 31a43d94cc26
child 3 8e3ae153e2c9
permissions -rw-r--r--
First official checkin.
     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         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         Key k = Key(key);
    54         IndexEntry newEntry(k);
    55         int index = newEntry.hash() % _tableSize;
    56         IndexEntry *found = &_table[index];
    57         int probe = 0;
    58         while (!found->isAvailable() && (!found->hasValue() || *found != newEntry)) {
    59             index = (index + ++probe) % _tableSize;
    60             found = &_table[index];
    61         }
    62         return *found;
    63     }
    64     
    65     Hash::IndexEntry& Hash::_findForPut (const Hash::IndexEntry &newEntry) {
    66         int index = newEntry.hash() % _tableSize;
    67         IndexEntry *found = &_table[index];
    68         int probe = 0;
    69         while (found->hasValue() && *found != newEntry) {
    70             index = (index + ++probe) % _tableSize;
    71             found = &_table[index];
    72         }
    73         return *found;
    74     }
    75     
    76     Hash::Value Hash::get(Key key) const {
    77         return _find(key).value();
    78     }
    79     
    80     void Hash::put(Key key, Hash::Value value) {
    81         IndexEntry newEntry = IndexEntry(Key(key),value);
    82         IndexEntry &found = _findForPut(newEntry);
    83         if (found.hasValue())
    84             found.setValue(value);
    85         else {
    86             found = newEntry;
    87             _count++;
    88             if (_count/(float)_tableSize > Dictionary::kMaxLoadFactor)
    89                 _resize(2*_tableSize);
    90         }
    91     }
    92     
    93     bool Hash::remove(Key key) {
    94         IndexEntry &entry = _find(key);
    95         if (!entry.value())
    96             return false;
    97         entry.markDeleted();
    98         _count--;
    99         if (_count/(float)_tableSize < Dictionary::kMinLoadFactor)
   100             _resize(_tableSize/2);
   101         return true;
   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                 Blob 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 }