Fix Hash Collisions in CDBehaviorParameter table (#930)

* Fix hashing

* Update CDBehaviorParameterTable.cpp
This commit is contained in:
David Markowitz 2022-12-29 01:43:52 -08:00 committed by GitHub
parent 99c0ca253c
commit bd28e4051f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 25 additions and 30 deletions

View File

@ -4,9 +4,9 @@
//! Constructor //! Constructor
CDBehaviorParameterTable::CDBehaviorParameterTable(void) { CDBehaviorParameterTable::CDBehaviorParameterTable(void) {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter");
size_t hash = 0; uint32_t uniqueParameterId = 0;
uint64_t hash = 0;
while (!tableData.eof()) { while (!tableData.eof()) {
hash = 0;
CDBehaviorParameter entry; CDBehaviorParameter entry;
entry.behaviorID = tableData.getIntField(0, -1); entry.behaviorID = tableData.getIntField(0, -1);
auto candidateStringToAdd = std::string(tableData.getStringField(1, "")); auto candidateStringToAdd = std::string(tableData.getStringField(1, ""));
@ -14,15 +14,13 @@ CDBehaviorParameterTable::CDBehaviorParameterTable(void) {
if (parameter != m_ParametersList.end()) { if (parameter != m_ParametersList.end()) {
entry.parameterID = parameter; entry.parameterID = parameter;
} else { } else {
entry.parameterID = m_ParametersList.insert(candidateStringToAdd).first; entry.parameterID = m_ParametersList.insert(std::make_pair(candidateStringToAdd, uniqueParameterId)).first;
uniqueParameterId++;
} }
hash = entry.behaviorID;
hash = (hash << 31U) | entry.parameterID->second;
entry.value = tableData.getFloatField(2, -1.0f); entry.value = tableData.getFloatField(2, -1.0f);
GeneralUtils::hash_combine(hash, entry.behaviorID);
GeneralUtils::hash_combine(hash, *entry.parameterID);
auto it = m_Entries.find(entry.behaviorID);
m_ParametersList.insert(*entry.parameterID);
m_Entries.insert(std::make_pair(hash, entry)); m_Entries.insert(std::make_pair(hash, entry));
tableData.nextRow(); tableData.nextRow();
@ -38,31 +36,28 @@ std::string CDBehaviorParameterTable::GetName(void) const {
return "BehaviorParameter"; return "BehaviorParameter";
} }
CDBehaviorParameter CDBehaviorParameterTable::GetEntry(const uint32_t behaviorID, const std::string& name, const float defaultValue) { float CDBehaviorParameterTable::GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue) {
CDBehaviorParameter returnValue; auto parameterID = this->m_ParametersList.find(name);
returnValue.behaviorID = 0; if (parameterID == this->m_ParametersList.end()) return defaultValue;
returnValue.parameterID = m_ParametersList.end();
returnValue.value = defaultValue;
size_t hash = 0; uint64_t hash = behaviorID;
GeneralUtils::hash_combine(hash, behaviorID);
GeneralUtils::hash_combine(hash, name); hash = (hash << 31U) | parameterID->second;
// Search for specific parameter // Search for specific parameter
const auto& it = m_Entries.find(hash); const auto& it = m_Entries.find(hash);
return it != m_Entries.end() ? it->second : returnValue; return it != m_Entries.end() ? it->second.value : defaultValue;
} }
std::map<std::string, float> CDBehaviorParameterTable::GetParametersByBehaviorID(uint32_t behaviorID) { std::map<std::string, float> CDBehaviorParameterTable::GetParametersByBehaviorID(uint32_t behaviorID) {
size_t hash; uint64_t hashBase = behaviorID;
std::map<std::string, float> returnInfo; std::map<std::string, float> returnInfo;
for (auto parameterCandidate : m_ParametersList) { uint64_t hash;
hash = 0; for (auto& parameterCandidate : m_ParametersList) {
GeneralUtils::hash_combine(hash, behaviorID); hash = (hashBase << 31U) | parameterCandidate.second;
GeneralUtils::hash_combine(hash, parameterCandidate);
auto infoCandidate = m_Entries.find(hash); auto infoCandidate = m_Entries.find(hash);
if (infoCandidate != m_Entries.end()) { if (infoCandidate != m_Entries.end()) {
returnInfo.insert(std::make_pair(*(infoCandidate->second.parameterID), infoCandidate->second.value)); returnInfo.insert(std::make_pair(infoCandidate->second.parameterID->first, infoCandidate->second.value));
} }
} }
return returnInfo; return returnInfo;

View File

@ -12,16 +12,16 @@
//! BehaviorParameter Entry Struct //! BehaviorParameter Entry Struct
struct CDBehaviorParameter { struct CDBehaviorParameter {
unsigned int behaviorID; //!< The Behavior ID unsigned int behaviorID; //!< The Behavior ID
std::unordered_set<std::string>::iterator parameterID; //!< The Parameter ID std::unordered_map<std::string, uint32_t>::iterator parameterID; //!< The Parameter ID
float value; //!< The value of the behavior template float value; //!< The value of the behavior template
}; };
//! BehaviorParameter table //! BehaviorParameter table
class CDBehaviorParameterTable : public CDTable { class CDBehaviorParameterTable : public CDTable {
private: private:
std::unordered_map<size_t, CDBehaviorParameter> m_Entries; std::unordered_map<uint64_t, CDBehaviorParameter> m_Entries;
std::unordered_set<std::string> m_ParametersList; std::unordered_map<std::string, uint32_t> m_ParametersList;
public: public:
//! Constructor //! Constructor
@ -36,7 +36,7 @@ public:
*/ */
std::string GetName(void) const override; std::string GetName(void) const override;
CDBehaviorParameter GetEntry(const uint32_t behaviorID, const std::string& name, const float defaultValue = 0); float GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue = 0);
std::map<std::string, float> GetParametersByBehaviorID(uint32_t behaviorID); std::map<std::string, float> GetParametersByBehaviorID(uint32_t behaviorID);
}; };

View File

@ -439,7 +439,7 @@ Behavior::Behavior(const uint32_t behaviorId) {
float Behavior::GetFloat(const std::string& name, const float defaultValue) const { float Behavior::GetFloat(const std::string& name, const float defaultValue) const {
// Get the behavior parameter entry and return its value. // Get the behavior parameter entry and return its value.
if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::Instance()->GetTable<CDBehaviorParameterTable>("BehaviorParameter"); if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::Instance()->GetTable<CDBehaviorParameterTable>("BehaviorParameter");
return BehaviorParameterTable->GetEntry(this->m_behaviorId, name, defaultValue).value; return BehaviorParameterTable->GetValue(this->m_behaviorId, name, defaultValue);
} }