Atrinik Server 2.5
server/shstr.c
Go to the documentation of this file.
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 }