(root)/
glib-2.79.0/
glib/
grefstring.c
       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  g_ref_string_new (const char *str)
      53  {
      54    char *res;
      55    gsize len;
      56  
      57    g_return_val_if_fail (str != NULL, NULL);
      58    
      59    len = strlen (str);
      60    
      61    res = (char *) g_atomic_rc_box_dup (sizeof (char) * len + 1, str);
      62  
      63    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  g_ref_string_new_len (const char *str, gssize len)
      83  {
      84    char *res;
      85  
      86    g_return_val_if_fail (str != NULL, NULL);
      87  
      88    if (len < 0)
      89      return g_ref_string_new (str);
      90  
      91    /* allocate then copy as str[len] may not be readable */
      92    res = (char *) g_atomic_rc_box_alloc ((gsize) len + 1);
      93    memcpy (res, str, len);
      94    res[len] = '\0';
      95  
      96    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  interned_str_equal (gconstpointer v1,
     108                      gconstpointer v2)
     109  {
     110    const char *str1 = v1;
     111    const char *str2 = v2;
     112  
     113    if (v1 == v2)
     114      return TRUE;
     115  
     116    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  g_ref_string_new_intern (const char *str)
     137  {
     138    char *res;
     139  
     140    g_return_val_if_fail (str != NULL, NULL);
     141  
     142    G_LOCK (interned_ref_strings);
     143  
     144    if (G_UNLIKELY (interned_ref_strings == NULL))
     145      interned_ref_strings = g_hash_table_new (g_str_hash, interned_str_equal);
     146  
     147    res = g_hash_table_lookup (interned_ref_strings, str);
     148    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        g_atomic_rc_box_acquire (res);
     156        G_UNLOCK (interned_ref_strings);
     157        return res;
     158      }
     159  
     160    res = g_ref_string_new (str);
     161    g_hash_table_add (interned_ref_strings, res);
     162    G_UNLOCK (interned_ref_strings);
     163  
     164    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  g_ref_string_acquire (char *str)
     179  {
     180    g_return_val_if_fail (str != NULL, NULL);
     181  
     182    return g_atomic_rc_box_acquire (str);
     183  }
     184  
     185  static void
     186  remove_if_interned (gpointer data)
     187  {
     188    char *str = data;
     189  
     190    G_LOCK (interned_ref_strings);
     191  
     192    if (G_LIKELY (interned_ref_strings != NULL))
     193      {
     194        g_hash_table_remove (interned_ref_strings, str);
     195  
     196        if (g_hash_table_size (interned_ref_strings) == 0)
     197          g_clear_pointer (&interned_ref_strings, g_hash_table_destroy);
     198      }
     199  
     200    G_UNLOCK (interned_ref_strings);
     201  }
     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  g_ref_string_release (char *str)
     214  {
     215    g_return_if_fail (str != NULL);
     216  
     217    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  g_ref_string_length (char *str)
     232  {
     233    g_return_val_if_fail (str != NULL, 0);
     234  
     235    return g_atomic_rc_box_get_size (str) - 1;
     236  }