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