StringIdTable.cpp
Go to the documentation of this file.00001 //*** StringIdTable.cpp ***/ 00002 00003 #include "StringIdTable.h" 00004 #include "StandardLibrary.h" 00005 00006 00007 //*** GetInstance *** 00008 00009 StringIdTable& StringIdTable::GetInstance() 00010 { 00011 // There is only one instance of StringIdTable ever created, and this is the one. 00012 // It is declared as a static member of the GetInstance method, which makes sure 00013 // it is instantiated only once, the first time it is used. The StringIdTable class 00014 // have private constructor/destructor, and private copy and assignment operators, 00015 // so it can not be created or duplicated. The only way to get hold of an instance 00016 // of the StringIdTable is by using this method, and this guarantees that there will 00017 // only ever be one single instance of the engine. 00018 // Note that the implementation of this method may NOT be moved to a .inl file, 00019 // because that means idTableInstance will be inlined in several places, and we 00020 // will end up with more than one instance, which will really mess things up! 00021 static StringIdTable idTableInstance; 00022 return idTableInstance; 00023 } 00024 00025 00026 //*** Constructor *** 00027 00028 StringIdTable::StringIdTable(): 00029 stringStorageBlockCount_(0), 00030 stringStorageBlockMaxCount_(8), 00031 idStringTableSlots_(256), 00032 idStringTableItemCount_(0) 00033 { 00034 // Allocate the array which stores the string block info 00035 stringStorageBlocks_=static_cast<StringStorageBlock*>(Malloc(stringStorageBlockMaxCount_*sizeof(StringStorageBlock))); 00036 00037 // Allocate the first string block 00038 stringStorageBlocks_[0].head=static_cast<char*>(Malloc(stringStorageBlockSize_)); 00039 stringStorageBlocks_[0].tail=stringStorageBlocks_[0].head; 00040 stringStorageBlockCount_++; 00041 00042 // Allocate the hash table 00043 idStringTable_=static_cast<char**>(Malloc(sizeof(char*)*idStringTableSlots_)); 00044 00045 // Mark all the slots of the string table as unused 00046 for (int i=0; i<idStringTableSlots_; i++) 00047 { 00048 idStringTable_[i]=0; 00049 } 00050 00051 } 00052 00053 //*** Destructor *** 00054 00055 StringIdTable::~StringIdTable() 00056 { 00057 // Free all the string storage blocks 00058 for (int i=0; i<stringStorageBlockCount_; i++) 00059 { 00060 Free(stringStorageBlocks_[i].head); 00061 } 00062 00063 // Free the array holding the string storage block info 00064 Free(stringStorageBlocks_); 00065 00066 Free(idStringTable_); 00067 } 00068 00069 00070 //*** CalculateHash *** 00071 00072 unsigned int StringIdTable::CalculateHash(const char* idString) const 00073 { 00074 unsigned long hash = 5381; // Seed value 00075 00076 // Modify hash for each character in the string 00077 const char* stringData=idString; 00078 while (*stringData) 00079 { 00080 // A little bit-manipulation magic to get a nice distribution of values 00081 hash = ((hash << 5) + hash) ^ ToUpper(*stringData); 00082 stringData++; 00083 } 00084 00085 // Return the final hash value 00086 return hash; 00087 } 00088 00089 00090 //*** FindIdString *** 00091 00092 const char* StringIdTable::FindIdString(unsigned int hash, const char* idString) 00093 { 00094 // Find slot for this string. This simply uses the passed in hash value, and modulates it by the number of slots. 00095 // Note that as the number of slots is always a power-of-two-number, we can use a binary AND instead of modulo 00096 unsigned int slot=hash&(idStringTableSlots_-1); 00097 00098 // Loop through all the entries until we find the one we are looking for or an empty slot 00099 char* entry=idStringTable_[slot]; 00100 while(entry) 00101 { 00102 // Is this the one we're looking for? 00103 unsigned int strhash=(*(reinterpret_cast<unsigned int*>(entry))); // Hash number is the first four bytes 00104 if (strhash==hash && StrICmp(entry+4,idString)==0) 00105 { 00106 // Yes, so we just return the shared idString 00107 return entry+4; // Skip the first four bytes, as that's the hash number 00108 } 00109 00110 // Not found the one we're looking for, so continue with the next entry 00111 slot=(slot+1)&(idStringTableSlots_-1); 00112 entry=idStringTable_[slot]; 00113 } 00114 00115 // We didn't find an entry for that string, so we need to add it 00116 00117 // If the table is more than two-thirds full, double its size and reinsert the strings 00118 if (idStringTableItemCount_>=(idStringTableSlots_-(idStringTableSlots_/3))) 00119 { 00120 // Make the new table twice the size 00121 int newidStringTableSlots=idStringTableSlots_*2; 00122 00123 // Allocate memory for it 00124 char** newIdStringTable=static_cast<char**>(Malloc(sizeof(char*)*newidStringTableSlots)); 00125 00126 // And initialize all slots to unused 00127 for (int i=0; i<newidStringTableSlots; i++) 00128 { 00129 newIdStringTable[i]=0; 00130 } 00131 00132 // Reinsert all the existing strings into the new table 00133 for (int i=0; i<idStringTableSlots_; i++) 00134 { 00135 // If slot is in use 00136 if (idStringTable_[i]) 00137 { 00138 // Get hash for string (stored as first four bytes of the string) 00139 unsigned int existinghash=(*(reinterpret_cast<unsigned int*>(idStringTable_[i]))); 00140 00141 // Calculate the slot 00142 int newslot=existinghash&(newidStringTableSlots-1); 00143 00144 // Find the nearest unused slot 00145 while (newIdStringTable[newslot]) 00146 { 00147 newslot=(newslot+1)&(newidStringTableSlots-1); 00148 } 00149 00150 // Store the string in the new table 00151 newIdStringTable[newslot]=idStringTable_[i]; 00152 } 00153 } 00154 00155 // Free memory used by the old table 00156 Free(idStringTable_); 00157 00158 // And replace the old table with the new one 00159 idStringTableSlots_=newidStringTableSlots; 00160 idStringTable_=newIdStringTable; 00161 00162 // Calculate the new slot for the string we're currently inserting 00163 slot=hash&(idStringTableSlots_-1); 00164 00165 // And find the nearest empty slot 00166 while (idStringTable_[slot]) 00167 { 00168 slot=(slot+1)&(idStringTableSlots_-1); 00169 } 00170 } 00171 00172 // Create a duplicate of the string 00173 char* newEntry=StoreString(hash, idString); 00174 00175 // And store it in the table 00176 idStringTable_[slot]=newEntry; 00177 00178 // Increase the total number of items stored 00179 idStringTableItemCount_++; 00180 00181 // Return the shared id string 00182 return newEntry+4; // Skip the first four bytes, as that's the hash number 00183 } 00184 00185 00186 //*** StoreString *** 00187 00188 char* StringIdTable::StoreString(unsigned int hash, const char* string) 00189 { 00190 // Get the length of the string 00191 int length=(int)StrLen(string); 00192 00193 // Calculate space needed to store the string 00194 int spaceNeeded=length+1+4; // Add space for terminator (1) and for storing hash number (4) 00195 00196 // Check if there's space in the current string storage block 00197 StringStorageBlock* block=&stringStorageBlocks_[stringStorageBlockCount_-1]; // Current block is always the last one 00198 if ((stringStorageBlockSize_-(block->tail-block->head))<spaceNeeded) 00199 { 00200 // No more room - make a new storage block 00201 if (stringStorageBlockCount_==stringStorageBlockMaxCount_) 00202 { 00203 // Need a bigger array for string storage blocks 00204 stringStorageBlockMaxCount_*=2; // Go with twice the current size 00205 00206 // Use realloc to get a bigger array while preserving the values it already holds 00207 stringStorageBlocks_=static_cast<StringStorageBlock*>(Realloc(stringStorageBlocks_,stringStorageBlockMaxCount_*sizeof(StringStorageBlock))); 00208 } 00209 00210 // Allocate the new string storage block 00211 stringStorageBlocks_[stringStorageBlockCount_].head=static_cast<char*>(Malloc(stringStorageBlockSize_)); 00212 stringStorageBlocks_[stringStorageBlockCount_].tail=stringStorageBlocks_[stringStorageBlockCount_].head; // Empty, so first free byte is at start 00213 00214 // Update the current string storage block 00215 block=&stringStorageBlocks_[stringStorageBlockCount_]; 00216 stringStorageBlockCount_++; 00217 } 00218 00219 // Get the next piece of free memory from the current block 00220 char* strdest=block->tail; 00221 00222 // Write hash number 00223 *(reinterpret_cast<unsigned int*>(strdest))=hash; 00224 00225 // Copy the string to the block 00226 StrCpy(strdest+4,string); // Offset by four to account for hash number 00227 00228 // Mark space as used 00229 block->tail+=spaceNeeded; 00230 00231 // Return pointer to the copy 00232 return strdest; 00233 } 00234 00235
Reproduction/republishing of any material on this site without permission is strictly prohibited.
