|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00031 #include <global.h> 00032 00033 #define SS_STATISTICS 00034 #include "shstr.h" 00035 00037 static shared_string *hash_table[TABLESIZE]; 00038 00041 void init_hash_table() 00042 { 00043 memset((void *) hash_table, 0, TABLESIZE * sizeof(shared_string *)); 00044 } 00045 00050 static unsigned long hashstr(const char *str) 00051 { 00052 unsigned long hash = 0; 00053 int i = 0; 00054 unsigned int rot = 0; 00055 const char *p; 00056 00057 GATHER(hash_stats.calls); 00058 00059 for (p = str; i < MAXSTRING && *p; p++, i++) 00060 { 00061 hash ^= (unsigned long) *p << rot; 00062 rot += 2; 00063 00064 if (rot >= (sizeof(unsigned long) - sizeof(char)) * CHAR_BIT) 00065 { 00066 rot = 0; 00067 } 00068 } 00069 00070 return (hash % TABLESIZE); 00071 } 00072 00078 static shared_string *new_shared_string(const char *str) 00079 { 00080 shared_string *ss; 00081 size_t n = strlen(str); 00082 00083 /* Allocate room for a struct which can hold str. Note 00084 * that some bytes for the string are already allocated in the 00085 * shared_string struct. */ 00086 ss = (shared_string *) malloc(sizeof(shared_string) - PADDING + n + 1); 00087 00088 if (!ss) 00089 { 00090 LOG(llevError, "new_shared_string(): Out of memory."); 00091 } 00092 00093 ss->u.previous = NULL; 00094 ss->next = NULL; 00095 ss->refcount = 1; 00096 memcpy(ss->string, str, n); 00097 ss->string[n] = '\0'; 00098 00099 return ss; 00100 } 00101 00107 shstr *add_string(const char *str) 00108 { 00109 shared_string *ss; 00110 unsigned long ind; 00111 00112 GATHER(add_stats.calls); 00113 00114 /* Should really core dump here, since functions should not be calling 00115 * add_string with a null parameter. But this will prevent a few 00116 * core dumps. */ 00117 if (str == NULL) 00118 { 00119 LOG(llevBug, "add_string(): Tried to add NULL string to hash table\n"); 00120 return NULL; 00121 } 00122 00123 ind = hashstr(str); 00124 ss = hash_table[ind]; 00125 00126 /* Is there an entry for that hash? */ 00127 if (ss) 00128 { 00129 /* Simple case first: See if the first pointer matches. */ 00130 if (str != ss->string) 00131 { 00132 GATHER(add_stats.strcmps); 00133 00134 if (strcmp(ss->string, str)) 00135 { 00136 /* Apparently, a string with the same hash value has this 00137 * slot. We must see in the list if "str" has been 00138 * registered earlier. */ 00139 while (ss->next) 00140 { 00141 GATHER(add_stats.search); 00142 ss = ss->next; 00143 00144 if (ss->string != str) 00145 { 00146 GATHER(add_stats.strcmps); 00147 00148 if (strcmp(ss->string, str)) 00149 { 00150 /* This wasn't the right string... */ 00151 continue; 00152 } 00153 } 00154 00155 /* We found an entry for this string. Fix the 00156 * refcount and exit. */ 00157 GATHER(add_stats.linked); 00158 ++(ss->refcount); 00159 00160 return ss->string; 00161 } 00162 00163 /* There are no occurrences of this string in the hash table. */ 00164 { 00165 shared_string *new_ss; 00166 00167 GATHER(add_stats.linked); 00168 new_ss = new_shared_string(str); 00169 ss->next = new_ss; 00170 new_ss->u.previous = ss; 00171 00172 return new_ss->string; 00173 } 00174 } 00175 00176 /* Fall through. */ 00177 } 00178 00179 GATHER(add_stats.hashed); 00180 ++(ss->refcount); 00181 00182 return ss->string; 00183 } 00184 /* The string isn't registered, and the slot is empty. */ 00185 else 00186 { 00187 GATHER(add_stats.hashed); 00188 hash_table[ind] = new_shared_string(str); 00189 00190 /* One bit in refcount is used to keep track of the union. */ 00191 hash_table[ind]->refcount |= TOPBIT; 00192 hash_table[ind]->u.array = &(hash_table[ind]); 00193 00194 return hash_table[ind]->string; 00195 } 00196 } 00197 00203 shstr *add_refcount(shstr *str) 00204 { 00205 #ifdef SECURE_SHSTR_HASH 00206 shstr *tmp_str = find_string(str); 00207 00208 if (!str || str != tmp_str) 00209 { 00210 LOG(llevBug, "add_refcount(): Tried to free an invalid string! >%s<\n", STRING_SAFE(str)); 00211 return NULL; 00212 } 00213 #endif 00214 00215 GATHER(add_ref_stats.calls); 00216 ++(SS(str)->refcount); 00217 00218 return str; 00219 } 00220 00226 int query_refcount(shstr *str) 00227 { 00228 return SS(str)->refcount & ~TOPBIT; 00229 } 00230 00235 shstr *find_string(const char *str) 00236 { 00237 shared_string *ss; 00238 unsigned long ind; 00239 00240 GATHER(find_stats.calls); 00241 00242 ind = hashstr(str); 00243 ss = hash_table[ind]; 00244 00245 /* Is there an entry for that hash? */ 00246 if (ss) 00247 { 00248 /* Simple case first: Is the first string the right one? */ 00249 GATHER(find_stats.strcmps); 00250 00251 if (!strcmp(ss->string, str)) 00252 { 00253 GATHER(find_stats.hashed); 00254 return ss->string; 00255 } 00256 else 00257 { 00258 /* Recurse through the linked list, if there's one. */ 00259 while (ss->next) 00260 { 00261 GATHER(find_stats.search); 00262 GATHER(find_stats.strcmps); 00263 ss = ss->next; 00264 00265 if (!strcmp(ss->string, str)) 00266 { 00267 GATHER(find_stats.linked); 00268 return ss->string; 00269 } 00270 } 00271 00272 /* No match. Fall through. */ 00273 } 00274 } 00275 00276 return NULL; 00277 } 00278 00284 void free_string_shared(shstr *str) 00285 { 00286 shared_string *ss; 00287 00288 #ifdef SECURE_SHSTR_HASH 00289 shstr *tmp_str = find_string(str); 00290 00291 if (!str || str != tmp_str) 00292 { 00293 LOG(llevBug, "free_string_shared(): Tried to free an invalid string! >%s<\n", STRING_SAFE(str)); 00294 return; 00295 } 00296 #endif 00297 00298 GATHER(free_stats.calls); 00299 ss = SS(str); 00300 00301 if ((--ss->refcount & ~TOPBIT) == 0) 00302 { 00303 /* Remove this entry. */ 00304 if (ss->refcount & TOPBIT) 00305 { 00306 /* We must put a new value into the hash_table[]. */ 00307 if (ss->next) 00308 { 00309 *(ss->u.array) = ss->next; 00310 ss->next->u.array = ss->u.array; 00311 ss->next->refcount |= TOPBIT; 00312 } 00313 else 00314 { 00315 *(ss->u.array) = NULL; 00316 } 00317 00318 free(ss); 00319 } 00320 else 00321 { 00322 /* Relink and free this struct. */ 00323 if (ss->next) 00324 { 00325 ss->next->u.previous = ss->u.previous; 00326 } 00327 00328 ss->u.previous->next = ss->next; 00329 free(ss); 00330 } 00331 } 00332 } 00333 00334 #ifdef SS_STATISTICS 00335 00343 void ss_dump_statistics(char *buf, size_t size) 00344 { 00345 static char line[MAX_BUF]; 00346 00347 snprintf(buf, size, "%-13s %6s %6s %6s %6s %6s\n", "", "calls", "hashed", "strcmp", "search", "linked"); 00348 snprintf(line, sizeof(line), "%-13s %6d %6d %6d %6d %6d\n", "add_string:", add_stats.calls, add_stats.hashed, add_stats.strcmps, add_stats.search, add_stats.linked); 00349 snprintf(buf + strlen(buf), size - strlen(buf), "%s", line); 00350 snprintf(line, sizeof(line), "%-13s %6d\n", "add_refcount:", add_ref_stats.calls); 00351 snprintf(buf + strlen(buf), size - strlen(buf), "%s", line); 00352 snprintf(line, sizeof(line), "%-13s %6d\n", "free_string:", free_stats.calls); 00353 snprintf(buf + strlen(buf), size - strlen(buf), "%s", line); 00354 snprintf(line, sizeof(line), "%-13s %6d %6d %6d %6d %6d\n", "find_string:", find_stats.calls, find_stats.hashed, find_stats.strcmps, find_stats.search, find_stats.linked); 00355 snprintf(buf + strlen(buf), size - strlen(buf), "%s", line); 00356 snprintf(line, sizeof(line), "%-13s %6d\n", "hashstr:", hash_stats.calls); 00357 snprintf(buf + strlen(buf), size - strlen(buf), "%s", line); 00358 } 00359 #endif 00360 00370 void ss_dump_table(int what, char *buf, size_t size) 00371 { 00372 int entries = 0, refs = 0, links = 0, i; 00373 00374 for (i = 0; i < TABLESIZE; i++) 00375 { 00376 shared_string *ss; 00377 00378 if ((ss = hash_table[i]) != NULL) 00379 { 00380 ++entries; 00381 refs += (ss->refcount & ~TOPBIT); 00382 00383 LOG(llevSystem, "%4d -- %4"FMT64U" refs '%s' %c\n", i, (uint64) (ss->refcount & ~TOPBIT), ss->string, (ss->refcount & TOPBIT ? ' ' : '#')); 00384 00385 while (ss->next) 00386 { 00387 ss = ss->next; 00388 ++links; 00389 refs += (ss->refcount & ~TOPBIT); 00390 00391 if (what & SS_DUMP_TABLE) 00392 { 00393 LOG(llevSystem, " -- %4"FMT64U" refs '%s' %c\n", (uint64) (ss->refcount & ~TOPBIT), ss->string, (ss->refcount & TOPBIT ? '*' : ' ')); 00394 } 00395 } 00396 } 00397 } 00398 00399 if (what & SS_DUMP_TOTALS) 00400 { 00401 snprintf(buf, size, "\n%d entries, %d refs, %d links.", entries, refs, links); 00402 } 00403 }
1.7.4