00001 /* 00002 * Audacious 00003 * Copyright © 2009 William Pitcock <nenolod@atheme.org> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation; under version 3 of the License. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program. If not, see <http://www.gnu.org/licenses>. 00016 * 00017 * The Audacious team does not consider modular code linking to 00018 * Audacious or using our public API to be a derived work. 00019 */ 00020 00021 /* 00022 * Note: This code used to do some normalization of strings: conversion to 00023 * UTF-8, conversion of the empty string to NULL, and (optionally) conversion 00024 * to uppercase. However, because such conversions can change the length of the 00025 * string, they can lead to a double-free. 00026 * 00027 * Consider: 00028 * 00029 * stringpool_get is called twice with the same 99-character ISO-8859-1 string. 00030 * The string is short enough to be cached, so stringpool_get returns a cached, 00031 * 101-character UTF-8 string. stringpool_unref is then called twice 00032 * with the cached string. Now that it has been converted, it is too long to be 00033 * cached, so stringpool_unref simply frees it, twice. 00034 * 00035 * Therefore, it is essential for stringpool_get to return a string that is 00036 * exactly the same as the one passed it. 00037 * 00038 * --jlindgren 00039 */ 00040 00041 #include <glib.h> 00042 #include <mowgli.h> 00043 00044 #include "audstrings.h" 00045 00046 #define MAXLEN 100 00047 00048 static void 00049 noopcanon(gchar *str) 00050 { 00051 return; 00052 } 00053 00055 typedef struct { 00056 gint refcount; 00057 gchar *str; 00058 } PooledString; 00059 00060 static mowgli_patricia_t *stringpool_tree = NULL; 00061 static GStaticMutex stringpool_mutex = G_STATIC_MUTEX_INIT; 00062 00063 static inline gboolean stringpool_should_cache(const gchar *string) 00064 { 00065 const gchar *end = memchr(string, '\0', MAXLEN + 1); 00066 return end != NULL ? TRUE : FALSE; 00067 } 00068 00069 gchar * 00070 stringpool_get(const gchar *str) 00071 { 00072 PooledString *ps; 00073 00074 g_return_val_if_fail(str != NULL, NULL); 00075 00076 if (!stringpool_should_cache(str)) 00077 return g_strdup(str); 00078 00079 g_static_mutex_lock(&stringpool_mutex); 00080 00081 if (stringpool_tree == NULL) 00082 stringpool_tree = mowgli_patricia_create(noopcanon); 00083 00084 if ((ps = mowgli_patricia_retrieve(stringpool_tree, str)) != NULL) 00085 { 00086 ps->refcount++; 00087 00088 g_static_mutex_unlock(&stringpool_mutex); 00089 return ps->str; 00090 } 00091 00092 ps = g_slice_new0(PooledString); 00093 ps->refcount++; 00094 ps->str = g_strdup(str); 00095 mowgli_patricia_add(stringpool_tree, str, ps); 00096 00097 g_static_mutex_unlock(&stringpool_mutex); 00098 return ps->str; 00099 } 00100 00101 void 00102 stringpool_unref(gchar *str) 00103 { 00104 PooledString *ps; 00105 00106 g_return_if_fail(str != NULL); 00107 00108 if (!stringpool_should_cache(str)) 00109 { 00110 g_free(str); 00111 return; 00112 } 00113 00114 g_return_if_fail(stringpool_tree != NULL); 00115 00116 g_static_mutex_lock(&stringpool_mutex); 00117 00118 ps = mowgli_patricia_retrieve(stringpool_tree, str); 00119 if (ps != NULL && --ps->refcount <= 0) 00120 { 00121 mowgli_patricia_delete(stringpool_tree, str); 00122 g_free(ps->str); 00123 g_slice_free(PooledString, ps); 00124 } 00125 00126 g_static_mutex_unlock(&stringpool_mutex); 00127 }