src/Hash.cpp
author jbm@humility
Mon Sep 28 23:39:08 2009 -0700 (2009-09-28)
changeset 8 21a6c17f4e3e
parent 3 8e3ae153e2c9
permissions -rw-r--r--
jbm: it compiles... but bombs in unit tests
     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 <stdio.h>
    13 #include <algorithm>
    14 
    15 namespace Mooseyard {
    16     
    17     class Hash::IndexEntry {
    18     public:
    19         IndexEntry (Key key, Hash::Value value =NULL)
    20             :_key(key), 
    21              _value(value) 
    22         { }
    23         
    24         const Key& key() const                      {return _key;}
    25         HashCode hash() const                       {return _key.hash;}
    26         Hash::Value value() const                   {return _key.bytes ?_value :NULL;}
    27         void setValue (Hash::Value value)           {_value = value;}
    28         
    29         bool isAvailable() const                    {return _value == NULL;}    // false if deleted
    30         bool hasValue() const                       {return _key.bytes != NULL;}// false if deleted
    31         bool operator==(const IndexEntry &e) const  {return _key.hash==e._key.hash && _key.equals(e._key);}
    32         bool operator!=(const IndexEntry &e) const  {return ! (*this==e);}
    33         
    34         void markDeleted()                          {_key.bytes = NULL; _value = (Hash::Value)-1;}
    35         void markEmpty()                            {_key.bytes = NULL; _value = NULL;}
    36         
    37     private:
    38         Key _key;                       // bytes will be NULL if empty or deleted
    39         Hash::Value _value;             // NULL if empty, -1 if deleted
    40     };
    41         
    42             
    43     Hash::Hash (int capacity)
    44         :_count(0),
    45          _tableSize( Dictionary::choosePowerOfTwoSize(capacity) ),
    46          _table( (IndexEntry*) calloc(_tableSize,sizeof(IndexEntry)) )
    47     { }
    48     
    49     Hash::~Hash() {
    50         free(_table);
    51     }
    52     
    53     Hash::IndexEntry& Hash::_find (Key key) const {
    54         IndexEntry newEntry(key);
    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,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     Hash::Value Hash::remove(Key key) {
    94         IndexEntry &entry = _find(key);
    95         Value formerValue = entry.value();
    96         if (!formerValue)
    97             return NULL;
    98         entry.markDeleted();
    99         _count--;
   100         if (_count/(float)_tableSize < Dictionary::kMinLoadFactor)
   101             _resize(_tableSize/2);
   102         return formerValue;
   103     }
   104         
   105     void Hash::removeAll() {
   106         memset(_table, 0, _tableSize*sizeof(*_table));
   107         _count = 0;
   108     }
   109     
   110     void Hash::dump() const {
   111         for (int index=0; index<_tableSize; index++)
   112             if (_table[index].hasValue()) {
   113                 const Key &key = _table[index].key();
   114                 ::printf("%3i: %*s -> %p\n", 
   115                        index, (int)key.length,(const char*)key.bytes, _table[index].value());
   116             }
   117     }
   118     
   119     
   120     void Hash::_resize(int newSize) {
   121         newSize = std::max(newSize, Dictionary::kMinSize);
   122         if (newSize != _tableSize) {
   123             IndexEntry* oldTable = _table;
   124             int oldTableSize = _tableSize;
   125             _tableSize = newSize;
   126             _table = (IndexEntry*) calloc(_tableSize,sizeof(IndexEntry));
   127             
   128             // Add existing entries to new table:
   129             for (int index=0; index<oldTableSize; index++) {
   130                 IndexEntry &entry = oldTable[index];
   131                 if (entry.value())
   132                     _findForPut(entry) = entry;
   133             }
   134             free(oldTable);
   135         }
   136     }
   137     
   138     Hash::Iterator::Iterator (const Hash *hash)
   139         :_entry(hash->_table-1), _end(hash->_table+hash->_tableSize)
   140     {
   141         next();
   142     }
   143 
   144     Hash::Iterator::Iterator (const Hash &hash)
   145         :_entry(hash._table-1), _end(hash._table+hash._tableSize)
   146     {
   147         next();
   148     }
   149 
   150     bool Hash::Iterator::next() {
   151         while (++_entry < _end) {
   152             if (_entry->hasValue())
   153                 return true;
   154         }
   155         return false;
   156     }
   157 
   158     Key Hash::Iterator::key() const                 {return _entry->key();}
   159     Hash::Value Hash::Iterator::value() const       {return _entry->value();}
   160     
   161 }