Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
strpool.c
Go to the documentation of this file.
00001 /*
00002  * strpool.c
00003  * Copyright 2011 John Lindgren
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright notice,
00009  *    this list of conditions, and the following disclaimer.
00010  *
00011  * 2. Redistributions in binary form must reproduce the above copyright notice,
00012  *    this list of conditions, and the following disclaimer in the documentation
00013  *    provided with the distribution.
00014  *
00015  * This software is provided "as is" and without any warranty, express or
00016  * implied. In no event shall the authors be liable for any damages arising from
00017  * the use of this software.
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 /* Each string in the pool is allocated with five leading bytes: a 32-bit
00032  * reference count and a one-byte signature, the '@' character. */
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 EXPORT char * str_get_debug (const char * str, const char * file, int line)
00068 #else
00069 EXPORT 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 EXPORT char * str_ref_debug (char * str, const char * file, int line)
00108 #else
00109 EXPORT 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 EXPORT void str_unref_debug (char * str, const char * file, int line)
00131 #else
00132 EXPORT 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 EXPORT 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 EXPORT 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 EXPORT 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 EXPORT 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 }