Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
strpool.c
Go to the documentation of this file.
1 /*
2  * strpool.c
3  * Copyright 2011 John Lindgren
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions, and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions, and the following disclaimer in the documentation
13  * provided with the distribution.
14  *
15  * This software is provided "as is" and without any warranty, express or
16  * implied. In no event shall the authors be liable for any damages arising from
17  * the use of this software.
18  */
19 
20 #include <glib.h>
21 #include <pthread.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "config.h"
29 #include "core.h"
30 
31 /* Each string in the pool is allocated with five leading bytes: a 32-bit
32  * reference count and a one-byte signature, the '@' character. */
33 
34 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
35 static GHashTable * table;
36 
37 #ifdef STRPOOL_DEBUG
38 static GHashTable * logged;
39 
40 static void str_log (const char * str, const char * op, const char * file, int line)
41 {
42  if (! logged)
43  logged = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
44 
45  GList * list = g_hash_table_lookup (logged, str);
46  list = g_list_prepend (list, g_strdup_printf ("%s by %s:%d", op, file, line));
47  g_hash_table_insert (logged, g_strdup (str), list);
48 }
49 
50 static void str_log_dump (const char * str)
51 {
52  if (! logged)
53  return;
54 
55  for (GList * node = g_hash_table_lookup (logged, str); node; node = node->next)
56  printf (" - %s\n", (char *) node->data);
57 }
58 #endif
59 
60 static void str_destroy (void * str)
61 {
62  * ((char *) str - 1) = 0;
63  free ((char *) str - 5);
64 }
65 
66 #ifdef STRPOOL_DEBUG
67 EXPORT char * str_get_debug (const char * str, const char * file, int line)
68 #else
69 EXPORT char * str_get (const char * str)
70 #endif
71 {
72  if (! str)
73  return NULL;
74 
75  char * copy;
76  pthread_mutex_lock (& mutex);
77 
78 #ifdef STRPOOL_DEBUG
79  str_log (str, "get", file, line);
80 #endif
81 
82  if (! table)
83  table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, str_destroy);
84 
85  if ((copy = g_hash_table_lookup (table, str)))
86  {
87  void * mem = copy - 5;
88  (* (int32_t *) mem) ++;
89  }
90  else
91  {
92  void * mem = malloc (6 + strlen (str));
93  (* (int32_t *) mem) = 1;
94 
95  copy = (char *) mem + 5;
96  copy[-1] = '@';
97  strcpy (copy, str);
98 
99  g_hash_table_insert (table, copy, copy);
100  }
101 
102  pthread_mutex_unlock (& mutex);
103  return copy;
104 }
105 
106 #ifdef STRPOOL_DEBUG
107 EXPORT char * str_ref_debug (char * str, const char * file, int line)
108 #else
109 EXPORT char * str_ref (char * str)
110 #endif
111 {
112  if (! str)
113  return NULL;
114 
115  pthread_mutex_lock (& mutex);
116  STR_CHECK (str);
117 
118 #ifdef STRPOOL_DEBUG
119  str_log (str, "ref", file, line);
120 #endif
121 
122  void * mem = str - 5;
123  (* (int32_t *) mem) ++;
124 
125  pthread_mutex_unlock (& mutex);
126  return str;
127 }
128 
129 #ifdef STRPOOL_DEBUG
130 EXPORT void str_unref_debug (char * str, const char * file, int line)
131 #else
132 EXPORT void str_unref (char * str)
133 #endif
134 {
135  if (! str)
136  return;
137 
138  pthread_mutex_lock (& mutex);
139  STR_CHECK (str);
140 
141 #ifdef STRPOOL_DEBUG
142  str_log (str, "unref", file, line);
143 #endif
144 
145  void * mem = str - 5;
146  if (! -- (* (int32_t *) mem))
147  g_hash_table_remove (table, str);
148 
149  pthread_mutex_unlock (& mutex);
150 }
151 
152 EXPORT char * str_nget (const char * str, int len)
153 {
154  if (strlen (str) <= len)
155  return str_get (str);
156 
157  char buf[len + 1];
158  memcpy (buf, str, len);
159  buf[len] = 0;
160 
161  return str_get (buf);
162 }
163 
164 EXPORT char * str_printf (const char * format, ...)
165 {
166  va_list args;
167 
168  va_start (args, format);
169  int len = vsnprintf (NULL, 0, format, args);
170  va_end (args);
171 
172  char buf[len + 1];
173 
174  va_start (args, format);
175  vsnprintf (buf, sizeof buf, format, args);
176  va_end (args);
177 
178  return str_get (buf);
179 }
180 
181 EXPORT void strpool_abort (char * str)
182 {
183  fprintf (stderr, "String not in pool: %s\n", str);
184 #ifdef STRPOOL_DEBUG
185  str_log_dump (str);
186 #endif
187  abort ();
188 }
189 
190 static void str_leaked (void * key, void * str, void * unused)
191 {
192  fprintf (stderr, "String not freed: %s\n", (char *) str);
193 #ifdef STRPOOL_DEBUG
194  str_log_dump (str);
195 #endif
196 }
197 
198 EXPORT void strpool_shutdown (void)
199 {
200  if (! table)
201  return;
202 
203  g_hash_table_foreach (table, str_leaked, NULL);
204  g_hash_table_destroy (table);
205  table = NULL;
206 }