2006-03-23 22:59:38 +00:00
|
|
|
/* ScummVM - Scumm Interpreter
|
|
|
|
* Copyright (C) 2006 The ScummVM project
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* $URL$
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2006-03-28 10:05:25 +00:00
|
|
|
// The hash map (associative array) implementation in this file is
|
|
|
|
// based on code by Andrew Y. Ng, 1996:
|
2006-03-23 22:59:38 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 1998-2003 Massachusetts Institute of Technology.
|
|
|
|
* This code was developed as part of the Haystack research project
|
|
|
|
* (http://haystack.lcs.mit.edu/). Permission is hereby granted,
|
|
|
|
* free of charge, to any person obtaining a copy of this software
|
|
|
|
* and associated documentation files (the "Software"), to deal in
|
|
|
|
* the Software without restriction, including without limitation
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute,
|
|
|
|
* sublicense, and/or sell copies of the Software, and to permit
|
|
|
|
* persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be
|
|
|
|
* included in all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common/stdafx.h"
|
2006-03-31 22:19:39 +00:00
|
|
|
#include "common/func.h"
|
2006-03-23 22:59:38 +00:00
|
|
|
#include "common/str.h"
|
|
|
|
#include "common/util.h"
|
|
|
|
|
2006-03-28 10:05:25 +00:00
|
|
|
#ifndef COMMON_HASHMAP_H
|
|
|
|
#define COMMON_HASHMAP_H
|
2006-03-23 22:59:38 +00:00
|
|
|
|
|
|
|
namespace Common {
|
|
|
|
|
|
|
|
typedef Common::String String;
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
uint hashit(const char *str);
|
|
|
|
uint hashit_lower(const char *str); // Generate a hash based on the lowercase version of the string
|
|
|
|
|
|
|
|
// Specalization of the Hash functor for String objects.
|
|
|
|
template <>
|
|
|
|
struct Hash<String> {
|
|
|
|
uint operator()(const String& s) const {
|
|
|
|
return hashit(s.c_str());
|
|
|
|
}
|
|
|
|
};
|
2006-03-24 16:53:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
// The table sizes ideally are primes. We use a helper function to find
|
|
|
|
// suitable table sizes.
|
|
|
|
uint nextTableSize(uint x);
|
|
|
|
|
|
|
|
|
|
|
|
// Enable the following #define if you want to check how many collisions the
|
|
|
|
// code produces (many collisions indicate either a bad hash function, or a
|
|
|
|
// hash table that is too small).
|
2006-03-31 22:38:27 +00:00
|
|
|
//#define DEBUG_HASH_COLLISIONS
|
2006-03-31 22:19:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* HashMap<Key,Val> maps objects of type Key to objects of type Val.
|
|
|
|
* For each used Key type, we need an "uint hashit(Key,uint)" function
|
|
|
|
* that computes a hash for the given Key object and returns it as an
|
|
|
|
* an integer from 0 to hashsize-1, and also an "equality functor".
|
|
|
|
* that returns true if if its two arguments are to be considered
|
|
|
|
* equal. Also, we assume that "=" works on Val objects for assignment.
|
|
|
|
*
|
|
|
|
* If aa is an HashMap<Key,Val>, then space is allocated each time aa[key] is
|
|
|
|
* referenced, for a new key. If the object is const, then an assertion is
|
|
|
|
* triggered instead. Hence if you are not sure whether a key is contained in
|
|
|
|
* the map, use contains() first to check for its presence.
|
|
|
|
*/
|
|
|
|
template <class Key, class Val, class HashFunc = Hash<Key>, class EqualFunc = EqualTo<Key> >
|
2006-03-28 10:05:25 +00:00
|
|
|
class HashMap {
|
2006-03-23 22:59:38 +00:00
|
|
|
private:
|
2006-03-28 10:05:25 +00:00
|
|
|
// data structure used by HashMap internally to keep
|
2006-03-24 15:39:07 +00:00
|
|
|
// track of what's mapped to what.
|
2006-03-28 11:21:13 +00:00
|
|
|
struct BaseNode {
|
|
|
|
Key _key;
|
|
|
|
Val _value;
|
|
|
|
BaseNode() {}
|
|
|
|
BaseNode(const Key &key) : _key(key) {}
|
2006-03-24 15:39:07 +00:00
|
|
|
};
|
|
|
|
|
2006-03-28 11:21:13 +00:00
|
|
|
BaseNode **_arr; // hashtable of size arrsize.
|
2006-03-24 16:53:32 +00:00
|
|
|
uint _arrsize, _nele;
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
HashFunc _hash;
|
|
|
|
EqualFunc _equal;
|
|
|
|
|
2006-03-24 16:53:32 +00:00
|
|
|
#ifdef DEBUG_HASH_COLLISIONS
|
2006-03-24 17:13:24 +00:00
|
|
|
mutable int _collisions, _lookups;
|
2006-03-24 16:53:32 +00:00
|
|
|
#endif
|
2006-03-23 22:59:38 +00:00
|
|
|
|
2006-03-24 15:39:07 +00:00
|
|
|
int lookup(const Key &key) const;
|
2006-03-28 10:54:02 +00:00
|
|
|
void expand_array(uint newsize);
|
2006-03-23 22:59:38 +00:00
|
|
|
|
2006-03-24 14:14:29 +00:00
|
|
|
public:
|
2006-03-28 12:34:34 +00:00
|
|
|
class const_iterator {
|
2006-03-31 22:19:39 +00:00
|
|
|
typedef const HashMap<Key, Val, HashFunc, EqualFunc> * hashmap_t;
|
|
|
|
friend class HashMap<Key, Val, HashFunc, EqualFunc>;
|
2006-03-28 12:34:34 +00:00
|
|
|
protected:
|
|
|
|
hashmap_t _hashmap;
|
|
|
|
uint _idx;
|
|
|
|
const_iterator(uint idx, hashmap_t hashmap) : _idx(idx), _hashmap(hashmap) {}
|
|
|
|
|
|
|
|
const BaseNode *deref() const {
|
|
|
|
assert(_hashmap != 0);
|
|
|
|
BaseNode *node(_hashmap->_arr[_idx]);
|
|
|
|
assert(node != 0);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
const_iterator() : _idx(0), _hashmap(0) {}
|
|
|
|
|
|
|
|
const BaseNode &operator *() const { return *deref(); }
|
|
|
|
const BaseNode *operator->() const { return deref(); }
|
2006-03-31 22:19:39 +00:00
|
|
|
bool operator ==(const const_iterator &iter) const { return _idx == iter._idx && _hashmap == iter._hashmap; }
|
|
|
|
bool operator !=(const const_iterator &iter) const { return !(*this == iter); }
|
|
|
|
const_iterator operator ++() {
|
2006-03-28 12:34:34 +00:00
|
|
|
assert(_hashmap);
|
|
|
|
do {
|
|
|
|
_idx++;
|
|
|
|
} while (_idx < _hashmap->_arrsize && _hashmap->_arr[_idx] == 0);
|
|
|
|
if (_idx >= _hashmap->_arrsize)
|
|
|
|
_idx = (uint)-1;
|
2006-03-31 22:19:39 +00:00
|
|
|
|
|
|
|
return *this;
|
2006-03-28 12:34:34 +00:00
|
|
|
}
|
|
|
|
};
|
2006-03-23 22:59:38 +00:00
|
|
|
|
2006-03-28 10:05:25 +00:00
|
|
|
HashMap();
|
|
|
|
~HashMap();
|
2006-03-24 15:39:07 +00:00
|
|
|
|
|
|
|
bool contains(const Key &key) const;
|
|
|
|
|
|
|
|
Val &operator [](const Key &key);
|
|
|
|
const Val &operator [](const Key &key) const;
|
|
|
|
const Val &queryVal(const Key &key) const;
|
|
|
|
|
2006-03-24 14:30:33 +00:00
|
|
|
void clear(bool shrinkArray = 0);
|
2006-03-24 15:22:17 +00:00
|
|
|
|
2006-03-28 12:34:34 +00:00
|
|
|
size_t erase(const Key &key);
|
2006-03-28 09:42:54 +00:00
|
|
|
|
2006-03-28 12:34:34 +00:00
|
|
|
const_iterator begin() const {
|
2006-03-31 22:19:39 +00:00
|
|
|
// Find and return the first non-empty entry
|
|
|
|
for (uint ctr = 0; ctr < _arrsize; ++ctr) {
|
|
|
|
if (_arr[ctr])
|
|
|
|
return const_iterator(ctr, this);
|
|
|
|
}
|
|
|
|
return end();
|
2006-03-28 12:34:34 +00:00
|
|
|
}
|
|
|
|
const_iterator end() const {
|
|
|
|
return const_iterator((uint)-1, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
const_iterator find(const String &key) const {
|
|
|
|
uint ctr = lookup(key);
|
|
|
|
if (_arr[ctr])
|
|
|
|
return const_iterator(ctr, this);
|
|
|
|
return end();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: insert() method?
|
2006-03-28 09:42:54 +00:00
|
|
|
|
|
|
|
bool empty() const {
|
|
|
|
return (_nele == 0);
|
|
|
|
}
|
2006-03-23 22:59:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//-------------------------------------------------------
|
2006-03-28 10:05:25 +00:00
|
|
|
// HashMap functions
|
2006-03-23 22:59:38 +00:00
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
template <class Key, class Val, class HashFunc, class EqualFunc>
|
|
|
|
HashMap<Key, Val, HashFunc, EqualFunc>::HashMap() {
|
2006-03-24 16:53:32 +00:00
|
|
|
_arrsize = nextTableSize(0);
|
2006-03-28 11:21:13 +00:00
|
|
|
_arr = new BaseNode *[_arrsize];
|
2006-03-24 15:22:17 +00:00
|
|
|
assert(_arr != NULL);
|
2006-03-28 11:21:13 +00:00
|
|
|
memset(_arr, 0, _arrsize * sizeof(BaseNode *));
|
2006-03-23 22:59:38 +00:00
|
|
|
|
|
|
|
_nele = 0;
|
2006-03-24 16:53:32 +00:00
|
|
|
|
|
|
|
#ifdef DEBUG_HASH_COLLISIONS
|
|
|
|
_collisions = 0;
|
2006-03-24 17:13:24 +00:00
|
|
|
_lookups = 0;
|
2006-03-24 16:53:32 +00:00
|
|
|
#endif
|
2006-03-23 22:59:38 +00:00
|
|
|
}
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
template <class Key, class Val, class HashFunc, class EqualFunc>
|
|
|
|
HashMap<Key, Val, HashFunc, EqualFunc>::~HashMap() {
|
2006-03-24 16:53:32 +00:00
|
|
|
uint ctr;
|
2006-03-23 22:59:38 +00:00
|
|
|
|
|
|
|
for (ctr = 0; ctr < _arrsize; ctr++)
|
|
|
|
if (_arr[ctr] != NULL)
|
|
|
|
delete _arr[ctr];
|
|
|
|
|
|
|
|
delete[] _arr;
|
|
|
|
}
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
template <class Key, class Val, class HashFunc, class EqualFunc>
|
|
|
|
void HashMap<Key, Val, HashFunc, EqualFunc>::clear(bool shrinkArray) {
|
2006-03-24 16:53:32 +00:00
|
|
|
for (uint ctr = 0; ctr < _arrsize; ctr++) {
|
2006-03-23 22:59:38 +00:00
|
|
|
if (_arr[ctr] != NULL) {
|
|
|
|
delete _arr[ctr];
|
|
|
|
_arr[ctr] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-24 16:53:32 +00:00
|
|
|
if (shrinkArray && _arrsize > nextTableSize(0)) {
|
|
|
|
delete[] _arr;
|
2006-03-23 22:59:38 +00:00
|
|
|
|
2006-03-24 16:53:32 +00:00
|
|
|
_arrsize = nextTableSize(0);
|
2006-03-28 11:21:13 +00:00
|
|
|
_arr = new BaseNode *[_arrsize];
|
2006-03-24 16:53:32 +00:00
|
|
|
assert(_arr != NULL);
|
2006-03-28 11:21:13 +00:00
|
|
|
memset(_arr, 0, _arrsize * sizeof(BaseNode *));
|
2006-03-23 22:59:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_nele = 0;
|
|
|
|
}
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
template <class Key, class Val, class HashFunc, class EqualFunc>
|
|
|
|
void HashMap<Key, Val, HashFunc, EqualFunc>::expand_array(uint newsize) {
|
2006-03-28 10:54:02 +00:00
|
|
|
assert(newsize > _arrsize);
|
2006-03-28 11:21:13 +00:00
|
|
|
BaseNode **old_arr;
|
2006-03-24 16:53:32 +00:00
|
|
|
uint old_arrsize, old_nele, ctr, dex;
|
2006-03-23 22:59:38 +00:00
|
|
|
|
|
|
|
old_nele = _nele;
|
|
|
|
old_arr = _arr;
|
|
|
|
old_arrsize = _arrsize;
|
|
|
|
|
|
|
|
// allocate a new array
|
2006-03-28 10:54:02 +00:00
|
|
|
_arrsize = newsize;
|
2006-03-28 11:21:13 +00:00
|
|
|
_arr = new BaseNode *[_arrsize];
|
2006-03-23 22:59:38 +00:00
|
|
|
assert(_arr != NULL);
|
2006-03-28 11:21:13 +00:00
|
|
|
memset(_arr, 0, _arrsize * sizeof(BaseNode *));
|
2006-03-23 22:59:38 +00:00
|
|
|
|
|
|
|
_nele = 0;
|
|
|
|
|
|
|
|
// rehash all the old elements
|
|
|
|
for (ctr = 0; ctr < old_arrsize; ctr++) {
|
|
|
|
if (old_arr[ctr] == NULL)
|
|
|
|
continue;
|
|
|
|
|
2006-03-28 11:21:13 +00:00
|
|
|
// Insert the element from the old table into the new table.
|
|
|
|
// Since we know that no key exists twice the old table, we
|
|
|
|
// can do this slightly better than by calling lookup, since we
|
2006-03-31 22:19:39 +00:00
|
|
|
// don't have to call _equal().
|
|
|
|
dex = _hash(old_arr[ctr]->_key) % _arrsize;
|
2006-03-24 17:13:24 +00:00
|
|
|
while (_arr[dex] != NULL) {
|
2006-03-28 11:21:13 +00:00
|
|
|
dex = (dex + 1) % _arrsize;
|
2006-03-24 17:13:24 +00:00
|
|
|
}
|
2006-03-23 22:59:38 +00:00
|
|
|
|
|
|
|
_arr[dex] = old_arr[ctr];
|
|
|
|
_nele++;
|
|
|
|
}
|
|
|
|
|
2006-03-28 10:54:02 +00:00
|
|
|
// Perform a sanity check: Old number of elements should match the new one!
|
2006-03-23 22:59:38 +00:00
|
|
|
assert(_nele == old_nele);
|
|
|
|
|
|
|
|
delete[] old_arr;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
template <class Key, class Val, class HashFunc, class EqualFunc>
|
|
|
|
int HashMap<Key, Val, HashFunc, EqualFunc>::lookup(const Key &key) const {
|
|
|
|
uint ctr = _hash(key) % _arrsize;
|
2006-03-28 11:21:13 +00:00
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
while (_arr[ctr] != NULL && !_equal(_arr[ctr]->_key, key)) {
|
2006-03-28 11:21:13 +00:00
|
|
|
ctr = (ctr + 1) % _arrsize;
|
|
|
|
|
|
|
|
#ifdef DEBUG_HASH_COLLISIONS
|
|
|
|
_collisions++;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_HASH_COLLISIONS
|
|
|
|
_lookups++;
|
|
|
|
fprintf(stderr, "collisions %d, lookups %d, ratio %f in HashMap %p; size %d num elements %d\n",
|
|
|
|
_collisions, _lookups, ((double) _collisions / (double)_lookups),
|
|
|
|
(const void *)this, _arrsize, _nele);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return ctr;
|
|
|
|
}
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
template <class Key, class Val, class HashFunc, class EqualFunc>
|
|
|
|
bool HashMap<Key, Val, HashFunc, EqualFunc>::contains(const Key &key) const {
|
2006-03-28 11:21:13 +00:00
|
|
|
uint ctr = lookup(key);
|
|
|
|
return (_arr[ctr] != NULL);
|
|
|
|
}
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
template <class Key, class Val, class HashFunc, class EqualFunc>
|
|
|
|
Val &HashMap<Key, Val, HashFunc, EqualFunc>::operator [](const Key &key) {
|
2006-03-24 16:53:32 +00:00
|
|
|
uint ctr = lookup(key);
|
2006-03-23 22:59:38 +00:00
|
|
|
|
|
|
|
if (_arr[ctr] == NULL) {
|
2006-03-28 11:21:13 +00:00
|
|
|
_arr[ctr] = new BaseNode(key);
|
2006-03-23 22:59:38 +00:00
|
|
|
_nele++;
|
|
|
|
|
2006-03-28 10:54:02 +00:00
|
|
|
// Keep the load factor below 75%.
|
2006-03-28 16:19:18 +00:00
|
|
|
if (_nele > _arrsize * 75 / 100) {
|
2006-03-28 10:54:02 +00:00
|
|
|
expand_array(nextTableSize(_arrsize));
|
2006-03-24 15:39:07 +00:00
|
|
|
ctr = lookup(key);
|
2006-03-23 22:59:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-28 11:21:13 +00:00
|
|
|
return _arr[ctr]->_value;
|
2006-03-23 22:59:38 +00:00
|
|
|
}
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
template <class Key, class Val, class HashFunc, class EqualFunc>
|
|
|
|
const Val &HashMap<Key, Val, HashFunc, EqualFunc>::operator [](const Key &key) const {
|
2006-03-24 15:39:07 +00:00
|
|
|
return queryVal(key);
|
|
|
|
}
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
template <class Key, class Val, class HashFunc, class EqualFunc>
|
|
|
|
const Val &HashMap<Key, Val, HashFunc, EqualFunc>::queryVal(const Key &key) const {
|
2006-03-24 16:53:32 +00:00
|
|
|
uint ctr = lookup(key);
|
2006-03-24 15:22:17 +00:00
|
|
|
assert(_arr[ctr] != NULL);
|
2006-03-28 11:21:13 +00:00
|
|
|
return _arr[ctr]->_value;
|
2006-03-23 22:59:38 +00:00
|
|
|
}
|
|
|
|
|
2006-03-31 22:19:39 +00:00
|
|
|
template <class Key, class Val, class HashFunc, class EqualFunc>
|
|
|
|
size_t HashMap<Key, Val, HashFunc, EqualFunc>::erase(const Key &key) {
|
2006-03-28 12:34:34 +00:00
|
|
|
// This is based on code in the Wikipedia article on Hash tables.
|
|
|
|
uint i = lookup(key);
|
|
|
|
if (_arr[i] == NULL)
|
|
|
|
return 0; // key wasn't present, so no work has to be done
|
|
|
|
uint j = i;
|
|
|
|
while (true) {
|
|
|
|
j = (j + 1) % _arrsize;
|
|
|
|
if (_arr[j] == NULL)
|
|
|
|
break;
|
2006-03-31 22:19:39 +00:00
|
|
|
uint k = _hash(_arr[j]->_key) % _arrsize;
|
2006-03-28 12:34:34 +00:00
|
|
|
if ((j > i && (k <= i || k > j)) ||
|
|
|
|
(j < i && (k <= i && k > j)) ) {
|
|
|
|
_arr[i] = _arr[j];
|
|
|
|
i = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_arr[i] = NULL;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-03-23 22:59:38 +00:00
|
|
|
} // End of namespace Common
|
|
|
|
|
|
|
|
#endif
|