open_toontown_panda3d/panda/src/putil/simpleHashMap.I

618 lines
21 KiB
Plaintext

// Filename: simpleHashMap.I
// Created by: drose (19Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE SimpleHashMap<Key, Value, Compare>::
SimpleHashMap(const Compare &comp) :
_table(NULL),
_deleted_chain(NULL),
_table_size(0),
_num_entries(0),
_comp(comp)
{
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE SimpleHashMap<Key, Value, Compare>::
~SimpleHashMap() {
clear();
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::swap
// Access: Public
// Description: Quickly exchanges the contents of this map and the
// other map.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE void SimpleHashMap<Key, Value, Compare>::
swap(SimpleHashMap<Key, Value, Compare> &other) {
TableEntry *t0 = _table;
_table = other._table;
other._table = t0;
DeletedBufferChain *t1 = _deleted_chain;
_deleted_chain = other._deleted_chain;
other._deleted_chain = t1;
size_t t2 = _table_size;
_table_size = other._table_size;
other._table_size = t2;
size_t t3 = _num_entries;
_num_entries = other._num_entries;
other._num_entries = t3;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::find
// Access: Public
// Description: Searches for the indicated key in the table. Returns
// its index number if it is found, or -1 if it is not
// present in the table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
int SimpleHashMap<Key, Value, Compare>::
find(const Key &key) const {
if (_table_size == 0) {
// Special case: the table is empty.
return -1;
}
size_t index = get_hash(key);
if (!has_element(index)) {
return -1;
}
if (is_element(index, key)) {
return index;
}
// There was some other key at the hashed slot. That's a hash
// conflict. Maybe our entry was recorded at a later slot position;
// scan the subsequent positions until we find the entry or an
// unused slot, indicating the end of the scan.
size_t i = index;
i = (i + 1) & (_table_size - 1);
while (i != index && has_element(i)) {
if (is_element(i, key)) {
return i;
}
i = (i + 1) & (_table_size - 1);
}
// The key is not in the table.
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::store
// Access: Public
// Description: Records the indicated key/data pair in the map. If
// the key was already present, silently replaces it.
// Returns a reference to the value in the map.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
Value &SimpleHashMap<Key, Value, Compare>::
store(const Key &key, const Value &data) {
if (_table_size == 0) {
// Special case: the first key in an empty table.
nassertr(_num_entries == 0, _table[0]._data);
new_table();
size_t index = get_hash(key);
store_new_element(index, key, data);
++_num_entries;
return _table[index]._data;
}
size_t index = get_hash(key);
if (!has_element(index)) {
if (consider_expand_table()) {
return store(key, data);
}
store_new_element(index, key, data);
++_num_entries;
return _table[index]._data;
}
if (is_element(index, key)) {
_table[index]._data = data;
return _table[index]._data;
}
// There was some other key at the hashed slot. That's a hash
// conflict. Record this entry at a later position.
size_t i = index;
i = (i + 1) & (_table_size - 1);
while (i != index) {
if (!has_element(i)) {
if (consider_expand_table()) {
return store(key, data);
}
store_new_element(i, key, data);
++_num_entries;
return _table[i]._data;
}
if (is_element(i, key)) {
_table[i]._data = data;
return _table[i]._data;
}
i = (i + 1) & (_table_size - 1);
}
// Shouldn't get here unless _num_entries == _table_size, which
// shouldn't be possible due to consider_expand_table().
nassertr(false, _table[0]._data);
return _table[0]._data; // To satisfy compiler
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::remove
// Access: Public
// Description: Removes the indicated key and its associated data
// from the table. Returns true if the key was removed,
// false if it was not present.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE bool SimpleHashMap<Key, Value, Compare>::
remove(const Key &key) {
int index = find(key);
if (index == -1) {
return false;
}
remove_element(index);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::clear
// Access: Public
// Description: Completely empties the table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
void SimpleHashMap<Key, Value, Compare>::
clear() {
if (_table_size != 0) {
for (size_t i = 0; i < _table_size; ++i) {
if (has_element(i)) {
clear_element(i);
}
}
_deleted_chain->deallocate(_table, TypeHandle::none());
_table = NULL;
_deleted_chain = NULL;
_table_size = 0;
_num_entries = 0;
}
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::operator []
// Access: Public
// Description: Returns a modifiable reference to the data associated
// with the indicated key, or creates a new data entry
// and returns its reference.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE Value &SimpleHashMap<Key, Value, Compare>::
operator [] (const Key &key) {
int index = find(key);
if (index != -1) {
return modify_data(index);
}
return store(key, Value());
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::get_size
// Access: Public
// Description: Returns the total number of slots in the table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE int SimpleHashMap<Key, Value, Compare>::
get_size() const {
return _table_size;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::has_element
// Access: Public
// Description: Returns true if there is an element stored in the nth
// slot, false otherwise.
//
// n should be in the range 0 <= n < get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE bool SimpleHashMap<Key, Value, Compare>::
has_element(int n) const {
nassertr(n >= 0 && n < (int)_table_size, false);
return (get_exists_array()[n] != 0);
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::get_key
// Access: Public
// Description: Returns the key in the nth slot of the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE const Key &SimpleHashMap<Key, Value, Compare>::
get_key(int n) const {
nassertr(has_element(n), _table[n]._key);
return _table[n]._key;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::get_data
// Access: Public
// Description: Returns the data in the nth slot of the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE const Value &SimpleHashMap<Key, Value, Compare>::
get_data(int n) const {
nassertr(has_element(n), _table[n]._data);
return _table[n]._data;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::modify_data
// Access: Public
// Description: Returns a modifiable reference to the data in the nth
// slot of the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE Value &SimpleHashMap<Key, Value, Compare>::
modify_data(int n) {
nassertr(has_element(n), _table[n]._data);
return _table[n]._data;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::set_data
// Access: Public
// Description: Changes the data for the nth slot of the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE void SimpleHashMap<Key, Value, Compare>::
set_data(int n, const Value &data) {
nassertv(has_element(n));
_table[n]._data = data;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::remove_element
// Access: Public
// Description: Removes the nth slot from the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
void SimpleHashMap<Key, Value, Compare>::
remove_element(int n) {
nassertv(has_element(n));
clear_element(n);
nassertv(_num_entries > 0);
--_num_entries;
// Now we have put a hole in the table. If there was a hash
// conflict in the slot following this one, we have to move it down
// to close the hole.
size_t i = n;
i = (i + 1) & (_table_size - 1);
while (has_element(i)) {
size_t wants_index = get_hash(_table[i]._key);
if (wants_index != i) {
// This one was a hash conflict; try to put it where it belongs.
// We can't just put it in n, since maybe it belongs somewhere
// after n.
while (wants_index != i && has_element(wants_index)) {
wants_index = (wants_index + 1) & (_table_size - 1);
}
if (wants_index != i) {
store_new_element(wants_index, _table[i]._key, _table[i]._data);
clear_element(i);
}
}
// Continue until we encounter the next unused slot. Until we do,
// we can't be sure we've found all of the potential hash
// conflicts.
i = (i + 1) & (_table_size - 1);
}
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::get_num_entries
// Access: Public
// Description: Returns the number of active entries in the table.
// This is not necessarily related to the number of
// slots in the table as reported by get_size(). Use
// get_size() to iterate through all of the slots, not
// get_num_entries().
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE int SimpleHashMap<Key, Value, Compare>::
get_num_entries() const {
return _num_entries;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::is_empty
// Access: Public
// Description: Returns true if the table is empty;
// i.e. get_num_entries() == 0.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE bool SimpleHashMap<Key, Value, Compare>::
is_empty() const {
return (_num_entries == 0);
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::output
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
void SimpleHashMap<Key, Value, Compare>::
output(ostream &out) const {
out << "SimpleHashMap (" << _num_entries << " entries): [";
for (size_t i = 0; i < _table_size; ++i) {
if (!has_element(i)) {
out << " *";
} else {
out << " " << _table[i]._key;
size_t index = get_hash(_table[i]._key);
if (index != i) {
// This was misplaced as the result of a hash conflict.
// Report how far off it is.
out << "(" << ((_table_size + i - index) & (_table_size - 1)) << ")";
}
}
}
out << " ]";
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::write
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
void SimpleHashMap<Key, Value, Compare>::
write(ostream &out) const {
output(out);
out << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::validate
// Access: Public
// Description: Returns true if the internal table appears to be
// consistent, false if there are some internal errors.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
bool SimpleHashMap<Key, Value, Compare>::
validate() const {
size_t count = 0;
for (size_t i = 0; i < _table_size; ++i) {
if (has_element(i)) {
++count;
size_t ideal_index = get_hash(_table[i]._key);
size_t wants_index = ideal_index;
while (wants_index != i && has_element(wants_index)) {
wants_index = (wants_index + 1) & (_table_size - 1);
}
if (wants_index != i) {
util_cat.error()
<< "SimpleHashMap is invalid: key " << _table[i]._key
<< " should be in slot " << wants_index << " instead of "
<< i << " (ideal is " << ideal_index << ")\n";
write(util_cat.error(false));
return false;
}
}
}
if (count != _num_entries) {
util_cat.error()
<< "SimpleHashMap is invalid: reports " << _num_entries
<< " entries, actually has " << count << "\n";
write(util_cat.error(false));
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::get_hash
// Access: Private
// Description: Computes an appropriate index number to store the
// given pointer.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE size_t SimpleHashMap<Key, Value, Compare>::
get_hash(const Key &key) const {
/*
// We want a hash constant 0 < k < 1. This one is suggested by
// Knuth:
static const double hash_constant = (sqrt(5.0) - 1.0) / 2.0;
double f = ((double)_comp(key) * hash_constant);
f -= floor(f);
return (size_t)floor(f * _table_size);
*/
return ((_comp(key) * (size_t)9973) >> 8) & (_table_size - 1);
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::is_element
// Access: Private
// Description: Returns true if element n matches key.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE bool SimpleHashMap<Key, Value, Compare>::
is_element(int n, const Key &key) const {
nassertr(has_element(n), false);
return _comp.is_equal(_table[n]._key, key);
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::store_new_element
// Access: Private
// Description: Constructs a new TableEntry at position n, storing
// the indicated key and value.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE void SimpleHashMap<Key, Value, Compare>::
store_new_element(int n, const Key &key, const Value &data) {
new(&_table[n]) TableEntry(key, data);
get_exists_array()[n] = true;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::clear_element
// Access: Private
// Description: Destructs the TableEntry at position n.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE void SimpleHashMap<Key, Value, Compare>::
clear_element(int n) {
_table[n].~TableEntry();
get_exists_array()[n] = false;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::get_exists_array
// Access: Private
// Description: Returns the beginning of the array of _table_size
// unsigned chars that are the boolean flags for whether
// each element exists (has been constructed) within the
// table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE unsigned char *SimpleHashMap<Key, Value, Compare>::
get_exists_array() const {
return (unsigned char *)(_table + _table_size);
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::new_table
// Access: Private
// Description: Allocates a brand new table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
void SimpleHashMap<Key, Value, Compare>::
new_table() {
nassertv(_table_size == 0 && _num_entries == 0);
// Pick a good initial table size. For now, we make it as small as
// possible. Maybe that's the right answer.
_table_size = 2;
// We allocate enough bytes for _table_size elements of TableEntry,
// plus _table_size more bytes at the end (for the exists array).
size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size;
_deleted_chain = memory_hook->get_deleted_chain(alloc_size);
_table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none());
memset(get_exists_array(), 0, _table_size);
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::consider_expand_table
// Access: Private
// Description: Expands the table if it will need it (assuming one
// more element is about to be added). Returns true if
// expanded, false otherwise.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
INLINE bool SimpleHashMap<Key, Value, Compare>::
consider_expand_table() {
if (_num_entries >= (_table_size >> 1)) {
expand_table();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleHashMap::expand_table
// Access: Private
// Description: Doubles the size of the existing table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value, class Compare>
void SimpleHashMap<Key, Value, Compare>::
expand_table() {
nassertv(_table_size != 0);
SimpleHashMap<Key, Value, Compare> old_map(_comp);
swap(old_map);
_table_size = (old_map._table_size << 1);
nassertv(_table == NULL);
// We allocate enough bytes for _table_size elements of TableEntry,
// plus _table_size more bytes at the end (for the exists array).
size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size;
_deleted_chain = memory_hook->get_deleted_chain(alloc_size);
_table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none());
memset(get_exists_array(), 0, _table_size);
// Now copy the entries from the old table into the new table.
for (size_t i = 0; i < old_map._table_size; ++i) {
if (old_map.has_element(i)) {
store(old_map._table[i]._key, old_map._table[i]._data);
}
}
nassertv(old_map._num_entries == _num_entries);
}