SimpleHashMap
This commit is contained in:
parent
982f614991
commit
15a52aa29f
|
|
@ -774,6 +774,36 @@ node_unref() const {
|
|||
#endif // DO_PSTATS
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: TransformState::get_composition_cache_size
|
||||
// Access: Published
|
||||
// Description: Returns the number of entries in the composition
|
||||
// cache for this TransformState. This is the number of
|
||||
// other TransformStates whose composition with this one
|
||||
// has been cached. This number is not useful for any
|
||||
// practical reason other than performance analysis.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE int TransformState::
|
||||
get_composition_cache_size() const {
|
||||
ReMutexHolder holder(*_states_lock);
|
||||
return _composition_cache.get_num_entries();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: TransformState::get_invert_composition_cache_size
|
||||
// Access: Published
|
||||
// Description: Returns the number of entries in the
|
||||
// invert_composition cache for this TransformState.
|
||||
// This is similar to the composition cache, but it
|
||||
// records cache entries for the invert_compose()
|
||||
// operation. See get_composition_cache_size().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE int TransformState::
|
||||
get_invert_composition_cache_size() const {
|
||||
ReMutexHolder holder(*_states_lock);
|
||||
return _invert_composition_cache.get_num_entries();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: TransformState::flush_level
|
||||
// Access: Public, Static
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "reMutexHolder.h"
|
||||
#include "mutexHolder.h"
|
||||
#include "thread.h"
|
||||
#include "clockObject.h"
|
||||
|
||||
ReMutex *TransformState::_states_lock = NULL;
|
||||
TransformState::States *TransformState::_states = NULL;
|
||||
|
|
@ -46,6 +47,71 @@ PStatCollector TransformState::_cache_counter("TransformStates:Cached");
|
|||
|
||||
TypeHandle TransformState::_type_handle;
|
||||
|
||||
class CacheStats {
|
||||
public:
|
||||
void init();
|
||||
void reset(double now);
|
||||
void write(ostream &out) const;
|
||||
void maybe_report();
|
||||
|
||||
int _cache_hits;
|
||||
int _cache_semi_hits;
|
||||
int _cache_misses;
|
||||
int _cache_adds;
|
||||
int _cache_new_adds;
|
||||
int _cache_dels;
|
||||
int _total_cache_size;
|
||||
int _num_states;
|
||||
double _last_reset;
|
||||
};
|
||||
static CacheStats _cache_stats;
|
||||
|
||||
void CacheStats::
|
||||
init() {
|
||||
reset(ClockObject::get_global_clock()->get_real_time());
|
||||
_total_cache_size = 0;
|
||||
_num_states = 0;
|
||||
}
|
||||
|
||||
void CacheStats::
|
||||
reset(double now) {
|
||||
_cache_hits = 0;
|
||||
_cache_semi_hits = 0;
|
||||
_cache_misses = 0;
|
||||
_cache_adds = 0;
|
||||
_cache_new_adds = 0;
|
||||
_cache_dels = 0;
|
||||
_last_reset = now;
|
||||
}
|
||||
|
||||
void CacheStats::
|
||||
write(ostream &out) const {
|
||||
out << "TransformState cache: " << _cache_hits << " hits, "
|
||||
<< _cache_semi_hits << " semi-hits, "
|
||||
<< _cache_misses << " misses\n";
|
||||
out << _cache_adds + _cache_new_adds << "(" << _cache_new_adds << ") adds(new), "
|
||||
<< _cache_dels << " dels, "
|
||||
<< (double)_total_cache_size / (double)_num_states
|
||||
<< " average cache size\n";
|
||||
}
|
||||
|
||||
static ConfigVariableBool cache_report("cache-report", false);
|
||||
static ConfigVariableDouble cache_report_interval("cache-report-interval", 5.0);
|
||||
|
||||
void CacheStats::
|
||||
maybe_report() {
|
||||
if (cache_report) {
|
||||
double now = ClockObject::get_global_clock()->get_real_time();
|
||||
if (now - _last_reset < cache_report_interval) {
|
||||
return;
|
||||
}
|
||||
write(Notify::out());
|
||||
reset(now);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: TransformState::Constructor
|
||||
// Access: Protected
|
||||
|
|
@ -61,6 +127,7 @@ TransformState() : _lock("TransformState") {
|
|||
_saved_entry = _states->end();
|
||||
_flags = F_is_identity | F_singular_known | F_is_2d;
|
||||
_inv_mat = (LMatrix4f *)NULL;
|
||||
_cache_stats._num_states++;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -105,11 +172,12 @@ TransformState::
|
|||
|
||||
// unref() should have cleared these.
|
||||
nassertv(_saved_entry == _states->end());
|
||||
nassertv(_composition_cache.empty() && _invert_composition_cache.empty());
|
||||
nassertv(_composition_cache.is_empty() && _invert_composition_cache.is_empty());
|
||||
|
||||
// If this was true at the beginning of the destructor, but is no
|
||||
// longer true now, probably we've been double-deleted.
|
||||
nassertv(get_ref_count() == 0);
|
||||
_cache_stats._num_states--;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -602,25 +670,29 @@ compose(const TransformState *other) const {
|
|||
ReMutexHolder holder(*_states_lock);
|
||||
|
||||
// Is this composition already cached?
|
||||
CompositionCache::const_iterator ci = _composition_cache.find(other);
|
||||
if (ci != _composition_cache.end()) {
|
||||
const Composition &comp = (*ci).second;
|
||||
int index = _composition_cache.find(other);
|
||||
if (index != -1) {
|
||||
Composition &comp = ((TransformState *)this)->_composition_cache.modify_data(index);
|
||||
if (comp._result == (const TransformState *)NULL) {
|
||||
// Well, it wasn't cached already, but we already had an entry
|
||||
// (probably created for the reverse direction), so use the same
|
||||
// entry to store the new result.
|
||||
CPT(TransformState) result = do_compose(other);
|
||||
((Composition &)comp)._result = result;
|
||||
comp._result = result;
|
||||
|
||||
if (result != (const TransformState *)this) {
|
||||
// See the comments below about the need to up the reference
|
||||
// count only when the result is not the same as this.
|
||||
result->cache_ref();
|
||||
}
|
||||
++_cache_stats._cache_semi_hits;
|
||||
} else {
|
||||
++_cache_stats._cache_hits;
|
||||
}
|
||||
// Here's the cache!
|
||||
return comp._result;
|
||||
}
|
||||
++_cache_stats._cache_misses;
|
||||
|
||||
// We need to make a new cache entry, both in this object and in the
|
||||
// other object. We make both records so the other TransformState
|
||||
|
|
@ -631,10 +703,25 @@ compose(const TransformState *other) const {
|
|||
// result; the other will be NULL for now.
|
||||
CPT(TransformState) result = do_compose(other);
|
||||
|
||||
// Order is important here, in case other == this.
|
||||
((TransformState *)other)->_composition_cache[this]._result = NULL;
|
||||
++_cache_stats._total_cache_size;
|
||||
if (_composition_cache.get_size() == 0) {
|
||||
++_cache_stats._cache_new_adds;
|
||||
} else {
|
||||
++_cache_stats._cache_adds;
|
||||
}
|
||||
|
||||
((TransformState *)this)->_composition_cache[other]._result = result;
|
||||
|
||||
if (other != this) {
|
||||
++_cache_stats._total_cache_size;
|
||||
if (other->_composition_cache.get_size() == 0) {
|
||||
++_cache_stats._cache_new_adds;
|
||||
} else {
|
||||
++_cache_stats._cache_adds;
|
||||
}
|
||||
((TransformState *)other)->_composition_cache[this]._result = NULL;
|
||||
}
|
||||
|
||||
if (result != (const TransformState *)this) {
|
||||
// If the result of compose() is something other than this,
|
||||
// explicitly increment the reference count. We have to be sure
|
||||
|
|
@ -647,6 +734,8 @@ compose(const TransformState *other) const {
|
|||
// that would be a self-referential leak.)
|
||||
}
|
||||
|
||||
_cache_stats.maybe_report();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -696,25 +785,29 @@ invert_compose(const TransformState *other) const {
|
|||
ReMutexHolder holder(*_states_lock);
|
||||
|
||||
// Is this composition already cached?
|
||||
CompositionCache::const_iterator ci = _invert_composition_cache.find(other);
|
||||
if (ci != _invert_composition_cache.end()) {
|
||||
const Composition &comp = (*ci).second;
|
||||
int index = _invert_composition_cache.find(other);
|
||||
if (index != -1) {
|
||||
Composition &comp = ((TransformState *)this)->_invert_composition_cache.modify_data(index);
|
||||
if (comp._result == (const TransformState *)NULL) {
|
||||
// Well, it wasn't cached already, but we already had an entry
|
||||
// (probably created for the reverse direction), so use the same
|
||||
// entry to store the new result.
|
||||
CPT(TransformState) result = do_invert_compose(other);
|
||||
((Composition &)comp)._result = result;
|
||||
comp._result = result;
|
||||
|
||||
if (result != (const TransformState *)this) {
|
||||
// See the comments below about the need to up the reference
|
||||
// count only when the result is not the same as this.
|
||||
result->cache_ref();
|
||||
}
|
||||
++_cache_stats._cache_semi_hits;
|
||||
} else {
|
||||
++_cache_stats._cache_hits;
|
||||
}
|
||||
// Here's the cache!
|
||||
return comp._result;
|
||||
}
|
||||
++_cache_stats._cache_misses;
|
||||
|
||||
// We need to make a new cache entry, both in this object and in the
|
||||
// other object. We make both records so the other TransformState
|
||||
|
|
@ -725,9 +818,24 @@ invert_compose(const TransformState *other) const {
|
|||
// result; the other will be NULL for now.
|
||||
CPT(TransformState) result = do_invert_compose(other);
|
||||
|
||||
((TransformState *)other)->_invert_composition_cache[this]._result = NULL;
|
||||
++_cache_stats._total_cache_size;
|
||||
if (_invert_composition_cache.get_size() == 0) {
|
||||
++_cache_stats._cache_new_adds;
|
||||
} else {
|
||||
++_cache_stats._cache_adds;
|
||||
}
|
||||
((TransformState *)this)->_invert_composition_cache[other]._result = result;
|
||||
|
||||
if (other != this) {
|
||||
++_cache_stats._total_cache_size;
|
||||
if (other->_invert_composition_cache.get_size() == 0) {
|
||||
++_cache_stats._cache_new_adds;
|
||||
} else {
|
||||
++_cache_stats._cache_adds;
|
||||
}
|
||||
((TransformState *)other)->_invert_composition_cache[this]._result = NULL;
|
||||
}
|
||||
|
||||
if (result != (const TransformState *)this) {
|
||||
// If the result of compose() is something other than this,
|
||||
// explicitly increment the reference count. We have to be sure
|
||||
|
|
@ -905,6 +1013,8 @@ output(ostream &out) const {
|
|||
void TransformState::
|
||||
write(ostream &out, int indent_level) const {
|
||||
indent(out, indent_level) << *this << "\n";
|
||||
indent(out, indent_level + 2) << _composition_cache << "\n";
|
||||
indent(out, indent_level + 2) << _invert_composition_cache << "\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -958,32 +1068,34 @@ get_num_unused_states() {
|
|||
for (si = _states->begin(); si != _states->end(); ++si) {
|
||||
const TransformState *state = (*si);
|
||||
|
||||
CompositionCache::const_iterator ci;
|
||||
for (ci = state->_composition_cache.begin();
|
||||
ci != state->_composition_cache.end();
|
||||
++ci) {
|
||||
const TransformState *result = (*ci).second._result;
|
||||
if (result != (const TransformState *)NULL && result != state) {
|
||||
// Here's a TransformState that's recorded in the cache.
|
||||
// Count it.
|
||||
pair<StateCount::iterator, bool> ir =
|
||||
state_count.insert(StateCount::value_type(result, 1));
|
||||
if (!ir.second) {
|
||||
// If the above insert operation fails, then it's already in
|
||||
// the cache; increment its value.
|
||||
(*(ir.first)).second++;
|
||||
int i;
|
||||
int cache_size = state->_composition_cache.get_size();
|
||||
for (i = 0; i < cache_size; ++i) {
|
||||
if (state->_composition_cache.has_element(i)) {
|
||||
const TransformState *result = state->_composition_cache.get_data(i)._result;
|
||||
if (result != (const TransformState *)NULL && result != state) {
|
||||
// Here's a TransformState that's recorded in the cache.
|
||||
// Count it.
|
||||
pair<StateCount::iterator, bool> ir =
|
||||
state_count.insert(StateCount::value_type(result, 1));
|
||||
if (!ir.second) {
|
||||
// If the above insert operation fails, then it's already in
|
||||
// the cache; increment its value.
|
||||
(*(ir.first)).second++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ci = state->_invert_composition_cache.begin();
|
||||
ci != state->_invert_composition_cache.end();
|
||||
++ci) {
|
||||
const TransformState *result = (*ci).second._result;
|
||||
if (result != (const TransformState *)NULL && result != state) {
|
||||
pair<StateCount::iterator, bool> ir =
|
||||
state_count.insert(StateCount::value_type(result, 1));
|
||||
if (!ir.second) {
|
||||
(*(ir.first)).second++;
|
||||
cache_size = state->_invert_composition_cache.get_size();
|
||||
for (i = 0; i < cache_size; ++i) {
|
||||
if (state->_invert_composition_cache.has_element(i)) {
|
||||
const TransformState *result = state->_invert_composition_cache.get_data(i)._result;
|
||||
if (result != (const TransformState *)NULL && result != state) {
|
||||
pair<StateCount::iterator, bool> ir =
|
||||
state_count.insert(StateCount::value_type(result, 1));
|
||||
if (!ir.second) {
|
||||
(*(ir.first)).second++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1064,27 +1176,31 @@ clear_cache() {
|
|||
for (ti = temp_states.begin(); ti != temp_states.end(); ++ti) {
|
||||
TransformState *state = (TransformState *)(*ti).p();
|
||||
|
||||
CompositionCache::const_iterator ci;
|
||||
for (ci = state->_composition_cache.begin();
|
||||
ci != state->_composition_cache.end();
|
||||
++ci) {
|
||||
const TransformState *result = (*ci).second._result;
|
||||
if (result != (const TransformState *)NULL && result != state) {
|
||||
result->cache_unref();
|
||||
nassertr(result->get_ref_count() > 0, 0);
|
||||
int i;
|
||||
int cache_size = state->_composition_cache.get_size();
|
||||
for (i = 0; i < cache_size; ++i) {
|
||||
if (state->_composition_cache.has_element(i)) {
|
||||
const TransformState *result = state->_composition_cache.get_data(i)._result;
|
||||
if (result != (const TransformState *)NULL && result != state) {
|
||||
result->cache_unref();
|
||||
nassertr(result->get_ref_count() > 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
_cache_stats._total_cache_size -= state->_composition_cache.get_num_entries();
|
||||
state->_composition_cache.clear();
|
||||
|
||||
for (ci = state->_invert_composition_cache.begin();
|
||||
ci != state->_invert_composition_cache.end();
|
||||
++ci) {
|
||||
const TransformState *result = (*ci).second._result;
|
||||
if (result != (const TransformState *)NULL && result != state) {
|
||||
result->cache_unref();
|
||||
nassertr(result->get_ref_count() > 0, 0);
|
||||
cache_size = state->_invert_composition_cache.get_size();
|
||||
for (i = 0; i < cache_size; ++i) {
|
||||
if (state->_invert_composition_cache.has_element(i)) {
|
||||
const TransformState *result = state->_invert_composition_cache.get_data(i)._result;
|
||||
if (result != (const TransformState *)NULL && result != state) {
|
||||
result->cache_unref();
|
||||
nassertr(result->get_ref_count() > 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
_cache_stats._total_cache_size -= state->_invert_composition_cache.get_num_entries();
|
||||
state->_invert_composition_cache.clear();
|
||||
}
|
||||
|
||||
|
|
@ -1260,6 +1376,7 @@ init_states() {
|
|||
// called at static init time, presumably when there is still only
|
||||
// one thread in the world.
|
||||
_states_lock = new ReMutex("TransformState::_states_lock");
|
||||
_cache_stats.init();
|
||||
nassertv(Thread::get_current_thread() == Thread::get_main_thread());
|
||||
}
|
||||
|
||||
|
|
@ -1558,38 +1675,42 @@ r_detect_cycles(const TransformState *start_state,
|
|||
return (current_state == start_state && length > 2);
|
||||
}
|
||||
((TransformState *)current_state)->_cycle_detect = this_seq;
|
||||
|
||||
CompositionCache::const_iterator ci;
|
||||
for (ci = current_state->_composition_cache.begin();
|
||||
ci != current_state->_composition_cache.end();
|
||||
++ci) {
|
||||
const TransformState *result = (*ci).second._result;
|
||||
if (result != (const TransformState *)NULL) {
|
||||
if (r_detect_cycles(start_state, result, length + 1,
|
||||
this_seq, cycle_desc)) {
|
||||
// Cycle detected.
|
||||
if (cycle_desc != (CompositionCycleDesc *)NULL) {
|
||||
CompositionCycleDescEntry entry((*ci).first, result, false);
|
||||
cycle_desc->push_back(entry);
|
||||
|
||||
int i;
|
||||
int cache_size = current_state->_composition_cache.get_size();
|
||||
for (i = 0; i < cache_size; ++i) {
|
||||
if (current_state->_composition_cache.has_element(i)) {
|
||||
const TransformState *result = current_state->_composition_cache.get_data(i)._result;
|
||||
if (result != (const TransformState *)NULL) {
|
||||
if (r_detect_cycles(start_state, result, length + 1,
|
||||
this_seq, cycle_desc)) {
|
||||
// Cycle detected.
|
||||
if (cycle_desc != (CompositionCycleDesc *)NULL) {
|
||||
const TransformState *other = current_state->_composition_cache.get_key(i);
|
||||
CompositionCycleDescEntry entry(other, result, false);
|
||||
cycle_desc->push_back(entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ci = current_state->_invert_composition_cache.begin();
|
||||
ci != current_state->_invert_composition_cache.end();
|
||||
++ci) {
|
||||
const TransformState *result = (*ci).second._result;
|
||||
if (result != (const TransformState *)NULL) {
|
||||
if (r_detect_cycles(start_state, result, length + 1,
|
||||
this_seq, cycle_desc)) {
|
||||
// Cycle detected.
|
||||
if (cycle_desc != (CompositionCycleDesc *)NULL) {
|
||||
CompositionCycleDescEntry entry((*ci).first, result, true);
|
||||
cycle_desc->push_back(entry);
|
||||
cache_size = current_state->_invert_composition_cache.get_size();
|
||||
for (i = 0; i < cache_size; ++i) {
|
||||
if (current_state->_invert_composition_cache.has_element(i)) {
|
||||
const TransformState *result = current_state->_invert_composition_cache.get_data(i)._result;
|
||||
if (result != (const TransformState *)NULL) {
|
||||
if (r_detect_cycles(start_state, result, length + 1,
|
||||
this_seq, cycle_desc)) {
|
||||
// Cycle detected.
|
||||
if (cycle_desc != (CompositionCycleDesc *)NULL) {
|
||||
const TransformState *other = current_state->_invert_composition_cache.get_key(i);
|
||||
CompositionCycleDescEntry entry(other, result, true);
|
||||
cycle_desc->push_back(entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1650,7 +1771,7 @@ remove_cache_pointers() {
|
|||
// it.
|
||||
|
||||
#ifdef DO_PSTATS
|
||||
if (_composition_cache.empty() && _invert_composition_cache.empty()) {
|
||||
if (_composition_cache.is_empty() && _invert_composition_cache.is_empty()) {
|
||||
return;
|
||||
}
|
||||
PStatTimer timer(_cache_update_pcollector);
|
||||
|
|
@ -1658,8 +1779,12 @@ remove_cache_pointers() {
|
|||
|
||||
// There are lots of ways to do this loop wrong. Be very careful if
|
||||
// you need to modify it for any reason.
|
||||
while (!_composition_cache.empty()) {
|
||||
CompositionCache::iterator ci = _composition_cache.begin();
|
||||
int i = 0;
|
||||
while (!_composition_cache.is_empty()) {
|
||||
// Scan for the next used slot in the table.
|
||||
while (!_composition_cache.has_element(i)) {
|
||||
++i;
|
||||
}
|
||||
|
||||
// It is possible that the "other" TransformState object is
|
||||
// currently within its own destructor. We therefore can't use a
|
||||
|
|
@ -1668,29 +1793,33 @@ remove_cache_pointers() {
|
|||
// reference count to ensure it doesn't destruct while we process
|
||||
// this loop; as long as we ensure that no *other* TransformState
|
||||
// objects destruct, there will be no reason for that one to.
|
||||
TransformState *other = (TransformState *)(*ci).first;
|
||||
TransformState *other = (TransformState *)_composition_cache.get_key(i);
|
||||
|
||||
// We hold a copy of the composition result so we can dereference
|
||||
// it later.
|
||||
Composition comp = (*ci).second;
|
||||
Composition comp = _composition_cache.get_data(i);
|
||||
|
||||
// Now we can remove the element from our cache. We do this now,
|
||||
// rather than later, before any other TransformState objects have
|
||||
// had a chance to destruct, so we are confident that our iterator
|
||||
// is still valid.
|
||||
_composition_cache.erase(ci);
|
||||
_composition_cache.remove_element(i);
|
||||
--_cache_stats._total_cache_size;
|
||||
++_cache_stats._cache_dels;
|
||||
|
||||
if (other != this) {
|
||||
CompositionCache::iterator oci = other->_composition_cache.find(this);
|
||||
int oi = other->_composition_cache.find(this);
|
||||
|
||||
// We may or may not still be listed in the other's cache (it
|
||||
// might be halfway through pulling entries out, from within its
|
||||
// own destructor).
|
||||
if (oci != other->_composition_cache.end()) {
|
||||
if (oi != -1) {
|
||||
// Hold a copy of the other composition result, too.
|
||||
Composition ocomp = (*oci).second;
|
||||
Composition ocomp = other->_composition_cache.get_data(oi);
|
||||
|
||||
other->_composition_cache.erase(oci);
|
||||
other->_composition_cache.remove_element(oi);
|
||||
--_cache_stats._total_cache_size;
|
||||
++_cache_stats._cache_dels;
|
||||
|
||||
// It's finally safe to let our held pointers go away. This may
|
||||
// have cascading effects as other TransformState objects are
|
||||
|
|
@ -1710,18 +1839,25 @@ remove_cache_pointers() {
|
|||
}
|
||||
|
||||
// A similar bit of code for the invert cache.
|
||||
while (!_invert_composition_cache.empty()) {
|
||||
CompositionCache::iterator ci = _invert_composition_cache.begin();
|
||||
TransformState *other = (TransformState *)(*ci).first;
|
||||
i = 0;
|
||||
while (!_invert_composition_cache.is_empty()) {
|
||||
while (!_invert_composition_cache.has_element(i)) {
|
||||
++i;
|
||||
}
|
||||
|
||||
TransformState *other = (TransformState *)_invert_composition_cache.get_key(i);
|
||||
nassertv(other != this);
|
||||
Composition comp = (*ci).second;
|
||||
_invert_composition_cache.erase(ci);
|
||||
Composition comp = _invert_composition_cache.get_data(i);
|
||||
_invert_composition_cache.remove_element(i);
|
||||
--_cache_stats._total_cache_size;
|
||||
++_cache_stats._cache_dels;
|
||||
if (other != this) {
|
||||
CompositionCache::iterator oci =
|
||||
other->_invert_composition_cache.find(this);
|
||||
if (oci != other->_invert_composition_cache.end()) {
|
||||
Composition ocomp = (*oci).second;
|
||||
other->_invert_composition_cache.erase(oci);
|
||||
int oi = other->_invert_composition_cache.find(this);
|
||||
if (oi != -1) {
|
||||
Composition ocomp = other->_invert_composition_cache.get_data(oi);
|
||||
other->_invert_composition_cache.remove_element(oi);
|
||||
--_cache_stats._total_cache_size;
|
||||
++_cache_stats._cache_dels;
|
||||
if (ocomp._result != (const TransformState *)NULL && ocomp._result != other) {
|
||||
cache_unref_delete(ocomp._result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "pmutex.h"
|
||||
#include "config_pgraph.h"
|
||||
#include "deletedChain.h"
|
||||
#include "simpleHashMap.h"
|
||||
|
||||
class GraphicsStateGuardianBase;
|
||||
class FactoryParams;
|
||||
|
|
@ -179,6 +180,9 @@ PUBLISHED:
|
|||
INLINE void node_ref() const;
|
||||
INLINE bool node_unref() const;
|
||||
|
||||
INLINE int get_composition_cache_size() const;
|
||||
INLINE int get_invert_composition_cache_size() const;
|
||||
|
||||
void output(ostream &out) const;
|
||||
void write(ostream &out, int indent_level) const;
|
||||
|
||||
|
|
@ -240,6 +244,11 @@ private:
|
|||
// remove the entry if *either* of the input TransformStates destructs.
|
||||
// To implement this, we always record Composition entries in pairs,
|
||||
// one in each of the two involved TransformState objects.
|
||||
|
||||
// The first element of the map is the object we compose with. This
|
||||
// is not reference counted within this map; instead we store a
|
||||
// companion pointer in the other object, and remove the references
|
||||
// explicitly when either object destructs.
|
||||
class Composition {
|
||||
public:
|
||||
INLINE Composition();
|
||||
|
|
@ -249,12 +258,8 @@ private:
|
|||
// pointer as this.
|
||||
const TransformState *_result;
|
||||
};
|
||||
|
||||
// The first element of the map is the object we compose with. This
|
||||
// is not reference counted within this map; instead we store a
|
||||
// companion pointer in the other object, and remove the references
|
||||
// explicitly when either object destructs.
|
||||
typedef phash_map<const TransformState *, Composition, pointer_hash> CompositionCache;
|
||||
|
||||
typedef SimpleHashMap<const TransformState *, Composition, pointer_hash> CompositionCache;
|
||||
CompositionCache _composition_cache;
|
||||
CompositionCache _invert_composition_cache;
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
portalMask.h \
|
||||
pta_double.h \
|
||||
pta_float.h pta_int.h \
|
||||
simpleHashMap.I simpleHashMap.h \
|
||||
sparseArray.I sparseArray.h \
|
||||
string_utils.I string_utils.N string_utils.h \
|
||||
stringStreamBuf.I stringStreamBuf.h \
|
||||
|
|
@ -99,6 +100,7 @@
|
|||
nodeCachedReferenceCount.cxx \
|
||||
pta_double.cxx pta_float.cxx \
|
||||
pta_int.cxx pta_ushort.cxx \
|
||||
simpleHashMap.cxx \
|
||||
sparseArray.cxx \
|
||||
string_utils.cxx \
|
||||
stringStreamBuf.cxx \
|
||||
|
|
@ -156,6 +158,7 @@
|
|||
portalMask.h \
|
||||
pta_double.h \
|
||||
pta_float.h pta_int.h pta_ushort.h \
|
||||
simpleHashMap.I simpleHashMap.h \
|
||||
sparseArray.I sparseArray.h \
|
||||
string_utils.I string_utils.h \
|
||||
stringStreamBuf.I stringStreamBuf.h \
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "pta_float.cxx"
|
||||
#include "pta_int.cxx"
|
||||
#include "pta_ushort.cxx"
|
||||
#include "simpleHashMap.cxx"
|
||||
#include "sparseArray.cxx"
|
||||
#include "string_utils.cxx"
|
||||
#include "stringStreamBuf.cxx"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,620 @@
|
|||
// 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
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// 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];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Filename: simpleHashMap.cxx
|
||||
// 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
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "simpleHashMap.h"
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
// Filename: simpleHashMap.h
|
||||
// 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
|
||||
// panda3d-general@lists.sourceforge.net .
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef SIMPLEHASHMAP_H
|
||||
#define SIMPLEHASHMAP_H
|
||||
|
||||
#include "pandabase.h"
|
||||
#include "pvector.h"
|
||||
#include "config_util.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : SimpleHashMap
|
||||
// Description : This template class implements an unordered map of
|
||||
// keys to data, implemented as a hashtable. It is
|
||||
// similar to STL's hash_map, but (a) it has a simpler
|
||||
// interface (we don't mess around with iterators), (b)
|
||||
// it wants an additional method on the Compare object,
|
||||
// Compare::is_equal(a, b), and (c) it doesn't depend on
|
||||
// the system STL providing hash_map.
|
||||
//
|
||||
// The map uses a void * as a unique key, which is
|
||||
// associated with another void * as data. You may pass
|
||||
// any pointer other than NULL as the key; you may pass
|
||||
// any pointer (including NULL) as the data. Only one
|
||||
// entry is stored per each different key pointer.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
template<class Key, class Value, class Compare = method_hash<Key, less<Key> > >
|
||||
class EXPCL_PANDA_PUTIL SimpleHashMap {
|
||||
public:
|
||||
#ifndef CPPPARSER
|
||||
INLINE SimpleHashMap(const Compare &comp = Compare());
|
||||
INLINE ~SimpleHashMap();
|
||||
|
||||
INLINE void swap(SimpleHashMap &other);
|
||||
|
||||
int find(const Key &key) const;
|
||||
Value &store(const Key &key, const Value &data);
|
||||
INLINE bool remove(const Key &key);
|
||||
void clear();
|
||||
|
||||
INLINE Value &operator [] (const Key &key);
|
||||
|
||||
INLINE int get_size() const;
|
||||
INLINE bool has_element(int n) const;
|
||||
INLINE const Key &get_key(int n) const;
|
||||
INLINE const Value &get_data(int n) const;
|
||||
INLINE Value &modify_data(int n);
|
||||
INLINE void set_data(int n, const Value &data);
|
||||
void remove_element(int n);
|
||||
|
||||
INLINE int get_num_entries() const;
|
||||
INLINE bool is_empty() const;
|
||||
|
||||
void output(ostream &out) const;
|
||||
void write(ostream &out) const;
|
||||
bool validate() const;
|
||||
|
||||
private:
|
||||
class TableEntry;
|
||||
|
||||
INLINE size_t get_hash(const Key &key) const;
|
||||
|
||||
INLINE bool is_element(int n, const Key &key) const;
|
||||
INLINE void store_new_element(int n, const Key &key, const Value &data);
|
||||
INLINE void clear_element(int n);
|
||||
INLINE unsigned char *get_exists_array() const;
|
||||
|
||||
void new_table();
|
||||
INLINE bool consider_expand_table();
|
||||
void expand_table();
|
||||
|
||||
class TableEntry {
|
||||
public:
|
||||
INLINE TableEntry(const Key &key, const Value &data) :
|
||||
_key(key),
|
||||
_data(data) {}
|
||||
Key _key;
|
||||
Value _data;
|
||||
};
|
||||
|
||||
TableEntry *_table;
|
||||
DeletedBufferChain *_deleted_chain;
|
||||
size_t _table_size;
|
||||
size_t _num_entries;
|
||||
|
||||
Compare _comp;
|
||||
#endif // CPPPARSER
|
||||
};
|
||||
|
||||
template<class Key, class Value, class Compare>
|
||||
inline ostream &operator << (ostream &out, const SimpleHashMap<Key, Value, Compare> &shm) {
|
||||
shm.output(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
#ifndef CPPPARSER
|
||||
#include "simpleHashMap.I"
|
||||
#endif // CPPPARSER
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue