00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <glib.h>
00021 #include <pthread.h>
00022 #include <stdarg.h>
00023 #include <stdio.h>
00024 #include <stdint.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027
00028 #include "config.h"
00029 #include "core.h"
00030
00031
00032
00033
00034 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
00035 static GHashTable * table;
00036
00037 #ifdef STRPOOL_DEBUG
00038 static GHashTable * logged;
00039
00040 static void str_log (const char * str, const char * op, const char * file, int line)
00041 {
00042 if (! logged)
00043 logged = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
00044
00045 GList * list = g_hash_table_lookup (logged, str);
00046 list = g_list_prepend (list, g_strdup_printf ("%s by %s:%d", op, file, line));
00047 g_hash_table_insert (logged, g_strdup (str), list);
00048 }
00049
00050 static void str_log_dump (const char * str)
00051 {
00052 if (! logged)
00053 return;
00054
00055 for (GList * node = g_hash_table_lookup (logged, str); node; node = node->next)
00056 printf (" - %s\n", (char *) node->data);
00057 }
00058 #endif
00059
00060 static void str_destroy (void * str)
00061 {
00062 * ((char *) str - 1) = 0;
00063 free ((char *) str - 5);
00064 }
00065
00066 #ifdef STRPOOL_DEBUG
00067 char * str_get_debug (const char * str, const char * file, int line)
00068 #else
00069 char * str_get (const char * str)
00070 #endif
00071 {
00072 if (! str)
00073 return NULL;
00074
00075 char * copy;
00076 pthread_mutex_lock (& mutex);
00077
00078 #ifdef STRPOOL_DEBUG
00079 str_log (str, "get", file, line);
00080 #endif
00081
00082 if (! table)
00083 table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, str_destroy);
00084
00085 if ((copy = g_hash_table_lookup (table, str)))
00086 {
00087 void * mem = copy - 5;
00088 (* (int32_t *) mem) ++;
00089 }
00090 else
00091 {
00092 void * mem = malloc (6 + strlen (str));
00093 (* (int32_t *) mem) = 1;
00094
00095 copy = (char *) mem + 5;
00096 copy[-1] = '@';
00097 strcpy (copy, str);
00098
00099 g_hash_table_insert (table, copy, copy);
00100 }
00101
00102 pthread_mutex_unlock (& mutex);
00103 return copy;
00104 }
00105
00106 #ifdef STRPOOL_DEBUG
00107 char * str_ref_debug (char * str, const char * file, int line)
00108 #else
00109 char * str_ref (char * str)
00110 #endif
00111 {
00112 if (! str)
00113 return NULL;
00114
00115 pthread_mutex_lock (& mutex);
00116 STR_CHECK (str);
00117
00118 #ifdef STRPOOL_DEBUG
00119 str_log (str, "ref", file, line);
00120 #endif
00121
00122 void * mem = str - 5;
00123 (* (int32_t *) mem) ++;
00124
00125 pthread_mutex_unlock (& mutex);
00126 return str;
00127 }
00128
00129 #ifdef STRPOOL_DEBUG
00130 void str_unref_debug (char * str, const char * file, int line)
00131 #else
00132 void str_unref (char * str)
00133 #endif
00134 {
00135 if (! str)
00136 return;
00137
00138 pthread_mutex_lock (& mutex);
00139 STR_CHECK (str);
00140
00141 #ifdef STRPOOL_DEBUG
00142 str_log (str, "unref", file, line);
00143 #endif
00144
00145 void * mem = str - 5;
00146 if (! -- (* (int32_t *) mem))
00147 g_hash_table_remove (table, str);
00148
00149 pthread_mutex_unlock (& mutex);
00150 }
00151
00152 char * str_nget (const char * str, int len)
00153 {
00154 if (strlen (str) <= len)
00155 return str_get (str);
00156
00157 char buf[len + 1];
00158 memcpy (buf, str, len);
00159 buf[len] = 0;
00160
00161 return str_get (buf);
00162 }
00163
00164 char * str_printf (const char * format, ...)
00165 {
00166 va_list args;
00167
00168 va_start (args, format);
00169 int len = vsnprintf (NULL, 0, format, args);
00170 va_end (args);
00171
00172 char buf[len + 1];
00173
00174 va_start (args, format);
00175 vsnprintf (buf, sizeof buf, format, args);
00176 va_end (args);
00177
00178 return str_get (buf);
00179 }
00180
00181 void strpool_abort (char * str)
00182 {
00183 fprintf (stderr, "String not in pool: %s\n", str);
00184 #ifdef STRPOOL_DEBUG
00185 str_log_dump (str);
00186 #endif
00187 abort ();
00188 }
00189
00190 static void str_leaked (void * key, void * str, void * unused)
00191 {
00192 fprintf (stderr, "String not freed: %s\n", (char *) str);
00193 #ifdef STRPOOL_DEBUG
00194 str_log_dump (str);
00195 #endif
00196 }
00197
00198 void strpool_shutdown (void)
00199 {
00200 if (! table)
00201 return;
00202
00203 g_hash_table_foreach (table, str_leaked, NULL);
00204 g_hash_table_destroy (table);
00205 table = NULL;
00206 }