Change SimpleHashMap implementation to improve performance of GC.

The new implementation stores elements contiguously and keeps a separate sparse array of indices, similar to the new PyPy and CPython implementations.  This vastly improves performance when iterating over the individual elements, such as when garbage collecting states, and decreases memory usage.
This commit is contained in:
rdb 2017-06-12 19:55:35 +02:00
parent a66b497f2b
commit 90f96fc1a9
9 changed files with 641 additions and 721 deletions

View File

@ -194,12 +194,9 @@ void RenderAttrib::
list_attribs(ostream &out) {
LightReMutexHolder holder(*_attribs_lock);
out << _attribs->get_num_entries() << " attribs:\n";
size_t size = _attribs->get_size();
size_t size = _attribs->get_num_entries();
out << size << " attribs:\n";
for (size_t si = 0; si < size; ++si) {
if (!_attribs->has_element(si)) {
continue;
}
const RenderAttrib *attrib = _attribs->get_key(si);
attrib->write(out, 2);
}
@ -217,40 +214,51 @@ garbage_collect() {
LightReMutexHolder holder(*_attribs_lock);
PStatTimer timer(_garbage_collect_pcollector);
int orig_size = _attribs->get_num_entries();
size_t orig_size = _attribs->get_num_entries();
#ifdef _DEBUG
nassertr(_attribs->validate(), 0);
#endif
// How many elements to process this pass?
int size = _attribs->get_size();
int num_this_pass = int(size * garbage_collect_states_rate);
size_t size = orig_size;
size_t num_this_pass = max(0, int(size * garbage_collect_states_rate));
if (num_this_pass <= 0) {
return 0;
}
num_this_pass = min(num_this_pass, size);
int stop_at_element = (_garbage_index + num_this_pass) % size;
size_t si = _garbage_index;
if (si >= size) {
si = 0;
}
num_this_pass = min(num_this_pass, size);
size_t stop_at_element = (_garbage_index + num_this_pass) % size;
do {
if (_attribs->has_element(si)) {
RenderAttrib *attrib = (RenderAttrib *)_attribs->get_key(si);
if (attrib->get_ref_count() == 1) {
// This attrib has recently been unreffed to 1 (the one we added when
// we stored it in the cache). Now it's time to delete it. This is
// safe, because we're holding the _attribs_lock, so it's not possible
// for some other thread to find the attrib in the cache and ref it
// while we're doing this.
attrib->release_new();
unref_delete(attrib);
}
RenderAttrib *attrib = (RenderAttrib *)_attribs->get_key(si);
if (attrib->get_ref_count() == 1) {
// This attrib has recently been unreffed to 1 (the one we added when
// we stored it in the cache). Now it's time to delete it. This is
// safe, because we're holding the _attribs_lock, so it's not possible
// for some other thread to find the attrib in the cache and ref it
// while we're doing this.
attrib->release_new();
unref_delete(attrib);
// When we removed it from the hash map, it swapped the last element
// with the one we just removed. So the current index contains one we
// still need to visit.
--size;
--si;
}
si = (si + 1) % size;
} while (si != stop_at_element);
_garbage_index = si;
nassertr(_attribs->get_num_entries() == size, 0);
#ifdef _DEBUG
nassertr(_attribs->validate(), 0);
#endif
@ -259,8 +267,7 @@ garbage_collect() {
// size. This will help reduce iteration overhead in the future.
_attribs->consider_shrink_table();
size_t new_size = _attribs->get_num_entries();
return orig_size - new_size;
return (int)orig_size - (int)size;
}
/**
@ -279,31 +286,22 @@ validate_attribs() {
pgraph_cat.error()
<< "RenderAttrib::_attribs cache is invalid!\n";
size_t size = _attribs->get_size();
size_t size = _attribs->get_num_entries();
for (size_t si = 0; si < size; ++si) {
if (!_attribs->has_element(si)) {
continue;
}
const RenderAttrib *attrib = _attribs->get_key(si);
cerr << si << ": " << attrib << "\n";
//cerr << si << ": " << attrib << "\n";
attrib->write(cerr, 2);
}
return false;
}
size_t size = _attribs->get_size();
size_t size = _attribs->get_num_entries();
size_t si = 0;
while (si < size && !_attribs->has_element(si)) {
++si;
}
nassertr(si < size, false);
nassertr(_attribs->get_key(si)->get_ref_count() >= 0, false);
size_t snext = si;
++snext;
while (snext < size && !_attribs->has_element(snext)) {
++snext;
}
while (snext < size) {
nassertr(_attribs->get_key(snext)->get_ref_count() >= 0, false);
const RenderAttrib *ssi = _attribs->get_key(si);
@ -325,9 +323,6 @@ validate_attribs() {
}
si = snext;
++snext;
while (snext < size && !_attribs->has_element(snext)) {
++snext;
}
}
return true;
@ -518,10 +513,8 @@ release_new() {
nassertv(_attribs_lock->debug_is_locked());
if (_saved_entry != -1) {
// nassertv(_attribs->find(this) == _saved_entry);
_saved_entry = _attribs->find(this);
_attribs->remove_element(_saved_entry);
_saved_entry = -1;
nassertv(_attribs->remove(this));
}
}

View File

@ -237,7 +237,7 @@ get_invert_composition_cache_num_entries() const {
INLINE size_t RenderState::
get_composition_cache_size() const {
LightReMutexHolder holder(*_states_lock);
return _composition_cache.get_size();
return _composition_cache.get_num_entries();
}
/**
@ -251,9 +251,6 @@ get_composition_cache_size() const {
INLINE const RenderState *RenderState::
get_composition_cache_source(size_t n) const {
LightReMutexHolder holder(*_states_lock);
if (!_composition_cache.has_element(n)) {
return NULL;
}
return _composition_cache.get_key(n);
}
@ -270,9 +267,6 @@ get_composition_cache_source(size_t n) const {
INLINE const RenderState *RenderState::
get_composition_cache_result(size_t n) const {
LightReMutexHolder holder(*_states_lock);
if (!_composition_cache.has_element(n)) {
return NULL;
}
return _composition_cache.get_data(n)._result;
}
@ -288,7 +282,7 @@ get_composition_cache_result(size_t n) const {
INLINE size_t RenderState::
get_invert_composition_cache_size() const {
LightReMutexHolder holder(*_states_lock);
return _invert_composition_cache.get_size();
return _invert_composition_cache.get_num_entries();
}
/**
@ -302,9 +296,6 @@ get_invert_composition_cache_size() const {
INLINE const RenderState *RenderState::
get_invert_composition_cache_source(size_t n) const {
LightReMutexHolder holder(*_states_lock);
if (!_invert_composition_cache.has_element(n)) {
return NULL;
}
return _invert_composition_cache.get_key(n);
}
@ -322,9 +313,6 @@ get_invert_composition_cache_source(size_t n) const {
INLINE const RenderState *RenderState::
get_invert_composition_cache_result(size_t n) const {
LightReMutexHolder holder(*_states_lock);
if (!_invert_composition_cache.has_element(n)) {
return NULL;
}
return _invert_composition_cache.get_data(n)._result;
}

View File

@ -408,13 +408,13 @@ compose(const RenderState *other) const {
CPT(RenderState) result = do_compose(other);
_cache_stats.add_total_size(1);
_cache_stats.inc_adds(_composition_cache.get_size() == 0);
_cache_stats.inc_adds(_composition_cache.is_empty());
((RenderState *)this)->_composition_cache[other]._result = result;
if (other != this) {
_cache_stats.add_total_size(1);
_cache_stats.inc_adds(other->_composition_cache.get_size() == 0);
_cache_stats.inc_adds(other->_composition_cache.is_empty());
((RenderState *)other)->_composition_cache[this]._result = NULL;
}
@ -497,12 +497,12 @@ invert_compose(const RenderState *other) const {
CPT(RenderState) result = do_invert_compose(other);
_cache_stats.add_total_size(1);
_cache_stats.inc_adds(_invert_composition_cache.get_size() == 0);
_cache_stats.inc_adds(_invert_composition_cache.is_empty());
((RenderState *)this)->_invert_composition_cache[other]._result = result;
if (other != this) {
_cache_stats.add_total_size(1);
_cache_stats.inc_adds(other->_invert_composition_cache.get_size() == 0);
_cache_stats.inc_adds(other->_invert_composition_cache.is_empty());
((RenderState *)other)->_invert_composition_cache[this]._result = NULL;
}
@ -782,40 +782,33 @@ get_num_unused_states() {
typedef pmap<const RenderState *, int> StateCount;
StateCount state_count;
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
if (!_states->has_element(si)) {
continue;
}
const RenderState *state = _states->get_key(si);
int i;
int cache_size = state->_composition_cache.get_size();
size_t i;
size_t cache_size = state->_composition_cache.get_num_entries();
for (i = 0; i < cache_size; ++i) {
if (state->_composition_cache.has_element(i)) {
const RenderState *result = state->_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL && result != state) {
// Here's a RenderState 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++;
}
const RenderState *result = state->_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL && result != state) {
// Here's a RenderState 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++;
}
}
}
cache_size = state->_invert_composition_cache.get_size();
cache_size = state->_invert_composition_cache.get_num_entries();
for (i = 0; i < cache_size; ++i) {
if (state->_invert_composition_cache.has_element(i)) {
const RenderState *result = state->_invert_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL && result != state) {
pair<StateCount::iterator, bool> ir =
state_count.insert(StateCount::value_type(result, 1));
if (!ir.second) {
(*(ir.first)).second++;
}
const RenderState *result = state->_invert_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL && result != state) {
pair<StateCount::iterator, bool> ir =
state_count.insert(StateCount::value_type(result, 1));
if (!ir.second) {
(*(ir.first)).second++;
}
}
}
@ -879,11 +872,8 @@ clear_cache() {
TempStates temp_states;
temp_states.reserve(orig_size);
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
if (!_states->has_element(si)) {
continue;
}
const RenderState *state = _states->get_key(si);
temp_states.push_back(state);
}
@ -894,28 +884,24 @@ clear_cache() {
for (ti = temp_states.begin(); ti != temp_states.end(); ++ti) {
RenderState *state = (RenderState *)(*ti).p();
int i;
int cache_size = (int)state->_composition_cache.get_size();
size_t i;
size_t cache_size = (int)state->_composition_cache.get_num_entries();
for (i = 0; i < cache_size; ++i) {
if (state->_composition_cache.has_element(i)) {
const RenderState *result = state->_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL && result != state) {
result->cache_unref();
nassertr(result->get_ref_count() > 0, 0);
}
const RenderState *result = state->_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL && result != state) {
result->cache_unref();
nassertr(result->get_ref_count() > 0, 0);
}
}
_cache_stats.add_total_size(-(int)state->_composition_cache.get_num_entries());
state->_composition_cache.clear();
cache_size = (int)state->_invert_composition_cache.get_size();
cache_size = (int)state->_invert_composition_cache.get_num_entries();
for (i = 0; i < cache_size; ++i) {
if (state->_invert_composition_cache.has_element(i)) {
const RenderState *result = state->_invert_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL && result != state) {
result->cache_unref();
nassertr(result->get_ref_count() > 0, 0);
}
const RenderState *result = state->_invert_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL && result != state) {
result->cache_unref();
nassertr(result->get_ref_count() > 0, 0);
}
}
_cache_stats.add_total_size(-(int)state->_invert_composition_cache.get_num_entries());
@ -950,53 +936,62 @@ garbage_collect() {
LightReMutexHolder holder(*_states_lock);
PStatTimer timer(_garbage_collect_pcollector);
int orig_size = _states->get_num_entries();
size_t orig_size = _states->get_num_entries();
// How many elements to process this pass?
size_t size = _states->get_size();
size_t num_this_pass = int((int)size * garbage_collect_states_rate);
if (size <= 0 || num_this_pass <= 0) {
size_t size = orig_size;
size_t num_this_pass = max(0, int(size * garbage_collect_states_rate));
if (num_this_pass <= 0) {
return num_attribs;
}
bool break_and_uniquify = (auto_break_cycles && uniquify_transforms);
size_t si = _garbage_index;
if (si >= size) {
si = 0;
}
num_this_pass = min(num_this_pass, size);
size_t stop_at_element = (si + num_this_pass) & (size - 1);
size_t stop_at_element = (si + num_this_pass) % size;
do {
if (_states->has_element(si)) {
RenderState *state = (RenderState *)_states->get_key(si);
if (break_and_uniquify) {
if (state->get_cache_ref_count() > 0 &&
state->get_ref_count() == state->get_cache_ref_count()) {
// If we have removed all the references to this state not in the
// cache, leaving only references in the cache, then we need to
// check for a cycle involving this RenderState and break it if it
// exists.
state->detect_and_break_cycles();
}
}
if (state->get_ref_count() == 1) {
// This state has recently been unreffed to 1 (the one we added when
// we stored it in the cache). Now it's time to delete it. This is
// safe, because we're holding the _states_lock, so it's not possible
// for some other thread to find the state in the cache and ref it
// while we're doing this.
state->release_new();
state->remove_cache_pointers();
state->cache_unref();
delete state;
RenderState *state = (RenderState *)_states->get_key(si);
if (break_and_uniquify) {
if (state->get_cache_ref_count() > 0 &&
state->get_ref_count() == state->get_cache_ref_count()) {
// If we have removed all the references to this state not in the
// cache, leaving only references in the cache, then we need to
// check for a cycle involving this RenderState and break it if it
// exists.
state->detect_and_break_cycles();
}
}
si = (si + 1) & (size - 1);
if (state->get_ref_count() == 1) {
// This state has recently been unreffed to 1 (the one we added when
// we stored it in the cache). Now it's time to delete it. This is
// safe, because we're holding the _states_lock, so it's not possible
// for some other thread to find the state in the cache and ref it
// while we're doing this.
state->release_new();
state->remove_cache_pointers();
state->cache_unref();
delete state;
// When we removed it from the hash map, it swapped the last element
// with the one we just removed. So the current index contains one we
// still need to visit.
--size;
--si;
}
si = (si + 1) % size;
} while (si != stop_at_element);
_garbage_index = si;
nassertr(_states->get_num_entries() == size, 0);
#ifdef _DEBUG
nassertr(_states->validate(), 0);
#endif
@ -1005,8 +1000,7 @@ garbage_collect() {
// size. This will help reduce iteration overhead in the future.
_states->consider_shrink_table();
int new_size = _states->get_num_entries();
return orig_size - new_size + num_attribs;
return (int)orig_size - (int)size + num_attribs;
}
/**
@ -1017,11 +1011,8 @@ void RenderState::
clear_munger_cache() {
LightReMutexHolder holder(*_states_lock);
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
if (!_states->has_element(si)) {
continue;
}
RenderState *state = (RenderState *)(_states->get_key(si));
state->_mungers.clear();
state->_last_mi = -1;
@ -1052,11 +1043,8 @@ list_cycles(ostream &out) {
VisitedStates visited;
CompositionCycleDesc cycle_desc;
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
if (!_states->has_element(si)) {
continue;
}
const RenderState *state = _states->get_key(si);
bool inserted = visited.insert(state).second;
@ -1129,13 +1117,9 @@ list_states(ostream &out) {
}
LightReMutexHolder holder(*_states_lock);
out << _states->get_num_entries() << " states:\n";
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
out << size << " states:\n";
for (size_t si = 0; si < size; ++si) {
if (!_states->has_element(si)) {
continue;
}
const RenderState *state = _states->get_key(si);
state->write(out, 2);
}
@ -1166,18 +1150,12 @@ validate_states() {
return false;
}
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
size_t si = 0;
while (si < size && !_states->has_element(si)) {
++si;
}
nassertr(si < size, false);
nassertr(_states->get_key(si)->get_ref_count() >= 0, false);
size_t snext = si;
++snext;
while (snext < size && !_states->has_element(snext)) {
++snext;
}
while (snext < size) {
nassertr(_states->get_key(snext)->get_ref_count() >= 0, false);
const RenderState *ssi = _states->get_key(si);
@ -1199,9 +1177,6 @@ validate_states() {
}
si = snext;
++snext;
while (snext < size && !_states->has_element(snext)) {
++snext;
}
}
return true;
@ -1593,41 +1568,37 @@ r_detect_cycles(const RenderState *start_state,
}
((RenderState *)current_state)->_cycle_detect = this_seq;
int i;
int cache_size = current_state->_composition_cache.get_size();
size_t i;
size_t cache_size = current_state->_composition_cache.get_num_entries();
for (i = 0; i < cache_size; ++i) {
if (current_state->_composition_cache.has_element(i)) {
const RenderState *result = current_state->_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL) {
if (r_detect_cycles(start_state, result, length + 1,
this_seq, cycle_desc)) {
// Cycle detected.
if (cycle_desc != (CompositionCycleDesc *)NULL) {
const RenderState *other = current_state->_composition_cache.get_key(i);
CompositionCycleDescEntry entry(other, result, false);
cycle_desc->push_back(entry);
}
return true;
const RenderState *result = current_state->_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL) {
if (r_detect_cycles(start_state, result, length + 1,
this_seq, cycle_desc)) {
// Cycle detected.
if (cycle_desc != (CompositionCycleDesc *)NULL) {
const RenderState *other = current_state->_composition_cache.get_key(i);
CompositionCycleDescEntry entry(other, result, false);
cycle_desc->push_back(entry);
}
return true;
}
}
}
cache_size = current_state->_invert_composition_cache.get_size();
cache_size = current_state->_invert_composition_cache.get_num_entries();
for (i = 0; i < cache_size; ++i) {
if (current_state->_invert_composition_cache.has_element(i)) {
const RenderState *result = current_state->_invert_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL) {
if (r_detect_cycles(start_state, result, length + 1,
this_seq, cycle_desc)) {
// Cycle detected.
if (cycle_desc != (CompositionCycleDesc *)NULL) {
const RenderState *other = current_state->_invert_composition_cache.get_key(i);
CompositionCycleDescEntry entry(other, result, true);
cycle_desc->push_back(entry);
}
return true;
const RenderState *result = current_state->_invert_composition_cache.get_data(i)._result;
if (result != (const RenderState *)NULL) {
if (r_detect_cycles(start_state, result, length + 1,
this_seq, cycle_desc)) {
// Cycle detected.
if (cycle_desc != (CompositionCycleDesc *)NULL) {
const RenderState *other = current_state->_invert_composition_cache.get_key(i);
CompositionCycleDescEntry entry(other, result, true);
cycle_desc->push_back(entry);
}
return true;
}
}
}
@ -1656,52 +1627,48 @@ r_detect_reverse_cycles(const RenderState *start_state,
}
((RenderState *)current_state)->_cycle_detect = this_seq;
int i;
int cache_size = current_state->_composition_cache.get_size();
size_t i;
size_t cache_size = current_state->_composition_cache.get_num_entries();
for (i = 0; i < cache_size; ++i) {
if (current_state->_composition_cache.has_element(i)) {
const RenderState *other = current_state->_composition_cache.get_key(i);
if (other != current_state) {
int oi = other->_composition_cache.find(current_state);
nassertr(oi != -1, false);
const RenderState *other = current_state->_composition_cache.get_key(i);
if (other != current_state) {
int oi = other->_composition_cache.find(current_state);
nassertr(oi != -1, false);
const RenderState *result = other->_composition_cache.get_data(oi)._result;
if (result != (const RenderState *)NULL) {
if (r_detect_reverse_cycles(start_state, result, length + 1,
this_seq, cycle_desc)) {
// Cycle detected.
if (cycle_desc != (CompositionCycleDesc *)NULL) {
const RenderState *other = current_state->_composition_cache.get_key(i);
CompositionCycleDescEntry entry(other, result, false);
cycle_desc->push_back(entry);
}
return true;
const RenderState *result = other->_composition_cache.get_data(oi)._result;
if (result != (const RenderState *)NULL) {
if (r_detect_reverse_cycles(start_state, result, length + 1,
this_seq, cycle_desc)) {
// Cycle detected.
if (cycle_desc != (CompositionCycleDesc *)NULL) {
const RenderState *other = current_state->_composition_cache.get_key(i);
CompositionCycleDescEntry entry(other, result, false);
cycle_desc->push_back(entry);
}
return true;
}
}
}
}
cache_size = current_state->_invert_composition_cache.get_size();
cache_size = current_state->_invert_composition_cache.get_num_entries();
for (i = 0; i < cache_size; ++i) {
if (current_state->_invert_composition_cache.has_element(i)) {
const RenderState *other = current_state->_invert_composition_cache.get_key(i);
if (other != current_state) {
int oi = other->_invert_composition_cache.find(current_state);
nassertr(oi != -1, false);
const RenderState *other = current_state->_invert_composition_cache.get_key(i);
if (other != current_state) {
int oi = other->_invert_composition_cache.find(current_state);
nassertr(oi != -1, false);
const RenderState *result = other->_invert_composition_cache.get_data(oi)._result;
if (result != (const RenderState *)NULL) {
if (r_detect_reverse_cycles(start_state, result, length + 1,
this_seq, cycle_desc)) {
// Cycle detected.
if (cycle_desc != (CompositionCycleDesc *)NULL) {
const RenderState *other = current_state->_invert_composition_cache.get_key(i);
CompositionCycleDescEntry entry(other, result, false);
cycle_desc->push_back(entry);
}
return true;
const RenderState *result = other->_invert_composition_cache.get_data(oi)._result;
if (result != (const RenderState *)NULL) {
if (r_detect_reverse_cycles(start_state, result, length + 1,
this_seq, cycle_desc)) {
// Cycle detected.
if (cycle_desc != (CompositionCycleDesc *)NULL) {
const RenderState *other = current_state->_invert_composition_cache.get_key(i);
CompositionCycleDescEntry entry(other, result, false);
cycle_desc->push_back(entry);
}
return true;
}
}
}
@ -1722,10 +1689,8 @@ release_new() {
nassertv(_states_lock->debug_is_locked());
if (_saved_entry != -1) {
// nassertv(_states->find(this) == _saved_entry);
_saved_entry = _states->find(this);
_states->remove_element(_saved_entry);
_saved_entry = -1;
nassertv(_states->remove(this));
}
}
@ -1771,13 +1736,8 @@ 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.
int i = 0;
size_t 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" RenderState object is currently within
// its own destructor. We therefore can't use a PT() to hold its pointer;
// that could end up calling its destructor twice. Fortunately, we don't
@ -1829,10 +1789,6 @@ remove_cache_pointers() {
// A similar bit of code for the invert cache.
i = 0;
while (!_invert_composition_cache.is_empty()) {
while (!_invert_composition_cache.has_element(i)) {
++i;
}
RenderState *other = (RenderState *)_invert_composition_cache.get_key(i);
nassertv(other != this);
Composition comp = _invert_composition_cache.get_data(i);

View File

@ -30,36 +30,29 @@ PyObject *Extension<RenderState>::
get_composition_cache() const {
extern struct Dtool_PyTypedObject Dtool_RenderState;
LightReMutexHolder holder(*RenderState::_states_lock);
size_t cache_size = _this->_composition_cache.get_size();
size_t cache_size = _this->_composition_cache.get_num_entries();
PyObject *list = PyList_New(cache_size);
for (size_t i = 0; i < cache_size; ++i) {
PyObject *tuple = PyTuple_New(2);
PyObject *a, *b;
if (!_this->_composition_cache.has_element(i)) {
const RenderState *source = _this->_composition_cache.get_key(i);
if (source == (RenderState *)NULL) {
a = Py_None;
Py_INCREF(a);
} else {
source->ref();
a = DTool_CreatePyInstanceTyped((void *)source, Dtool_RenderState,
true, true, source->get_type_index());
}
const RenderState *result = _this->_composition_cache.get_data(i)._result;
if (result == (RenderState *)NULL) {
b = Py_None;
Py_INCREF(b);
} else {
const RenderState *source = _this->_composition_cache.get_key(i);
if (source == (RenderState *)NULL) {
a = Py_None;
Py_INCREF(a);
} else {
source->ref();
a = DTool_CreatePyInstanceTyped((void *)source, Dtool_RenderState,
true, true, source->get_type_index());
}
const RenderState *result = _this->_composition_cache.get_data(i)._result;
if (result == (RenderState *)NULL) {
b = Py_None;
Py_INCREF(b);
} else {
result->ref();
b = DTool_CreatePyInstanceTyped((void *)result, Dtool_RenderState,
true, true, result->get_type_index());
}
result->ref();
b = DTool_CreatePyInstanceTyped((void *)result, Dtool_RenderState,
true, true, result->get_type_index());
}
PyTuple_SET_ITEM(tuple, 0, a);
PyTuple_SET_ITEM(tuple, 1, b);
@ -85,36 +78,29 @@ PyObject *Extension<RenderState>::
get_invert_composition_cache() const {
extern struct Dtool_PyTypedObject Dtool_RenderState;
LightReMutexHolder holder(*RenderState::_states_lock);
size_t cache_size = _this->_invert_composition_cache.get_size();
size_t cache_size = _this->_invert_composition_cache.get_num_entries();
PyObject *list = PyList_New(cache_size);
for (size_t i = 0; i < cache_size; ++i) {
PyObject *tuple = PyTuple_New(2);
PyObject *a, *b;
if (!_this->_invert_composition_cache.has_element(i)) {
const RenderState *source = _this->_invert_composition_cache.get_key(i);
if (source == (RenderState *)NULL) {
a = Py_None;
Py_INCREF(a);
} else {
source->ref();
a = DTool_CreatePyInstanceTyped((void *)source, Dtool_RenderState,
true, true, source->get_type_index());
}
const RenderState *result = _this->_invert_composition_cache.get_data(i)._result;
if (result == (RenderState *)NULL) {
b = Py_None;
Py_INCREF(b);
} else {
const RenderState *source = _this->_invert_composition_cache.get_key(i);
if (source == (RenderState *)NULL) {
a = Py_None;
Py_INCREF(a);
} else {
source->ref();
a = DTool_CreatePyInstanceTyped((void *)source, Dtool_RenderState,
true, true, source->get_type_index());
}
const RenderState *result = _this->_invert_composition_cache.get_data(i)._result;
if (result == (RenderState *)NULL) {
b = Py_None;
Py_INCREF(b);
} else {
result->ref();
b = DTool_CreatePyInstanceTyped((void *)result, Dtool_RenderState,
true, true, result->get_type_index());
}
result->ref();
b = DTool_CreatePyInstanceTyped((void *)result, Dtool_RenderState,
true, true, result->get_type_index());
}
PyTuple_SET_ITEM(tuple, 0, a);
PyTuple_SET_ITEM(tuple, 1, b);
@ -141,11 +127,8 @@ get_states() {
PyObject *list = PyList_New(num_states);
size_t i = 0;
int size = RenderState::_states->get_size();
for (int si = 0; si < size; ++si) {
if (!RenderState::_states->has_element(si)) {
continue;
}
size_t size = RenderState::_states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
const RenderState *state = RenderState::_states->get_key(si);
state->ref();
PyObject *a =

View File

@ -664,7 +664,7 @@ get_invert_composition_cache_num_entries() const {
INLINE size_t TransformState::
get_composition_cache_size() const {
LightReMutexHolder holder(*_states_lock);
return _composition_cache.get_size();
return _composition_cache.get_num_entries();
}
/**
@ -678,9 +678,6 @@ get_composition_cache_size() const {
INLINE const TransformState *TransformState::
get_composition_cache_source(size_t n) const {
LightReMutexHolder holder(*_states_lock);
if (!_composition_cache.has_element(n)) {
return NULL;
}
return _composition_cache.get_key(n);
}
@ -698,9 +695,6 @@ get_composition_cache_source(size_t n) const {
INLINE const TransformState *TransformState::
get_composition_cache_result(size_t n) const {
LightReMutexHolder holder(*_states_lock);
if (!_composition_cache.has_element(n)) {
return NULL;
}
return _composition_cache.get_data(n)._result;
}
@ -716,7 +710,7 @@ get_composition_cache_result(size_t n) const {
INLINE size_t TransformState::
get_invert_composition_cache_size() const {
LightReMutexHolder holder(*_states_lock);
return _invert_composition_cache.get_size();
return _invert_composition_cache.get_num_entries();
}
/**
@ -730,9 +724,6 @@ get_invert_composition_cache_size() const {
INLINE const TransformState *TransformState::
get_invert_composition_cache_source(size_t n) const {
LightReMutexHolder holder(*_states_lock);
if (!_invert_composition_cache.has_element(n)) {
return NULL;
}
return _invert_composition_cache.get_key(n);
}
@ -750,9 +741,6 @@ get_invert_composition_cache_source(size_t n) const {
INLINE const TransformState *TransformState::
get_invert_composition_cache_result(size_t n) const {
LightReMutexHolder holder(*_states_lock);
if (!_invert_composition_cache.has_element(n)) {
return NULL;
}
return _invert_composition_cache.get_data(n)._result;
}

View File

@ -656,13 +656,13 @@ compose(const TransformState *other) const {
// The cache entry in this object is the only one that indicates the result;
// the other will be NULL for now.
_cache_stats.add_total_size(1);
_cache_stats.inc_adds(_composition_cache.get_size() == 0);
_cache_stats.inc_adds(_composition_cache.is_empty());
_composition_cache[other]._result = result;
if (other != this) {
_cache_stats.add_total_size(1);
_cache_stats.inc_adds(other->_composition_cache.get_size() == 0);
_cache_stats.inc_adds(other->_composition_cache.is_empty());
other->_composition_cache[this]._result = NULL;
}
@ -763,12 +763,12 @@ invert_compose(const TransformState *other) const {
// The cache entry in this object is the only one that indicates the result;
// the other will be NULL for now.
_cache_stats.add_total_size(1);
_cache_stats.inc_adds(_invert_composition_cache.get_size() == 0);
_cache_stats.inc_adds(_invert_composition_cache.is_empty());
_invert_composition_cache[other]._result = result;
if (other != this) {
_cache_stats.add_total_size(1);
_cache_stats.inc_adds(other->_invert_composition_cache.get_size() == 0);
_cache_stats.inc_adds(other->_invert_composition_cache.is_empty());
other->_invert_composition_cache[this]._result = NULL;
}
@ -842,11 +842,8 @@ bool TransformState::
validate_composition_cache() const {
LightReMutexHolder holder(*_states_lock);
size_t size = _composition_cache.get_size();
size_t size = _composition_cache.get_num_entries();
for (size_t i = 0; i < size; ++i) {
if (!_composition_cache.has_element(i)) {
continue;
}
const TransformState *source = _composition_cache.get_key(i);
if (source != (TransformState *)NULL) {
// Check that the source also has a pointer back to this one. We always
@ -865,11 +862,8 @@ validate_composition_cache() const {
}
}
size = _invert_composition_cache.get_size();
for (int i = 0; i < size; ++i) {
if (!_invert_composition_cache.has_element(i)) {
continue;
}
size = _invert_composition_cache.get_num_entries();
for (size_t i = 0; i < size; ++i) {
const TransformState *source = _invert_composition_cache.get_key(i);
if (source != (TransformState *)NULL) {
// Check that the source also has a pointer back to this one. We always
@ -1037,40 +1031,33 @@ get_num_unused_states() {
typedef pmap<const TransformState *, int> StateCount;
StateCount state_count;
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
if (!_states->has_element(si)) {
continue;
}
const TransformState *state = _states->get_key(si);
size_t i;
size_t cache_size = state->_composition_cache.get_size();
size_t cache_size = state->_composition_cache.get_num_entries();
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++;
}
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++;
}
}
}
cache_size = state->_invert_composition_cache.get_size();
cache_size = state->_invert_composition_cache.get_num_entries();
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++;
}
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++;
}
}
}
@ -1135,11 +1122,8 @@ clear_cache() {
TempStates temp_states;
temp_states.reserve(orig_size);
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
if (!_states->has_element(si)) {
continue;
}
const TransformState *state = _states->get_key(si);
temp_states.push_back(state);
}
@ -1150,28 +1134,24 @@ clear_cache() {
for (ti = temp_states.begin(); ti != temp_states.end(); ++ti) {
TransformState *state = (TransformState *)(*ti).p();
int i;
int cache_size = (int)state->_composition_cache.get_size();
size_t i;
size_t cache_size = state->_composition_cache.get_num_entries();
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);
}
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.add_total_size(-(int)state->_composition_cache.get_num_entries());
state->_composition_cache.clear();
cache_size = state->_invert_composition_cache.get_size();
cache_size = state->_invert_composition_cache.get_num_entries();
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);
}
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.add_total_size(-(int)state->_invert_composition_cache.get_num_entries());
@ -1199,56 +1179,65 @@ garbage_collect() {
return 0;
}
bool break_and_uniquify = (auto_break_cycles && uniquify_transforms);
LightReMutexHolder holder(*_states_lock);
PStatTimer timer(_garbage_collect_pcollector);
size_t orig_size = _states->get_num_entries();
// How many elements to process this pass?
size_t size = _states->get_size();
size_t num_this_pass = int(size * garbage_collect_states_rate);
if (size <= 0 || num_this_pass <= 0) {
size_t size = orig_size;
size_t num_this_pass = max(0, int(size * garbage_collect_states_rate));
if (num_this_pass <= 0) {
return 0;
}
bool break_and_uniquify = (auto_break_cycles && uniquify_transforms);
size_t si = _garbage_index;
if (si >= size) {
si = 0;
}
num_this_pass = min(num_this_pass, size);
size_t stop_at_element = (si + num_this_pass) & (size - 1);
size_t stop_at_element = (si + num_this_pass) % size;
do {
if (_states->has_element(si)) {
TransformState *state = (TransformState *)_states->get_key(si);
if (break_and_uniquify) {
if (state->get_cache_ref_count() > 0 &&
state->get_ref_count() == state->get_cache_ref_count()) {
// If we have removed all the references to this state not in the
// cache, leaving only references in the cache, then we need to
// check for a cycle involving this TransformState and break it if
// it exists.
state->detect_and_break_cycles();
}
}
if (state->get_ref_count() == 1) {
// This state has recently been unreffed to 1 (the one we added when
// we stored it in the cache). Now it's time to delete it. This is
// safe, because we're holding the _states_lock, so it's not possible
// for some other thread to find the state in the cache and ref it
// while we're doing this.
state->release_new();
state->remove_cache_pointers();
state->cache_unref();
delete state;
TransformState *state = (TransformState *)_states->get_key(si);
if (break_and_uniquify) {
if (state->get_cache_ref_count() > 0 &&
state->get_ref_count() == state->get_cache_ref_count()) {
// If we have removed all the references to this state not in the
// cache, leaving only references in the cache, then we need to
// check for a cycle involving this TransformState and break it if
// it exists.
state->detect_and_break_cycles();
}
}
si = (si + 1) & (size - 1);
if (state->get_ref_count() == 1) {
// This state has recently been unreffed to 1 (the one we added when
// we stored it in the cache). Now it's time to delete it. This is
// safe, because we're holding the _states_lock, so it's not possible
// for some other thread to find the state in the cache and ref it
// while we're doing this.
state->release_new();
state->remove_cache_pointers();
state->cache_unref();
delete state;
// When we removed it from the hash map, it swapped the last element
// with the one we just removed. So the current index contains one we
// still need to visit.
--size;
--si;
}
si = (si + 1) % size;
} while (si != stop_at_element);
_garbage_index = si;
nassertr(_states->get_num_entries() == size, 0);
#ifdef _DEBUG
nassertr(_states->validate(), 0);
#endif
@ -1257,8 +1246,7 @@ garbage_collect() {
// size. This will help reduce iteration overhead in the future.
_states->consider_shrink_table();
int new_size = _states->get_num_entries();
return orig_size - new_size;
return (int)orig_size - (int)size;
}
/**
@ -1285,11 +1273,8 @@ list_cycles(ostream &out) {
VisitedStates visited;
CompositionCycleDesc cycle_desc;
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
if (!_states->has_element(si)) {
continue;
}
const TransformState *state = _states->get_key(si);
bool inserted = visited.insert(state).second;
@ -1362,13 +1347,9 @@ list_states(ostream &out) {
}
LightReMutexHolder holder(*_states_lock);
out << _states->get_num_entries() << " states:\n";
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
out << size << " states:\n";
for (size_t si = 0; si < size; ++si) {
if (!_states->has_element(si)) {
continue;
}
const TransformState *state = _states->get_key(si);
state->write(out, 2);
}
@ -1399,18 +1380,12 @@ validate_states() {
return false;
}
size_t size = _states->get_size();
size_t size = _states->get_num_entries();
size_t si = 0;
while (si < size && !_states->has_element(si)) {
++si;
}
nassertr(si < size, false);
nassertr(_states->get_key(si)->get_ref_count() >= 0, false);
size_t snext = si;
++snext;
while (snext < size && !_states->has_element(snext)) {
++snext;
}
while (snext < size) {
nassertr(_states->get_key(snext)->get_ref_count() >= 0, false);
const TransformState *ssi = _states->get_key(si);
@ -1433,9 +1408,6 @@ validate_states() {
}
si = snext;
++snext;
while (snext < size && !_states->has_element(snext)) {
++snext;
}
}
return true;
@ -1805,41 +1777,37 @@ r_detect_cycles(const TransformState *start_state,
}
((TransformState *)current_state)->_cycle_detect = this_seq;
int i;
int cache_size = current_state->_composition_cache.get_size();
size_t i;
size_t cache_size = current_state->_composition_cache.get_num_entries();
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;
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;
}
}
}
cache_size = current_state->_invert_composition_cache.get_size();
cache_size = current_state->_invert_composition_cache.get_num_entries();
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;
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;
}
}
}
@ -1868,52 +1836,48 @@ r_detect_reverse_cycles(const TransformState *start_state,
}
((TransformState *)current_state)->_cycle_detect = this_seq;
int i;
int cache_size = current_state->_composition_cache.get_size();
size_t i;
size_t cache_size = current_state->_composition_cache.get_num_entries();
for (i = 0; i < cache_size; ++i) {
if (current_state->_composition_cache.has_element(i)) {
const TransformState *other = current_state->_composition_cache.get_key(i);
if (other != current_state) {
int oi = other->_composition_cache.find(current_state);
nassertr(oi != -1, false);
const TransformState *other = current_state->_composition_cache.get_key(i);
if (other != current_state) {
int oi = other->_composition_cache.find(current_state);
nassertr(oi != -1, false);
const TransformState *result = other->_composition_cache.get_data(oi)._result;
if (result != (const TransformState *)NULL) {
if (r_detect_reverse_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;
const TransformState *result = other->_composition_cache.get_data(oi)._result;
if (result != (const TransformState *)NULL) {
if (r_detect_reverse_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;
}
}
}
}
cache_size = current_state->_invert_composition_cache.get_size();
cache_size = current_state->_invert_composition_cache.get_num_entries();
for (i = 0; i < cache_size; ++i) {
if (current_state->_invert_composition_cache.has_element(i)) {
const TransformState *other = current_state->_invert_composition_cache.get_key(i);
if (other != current_state) {
int oi = other->_invert_composition_cache.find(current_state);
nassertr(oi != -1, false);
const TransformState *other = current_state->_invert_composition_cache.get_key(i);
if (other != current_state) {
int oi = other->_invert_composition_cache.find(current_state);
nassertr(oi != -1, false);
const TransformState *result = other->_invert_composition_cache.get_data(oi)._result;
if (result != (const TransformState *)NULL) {
if (r_detect_reverse_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, false);
cycle_desc->push_back(entry);
}
return true;
const TransformState *result = other->_invert_composition_cache.get_data(oi)._result;
if (result != (const TransformState *)NULL) {
if (r_detect_reverse_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, false);
cycle_desc->push_back(entry);
}
return true;
}
}
}
@ -1935,10 +1899,8 @@ release_new() {
nassertv(_states_lock->debug_is_locked());
if (_saved_entry != -1) {
// nassertv(_states->find(this) == _saved_entry);
_saved_entry = _states->find(this);
_states->remove_element(_saved_entry);
_saved_entry = -1;
nassertv(_states->remove(this));
}
}
@ -1977,13 +1939,8 @@ 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.
int i = 0;
size_t 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 PT() to hold its
// pointer; that could end up calling its destructor twice. Fortunately,
@ -2036,10 +1993,6 @@ remove_cache_pointers() {
// A similar bit of code for the invert cache.
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 = _invert_composition_cache.get_data(i);

View File

@ -35,12 +35,8 @@ get_composition_cache() const {
PyObject *list = PyList_New(num_states);
size_t i = 0;
int size = _this->_composition_cache.get_size();
for (int si = 0; si < size; ++si) {
if (!_this->_composition_cache.has_element(si)) {
continue;
}
size_t size = _this->_composition_cache.get_num_entries();
for (size_t si = 0; si < size; ++si) {
PyObject *tuple = PyTuple_New(2);
PyObject *a, *b;
@ -94,12 +90,8 @@ get_invert_composition_cache() const {
PyObject *list = PyList_New(num_states);
size_t i = 0;
int size = _this->_invert_composition_cache.get_size();
for (int si = 0; si < size; ++si) {
if (!_this->_invert_composition_cache.has_element(si)) {
continue;
}
size_t size = _this->_invert_composition_cache.get_num_entries();
for (size_t si = 0; si < size; ++si) {
PyObject *tuple = PyTuple_New(2);
PyObject *a, *b;
@ -149,11 +141,8 @@ get_states() {
PyObject *list = PyList_New(num_states);
size_t i = 0;
int size = TransformState::_states->get_size();
for (int si = 0; si < size; ++si) {
if (!TransformState::_states->has_element(si)) {
continue;
}
size_t size = TransformState::_states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
const TransformState *state = TransformState::_states->get_key(si);
state->ref();
PyObject *a =
@ -180,11 +169,8 @@ get_unused_states() {
LightReMutexHolder holder(*TransformState::_states_lock);
PyObject *list = PyList_New(0);
int size = TransformState::_states->get_size();
for (int si = 0; si < size; ++si) {
if (!TransformState::_states->has_element(si)) {
continue;
}
size_t size = TransformState::_states->get_num_entries();
for (size_t si = 0; si < size; ++si) {
const TransformState *state = TransformState::_states->get_key(si);
if (state->get_cache_ref_count() == state->get_ref_count()) {
state->ref();

View File

@ -15,16 +15,31 @@
*
*/
template<class Key, class Value, class Compare>
INLINE SimpleHashMap<Key, Value, Compare>::
CONSTEXPR SimpleHashMap<Key, Value, Compare>::
SimpleHashMap(const Compare &comp) :
_table(NULL),
_deleted_chain(NULL),
_table(nullptr),
_deleted_chain(nullptr),
_table_size(0),
_num_entries(0),
_comp(comp)
{
}
/**
*
*/
template<class Key, class Value, class Compare>
INLINE SimpleHashMap<Key, Value, Compare>::
SimpleHashMap(SimpleHashMap &&from) NOEXCEPT :
_table(from._table),
_deleted_chain(from._deleted_chain),
_table_size(from._table_size),
_num_entries(from._num_entries),
_comp(move(from._comp))
{
from._table_size = 0;
}
/**
*
*/
@ -69,29 +84,13 @@ find(const Key &key) const {
return -1;
}
size_t index = get_hash(key);
if (!has_element(index)) {
int slot = find_slot(key);
if (slot >= 0) {
return get_index_array()[slot];
} else {
// The key is not in the table.
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;
}
/**
@ -105,23 +104,23 @@ store(const Key &key, const Value &data) {
// Special case: the first key in an empty table.
nassertr(_num_entries == 0, -1);
new_table();
size_t index = get_hash(key);
store_new_element(index, key, data);
++_num_entries;
int pos = store_new_element(get_hash(key), key, data);
#ifdef _DEBUG
nassertr(validate(), index);
nassertr(validate(), pos);
#endif
return index;
return pos;
}
consider_expand_table();
size_t index = get_hash(key);
if (!has_element(index)) {
const int *index_array = get_index_array();
size_t hash = get_hash(key);
int index = index_array[hash];
if (index < 0) {
// This element is not already in the map; add it.
if (consider_expand_table()) {
return store(key, data);
}
store_new_element(index, key, data);
++_num_entries;
index = store_new_element(hash, key, data);
#ifdef _DEBUG
nassertr(validate(), index);
#endif
@ -138,28 +137,27 @@ store(const Key &key, const Value &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)) {
size_t slot = next_hash(hash);
while (slot != hash) {
index = index_array[slot];
if (index < 0) {
if (consider_expand_table()) {
return store(key, data);
}
store_new_element(i, key, data);
++_num_entries;
index = store_new_element(slot, key, data);
#ifdef _DEBUG
nassertr(validate(), i);
nassertr(validate(), index);
#endif
return i;
return index;
}
if (is_element(i, key)) {
_table[i]._data = data;
if (is_element(index, key)) {
_table[index]._data = data;
#ifdef _DEBUG
nassertr(validate(), i);
nassertr(validate(), index);
#endif
return i;
return index;
}
i = (i + 1) & (_table_size - 1);
slot = next_hash(slot);
}
// Shouldn't get here unless _num_entries == _table_size, which shouldn't be
@ -171,15 +169,83 @@ store(const Key &key, const Value &data) {
/**
* Removes the indicated key and its associated data from the table. Returns
* true if the key was removed, false if it was not present.
*
* Iterator safety: To perform removal during iteration, revisit the element
* at the current index if removal succeeds, keeping in mind that the number
* of elements has now shrunk by one.
*/
template<class Key, class Value, class Compare>
INLINE bool SimpleHashMap<Key, Value, Compare>::
remove(const Key &key) {
int index = find(key);
if (index == -1) {
if (_num_entries == 0) {
// Special case: the table is empty.
return false;
}
remove_element(index);
int *index_array = get_index_array();
size_t slot = (size_t)find_slot(key);
if (slot == (size_t)-1) {
// It wasn't in the hash map.
return false;
}
// Now remove this element.
size_t last = _num_entries - 1;
size_t index = (size_t)index_array[slot];
if (index < _num_entries) {
// Find the last element in the index array.
int other_slot = find_slot(_table[last]._key);
nassertr(other_slot != -1, false);
nassertr(index_array[(size_t)other_slot] == (int)last, false);
// Swap it with the last one, so that we don't get any gaps in the table
// of entries.
_table[index]._key = move(_table[last]._key);
_table[index]._data = move(_table[last]._data);
index_array[(size_t)other_slot] = index;
}
_table[last].~TableEntry();
_num_entries = last;
// It's important that we do this after the second find_slot, above, since
// it might otherwise fail due to the unexpected gap, since some indices may
// not be at their ideal positions right now.
index_array[slot] = -1;
//if (consider_shrink_table()) {
// // No need to worry about that gap; resize_table() will rebuild the index.
// return true;
//}
// Now we have put a hole in the index array. If there was a hash conflict
// in the slot after this one, we have to move it down to close the hole.
slot = next_hash(slot);
while (has_slot(slot)) {
size_t index = (size_t)index_array[slot];
size_t wants_slot = get_hash(_table[index]._key);
if (wants_slot != slot) {
// 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_slot != slot && has_slot(wants_slot)) {
wants_slot = next_hash(wants_slot);
}
if (wants_slot != slot) {
// We just have to flip the slots in the index array; we can keep the
// elements in the table where they are.
index_array[wants_slot] = index;
index_array[slot] = -1;
}
}
// 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.
slot = next_hash(slot);
}
#ifdef _DEBUG
nassertr(validate(), true);
#endif
return true;
}
@ -190,15 +256,13 @@ 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);
}
for (size_t i = 0; i < _num_entries; ++i) {
_table[i].~TableEntry();
}
_deleted_chain->deallocate(_table, TypeHandle::none());
_table = NULL;
_deleted_chain = NULL;
_table = nullptr;
_deleted_chain = nullptr;
_table_size = 0;
_num_entries = 0;
}
@ -219,131 +283,76 @@ operator [] (const Key &key) {
}
/**
* Returns the total number of slots in the table.
* Returns the total number of entries in the table. Same as get_num_entries.
*/
template<class Key, class Value, class Compare>
INLINE size_t SimpleHashMap<Key, Value, Compare>::
get_size() const {
return _table_size;
CONSTEXPR size_t SimpleHashMap<Key, Value, Compare>::
size() const {
return _num_entries;
}
/**
* Returns true if there is an element stored in the nth slot, false
* otherwise.
* Returns the key in the nth entry of the table.
*
* 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(size_t n) const {
nassertr(n < _table_size, false);
return (get_exists_array()[n] != 0);
}
/**
* 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().
* @param n should be in the range 0 <= n < size().
*/
template<class Key, class Value, class Compare>
INLINE const Key &SimpleHashMap<Key, Value, Compare>::
get_key(size_t n) const {
nassertr(has_element(n), _table[n]._key);
nassertr(n < _num_entries, _table[n]._key);
return _table[n]._key;
}
/**
* Returns the data in the nth slot of the table.
* Returns the data in the nth entry 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().
* @param n should be in the range 0 <= n < size().
*/
template<class Key, class Value, class Compare>
INLINE const Value &SimpleHashMap<Key, Value, Compare>::
get_data(size_t n) const {
nassertr(has_element(n), _table[n]._data);
nassertr(n < _num_entries, _table[n]._data);
return _table[n]._data;
}
/**
* Returns a modifiable reference to the data in the nth slot of the table.
* Returns a modifiable reference to the data in the nth entry 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().
* @param n should be in the range 0 <= n < size().
*/
template<class Key, class Value, class Compare>
INLINE Value &SimpleHashMap<Key, Value, Compare>::
modify_data(size_t n) {
nassertr(has_element(n), _table[n]._data);
nassertr(n < _num_entries, _table[n]._data);
return _table[n]._data;
}
/**
* Changes the data for the nth slot of the table.
* Changes the data for the nth entry 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().
* @param n should be in the range 0 <= n < size().
*/
template<class Key, class Value, class Compare>
INLINE void SimpleHashMap<Key, Value, Compare>::
set_data(size_t n, const Value &data) {
nassertv(has_element(n));
nassertv(n < _num_entries);
_table[n]._data = data;
}
/**
* Removes the nth slot from the table.
* Removes the nth entry 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().
* @param n should be in the range 0 <= n < size().
*/
template<class Key, class Value, class Compare>
void SimpleHashMap<Key, Value, Compare>::
remove_element(size_t 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);
}
#ifdef _DEBUG
nassertv(validate());
#endif
nassertv(n < _num_entries);
remove(_table[n]._key);
}
/**
* 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().
* Returns the number of active entries in the table. Same as size().
*/
template<class Key, class Value, class Compare>
INLINE size_t SimpleHashMap<Key, Value, Compare>::
@ -352,7 +361,7 @@ get_num_entries() const {
}
/**
* Returns true if the table is empty; i.e. get_num_entries() == 0.
* 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>::
@ -367,17 +376,20 @@ 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)) {
const int *index_array = get_index_array();
size_t num_slots = _table_size * sparsity;
for (size_t slot = 0; slot < num_slots; ++slot) {
if (!has_slot(slot)) {
out << " *";
} else {
out << " " << _table[i]._key;
size_t index = get_hash(_table[i]._key);
if (index != i) {
size_t index = (size_t)index_array[slot];
out << " " << index;
size_t ideal_slot = get_hash(_table[index]._key);
if (ideal_slot != slot) {
// 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 << "(" << ((_table_size + slot - ideal_slot) & (num_slots - 1)) << ")";
}
}
}
@ -392,6 +404,9 @@ void SimpleHashMap<Key, Value, Compare>::
write(ostream &out) const {
output(out);
out << "\n";
for (size_t i = 0; i < _num_entries; ++i) {
out << " " << _table[i]._key << " (hash " << get_hash(_table[i]._key) << ")\n";
}
}
/**
@ -403,21 +418,31 @@ bool SimpleHashMap<Key, Value, Compare>::
validate() const {
size_t count = 0;
const unsigned char *exists_array = get_exists_array();
for (size_t i = 0; i < _table_size; ++i) {
if (exists_array[i] != 0) {
const int *index_array = get_index_array();
size_t num_slots = _table_size * sparsity;
for (size_t slot = 0; slot < num_slots; ++slot) {
if (has_slot(slot)) {
size_t index = (size_t)index_array[slot];
++count;
size_t ideal_index = get_hash(_table[i]._key);
size_t wants_index = ideal_index;
while (wants_index != i && exists_array[wants_index] != 0) {
wants_index = (wants_index + 1) & (_table_size - 1);
}
if (wants_index != i) {
if (index >= _num_entries) {
util_cat.error()
<< "SimpleHashMap is invalid: key " << _table[i]._key
<< " should be in slot " << wants_index << " instead of "
<< i << " (ideal is " << ideal_index << ")\n";
<< "SimpleHashMap " << this << " is invalid: slot " << slot
<< " contains index " << index << " which is past the end of the"
" table\n";
write(util_cat.error(false));
return false;
}
nassertd(index < _num_entries) continue;
size_t ideal_slot = get_hash(_table[index]._key);
size_t wants_slot = ideal_slot;
while (wants_slot != slot && has_slot(wants_slot)) {
wants_slot = next_hash(wants_slot);
}
if (wants_slot != slot) {
util_cat.error()
<< "SimpleHashMap " << this << " is invalid: key "
<< _table[index]._key << " should be in slot " << wants_slot
<< " instead of " << slot << " (ideal is " << ideal_slot << ")\n";
write(util_cat.error(false));
return false;
}
@ -426,7 +451,7 @@ validate() const {
if (count != _num_entries) {
util_cat.error()
<< "SimpleHashMap is invalid: reports " << _num_entries
<< "SimpleHashMap " << this << " is invalid: reports " << _num_entries
<< " entries, actually has " << count << "\n";
write(util_cat.error(false));
return false;
@ -449,7 +474,57 @@ get_hash(const Key &key) const {
return (size_t)floor(f * _table_size);
*/
return ((_comp(key) * (size_t)9973) >> 8) & (_table_size - 1);
return ((_comp(key) * (size_t)9973) >> 8) & ((_table_size * sparsity) - 1);
}
/**
* Given a hash value, increments it, looping around the hash space.
*/
template<class Key, class Value, class Compare>
INLINE size_t SimpleHashMap<Key, Value, Compare>::
next_hash(size_t hash) const {
return (hash + 1) & ((_table_size * sparsity) - 1);
}
/**
* Finds the slot in which the given key should fit.
*/
template<class Key, class Value, class Compare>
INLINE int SimpleHashMap<Key, Value, Compare>::
find_slot(const Key &key) const {
const int *index_array = get_index_array();
size_t hash = get_hash(key);
int index = index_array[hash];
if (index < 0) {
return -1;
}
if (is_element((size_t)index, key)) {
return hash;
}
// 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 slot = next_hash(hash);
while (slot != hash && has_slot(slot)) {
if (is_element((size_t)index_array[slot], key)) {
return (int)slot;
}
slot = next_hash(slot);
}
return -1;
}
/**
* Returns true if the given slot refers to an element.
*/
template<class Key, class Value, class Compare>
INLINE bool SimpleHashMap<Key, Value, Compare>::
has_slot(size_t slot) const {
return get_index_array()[slot] >= 0;
}
/**
@ -458,40 +533,33 @@ get_hash(const Key &key) const {
template<class Key, class Value, class Compare>
INLINE bool SimpleHashMap<Key, Value, Compare>::
is_element(size_t n, const Key &key) const {
nassertr(has_element(n), false);
nassertr(n < _num_entries, false);
return _comp.is_equal(_table[n]._key, key);
}
/**
* Constructs a new TableEntry at position n, storing the indicated key and
* value.
* Constructs a new TableEntry with the given slot, storing the indicated key
* and value.
*/
template<class Key, class Value, class Compare>
INLINE void SimpleHashMap<Key, Value, Compare>::
store_new_element(size_t n, const Key &key, const Value &data) {
new(&_table[n]) TableEntry(key, data);
get_exists_array()[n] = true;
INLINE size_t SimpleHashMap<Key, Value, Compare>::
store_new_element(size_t slot, const Key &key, const Value &data) {
size_t index = _num_entries++;
new(&_table[index]) TableEntry(key, data);
nassertr(get_index_array()[slot] == -1, index)
get_index_array()[slot] = index;
return index;
}
/**
* Destructs the TableEntry at position n.
*/
template<class Key, class Value, class Compare>
INLINE void SimpleHashMap<Key, Value, Compare>::
clear_element(size_t n) {
_table[n].~TableEntry();
get_exists_array()[n] = false;
}
/**
* Returns the beginning of the array of _table_size unsigned chars that are
* the boolean flags for whether each element exists (has been constructed)
* Returns the beginning of the array of _table_size ints that are the indices
* pointing to the location within the table where the elements are stored.
* 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);
INLINE int *SimpleHashMap<Key, Value, Compare>::
get_index_array() const {
return (int *)(_table + _table_size);
}
/**
@ -504,15 +572,15 @@ new_table() {
// Pick a good initial table size. For now, we make it really small. Maybe
// that's the right answer.
_table_size = 4;
_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;
// _table_size * 4 more ints at the end (for the index array).
size_t alloc_size = _table_size * (sizeof(TableEntry) + sizeof(int) * sparsity);
_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);
memset(get_index_array(), -1, _table_size * sizeof(int) * sparsity);
}
/**
@ -522,7 +590,7 @@ new_table() {
template<class Key, class Value, class Compare>
INLINE bool SimpleHashMap<Key, Value, Compare>::
consider_expand_table() {
if (_num_entries < (_table_size >> 1)) {
if (_num_entries < _table_size) {
return false;
} else {
resize_table(_table_size << 1);
@ -538,14 +606,14 @@ template<class Key, class Value, class Compare>
INLINE bool SimpleHashMap<Key, Value, Compare>::
consider_shrink_table() {
// If the number of elements gets less than an eighth of the table size, we
// know it's probably time to shrink it down, lest it hurt iteration time.
if (_table_size <= 8 || (_table_size >> 3) < _num_entries) {
// know it's probably time to shrink it down.
if (_table_size <= 16 || _num_entries >= (_table_size >> 3)) {
return false;
} else {
size_t new_size = _table_size;
do {
new_size >>= 1;
} while ((new_size >> 3) >= _num_entries);
} while (new_size >= 16 && _num_entries < (new_size >> 2));
resize_table(new_size);
return true;
}
@ -558,47 +626,42 @@ template<class Key, class Value, class Compare>
void SimpleHashMap<Key, Value, Compare>::
resize_table(size_t new_size) {
nassertv(_table_size != 0);
nassertv(new_size >= _num_entries);
SimpleHashMap<Key, Value, Compare> old_map(_comp);
swap(old_map);
DeletedBufferChain *old_chain = _deleted_chain;
TableEntry *old_table = _table;
size_t old_table_size = old_map._table_size;
_table_size = new_size;
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;
// _table_size * sparsity more ints at the end (for the sparse index array).
size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size * sparsity * sizeof(int);
_deleted_chain = memory_hook->get_deleted_chain(alloc_size);
_table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none());
unsigned char *exists_array = get_exists_array();
memset(exists_array, 0, _table_size);
nassertv(_num_entries == 0);
int *index_array = get_index_array();
memset(index_array, -1, _table_size * sizeof(int) * sparsity);
// Now copy the entries from the old table into the new table.
for (size_t i = 0; i < old_table_size; ++i) {
if (old_map.has_element(i)) {
size_t new_index = get_hash(old_map._table[i]._key);
// Now copy the entries from the old table into the new table. We don't
// have to reorder these, fortunately. Hopefully, a smart compiler will
// optimize this to a memcpy.
for (size_t i = 0; i < _num_entries; ++i) {
new(&_table[i]) TableEntry(move(old_table[i]));
old_table[i].~TableEntry();
}
while (exists_array[new_index] != 0) {
// Hash conflict; look for a better spot. This has to succeed.
new_index = (new_index + 1) & (_table_size - 1);
}
// We don't need this old thing anymore.
old_chain->deallocate(old_table, TypeHandle::none());
#ifdef USE_MOVE_SEMANTICS
// Use C++11 rvalue references to invoke the move constructor, which may
// be more efficient.
new(&_table[new_index]) TableEntry(move(old_map._table[i]));
#else
new(&_table[new_index]) TableEntry(old_map._table[i]);
#endif
exists_array[new_index] = true;
++_num_entries;
// Reindex the table.
for (size_t i = 0; i < _num_entries; ++i) {
size_t slot = get_hash(_table[i]._key);
while (has_slot(slot)) {
// Hash conflict; look for a better spot. This has to succeed.
slot = next_hash(slot);
}
index_array[slot] = (int)i;
}
nassertv(validate());
nassertv(old_map.validate());
nassertv(_num_entries == old_map._num_entries);
}

View File

@ -20,16 +20,25 @@
/**
* 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.
* 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),
* (c) it doesn't depend on the system STL providing hash_map,
* (d) it allows for efficient iteration over the entries,
* (e) permits removal and resizing during forward iteration, and
* (f) it has a constexpr constructor.
*/
template<class Key, class Value, class Compare = method_hash<Key, less<Key> > >
class SimpleHashMap {
// Per-entry overhead is determined by sizeof(int) * sparsity. Should be a
// power of two.
static const unsigned int sparsity = 2u;
public:
#ifndef CPPPARSER
INLINE SimpleHashMap(const Compare &comp = Compare());
CONSTEXPR SimpleHashMap(const Compare &comp = Compare());
INLINE SimpleHashMap(SimpleHashMap &&from) NOEXCEPT;
INLINE ~SimpleHashMap();
INLINE void swap(SimpleHashMap &other);
@ -40,9 +49,8 @@ public:
void clear();
INLINE Value &operator [] (const Key &key);
CONSTEXPR size_t size() const;
INLINE size_t get_size() const;
INLINE bool has_element(size_t n) const;
INLINE const Key &get_key(size_t n) const;
INLINE const Value &get_data(size_t n) const;
INLINE Value &modify_data(size_t n);
@ -62,11 +70,13 @@ private:
class TableEntry;
INLINE size_t get_hash(const Key &key) const;
INLINE size_t next_hash(size_t hash) const;
INLINE int find_slot(const Key &key) const;
INLINE bool has_slot(size_t slot) const;
INLINE bool is_element(size_t n, const Key &key) const;
INLINE void store_new_element(size_t n, const Key &key, const Value &data);
INLINE void clear_element(size_t n);
INLINE unsigned char *get_exists_array() const;
INLINE size_t store_new_element(size_t n, const Key &key, const Value &data);
INLINE int *get_index_array() const;
void new_table();
INLINE bool consider_expand_table();