| |
1 /** |
| |
2 * @file stringref.c Reference-counted immutable strings |
| |
3 * @ingroup core |
| |
4 * |
| |
5 * purple |
| |
6 * |
| |
7 * Purple is the legal property of its developers, whose names are too numerous |
| |
8 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
9 * source distribution. |
| |
10 * |
| |
11 * This program is free software; you can redistribute it and/or modify |
| |
12 * it under the terms of the GNU General Public License as published by |
| |
13 * the Free Software Foundation; either version 2 of the License, or |
| |
14 * (at your option) any later version. |
| |
15 * |
| |
16 * This program is distributed in the hope that it will be useful, |
| |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
19 * GNU General Public License for more details. |
| |
20 * |
| |
21 * You should have received a copy of the GNU General Public License |
| |
22 * along with this program; if not, write to the Free Software |
| |
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
24 * |
| |
25 */ |
| |
26 |
| |
27 #include "internal.h" |
| |
28 |
| |
29 #include <string.h> |
| |
30 #include <stdarg.h> |
| |
31 |
| |
32 #include "debug.h" |
| |
33 #include "stringref.h" |
| |
34 |
| |
35 /** |
| |
36 * The internal representation of a stringref. |
| |
37 * |
| |
38 * @note For this structure to be useful, the string contained within |
| |
39 * it must be immutable -- for this reason, do _not_ access it |
| |
40 * directly! |
| |
41 */ |
| |
42 struct _PurpleStringref { |
| |
43 guint32 ref; /**< The reference count of this string. |
| |
44 * Note that reference counts are only |
| |
45 * 31 bits, and the high-order bit |
| |
46 * indicates whether this string is up |
| |
47 * for GC at the next idle handler... |
| |
48 * But you aren't going to touch this |
| |
49 * anyway, right? */ |
| |
50 char value[1]; /**< The string contained in this ref. |
| |
51 * Notice that it is simply "hanging |
| |
52 * off the end" of the ref ... this |
| |
53 * is to save an allocation. */ |
| |
54 }; |
| |
55 |
| |
56 #define REFCOUNT(x) ((x) & 0x7fffffff) |
| |
57 |
| |
58 static GList *gclist = NULL; |
| |
59 |
| |
60 static void stringref_free(PurpleStringref *stringref); |
| |
61 static gboolean gs_idle_cb(gpointer data); |
| |
62 |
| |
63 PurpleStringref *purple_stringref_new(const char *value) |
| |
64 { |
| |
65 PurpleStringref *newref; |
| |
66 |
| |
67 if (value == NULL) |
| |
68 return NULL; |
| |
69 |
| |
70 newref = g_malloc(sizeof(PurpleStringref) + strlen(value)); |
| |
71 strcpy(newref->value, value); |
| |
72 newref->ref = 1; |
| |
73 |
| |
74 return newref; |
| |
75 } |
| |
76 |
| |
77 PurpleStringref *purple_stringref_new_noref(const char *value) |
| |
78 { |
| |
79 PurpleStringref *newref; |
| |
80 |
| |
81 if (value == NULL) |
| |
82 return NULL; |
| |
83 |
| |
84 newref = g_malloc(sizeof(PurpleStringref) + strlen(value)); |
| |
85 strcpy(newref->value, value); |
| |
86 newref->ref = 0x80000000; |
| |
87 |
| |
88 if (gclist == NULL) |
| |
89 g_idle_add(gs_idle_cb, NULL); |
| |
90 gclist = g_list_prepend(gclist, newref); |
| |
91 |
| |
92 return newref; |
| |
93 } |
| |
94 |
| |
95 PurpleStringref *purple_stringref_printf(const char *format, ...) |
| |
96 { |
| |
97 PurpleStringref *newref; |
| |
98 va_list ap; |
| |
99 |
| |
100 if (format == NULL) |
| |
101 return NULL; |
| |
102 |
| |
103 va_start(ap, format); |
| |
104 newref = g_malloc(sizeof(PurpleStringref) + g_printf_string_upper_bound(format, ap)); |
| |
105 vsprintf(newref->value, format, ap); |
| |
106 va_end(ap); |
| |
107 newref->ref = 1; |
| |
108 |
| |
109 return newref; |
| |
110 } |
| |
111 |
| |
112 PurpleStringref *purple_stringref_ref(PurpleStringref *stringref) |
| |
113 { |
| |
114 if (stringref == NULL) |
| |
115 return NULL; |
| |
116 stringref->ref++; |
| |
117 return stringref; |
| |
118 } |
| |
119 |
| |
120 void purple_stringref_unref(PurpleStringref *stringref) |
| |
121 { |
| |
122 if (stringref == NULL) |
| |
123 return; |
| |
124 if (REFCOUNT(--(stringref->ref)) == 0) { |
| |
125 if (stringref->ref & 0x80000000) |
| |
126 gclist = g_list_remove(gclist, stringref); |
| |
127 stringref_free(stringref); |
| |
128 } |
| |
129 } |
| |
130 |
| |
131 const char *purple_stringref_value(const PurpleStringref *stringref) |
| |
132 { |
| |
133 return (stringref == NULL ? NULL : stringref->value); |
| |
134 } |
| |
135 |
| |
136 int purple_stringref_cmp(const PurpleStringref *s1, const PurpleStringref *s2) |
| |
137 { |
| |
138 return (s1 == s2 ? 0 : strcmp(purple_stringref_value(s1), purple_stringref_value(s2))); |
| |
139 } |
| |
140 |
| |
141 size_t purple_stringref_len(const PurpleStringref *stringref) |
| |
142 { |
| |
143 return strlen(purple_stringref_value(stringref)); |
| |
144 } |
| |
145 |
| |
146 static void stringref_free(PurpleStringref *stringref) |
| |
147 { |
| |
148 #ifdef DEBUG |
| |
149 if (REFCOUNT(stringref->ref) != 0) { |
| |
150 purple_debug(PURPLE_DEBUG_ERROR, "stringref", "Free of nonzero (%d) ref stringref!\n", REFCOUNT(stringref->ref)); |
| |
151 return; |
| |
152 } |
| |
153 #endif /* DEBUG */ |
| |
154 g_free(stringref); |
| |
155 } |
| |
156 |
| |
157 static gboolean gs_idle_cb(gpointer data) |
| |
158 { |
| |
159 PurpleStringref *ref; |
| |
160 GList *del; |
| |
161 |
| |
162 while (gclist != NULL) { |
| |
163 ref = gclist->data; |
| |
164 if (REFCOUNT(ref->ref) == 0) { |
| |
165 stringref_free(ref); |
| |
166 } |
| |
167 del = gclist; |
| |
168 gclist = gclist->next; |
| |
169 g_list_free_1(del); |
| |
170 } |
| |
171 |
| |
172 return FALSE; |
| |
173 } |