/src/glib/glib/grefstring.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* grefstring.c: Reference counted strings |
2 | | * |
3 | | * Copyright 2018 Emmanuele Bassi |
4 | | * |
5 | | * This library is free software; you can redistribute it and/or |
6 | | * modify it under the terms of the GNU Lesser General Public |
7 | | * License as published by the Free Software Foundation; either |
8 | | * version 2.1 of the License, or (at your option) any later version. |
9 | | * |
10 | | * This library is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | | * Lesser General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU Lesser General Public |
16 | | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | /** |
20 | | * SECTION:refstring |
21 | | * @Title: Reference counted strings |
22 | | * @Short_description: Strings with reference counted memory management |
23 | | * |
24 | | * Reference counted strings are normal C strings that have been augmented |
25 | | * with a reference counter to manage their resources. You allocate a new |
26 | | * reference counted string and acquire and release references as needed, |
27 | | * instead of copying the string among callers; when the last reference on |
28 | | * the string is released, the resources allocated for it are freed. |
29 | | * |
30 | | * Typically, reference counted strings can be used when parsing data from |
31 | | * files and storing them into data structures that are passed to various |
32 | | * callers: |
33 | | * |
34 | | * |[<!-- language="C" --> |
35 | | * PersonDetails * |
36 | | * person_details_from_data (const char *data) |
37 | | * { |
38 | | * // Use g_autoptr() to simplify error cases |
39 | | * g_autoptr(GRefString) full_name = NULL; |
40 | | * g_autoptr(GRefString) address = NULL; |
41 | | * g_autoptr(GRefString) city = NULL; |
42 | | * g_autoptr(GRefString) state = NULL; |
43 | | * g_autoptr(GRefString) zip_code = NULL; |
44 | | * |
45 | | * // parse_person_details() is defined elsewhere; returns refcounted strings |
46 | | * if (!parse_person_details (data, &full_name, &address, &city, &state, &zip_code)) |
47 | | * return NULL; |
48 | | * |
49 | | * if (!validate_zip_code (zip_code)) |
50 | | * return NULL; |
51 | | * |
52 | | * // add_address_to_cache() and add_full_name_to_cache() are defined |
53 | | * // elsewhere; they add strings to various caches, using refcounted |
54 | | * // strings to avoid copying data over and over again |
55 | | * add_address_to_cache (address, city, state, zip_code); |
56 | | * add_full_name_to_cache (full_name); |
57 | | * |
58 | | * // person_details_new() is defined elsewhere; it takes a reference |
59 | | * // on each string |
60 | | * PersonDetails *res = person_details_new (full_name, |
61 | | * address, |
62 | | * city, |
63 | | * state, |
64 | | * zip_code); |
65 | | * |
66 | | * return res; |
67 | | * } |
68 | | * ]| |
69 | | * |
70 | | * In the example above, we have multiple functions taking the same strings |
71 | | * for different uses; with typical C strings, we'd have to copy the strings |
72 | | * every time the life time rules of the data differ from the life time of |
73 | | * the string parsed from the original buffer. With reference counted strings, |
74 | | * each caller can take a reference on the data, and keep it as long as it |
75 | | * needs to own the string. |
76 | | * |
77 | | * Reference counted strings can also be "interned" inside a global table |
78 | | * owned by GLib; while an interned string has at least a reference, creating |
79 | | * a new interned reference counted string with the same contents will return |
80 | | * a reference to the existing string instead of creating a new reference |
81 | | * counted string instance. Once the string loses its last reference, it will |
82 | | * be automatically removed from the global interned strings table. |
83 | | * |
84 | | * Since: 2.58 |
85 | | */ |
86 | | |
87 | | #include "config.h" |
88 | | |
89 | | #include "grefstring.h" |
90 | | |
91 | | #include "ghash.h" |
92 | | #include "gmessages.h" |
93 | | #include "grcbox.h" |
94 | | #include "gthread.h" |
95 | | |
96 | | #include <string.h> |
97 | | |
98 | | /* A global table of refcounted strings; the hash table does not own |
99 | | * the strings, just a pointer to them. Strings are interned as long |
100 | | * as they are alive; once their reference count drops to zero, they |
101 | | * are removed from the table |
102 | | */ |
103 | | G_LOCK_DEFINE_STATIC (interned_ref_strings); |
104 | | static GHashTable *interned_ref_strings; |
105 | | |
106 | | /** |
107 | | * g_ref_string_new: |
108 | | * @str: (not nullable): a NUL-terminated string |
109 | | * |
110 | | * Creates a new reference counted string and copies the contents of @str |
111 | | * into it. |
112 | | * |
113 | | * Returns: (transfer full) (not nullable): the newly created reference counted string |
114 | | * |
115 | | * Since: 2.58 |
116 | | */ |
117 | | char * |
118 | | g_ref_string_new (const char *str) |
119 | 0 | { |
120 | 0 | char *res; |
121 | 0 | gsize len; |
122 | |
|
123 | 0 | g_return_val_if_fail (str != NULL, NULL); |
124 | | |
125 | 0 | len = strlen (str); |
126 | | |
127 | 0 | res = (char *) g_atomic_rc_box_dup (sizeof (char) * len + 1, str); |
128 | |
|
129 | 0 | return res; |
130 | 0 | } |
131 | | |
132 | | /** |
133 | | * g_ref_string_new_len: |
134 | | * @str: (not nullable): a string |
135 | | * @len: length of @str to use, or -1 if @str is nul-terminated |
136 | | * |
137 | | * Creates a new reference counted string and copies the contents of @str |
138 | | * into it, up to @len bytes. |
139 | | * |
140 | | * Since this function does not stop at nul bytes, it is the caller's |
141 | | * responsibility to ensure that @str has at least @len addressable bytes. |
142 | | * |
143 | | * Returns: (transfer full) (not nullable): the newly created reference counted string |
144 | | * |
145 | | * Since: 2.58 |
146 | | */ |
147 | | char * |
148 | | g_ref_string_new_len (const char *str, gssize len) |
149 | 0 | { |
150 | 0 | char *res; |
151 | |
|
152 | 0 | g_return_val_if_fail (str != NULL, NULL); |
153 | | |
154 | 0 | if (len < 0) |
155 | 0 | return g_ref_string_new (str); |
156 | | |
157 | | /* allocate then copy as str[len] may not be readable */ |
158 | 0 | res = (char *) g_atomic_rc_box_alloc ((gsize) len + 1); |
159 | 0 | memcpy (res, str, len); |
160 | 0 | res[len] = '\0'; |
161 | |
|
162 | 0 | return res; |
163 | 0 | } |
164 | | |
165 | | /* interned_str_equal: variant of g_str_equal() that compares |
166 | | * pointers as well as contents; this avoids running strcmp() |
167 | | * on arbitrarily long strings, as it's more likely to have |
168 | | * g_ref_string_new_intern() being called on the same refcounted |
169 | | * string instance, than on a different string with the same |
170 | | * contents |
171 | | */ |
172 | | static gboolean |
173 | | interned_str_equal (gconstpointer v1, |
174 | | gconstpointer v2) |
175 | 0 | { |
176 | 0 | const char *str1 = v1; |
177 | 0 | const char *str2 = v2; |
178 | |
|
179 | 0 | if (v1 == v2) |
180 | 0 | return TRUE; |
181 | | |
182 | 0 | return strcmp (str1, str2) == 0; |
183 | 0 | } |
184 | | |
185 | | /** |
186 | | * g_ref_string_new_intern: |
187 | | * @str: (not nullable): a NUL-terminated string |
188 | | * |
189 | | * Creates a new reference counted string and copies the content of @str |
190 | | * into it. |
191 | | * |
192 | | * If you call this function multiple times with the same @str, or with |
193 | | * the same contents of @str, it will return a new reference, instead of |
194 | | * creating a new string. |
195 | | * |
196 | | * Returns: (transfer full) (not nullable): the newly created reference |
197 | | * counted string, or a new reference to an existing string |
198 | | * |
199 | | * Since: 2.58 |
200 | | */ |
201 | | char * |
202 | | g_ref_string_new_intern (const char *str) |
203 | 0 | { |
204 | 0 | char *res; |
205 | |
|
206 | 0 | g_return_val_if_fail (str != NULL, NULL); |
207 | | |
208 | 0 | G_LOCK (interned_ref_strings); |
209 | |
|
210 | 0 | if (G_UNLIKELY (interned_ref_strings == NULL)) |
211 | 0 | interned_ref_strings = g_hash_table_new (g_str_hash, interned_str_equal); |
212 | |
|
213 | 0 | res = g_hash_table_lookup (interned_ref_strings, str); |
214 | 0 | if (res != NULL) |
215 | 0 | { |
216 | | /* We acquire the reference while holding the lock, to |
217 | | * avoid a potential race between releasing the lock on |
218 | | * the hash table and another thread releasing the reference |
219 | | * on the same string |
220 | | */ |
221 | 0 | g_atomic_rc_box_acquire (res); |
222 | 0 | G_UNLOCK (interned_ref_strings); |
223 | 0 | return res; |
224 | 0 | } |
225 | | |
226 | 0 | res = g_ref_string_new (str); |
227 | 0 | g_hash_table_add (interned_ref_strings, res); |
228 | 0 | G_UNLOCK (interned_ref_strings); |
229 | |
|
230 | 0 | return res; |
231 | 0 | } |
232 | | |
233 | | /** |
234 | | * g_ref_string_acquire: |
235 | | * @str: a reference counted string |
236 | | * |
237 | | * Acquires a reference on a string. |
238 | | * |
239 | | * Returns: the given string, with its reference count increased |
240 | | * |
241 | | * Since: 2.58 |
242 | | */ |
243 | | char * |
244 | | g_ref_string_acquire (char *str) |
245 | 0 | { |
246 | 0 | g_return_val_if_fail (str != NULL, NULL); |
247 | | |
248 | 0 | return g_atomic_rc_box_acquire (str); |
249 | 0 | } |
250 | | |
251 | | static void |
252 | | remove_if_interned (gpointer data) |
253 | 0 | { |
254 | 0 | char *str = data; |
255 | |
|
256 | 0 | G_LOCK (interned_ref_strings); |
257 | |
|
258 | 0 | if (G_LIKELY (interned_ref_strings != NULL)) |
259 | 0 | { |
260 | 0 | g_hash_table_remove (interned_ref_strings, str); |
261 | |
|
262 | 0 | if (g_hash_table_size (interned_ref_strings) == 0) |
263 | 0 | g_clear_pointer (&interned_ref_strings, g_hash_table_destroy); |
264 | 0 | } |
265 | |
|
266 | 0 | G_UNLOCK (interned_ref_strings); |
267 | 0 | } |
268 | | |
269 | | /** |
270 | | * g_ref_string_release: |
271 | | * @str: a reference counted string |
272 | | * |
273 | | * Releases a reference on a string; if it was the last reference, the |
274 | | * resources allocated by the string are freed as well. |
275 | | * |
276 | | * Since: 2.58 |
277 | | */ |
278 | | void |
279 | | g_ref_string_release (char *str) |
280 | 0 | { |
281 | 0 | g_return_if_fail (str != NULL); |
282 | | |
283 | 0 | g_atomic_rc_box_release_full (str, remove_if_interned); |
284 | 0 | } |
285 | | |
286 | | /** |
287 | | * g_ref_string_length: |
288 | | * @str: a reference counted string |
289 | | * |
290 | | * Retrieves the length of @str. |
291 | | * |
292 | | * Returns: the length of the given string, in bytes |
293 | | * |
294 | | * Since: 2.58 |
295 | | */ |
296 | | gsize |
297 | | g_ref_string_length (char *str) |
298 | 0 | { |
299 | 0 | g_return_val_if_fail (str != NULL, 0); |
300 | | |
301 | 0 | return g_atomic_rc_box_get_size (str) - 1; |
302 | 0 | } |