No issues found
1 /*
2 * Copyright (C) 2004 Colin Walters <walters@redhat.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * The Rhythmbox authors hereby grant permission for non-GPL compatible
10 * GStreamer plugins to be used and distributed together with GStreamer
11 * and Rhythmbox. This permission is above and beyond the permissions granted
12 * by the GPL license by which Rhythmbox is covered. If you modify this code
13 * you may extend this exception to your version of the code, but you are not
14 * obligated to do so. If you do not wish to do so, delete this exception
15 * statement from your version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
25 *
26 */
27
28 #include <config.h>
29
30 #include <glib.h>
31 #include <string.h>
32 #include "rb-util.h"
33 #include "rb-cut-and-paste-code.h"
34 #include "rb-refstring.h"
35
36 GHashTable *rb_refstrings;
37 GMutex rb_refstrings_mutex;
38
39 struct RBRefString
40 {
41 gint refcount;
42 gpointer folded;
43 gpointer sortkey;
44 char value[1];
45 };
46
47 static void
48 rb_refstring_free (RBRefString *refstr)
49 {
50 refstr->refcount = 0xdeadbeef;
51 g_free (refstr->folded);
52 refstr->folded = NULL;
53 g_free (refstr->sortkey);
54 refstr->sortkey = NULL;
55 g_free (refstr);
56 }
57
58 /**
59 * rb_refstring_system_init:
60 *
61 * Sets up the refstring system. Called on startup.
62 */
63 void
64 rb_refstring_system_init ()
65 {
66 rb_refstrings = g_hash_table_new_full (g_str_hash, g_str_equal,
67 NULL, (GDestroyNotify) rb_refstring_free);
68 }
69
70 /**
71 * rb_refstring_new:
72 * @init: string to intern
73 *
74 * Returns an #RBRefString for the specified string.
75 * If one already exists, its reference count is incremented and it is
76 * returned. Otherwise, a new #RBRefString is created and returned.
77 *
78 * Return value: #RBRefString for @init
79 */
80 RBRefString *
81 rb_refstring_new (const char *init)
82 {
83 RBRefString *ret;
84
85 g_mutex_lock (&rb_refstrings_mutex);
86 ret = g_hash_table_lookup (rb_refstrings, init);
87
88 if (ret) {
89 rb_refstring_ref (ret);
90 g_mutex_unlock (&rb_refstrings_mutex);
91 return ret;
92 }
93
94 ret = g_malloc (sizeof (RBRefString) + strlen (init));
95
96 g_strlcpy (ret->value, init, strlen (init) + 1);
97 g_atomic_int_set (&ret->refcount, 1);
98 ret->folded = NULL;
99 ret->sortkey = NULL;
100
101 g_hash_table_insert (rb_refstrings, ret->value, ret);
102 g_mutex_unlock (&rb_refstrings_mutex);
103 return ret;
104 }
105
106 /**
107 * rb_refstring_find:
108 * @init: string to find
109 *
110 * Returns an existing #RBRefString for @init if one exists,
111 * otherwise returns NULL. If an existing refstring is found,
112 * its reference count is increased.
113 *
114 * Return value: existing #RBRefString, or NULL
115 */
116 RBRefString *
117 rb_refstring_find (const char *init)
118 {
119 RBRefString *ret;
120
121 g_mutex_lock (&rb_refstrings_mutex);
122 ret = g_hash_table_lookup (rb_refstrings, init);
123
124 if (ret)
125 rb_refstring_ref (ret);
126
127 g_mutex_unlock (&rb_refstrings_mutex);
128 return ret;
129 }
130
131 /**
132 * rb_refstring_unref:
133 * @val: #RBRefString to unref
134 *
135 * Drops a reference to an #RBRefString. If this is the last
136 * reference, the string will be freed.
137 */
138 void
139 rb_refstring_unref (RBRefString *val)
140 {
141 if (val == NULL)
142 return;
143
144 g_return_if_fail (g_atomic_int_get (&val->refcount) > 0);
145
146 if (g_atomic_int_dec_and_test (&val->refcount)) {
147 g_mutex_lock (&rb_refstrings_mutex);
148 /* ensure it's still not referenced, as something may have called
149 * rb_refstring_new since we decremented the count */
150 if (g_atomic_int_get (&val->refcount) == 0)
151 g_hash_table_remove (rb_refstrings, val->value);
152 g_mutex_unlock (&rb_refstrings_mutex);
153 }
154 }
155
156 /**
157 * rb_refstring_system_shutdown:
158 *
159 * Frees all data associated with the refstring system.
160 * Only called on shutdown.
161 */
162 void
163 rb_refstring_system_shutdown (void)
164 {
165 g_hash_table_destroy (rb_refstrings);
166 }
167
168 /**
169 * rb_refstring_ref:
170 * @val: a #RBRefString to reference
171 *
172 * Increases the reference count for an existing #RBRefString.
173 * The refstring is returned for convenience.
174 *
175 * Return value: the same refstring
176 */
177 RBRefString *
178 rb_refstring_ref (RBRefString *val)
179 {
180 if (val == NULL)
181 return NULL;
182
183 g_return_val_if_fail (g_atomic_int_get (&val->refcount) > 0, NULL);
184
185 g_atomic_int_inc (&val->refcount);
186 return val;
187 }
188
189 /**
190 * rb_refstring_get:
191 * @val: an #RBRefString
192 *
193 * Returns the actual string for a #RBRefString.
194 *
195 * Return value: underlying string, must not be freed
196 */
197 const char *
198 rb_refstring_get (const RBRefString *val)
199 {
200 return val ? val->value : NULL;
201 }
202
203 /*
204 * The next two functions will compute the values if they haven't
205 * been already done. Using g_atomic_* is much more efficient than
206 * using mutexes (since mutexes may require kernel calls) and these
207 * get called often.
208 */
209
210 /**
211 * rb_refstring_get_folded:
212 * @val: an #RBRefString
213 *
214 * Returns the case-folded version of the string underlying @val.
215 * The case-folded string is cached inside the #RBRefString for
216 * speed. See @rb_search_fold for information on case-folding
217 * strings.
218 *
219 * Return value: case-folded string, must not be freed
220 */
221 const char *
222 rb_refstring_get_folded (RBRefString *val)
223 {
224 gpointer *ptr;
225 const char *string;
226
227 if (val == NULL)
228 return NULL;
229
230 ptr = &val->folded;
231 string = (const char*)g_atomic_pointer_get (ptr);
232 if (string == NULL) {
233 char *newstring;
234
235 newstring = rb_search_fold (rb_refstring_get (val));
236 if (g_atomic_pointer_compare_and_exchange (ptr, NULL, newstring)) {
237 string = newstring;
238 } else {
239 g_free (newstring);
240 string = (const char *)g_atomic_pointer_get (ptr);
241 g_assert (string);
242 }
243 }
244
245 return string;
246 }
247
248 /**
249 * rb_refstring_get_sort_key:
250 * @val: an #RBRefString
251 *
252 * Returns the sort key version of the string underlying @val.
253 * The sort key string is cached inside the #RBRefString for speed.
254 * Sort key strings are not generally human readable, so don't display
255 * them anywhere. See @g_utf8_collate_key_for_filename for information
256 * on sort keys.
257 *
258 * Return value: sort key string, must not be freed.
259 */
260 const char *
261 rb_refstring_get_sort_key (RBRefString *val)
262 {
263 gpointer *ptr;
264 const char *string;
265
266 if (val == NULL)
267 return NULL;
268
269 ptr = &val->sortkey;
270 string = (const char *)g_atomic_pointer_get (ptr);
271 if (string == NULL) {
272 char *newstring;
273 char *s;
274
275 s = g_utf8_casefold (val->value, -1);
276 newstring = g_utf8_collate_key_for_filename (s, -1);
277 g_free (s);
278
279 if (g_atomic_pointer_compare_and_exchange (ptr, NULL, newstring)) {
280 string = newstring;
281 } else {
282 g_free (newstring);
283 string = (const char*)g_atomic_pointer_get (ptr);
284 g_assert (string);
285 }
286 }
287
288 return string;
289 }
290
291 /**
292 * rb_refstring_hash:
293 * @p: an #RBRefString
294 *
295 * Hash function suitable for use with @GHashTable.
296 *
297 * Return value: hash value for the string underlying @p
298 */
299 guint
300 rb_refstring_hash (gconstpointer p)
301 {
302 const RBRefString *ref = p;
303 return g_str_hash (rb_refstring_get (ref));
304 }
305
306 /**
307 * rb_refstring_equal:
308 * @ap: an #RBRefString
309 * @bp: another #RBRefstring
310 *
311 * Key equality function suitable for use with #GHashTable.
312 * Equality checks for #RBRefString are just pointer comparisons,
313 * since there can only be one refstring for a given string.
314 *
315 * Return value: %TRUE if the strings are the same
316 */
317 gboolean
318 rb_refstring_equal (gconstpointer ap, gconstpointer bp)
319 {
320 return (ap == bp);
321 }
322
323 GType
324 rb_refstring_get_type (void)
325 {
326 static GType type = 0;
327
328 if (G_UNLIKELY (type == 0)) {
329 type = g_boxed_type_register_static ("RBRefString",
330 (GBoxedCopyFunc)rb_refstring_ref,
331 (GBoxedFreeFunc)rb_refstring_unref);
332 }
333
334 return type;
335 }