Branch data Line data Source code
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 : : #include "config.h" 22 : : 23 : : #include "grefstring.h" 24 : : 25 : : #include "ghash.h" 26 : : #include "gmessages.h" 27 : : #include "grcbox.h" 28 : : #include "gthread.h" 29 : : 30 : : #include <string.h> 31 : : 32 : : /* A global table of refcounted strings; the hash table does not own 33 : : * the strings, just a pointer to them. Strings are interned as long 34 : : * as they are alive; once their reference count drops to zero, they 35 : : * are removed from the table 36 : : */ 37 : : G_LOCK_DEFINE_STATIC (interned_ref_strings); 38 : : static GHashTable *interned_ref_strings; 39 : : 40 : : /** 41 : : * g_ref_string_new: 42 : : * @str: (not nullable): a NUL-terminated string 43 : : * 44 : : * Creates a new reference counted string and copies the contents of @str 45 : : * into it. 46 : : * 47 : : * Returns: (transfer full) (not nullable): the newly created reference counted string 48 : : * 49 : : * Since: 2.58 50 : : */ 51 : : char * 52 : 5 : g_ref_string_new (const char *str) 53 : : { 54 : : char *res; 55 : : gsize len; 56 : : 57 : 5 : g_return_val_if_fail (str != NULL, NULL); 58 : : 59 : 5 : len = strlen (str); 60 : : 61 : 5 : res = (char *) g_atomic_rc_box_dup (sizeof (char) * len + 1, str); 62 : : 63 : 5 : return res; 64 : : } 65 : : 66 : : /** 67 : : * g_ref_string_new_len: 68 : : * @str: (not nullable): a string 69 : : * @len: length of @str to use, or -1 if @str is nul-terminated 70 : : * 71 : : * Creates a new reference counted string and copies the contents of @str 72 : : * into it, up to @len bytes. 73 : : * 74 : : * Since this function does not stop at nul bytes, it is the caller's 75 : : * responsibility to ensure that @str has at least @len addressable bytes. 76 : : * 77 : : * Returns: (transfer full) (not nullable): the newly created reference counted string 78 : : * 79 : : * Since: 2.58 80 : : */ 81 : : char * 82 : 3 : g_ref_string_new_len (const char *str, gssize len) 83 : : { 84 : : char *res; 85 : : 86 : 3 : g_return_val_if_fail (str != NULL, NULL); 87 : : 88 [ + + ]: 3 : if (len < 0) 89 : 1 : return g_ref_string_new (str); 90 : : 91 : : /* allocate then copy as str[len] may not be readable */ 92 : 2 : res = (char *) g_atomic_rc_box_alloc ((gsize) len + 1); 93 : 2 : memcpy (res, str, len); 94 : 2 : res[len] = '\0'; 95 : : 96 : 2 : return res; 97 : : } 98 : : 99 : : /* interned_str_equal: variant of g_str_equal() that compares 100 : : * pointers as well as contents; this avoids running strcmp() 101 : : * on arbitrarily long strings, as it's more likely to have 102 : : * g_ref_string_new_intern() being called on the same refcounted 103 : : * string instance, than on a different string with the same 104 : : * contents 105 : : */ 106 : : static gboolean 107 : 3 : interned_str_equal (gconstpointer v1, 108 : : gconstpointer v2) 109 : : { 110 : 3 : const char *str1 = v1; 111 : 3 : const char *str2 = v2; 112 : : 113 [ + + ]: 3 : if (v1 == v2) 114 : 2 : return TRUE; 115 : : 116 : 1 : return strcmp (str1, str2) == 0; 117 : : } 118 : : 119 : : /** 120 : : * g_ref_string_new_intern: 121 : : * @str: (not nullable): a NUL-terminated string 122 : : * 123 : : * Creates a new reference counted string and copies the content of @str 124 : : * into it. 125 : : * 126 : : * If you call this function multiple times with the same @str, or with 127 : : * the same contents of @str, it will return a new reference, instead of 128 : : * creating a new string. 129 : : * 130 : : * Returns: (transfer full) (not nullable): the newly created reference 131 : : * counted string, or a new reference to an existing string 132 : : * 133 : : * Since: 2.58 134 : : */ 135 : : char * 136 : 3 : g_ref_string_new_intern (const char *str) 137 : : { 138 : : char *res; 139 : : 140 : 3 : g_return_val_if_fail (str != NULL, NULL); 141 : : 142 : 3 : G_LOCK (interned_ref_strings); 143 : : 144 [ + + ]: 3 : if (G_UNLIKELY (interned_ref_strings == NULL)) 145 : 1 : interned_ref_strings = g_hash_table_new (g_str_hash, interned_str_equal); 146 : : 147 : 3 : res = g_hash_table_lookup (interned_ref_strings, str); 148 [ + + ]: 3 : if (res != NULL) 149 : : { 150 : : /* We acquire the reference while holding the lock, to 151 : : * avoid a potential race between releasing the lock on 152 : : * the hash table and another thread releasing the reference 153 : : * on the same string 154 : : */ 155 : 1 : g_atomic_rc_box_acquire (res); 156 : 1 : G_UNLOCK (interned_ref_strings); 157 : 1 : return res; 158 : : } 159 : : 160 : 2 : res = g_ref_string_new (str); 161 : 2 : g_hash_table_add (interned_ref_strings, res); 162 : 2 : G_UNLOCK (interned_ref_strings); 163 : : 164 : 2 : return res; 165 : : } 166 : : 167 : : /** 168 : : * g_ref_string_acquire: 169 : : * @str: a reference counted string 170 : : * 171 : : * Acquires a reference on a string. 172 : : * 173 : : * Returns: the given string, with its reference count increased 174 : : * 175 : : * Since: 2.58 176 : : */ 177 : : char * 178 : 1 : g_ref_string_acquire (char *str) 179 : : { 180 : 1 : g_return_val_if_fail (str != NULL, NULL); 181 : : 182 : 1 : return g_atomic_rc_box_acquire (str); 183 : : } 184 : : 185 : : static void 186 : 7 : remove_if_interned (gpointer data) 187 : : { 188 : 7 : char *str = data; 189 : : 190 : 7 : G_LOCK (interned_ref_strings); 191 : : 192 [ + + ]: 7 : if (G_LIKELY (interned_ref_strings != NULL)) 193 : : { 194 : 2 : g_hash_table_remove (interned_ref_strings, str); 195 : : 196 [ + + ]: 2 : if (g_hash_table_size (interned_ref_strings) == 0) 197 : 1 : g_clear_pointer (&interned_ref_strings, g_hash_table_destroy); 198 : : } 199 : : 200 : 7 : G_UNLOCK (interned_ref_strings); 201 : 7 : } 202 : : 203 : : /** 204 : : * g_ref_string_release: 205 : : * @str: a reference counted string 206 : : * 207 : : * Releases a reference on a string; if it was the last reference, the 208 : : * resources allocated by the string are freed as well. 209 : : * 210 : : * Since: 2.58 211 : : */ 212 : : void 213 : 9 : g_ref_string_release (char *str) 214 : : { 215 : 9 : g_return_if_fail (str != NULL); 216 : : 217 : 9 : g_atomic_rc_box_release_full (str, remove_if_interned); 218 : : } 219 : : 220 : : /** 221 : : * g_ref_string_length: 222 : : * @str: a reference counted string 223 : : * 224 : : * Retrieves the length of @str. 225 : : * 226 : : * Returns: the length of the given string, in bytes 227 : : * 228 : : * Since: 2.58 229 : : */ 230 : : gsize 231 : 4 : g_ref_string_length (char *str) 232 : : { 233 : 4 : g_return_val_if_fail (str != NULL, 0); 234 : : 235 : 4 : return g_atomic_rc_box_get_size (str) - 1; 236 : : }