Coverage Report

Created: 2025-07-01 06:23

/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
}