LCOV - code coverage report
Current view: top level - glib/glib - grefstring.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 51 51 100.0 %
Date: 2024-04-30 05:17:35 Functions: 8 8 100.0 %
Branches: 12 12 100.0 %

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

Generated by: LCOV version 1.14