| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- // Filename: simpleHashMap.I
- // Created by: drose (19Jul07)
- //
- ////////////////////////////////////////////////////////////////////
- //
- // PANDA 3D SOFTWARE
- // Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
- //
- // All use of this software is subject to the terms of the Panda 3d
- // Software license. You should have received a copy of this license
- // along with this source code; you will also find a current copy of
- // the license at http://etc.cmu.edu/panda3d/docs/license/ .
- //
- // To contact the maintainers of this program write to
- // [email protected] .
- //
- ////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////
- // 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);
- }
|