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