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.
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.
11 #include "Dictionary.h"
16 class Hash::IndexEntry {
18 IndexEntry (Key key, Hash::Value value =NULL)
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;}
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);}
33 void markDeleted() {_key.bytes = NULL; _value = (Hash::Value)-1;}
34 void markEmpty() {_key.bytes = NULL; _value = NULL;}
37 Key _key; // bytes will be NULL if empty or deleted
38 Hash::Value _value; // NULL if empty, -1 if deleted
42 Hash::Hash (int capacity)
44 _tableSize( Dictionary::choosePowerOfTwoSize(capacity) ),
45 _table( (IndexEntry*) calloc(_tableSize,sizeof(IndexEntry)) )
52 Hash::IndexEntry& Hash::_find (Key key) const {
53 IndexEntry newEntry(key);
54 int index = newEntry.hash() % _tableSize;
55 IndexEntry *found = &_table[index];
57 while (!found->isAvailable() && (!found->hasValue() || *found != newEntry)) {
58 index = (index + ++probe) % _tableSize;
59 found = &_table[index];
64 Hash::IndexEntry& Hash::_findForPut (const Hash::IndexEntry &newEntry) {
65 int index = newEntry.hash() % _tableSize;
66 IndexEntry *found = &_table[index];
68 while (found->hasValue() && *found != newEntry) {
69 index = (index + ++probe) % _tableSize;
70 found = &_table[index];
75 Hash::Value Hash::get(Key key) const {
76 return _find(key).value();
79 void Hash::put(Key key, Hash::Value value) {
80 IndexEntry newEntry = IndexEntry(key,value);
81 IndexEntry &found = _findForPut(newEntry);
83 found.setValue(value);
87 if (_count/(float)_tableSize > Dictionary::kMaxLoadFactor)
88 _resize(2*_tableSize);
92 Hash::Value Hash::remove(Key key) {
93 IndexEntry &entry = _find(key);
94 Value formerValue = entry.value();
99 if (_count/(float)_tableSize < Dictionary::kMinLoadFactor)
100 _resize(_tableSize/2);
104 void Hash::removeAll() {
105 memset(_table, 0, _tableSize*sizeof(*_table));
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());
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));
127 // Add existing entries to new table:
128 for (int index=0; index<oldTableSize; index++) {
129 IndexEntry &entry = oldTable[index];
131 _findForPut(entry) = entry;
137 Hash::Iterator::Iterator (const Hash *hash)
138 :_entry(hash->_table-1), _end(hash->_table+hash->_tableSize)
143 Hash::Iterator::Iterator (const Hash &hash)
144 :_entry(hash._table-1), _end(hash._table+hash._tableSize)
149 bool Hash::Iterator::next() {
150 while (++_entry < _end) {
151 if (_entry->hasValue())
157 Key Hash::Iterator::key() const {return _entry->key();}
158 Hash::Value Hash::Iterator::value() const {return _entry->value();}