diff --git a/glib/Makefile.am b/glib/Makefile.am index 4f31d53d4..276a60b30 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -168,6 +168,7 @@ libglib_2_0_la_SOURCES = \ gpoll.c \ gprimes.c \ gqsort.c \ + gquark.c \ gqueue.c \ grand.c \ gregex.c \ diff --git a/glib/gdataset.c b/glib/gdataset.c index 80f5094b9..7248352c7 100644 --- a/glib/gdataset.c +++ b/glib/gdataset.c @@ -185,10 +185,8 @@ static inline gpointer g_data_set_internal (GData **datalist, GDestroyNotify destroy_func, GDataset *dataset); static void g_data_initialize (void); -static inline GQuark g_quark_new (gchar *string); - -/* Locking model: +/* Locking model: * Each standalone GDataList is protected by a bitlock in the datalist pointer, * which protects that modification of the non-flags part of the datalist pointer * and the contents of the datalist. @@ -204,10 +202,6 @@ G_LOCK_DEFINE_STATIC (g_dataset_global); static GHashTable *g_dataset_location_ht = NULL; static GDataset *g_dataset_cached = NULL; /* should this be thread specific? */ -G_LOCK_DEFINE_STATIC (g_quark_global); -static GHashTable *g_quark_ht = NULL; -static gchar **g_quarks = NULL; -static int g_quark_seq_id = 0; /* --- functions --- */ @@ -1069,294 +1063,3 @@ g_data_initialize (void) g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL); g_dataset_cached = NULL; } - -/** - * SECTION:quarks - * @title: Quarks - * @short_description: a 2-way association between a string and a - * unique integer identifier - * - * Quarks are associations between strings and integer identifiers. - * Given either the string or the #GQuark identifier it is possible to - * retrieve the other. - * - * Quarks are used for both Datasets and Keyed Data Lists. - * - * To create a new quark from a string, use g_quark_from_string() or - * g_quark_from_static_string(). - * - * To find the string corresponding to a given #GQuark, use - * g_quark_to_string(). - * - * To find the #GQuark corresponding to a given string, use - * g_quark_try_string(). - * - * Another use for the string pool maintained for the quark functions - * is string interning, using g_intern_string() or - * g_intern_static_string(). An interned string is a canonical - * representation for a string. One important advantage of interned - * strings is that they can be compared for equality by a simple - * pointer comparison, rather than using strcmp(). - **/ - -/** - * GQuark: - * - * A GQuark is a non-zero integer which uniquely identifies a - * particular string. A GQuark value of zero is associated to %NULL. - **/ - -/** - * g_quark_try_string: - * @string: (allow-none): a string. - * @Returns: the #GQuark associated with the string, or 0 if @string is - * %NULL or there is no #GQuark associated with it. - * - * Gets the #GQuark associated with the given string, or 0 if string is - * %NULL or it has no associated #GQuark. - * - * If you want the GQuark to be created if it doesn't already exist, - * use g_quark_from_string() or g_quark_from_static_string(). - **/ -GQuark -g_quark_try_string (const gchar *string) -{ - GQuark quark = 0; - - if (string == NULL) - return 0; - - G_LOCK (g_quark_global); - if (g_quark_ht) - quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string)); - G_UNLOCK (g_quark_global); - - return quark; -} - -#define QUARK_STRING_BLOCK_SIZE (4096 - sizeof (gsize)) -static char *quark_block = NULL; -static int quark_block_offset = 0; - -/* HOLDS: g_quark_global_lock */ -static char * -quark_strdup(const gchar *string) -{ - gchar *copy; - gsize len; - - len = strlen (string) + 1; - - /* For strings longer than half the block size, fall back - to strdup so that we fill our blocks at least 50%. */ - if (len > QUARK_STRING_BLOCK_SIZE / 2) - return g_strdup (string); - - if (quark_block == NULL || - QUARK_STRING_BLOCK_SIZE - quark_block_offset < len) - { - quark_block = g_malloc (QUARK_STRING_BLOCK_SIZE); - quark_block_offset = 0; - } - - copy = quark_block + quark_block_offset; - memcpy (copy, string, len); - quark_block_offset += len; - - return copy; -} - -/* HOLDS: g_quark_global_lock */ -static inline GQuark -g_quark_from_string_internal (const gchar *string, - gboolean duplicate) -{ - GQuark quark = 0; - - if (g_quark_ht) - quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string)); - - if (!quark) - { - quark = g_quark_new (duplicate ? quark_strdup (string) : (gchar *)string); - TRACE(GLIB_QUARK_NEW(string, quark)); - } - - return quark; -} - -/** - * g_quark_from_string: - * @string: (allow-none): a string. - * @Returns: the #GQuark identifying the string, or 0 if @string is - * %NULL. - * - * Gets the #GQuark identifying the given string. If the string does - * not currently have an associated #GQuark, a new #GQuark is created, - * using a copy of the string. - **/ -GQuark -g_quark_from_string (const gchar *string) -{ - GQuark quark; - - if (!string) - return 0; - - G_LOCK (g_quark_global); - quark = g_quark_from_string_internal (string, TRUE); - G_UNLOCK (g_quark_global); - - return quark; -} - -/** - * g_quark_from_static_string: - * @string: (allow-none): a string. - * @Returns: the #GQuark identifying the string, or 0 if @string is - * %NULL. - * - * Gets the #GQuark identifying the given (static) string. If the - * string does not currently have an associated #GQuark, a new #GQuark - * is created, linked to the given string. - * - * Note that this function is identical to g_quark_from_string() except - * that if a new #GQuark is created the string itself is used rather - * than a copy. This saves memory, but can only be used if the string - * will always exist. It can be used with - * statically allocated strings in the main program, but not with - * statically allocated memory in dynamically loaded modules, if you - * expect to ever unload the module again (e.g. do not use this - * function in GTK+ theme engines). - **/ -GQuark -g_quark_from_static_string (const gchar *string) -{ - GQuark quark; - - if (!string) - return 0; - - G_LOCK (g_quark_global); - quark = g_quark_from_string_internal (string, FALSE); - G_UNLOCK (g_quark_global); - - return quark; -} - -/** - * g_quark_to_string: - * @quark: a #GQuark. - * @Returns: the string associated with the #GQuark. - * - * Gets the string associated with the given #GQuark. - **/ -const gchar * -g_quark_to_string (GQuark quark) -{ - gchar* result = NULL; - gchar **quarks; - gint quark_seq_id; - - quark_seq_id = g_atomic_int_get (&g_quark_seq_id); - quarks = g_atomic_pointer_get (&g_quarks); - - if (quark < quark_seq_id) - result = quarks[quark]; - - return result; -} - -/* HOLDS: g_quark_global_lock */ -static inline GQuark -g_quark_new (gchar *string) -{ - GQuark quark; - gchar **g_quarks_new; - - if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0) - { - g_quarks_new = g_new (gchar*, g_quark_seq_id + G_QUARK_BLOCK_SIZE); - if (g_quark_seq_id != 0) - memcpy (g_quarks_new, g_quarks, sizeof (char *) * g_quark_seq_id); - memset (g_quarks_new + g_quark_seq_id, 0, sizeof (char *) * G_QUARK_BLOCK_SIZE); - /* This leaks the old quarks array. Its unfortunate, but it allows - us to do lockless lookup of the arrays, and there shouldn't be that - many quarks in an app */ - g_atomic_pointer_set (&g_quarks, g_quarks_new); - } - if (!g_quark_ht) - { - g_assert (g_quark_seq_id == 0); - g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal); - g_quarks[g_quark_seq_id] = NULL; - g_atomic_int_inc (&g_quark_seq_id); - } - - quark = g_quark_seq_id; - g_atomic_pointer_set (&g_quarks[quark], string); - g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark)); - g_atomic_int_inc (&g_quark_seq_id); - - return quark; -} - -/** - * g_intern_string: - * @string: (allow-none): a string - * - * Returns a canonical representation for @string. Interned strings can - * be compared for equality by comparing the pointers, instead of using strcmp(). - * - * Returns: a canonical representation for the string - * - * Since: 2.10 - */ -const gchar * -g_intern_string (const gchar *string) -{ - const gchar *result; - GQuark quark; - - if (!string) - return NULL; - - G_LOCK (g_quark_global); - quark = g_quark_from_string_internal (string, TRUE); - result = g_quarks[quark]; - G_UNLOCK (g_quark_global); - - return result; -} - -/** - * g_intern_static_string: - * @string: (allow-none): a static string - * - * Returns a canonical representation for @string. Interned strings can - * be compared for equality by comparing the pointers, instead of using strcmp(). - * g_intern_static_string() does not copy the string, therefore @string must - * not be freed or modified. - * - * Returns: a canonical representation for the string - * - * Since: 2.10 - */ -const gchar * -g_intern_static_string (const gchar *string) -{ - GQuark quark; - const gchar *result; - - if (!string) - return NULL; - - G_LOCK (g_quark_global); - quark = g_quark_from_string_internal (string, FALSE); - result = g_quarks[quark]; - G_UNLOCK (g_quark_global); - - return result; -} diff --git a/glib/gquark.c b/glib/gquark.c new file mode 100644 index 000000000..2afbf8aff --- /dev/null +++ b/glib/gquark.c @@ -0,0 +1,347 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * Copyright (C) 1998 Tim Janik + * + * gquark.c: Functions for dealing with quarks and interned strings + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/* + * MT safe + */ + +#include "config.h" + +#include + +#include "gslice.h" +#include "ghash.h" +#include "gquark.h" +#include "gstrfuncs.h" +#include "gthread.h" +#include "gtestutils.h" +#include "glib_trace.h" + +#define QUARK_BLOCK_SIZE 2048 +#define QUARK_STRING_BLOCK_SIZE (4096 - sizeof (gsize)) + +static inline GQuark quark_new (gchar *string); + +G_LOCK_DEFINE_STATIC (quark_global); +static GHashTable *quark_ht = NULL; +static gchar **quarks = NULL; +static gint quark_seq_id = 0; +static gchar *quark_block = NULL; +static gint quark_block_offset = 0; + +/** + * SECTION:quarks + * @title: Quarks + * @short_description: a 2-way association between a string and a + * unique integer identifier + * + * Quarks are associations between strings and integer identifiers. + * Given either the string or the #GQuark identifier it is possible to + * retrieve the other. + * + * Quarks are used for both Datasets and Keyed Data Lists. + * + * To create a new quark from a string, use g_quark_from_string() or + * g_quark_from_static_string(). + * + * To find the string corresponding to a given #GQuark, use + * g_quark_to_string(). + * + * To find the #GQuark corresponding to a given string, use + * g_quark_try_string(). + * + * Another use for the string pool maintained for the quark functions + * is string interning, using g_intern_string() or + * g_intern_static_string(). An interned string is a canonical + * representation for a string. One important advantage of interned + * strings is that they can be compared for equality by a simple + * pointer comparison, rather than using strcmp(). + **/ + +/** + * GQuark: + * + * A GQuark is a non-zero integer which uniquely identifies a + * particular string. A GQuark value of zero is associated to %NULL. + **/ + +/** + * g_quark_try_string: + * @string: (allow-none): a string. + * @Returns: the #GQuark associated with the string, or 0 if @string is + * %NULL or there is no #GQuark associated with it. + * + * Gets the #GQuark associated with the given string, or 0 if string is + * %NULL or it has no associated #GQuark. + * + * If you want the GQuark to be created if it doesn't already exist, + * use g_quark_from_string() or g_quark_from_static_string(). + **/ +GQuark +g_quark_try_string (const gchar *string) +{ + GQuark quark = 0; + + if (string == NULL) + return 0; + + G_LOCK (quark_global); + if (quark_ht) + quark = GPOINTER_TO_UINT (g_hash_table_lookup (quark_ht, string)); + G_UNLOCK (quark_global); + + return quark; +} + +/* HOLDS: quark_global_lock */ +static char * +quark_strdup (const gchar *string) +{ + gchar *copy; + gsize len; + + len = strlen (string) + 1; + + /* For strings longer than half the block size, fall back + to strdup so that we fill our blocks at least 50%. */ + if (len > QUARK_STRING_BLOCK_SIZE / 2) + return g_strdup (string); + + if (quark_block == NULL || + QUARK_STRING_BLOCK_SIZE - quark_block_offset < len) + { + quark_block = g_malloc (QUARK_STRING_BLOCK_SIZE); + quark_block_offset = 0; + } + + copy = quark_block + quark_block_offset; + memcpy (copy, string, len); + quark_block_offset += len; + + return copy; +} + +/* HOLDS: quark_global_lock */ +static inline GQuark +quark_from_string (const gchar *string, + gboolean duplicate) +{ + GQuark quark = 0; + + if (quark_ht) + quark = GPOINTER_TO_UINT (g_hash_table_lookup (quark_ht, string)); + + if (!quark) + { + quark = quark_new (duplicate ? quark_strdup (string) : (gchar *)string); + TRACE(GLIB_QUARK_NEW(string, quark)); + } + + return quark; +} + +/** + * g_quark_from_string: + * @string: (allow-none): a string. + * + * Gets the #GQuark identifying the given string. If the string does + * not currently have an associated #GQuark, a new #GQuark is created, + * using a copy of the string. + * + * Returns: the #GQuark identifying the string, or 0 if @string is + * %NULL. + */ +GQuark +g_quark_from_string (const gchar *string) +{ + GQuark quark; + + if (!string) + return 0; + + G_LOCK (quark_global); + quark = quark_from_string (string, TRUE); + G_UNLOCK (quark_global); + + return quark; +} + +/** + * g_quark_from_static_string: + * @string: (allow-none): a string. + * + * Gets the #GQuark identifying the given (static) string. If the + * string does not currently have an associated #GQuark, a new #GQuark + * is created, linked to the given string. + * + * Note that this function is identical to g_quark_from_string() except + * that if a new #GQuark is created the string itself is used rather + * than a copy. This saves memory, but can only be used if the string + * will always exist. It can be used with + * statically allocated strings in the main program, but not with + * statically allocated memory in dynamically loaded modules, if you + * expect to ever unload the module again (e.g. do not use this + * function in GTK+ theme engines). + * + * Returns: the #GQuark identifying the string, or 0 if @string is + * %NULL. + */ +GQuark +g_quark_from_static_string (const gchar *string) +{ + GQuark quark; + + if (!string) + return 0; + + G_LOCK (quark_global); + quark = quark_from_string (string, FALSE); + G_UNLOCK (quark_global); + + return quark; +} + +/** + * g_quark_to_string: + * @quark: a #GQuark. + * + * Gets the string associated with the given #GQuark. + * + * Returns: the string associated with the #GQuark + */ +const gchar * +g_quark_to_string (GQuark quark) +{ + gchar* result = NULL; + gchar **strings; + gint seq_id; + + seq_id = g_atomic_int_get (&quark_seq_id); + strings = g_atomic_pointer_get (&quarks); + + if (quark < seq_id) + result = strings[quark]; + + return result; +} + +/* HOLDS: g_quark_global_lock */ +static inline GQuark +quark_new (gchar *string) +{ + GQuark quark; + gchar **quarks_new; + + if (quark_seq_id % QUARK_BLOCK_SIZE == 0) + { + quarks_new = g_new (gchar*, quark_seq_id + QUARK_BLOCK_SIZE); + if (quark_seq_id != 0) + memcpy (quarks_new, quarks, sizeof (char *) * quark_seq_id); + memset (quarks_new + quark_seq_id, 0, sizeof (char *) * QUARK_BLOCK_SIZE); + /* This leaks the old quarks array. Its unfortunate, but it allows + * us to do lockless lookup of the arrays, and there shouldn't be that + * many quarks in an app + */ + g_atomic_pointer_set (&quarks, quarks_new); + } + if (!quark_ht) + { + g_assert (quark_seq_id == 0); + quark_ht = g_hash_table_new (g_str_hash, g_str_equal); + quarks[quark_seq_id] = NULL; + g_atomic_int_inc (&quark_seq_id); + } + + quark = quark_seq_id; + g_atomic_pointer_set (&quarks[quark], string); + g_hash_table_insert (quark_ht, string, GUINT_TO_POINTER (quark)); + g_atomic_int_inc (&quark_seq_id); + + return quark; +} + +/** + * g_intern_string: + * @string: (allow-none): a string + * + * Returns a canonical representation for @string. Interned strings + * can be compared for equality by comparing the pointers, instead of + * using strcmp(). + * + * Returns: a canonical representation for the string + * + * Since: 2.10 + */ +const gchar * +g_intern_string (const gchar *string) +{ + const gchar *result; + GQuark quark; + + if (!string) + return NULL; + + G_LOCK (quark_global); + quark = quark_from_string (string, TRUE); + result = quarks[quark]; + G_UNLOCK (quark_global); + + return result; +} + +/** + * g_intern_static_string: + * @string: (allow-none): a static string + * + * Returns a canonical representation for @string. Interned strings + * can be compared for equality by comparing the pointers, instead of + * using strcmp(). g_intern_static_string() does not copy the string, + * therefore @string must not be freed or modified. + * + * Returns: a canonical representation for the string + * + * Since: 2.10 + */ +const gchar * +g_intern_static_string (const gchar *string) +{ + GQuark quark; + const gchar *result; + + if (!string) + return NULL; + + G_LOCK (quark_global); + quark = quark_from_string (string, FALSE); + result = quarks[quark]; + G_UNLOCK (quark_global); + + return result; +}