hythmbox-2.98/rhythmdb/rb-refstring.c

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 }