/////////////////////////////////////////////////////////////////////////////////////// // // symboltable.cpp KSH 17 Oct 2001 // // Symbol hash table stuff. // /////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include DefinePoolableClass(Script::CSymbolTableEntry); namespace Script { CSymbolTableEntry::CSymbolTableEntry() { // Initialise everything. CSymbolTableEntry is not derived from CClass so we don't get // the auro-zeroing. Reset(); } // The destructor does not delete anything to avoid cyclic dependencies, so just assert // if it look like there could be a non-NULL pointer left. #ifdef __NOPT_ASSERT__ CSymbolTableEntry::~CSymbolTableEntry() { Dbg_MsgAssert(mUnion==0,("CSymbolTableEntry still contains data, possibly an undeleted pointer")); } #endif void CSymbolTableEntry::Reset() { mNameChecksum=NO_NAME; mGotReloaded=false; mUsed=false; mType=ESYMBOLTYPE_NONE; mUnion=0; mpNext=NULL; mSourceFileNameChecksum=NO_NAME; #ifdef COUNT_USAGE mUsage=0; #endif } static CSymbolTableEntry *sp_hash_table=NULL; void CreateSymbolHashTable() { Dbg_MsgAssert(sp_hash_table==NULL,("sp_hash_table not NULL ?")); sp_hash_table=new CSymbolTableEntry[1<mUsed) // { // //++NothingCount; // return NULL; // } #if 0 //MCozzini 4-30-03 optimization to avoid the p_sym test if(p_sym->mNameChecksum==checksum) { return p_sym; } p_sym=p_sym->mpNext; // Scan through the linked list until the complete checksum is found. while (p_sym && p_sym->mNameChecksum!=checksum) { p_sym=p_sym->mpNext; } #else // it it's an unused slot, then both mNameChecksum and mpNext will be 0 (NULL) // so it will just drop through this test do { if(p_sym->mNameChecksum == checksum) { return p_sym; // Quicker to return here than to break } p_sym=p_sym->mpNext; } while (p_sym && p_sym->mNameChecksum!=checksum); // Here p_sym will be NULL return p_sym; #endif // p_sym will be NULL if the checksum was not found. /* if (p_sym) { if (p_sym->mType==ESYMBOLTYPE_QSCRIPT) { ++ScriptCount; } else if (p_sym->mType==ESYMBOLTYPE_CFUNCTION) { ++CFuncCount; } else if (p_sym->mType==ESYMBOLTYPE_MEMBERFUNCTION) { ++MemberFuncCount; } else if (p_sym->mType==ESYMBOLTYPE_FLOAT) { ++FloatCount; } else if (p_sym->mType==ESYMBOLTYPE_INTEGER) { ++IntegerCount; } else if (p_sym->mType==ESYMBOLTYPE_STRUCTURE) { ++StructureCount; } else if (p_sym->mType==ESYMBOLTYPE_ARRAY) { ++ArrayCount; } } else { ++NothingCount; }*/ return p_sym; } CSymbolTableEntry *LookUpSymbol(const char *p_name) { return LookUpSymbol(Crc::GenerateCRCFromString(p_name)); } // Resolves a checksum into a symbol table entry. Does any stepping over of chains of symbols linked by // name. Eg, Resolving a where a=b, b=c, c=d, d=6 will return the symbol d, with value 6. // Returns NULL if the checksum cannot be resolved. CSymbolTableEntry *Resolve(uint32 checksum) { // Look up the checksum. CSymbolTableEntry *p_entry=LookUpSymbol(checksum); if (p_entry && p_entry->mType==ESYMBOLTYPE_NAME) { // If the symbol is of type name, then it may be that the name is the name of another symbol, // in which case we need to find that. // Eg we could be trying to resolve a, where a=b, b=c, c=7, in which case we need to find 7. // So loop until either a non-name symbol is reached, or no further symbol is reached. #ifdef __NOPT_ASSERT__ // An infinite loop could occur, for example if we have a global a=b where b=a // This counter will make an assert go off if we try and loop a suspicious number of times. uint32 num_loops=0; #endif while (true) { CSymbolTableEntry *p_new=LookUpSymbol(p_entry->mChecksum); if (!p_new) { // Reached the end of the chain, so return the last entry, which will have type name. return p_entry; } if (p_new->mType!=ESYMBOLTYPE_NAME) { // Reached something not of type name, so return that. return p_new; } // Repeat. p_entry=p_new; Dbg_MsgAssert(num_loops++<10,("Possible infinite recursion of the Resolve function when trying to resolve '%s', bailing out!",FindChecksumName(checksum))); } } return p_entry; } CSymbolTableEntry *Resolve(const char *p_name) { return Resolve(Crc::GenerateCRCFromString(p_name)); } // Removes the symbol from the table. // Used when reloading a .qb file. // Note: Will not delete any entities referred to by the symbol, and will assert if any of the // pointers are not NULL. // This is to avoid a cyclic dependency between symboltable and struct. // It is up to the caller to have deleted these before calling this. void RemoveSymbol(CSymbolTableEntry *p_sym) { Dbg_MsgAssert(p_sym,("NULL p_sym")); Dbg_MsgAssert(p_sym->mUnion==0,("CSymbolTableEntry still contains data, possibly an undeleted pointer. Type='%s'",GetTypeName(p_sym->mType))); Dbg_MsgAssert(p_sym->mUsed,("Tried to call RemoveSymbol on an unused CSymbolTableEntry")); Dbg_MsgAssert(sp_hash_table!=NULL,("sp_hash_table is NULL ?")); // Get the head pointer of the list that p_sym is in (or should be in) CSymbolTableEntry *p_head=&sp_hash_table[ p_sym->mNameChecksum & ((1<mpNext) { // There are later entries in the list, so copy the first of these down // to fill the hole. CSymbolTableEntry *p_source=p_sym->mpNext; // Copy the contents. Not using the assignement operator because that won't link. // It is declared but not defined so as to cause it not to link, to prevent any problems // with copying pointers if it gets used anywhere else. uint32 *p_source_word=(uint32*)p_source; uint32 *p_dest_word=(uint32*)p_sym; Dbg_MsgAssert((sizeof(CSymbolTableEntry)&3)==0,("sizeof(CSymbolTableEntry) not a multiple of 4 ?")); for (uint32 i=0; imUnion=0; delete p_source; } else { // Just reset the contents of p_sym. p_sym->Reset(); } } else { // p_sym is not in the hash table, so it must have been allocated off the pool. // In order to unlink it from the list we need to find the previous entry in the list. // So search through the list until p_sym is found. // Start with mpNext, cos we know p_sym is not p_head CSymbolTableEntry *p_previous=p_head; CSymbolTableEntry *p_search=p_head->mpNext; while (p_search) { if (p_search==p_sym) { break; } p_previous=p_search; p_search=p_search->mpNext; } Dbg_MsgAssert(p_search,("p_sym not found in list ? (checksum='%s')",FindChecksumName(p_sym->mNameChecksum))); // Unlink p_sym and delete it. p_previous->mpNext=p_sym->mpNext; delete p_sym; } } // Adds a new checksum to the directory and returns a pointer to the new entry. CSymbolTableEntry *CreateNewSymbolEntry(uint32 checksum) { #ifdef __NOPT_ASSERT__ CSymbolTableEntry *p_entry=LookUpSymbol(checksum); Dbg_MsgAssert(p_entry==NULL,("Symbol '%s' defined twice.",FindChecksumName(checksum))); #endif // Get the head pointer of the list where the new symbol needs to go. Dbg_MsgAssert(sp_hash_table!=NULL,("sp_hash_table is NULL ?")); CSymbolTableEntry *p_sym=&sp_hash_table[ checksum & ((1<mUsed) { p_sym->Reset(); // Set the checksum. p_sym->mNameChecksum=checksum; // Flag it as used. p_sym->mUsed=true; // Set the mGotReloaded flag so that other game-specific code can see // that the symbol has changed in value. p_sym->mGotReloaded=true; return p_sym; } // Find the last element of the linked list, checking for any checksum // clashes on the way. CSymbolTableEntry *p_previous=NULL; while (p_sym) { Dbg_MsgAssert(p_sym->mNameChecksum!=checksum,("Checksum clash in symbol table: '%s'",FindChecksumName(checksum))); p_previous=p_sym; p_sym=p_sym->mpNext; } // Get a new entry from the pool. CSymbolTableEntry *p_new=new CSymbolTableEntry; // Set the checksum & next pointer. p_new->mNameChecksum=checksum; // Flag it as used. p_new->mUsed=true; // Set the mGotReloaded flag so that other game-specific code can see // that the symbol has changed in value. p_new->mGotReloaded=true; p_new->mpNext=NULL; // This assert should never go off, but do it just in case. Dbg_MsgAssert(p_previous,("NULL p_previous ???")); // Stick pNew on the end of the list. p_previous->mpNext=p_new; return p_new; } // This function provides an easy way to loop through all the symbols in the symbol table by // hiding all the messy logic for stepping through the hash table. // // If passed NULL, it will return the first entry in the symbol table (or NULL if there is nothing in it) // If passed a pointer to a CSymbolTableEntry, it will return the next one, or NULL if none left. // // So if you want to loop through all the symbols just do something like: // CSymbolTableEntry *p_sym=GetNextSymbolTableEntry(); // No need to pass NULL, it defaults to it. // while (p_sym) // { // // ... // p_sym=GetNextSymbolTableEntry(p_sym); // } // CSymbolTableEntry *GetNextSymbolTableEntry(CSymbolTableEntry *p_sym) { // static's for keeping track of where we are in the hash table. static uint32 s_current_hash_index=0; static CSymbolTableEntry *sp_last=NULL; if (p_sym==NULL) { // They want the first symbol. s_current_hash_index=0; p_sym=sp_hash_table; // Step through the hash table entries until a used one is found. while (!p_sym->mUsed) { ++s_current_hash_index; ++p_sym; if (s_current_hash_index>=(1<mpNext; if (!p_sym) { // We've gone off the end of this linked list, so go onto the next entry in the hash table. ++s_current_hash_index; p_sym=sp_hash_table+s_current_hash_index; if (s_current_hash_index>=(1<mUsed) { ++s_current_hash_index; ++p_sym; if (s_current_hash_index>=(1<mType) { case ESYMBOLTYPE_FLOAT: return p_entry->mFloatValue; break; case ESYMBOLTYPE_INTEGER: return (float)p_entry->mIntegerValue; break; default: Dbg_MsgAssert(0,("Symbol '%s' of type '%s' cannot be converted to a float.",FindChecksumName(checksum),GetTypeName(p_entry->mType))); return 0.0f; break; } return 0.0f; } float GetFloat(const char *p_name, EAssertType assert) { return GetFloat(Crc::GenerateCRCFromString(p_name),assert); } int GetInteger(uint32 checksum, EAssertType assert) { CSymbolTableEntry *p_entry=Resolve(checksum); if (!p_entry) { Dbg_MsgAssert(!assert,("Integer '%s' not found.",FindChecksumName(checksum))); return 0; } switch (p_entry->mType) { case ESYMBOLTYPE_FLOAT: return (int)p_entry->mFloatValue; break; case ESYMBOLTYPE_INTEGER: return p_entry->mIntegerValue; break; default: Dbg_MsgAssert(0,("Symbol '%s' of type '%s' cannot be converted to an integer.",FindChecksumName(checksum),GetTypeName(p_entry->mType))); return 0; break; } return 0; } int GetInteger(const char *p_name, EAssertType assert) { return GetInteger(Crc::GenerateCRCFromString(p_name),assert); } uint32 GetChecksum(uint32 checksum, EAssertType assert) { CSymbolTableEntry *p_entry=Resolve(checksum); if (!p_entry) { Dbg_MsgAssert(!assert,("Checksum named '%s' not found.",FindChecksumName(checksum))); return 0; } Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_NAME,("GetChecksum: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType))); return p_entry->mChecksum; } uint32 GetChecksum(const char *p_name, EAssertType assert) { return GetChecksum(Crc::GenerateCRCFromString(p_name),assert); } const char *GetString(uint32 checksum, EAssertType assert) { CSymbolTableEntry *p_entry=Resolve(checksum); if (!p_entry) { Dbg_MsgAssert(!assert,("String named '%s' not found.",FindChecksumName(checksum))); return ""; } Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_STRING || p_entry->mType==ESYMBOLTYPE_LOCALSTRING,("GetString: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType))); Dbg_MsgAssert(p_entry->mpString,("NULL p_entry->mpString")); return p_entry->mpString; } const char *GetString(const char *p_name, EAssertType assert) { return GetString(Crc::GenerateCRCFromString(p_name),assert); } const char *GetLocalString(uint32 checksum, EAssertType assert) { CSymbolTableEntry *p_entry=Resolve(checksum); if (!p_entry) { Dbg_MsgAssert(!assert,("Local string named '%s' not found.",FindChecksumName(checksum))); return ""; } Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_LOCALSTRING || p_entry->mType==ESYMBOLTYPE_STRING,("GetLocalString: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType))); Dbg_MsgAssert(p_entry->mpLocalString,("NULL p_entry->mpLocalString")); return p_entry->mpLocalString; } const char *GetLocalString(const char *p_name, EAssertType assert) { return GetLocalString(Crc::GenerateCRCFromString(p_name),assert); } CVector *GetVector(uint32 checksum, EAssertType assert) { CSymbolTableEntry *p_entry=Resolve(checksum); if (!p_entry) { Dbg_MsgAssert(!assert,("Vector named '%s' not found.",FindChecksumName(checksum))); return NULL; } Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_VECTOR,("GetVector: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType))); Dbg_MsgAssert(p_entry->mpVector,("NULL p_entry->mpVector")); return p_entry->mpVector; } CVector *GetVector(const char *p_name, EAssertType assert) { return GetVector(Crc::GenerateCRCFromString(p_name),assert); } CPair *GetPair(uint32 checksum, EAssertType assert) { CSymbolTableEntry *p_entry=Resolve(checksum); if (!p_entry) { Dbg_MsgAssert(!assert,("Pair named '%s' not found.",FindChecksumName(checksum))); return NULL; } Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_PAIR,("GetPair: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType))); Dbg_MsgAssert(p_entry->mpPair,("NULL p_entry->mpPair")); return p_entry->mpPair; } CPair *GetPair(const char *p_name, EAssertType assert) { return GetPair(Crc::GenerateCRCFromString(p_name),assert); } CStruct *GetStructure(uint32 checksum, EAssertType assert) { CSymbolTableEntry *p_entry=Resolve(checksum); if (!p_entry) { Dbg_MsgAssert(!assert,("Structure named '%s' not found.",FindChecksumName(checksum))); return NULL; } #ifdef __NOPT_ASSERT__ if (assert) { Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_STRUCTURE,("GetStructure: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType))); } else #endif { if (p_entry->mType!=ESYMBOLTYPE_STRUCTURE) { return NULL; } } Dbg_MsgAssert(p_entry->mpStructure,("NULL p_entry->mpStructure")); return p_entry->mpStructure; } CStruct *GetStructure(const char *p_name, EAssertType assert) { return GetStructure(Crc::GenerateCRCFromString(p_name),assert); } CArray *GetArray(uint32 checksum, EAssertType assert) { CSymbolTableEntry *p_entry=Resolve(checksum); if (!p_entry) { Dbg_MsgAssert(!assert,("Array named '%s' not found.",FindChecksumName(checksum))); return NULL; } #ifdef __NOPT_ASSERT__ if (assert) { Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_ARRAY,("GetArray: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType))); } else #endif { if (p_entry->mType!=ESYMBOLTYPE_ARRAY) { return NULL; } } Dbg_MsgAssert(p_entry->mpArray,("NULL p_entry->mpArray")); return p_entry->mpArray; } CArray *GetArray(const char *p_name, EAssertType assert) { return GetArray(Crc::GenerateCRCFromString(p_name),assert); } bool (*GetCFunc(uint32 checksum, EAssertType assert))(CStruct *, CScript *) { CSymbolTableEntry *p_entry=Resolve(checksum); if (!p_entry) { Dbg_MsgAssert(!assert,("GetCFunc could not find the symbol '%s'. No symbol of any type exists with that name.",FindChecksumName(checksum))); return NULL; } switch (p_entry->mType) { case ESYMBOLTYPE_CFUNCTION: Dbg_MsgAssert(p_entry->mpCFunction,("NULL p_entry->pCFunction ?")); return p_entry->mpCFunction; break; case ESYMBOLTYPE_MEMBERFUNCTION: Dbg_MsgAssert(0,("'%s' is not a C-function, it is a member function.",FindChecksumName(checksum))); break; default: Dbg_MsgAssert(0,("'%s' is not a C-function, but has type '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType))); break; } return NULL; } bool (*GetCFunc(const char *p_name, EAssertType assert))(CStruct *, CScript *) { return GetCFunc(Crc::GenerateCRCFromString(p_name),assert); } /////////////////////////////////////////////////////////////////////////////////// // TODO: Remove these next functions at some point. // They are only included to provide back compatibility with the old code without // having to change thousands of occurrences. int GetInt(uint32 checksum, bool assert) { return GetInteger(checksum,assert?ASSERT:NO_ASSERT); } int GetInt(const char *p_name, bool assert) { return GetInteger(p_name,assert?ASSERT:NO_ASSERT); } bool (*GetCFuncPointer(uint32 checksum, EAssertType assert))(CStruct *, CScript *) { return GetCFunc(checksum, assert); } bool (*GetCFuncPointer(const char *p_name, EAssertType assert))(CStruct *, CScript *) { return GetCFunc(p_name, assert); } /////////////////////////////////////////////////////////////////////////////////// } // namespace Script