diff -r 000000000000 -r e4d67989cc36 glib/libglib/src/gdataset.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/glib/libglib/src/gdataset.c Tue Feb 02 02:01:42 2010 +0200 @@ -0,0 +1,794 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * gdataset.c: Generic dataset mechanism, similar to GtkObject data. + * Copyright (C) 1998 Tim Janik + * Portions copyright (c) 2006 Nokia Corporation. All rights reserved. + * + * 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 ; except for g_data*_foreach() + */ + +#include "config.h" + +#include + +#include "glib.h" +#include "galias.h" +#include "gdatasetprivate.h" + +#ifdef __SYMBIAN32__ +#include +#endif /* __SYMBIAN32__ */ + +#if EMULATOR +#define g_thread_functions_for_glib_use (*_g_thread_functions_for_glib_use()) +#define g_thread_use_default_impl (*_g_thread_use_default_impl()) +#endif /* EMULATOR */ + +/* --- defines --- */ +#define G_QUARK_BLOCK_SIZE (512) + +/* datalist pointer accesses vae to be carried out atomically */ +#define G_DATALIST_GET_POINTER(datalist) \ + ((GData*) ((gsize) g_atomic_pointer_get ((gpointer*) datalist) & ~(gsize) G_DATALIST_FLAGS_MASK)) + +#define G_DATALIST_SET_POINTER(datalist, pointer) G_STMT_START { \ + gpointer _oldv, _newv; \ + do { \ + _oldv = g_atomic_pointer_get (datalist); \ + _newv = (gpointer) (((gsize) _oldv & G_DATALIST_FLAGS_MASK) | (gsize) pointer); \ + } while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, _oldv, _newv)); \ +} G_STMT_END + +/* --- structures --- */ +#if !(EMULATOR) +typedef struct _GDataset GDataset; +#endif /* EMULATOR */ +struct _GData +{ + GData *next; + GQuark id; + gpointer data; + GDestroyNotify destroy_func; +}; + +#if !(EMULATOR) +struct _GDataset +{ + gconstpointer location; + GData *datalist; +}; +#endif /* EMULATOR */ + + +/* --- prototypes --- */ +static inline GDataset* g_dataset_lookup (gconstpointer dataset_location); +static inline void g_datalist_clear_i (GData **datalist); +static void g_dataset_destroy_internal (GDataset *dataset); +static inline gpointer g_data_set_internal (GData **datalist, + GQuark key_id, + gpointer data, + GDestroyNotify destroy_func, + GDataset *dataset); +static void g_data_initialize (void); +static inline GQuark g_quark_new (gchar *string); + + +/* --- variables --- */ +#if EMULATOR + +PLS_MACRO(g_dataset_global,gdataset,GStaticMutex) +PLS(g_dataset_location_ht,gdataset,GHashTable *) +PLS(g_dataset_cached,gdataset,GDataset *) +PLS_MACRO(g_quark_global,gdataset,GStaticMutex) +PLS(g_quark_ht,gdataset,GHashTable *) +PLS(g_quarks,gdataset,gchar **) +PLS(g_quark_seq_id,gdataset,GQuark) + +#define g__g_dataset_global_lock (*FUNCTION_NAME_MACRO(g_dataset_global,gdataset)()) +#define g_dataset_location_ht (*FUNCTION_NAME(g_dataset_location_ht,gdataset)()) +#define g_dataset_cached (*FUNCTION_NAME(g_dataset_cached,gdataset)()) +#define g__g_quark_global_lock (*FUNCTION_NAME_MACRO(g_quark_global,gdataset)()) +#define g_quark_ht (*FUNCTION_NAME(g_quark_ht,gdataset)()) +#define g_quarks (*FUNCTION_NAME(g_quarks,gdataset)()) +#define g_quark_seq_id (*FUNCTION_NAME(g_quark_seq_id,gdataset)()) + +#else + +G_LOCK_DEFINE_STATIC (g_dataset_global); +static GHashTable *g_dataset_location_ht = NULL; +static GDataset *g_dataset_cached = NULL; /* should this be + threadspecific? */ +G_LOCK_DEFINE_STATIC (g_quark_global); +static GHashTable *g_quark_ht = NULL; +static gchar **g_quarks = NULL; +static GQuark g_quark_seq_id = 0; + +#endif /* EMULATOR */ + +/* --- functions --- */ + +/* HOLDS: g_dataset_global_lock */ +static inline void +g_datalist_clear_i (GData **datalist) +{ + register GData *list; + + /* unlink *all* items before walking their destructors + */ + list = G_DATALIST_GET_POINTER (datalist); + G_DATALIST_SET_POINTER (datalist, NULL); + + while (list) + { + register GData *prev; + + prev = list; + list = prev->next; + + if (prev->destroy_func) + { + G_UNLOCK (g_dataset_global); + prev->destroy_func (prev->data); + G_LOCK (g_dataset_global); + } + + g_slice_free (GData, prev); + } +} + +EXPORT_C void +g_datalist_clear (GData **datalist) +{ + g_return_if_fail (datalist != NULL); + + G_LOCK (g_dataset_global); + if (!g_dataset_location_ht) + g_data_initialize (); + + while (G_DATALIST_GET_POINTER (datalist)) + g_datalist_clear_i (datalist); + G_UNLOCK (g_dataset_global); +} + +/* HOLDS: g_dataset_global_lock */ +static inline GDataset* +g_dataset_lookup (gconstpointer dataset_location) +{ + register GDataset *dataset; + + if (g_dataset_cached && g_dataset_cached->location == dataset_location) + return g_dataset_cached; + + dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location); + if (dataset) + g_dataset_cached = dataset; + + return dataset; +} + +/* HOLDS: g_dataset_global_lock */ +static void +g_dataset_destroy_internal (GDataset *dataset) +{ + register gconstpointer dataset_location; + + dataset_location = dataset->location; + while (dataset) + { + if (!dataset->datalist) + { + if (dataset == g_dataset_cached) + g_dataset_cached = NULL; + g_hash_table_remove (g_dataset_location_ht, dataset_location); + g_slice_free (GDataset, dataset); + break; + } + + g_datalist_clear_i (&dataset->datalist); + dataset = g_dataset_lookup (dataset_location); + } +} + +EXPORT_C void +g_dataset_destroy (gconstpointer dataset_location) +{ + g_return_if_fail (dataset_location != NULL); + + G_LOCK (g_dataset_global); + if (g_dataset_location_ht) + { + register GDataset *dataset; + + dataset = g_dataset_lookup (dataset_location); + if (dataset) + g_dataset_destroy_internal (dataset); + } + G_UNLOCK (g_dataset_global); +} + +/* HOLDS: g_dataset_global_lock */ +static inline gpointer +g_data_set_internal (GData **datalist, + GQuark key_id, + gpointer data, + GDestroyNotify destroy_func, + GDataset *dataset) +{ + register GData *list; + + list = G_DATALIST_GET_POINTER (datalist); + if (!data) + { + register GData *prev; + + prev = NULL; + while (list) + { + if (list->id == key_id) + { + gpointer ret_data = NULL; + + if (prev) + prev->next = list->next; + else + { + G_DATALIST_SET_POINTER (datalist, list->next); + + /* the dataset destruction *must* be done + * prior to invocation of the data destroy function + */ + if (!list->next && dataset) + g_dataset_destroy_internal (dataset); + } + + /* the GData struct *must* already be unlinked + * when invoking the destroy function. + * we use (data==NULL && destroy_func!=NULL) as + * a special hint combination to "steal" + * data without destroy notification + */ + if (list->destroy_func && !destroy_func) + { + G_UNLOCK (g_dataset_global); + list->destroy_func (list->data); + G_LOCK (g_dataset_global); + } + else + ret_data = list->data; + + g_slice_free (GData, list); + + return ret_data; + } + + prev = list; + list = list->next; + } + } + else + { + while (list) + { + if (list->id == key_id) + { + if (!list->destroy_func) + { + list->data = data; + list->destroy_func = destroy_func; + } + else + { + register GDestroyNotify dfunc; + register gpointer ddata; + + dfunc = list->destroy_func; + ddata = list->data; + list->data = data; + list->destroy_func = destroy_func; + + /* we need to have updated all structures prior to + * invocation of the destroy function + */ + G_UNLOCK (g_dataset_global); + dfunc (ddata); + G_LOCK (g_dataset_global); + } + + return NULL; + } + + list = list->next; + } + + + list = g_slice_new (GData); + + list->next = G_DATALIST_GET_POINTER (datalist); + list->id = key_id; + list->data = data; + list->destroy_func = destroy_func; + G_DATALIST_SET_POINTER (datalist, list); + } + + return NULL; +} + +EXPORT_C void +g_dataset_id_set_data_full (gconstpointer dataset_location, + GQuark key_id, + gpointer data, + GDestroyNotify destroy_func) +{ + register GDataset *dataset; + + g_return_if_fail (dataset_location != NULL); + if (!data) + g_return_if_fail (destroy_func == NULL); + if (!key_id) + { + if (data) + g_return_if_fail (key_id > 0); + else + return; + } + + G_LOCK (g_dataset_global); + if (!g_dataset_location_ht) + g_data_initialize (); + + dataset = g_dataset_lookup (dataset_location); + if (!dataset) + { + dataset = g_slice_new (GDataset); + dataset->location = dataset_location; + g_datalist_init (&dataset->datalist); + g_hash_table_insert (g_dataset_location_ht, + (gpointer) dataset->location, + dataset); + } + + g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset); + G_UNLOCK (g_dataset_global); +} + +EXPORT_C void +g_datalist_id_set_data_full (GData **datalist, + GQuark key_id, + gpointer data, + GDestroyNotify destroy_func) +{ + g_return_if_fail (datalist != NULL); + if (!data) + g_return_if_fail (destroy_func == NULL); + if (!key_id) + { + if (data) + g_return_if_fail (key_id > 0); + else + return; + } + + G_LOCK (g_dataset_global); + if (!g_dataset_location_ht) + g_data_initialize (); + + g_data_set_internal (datalist, key_id, data, destroy_func, NULL); + G_UNLOCK (g_dataset_global); +} + +EXPORT_C gpointer +g_dataset_id_remove_no_notify (gconstpointer dataset_location, + GQuark key_id) +{ + gpointer ret_data = NULL; + + g_return_val_if_fail (dataset_location != NULL, NULL); + + G_LOCK (g_dataset_global); + if (key_id && g_dataset_location_ht) + { + GDataset *dataset; + + dataset = g_dataset_lookup (dataset_location); + if (dataset) + ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset); + } + G_UNLOCK (g_dataset_global); + + return ret_data; +} + +EXPORT_C gpointer +g_datalist_id_remove_no_notify (GData **datalist, + GQuark key_id) +{ + gpointer ret_data = NULL; + + g_return_val_if_fail (datalist != NULL, NULL); + + G_LOCK (g_dataset_global); + if (key_id && g_dataset_location_ht) + ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL); + G_UNLOCK (g_dataset_global); + + return ret_data; +} + +EXPORT_C gpointer +g_dataset_id_get_data (gconstpointer dataset_location, + GQuark key_id) +{ + g_return_val_if_fail (dataset_location != NULL, NULL); + + G_LOCK (g_dataset_global); + if (key_id && g_dataset_location_ht) + { + register GDataset *dataset; + + dataset = g_dataset_lookup (dataset_location); + if (dataset) + { + register GData *list; + + for (list = dataset->datalist; list; list = list->next) + if (list->id == key_id) + { + G_UNLOCK (g_dataset_global); + return list->data; + } + } + } + G_UNLOCK (g_dataset_global); + + return NULL; +} + +EXPORT_C gpointer +g_datalist_id_get_data (GData **datalist, + GQuark key_id) +{ + gpointer data = NULL; + g_return_val_if_fail (datalist != NULL, NULL); + if (key_id) + { + register GData *list; + G_LOCK (g_dataset_global); + for (list = G_DATALIST_GET_POINTER (datalist); list; list = list->next) + if (list->id == key_id) + { + data = list->data; + break; + } + G_UNLOCK (g_dataset_global); + } + return data; +} + +EXPORT_C void +g_dataset_foreach (gconstpointer dataset_location, + GDataForeachFunc func, + gpointer user_data) +{ + register GDataset *dataset; + + g_return_if_fail (dataset_location != NULL); + g_return_if_fail (func != NULL); + + G_LOCK (g_dataset_global); + if (g_dataset_location_ht) + { + dataset = g_dataset_lookup (dataset_location); + G_UNLOCK (g_dataset_global); + if (dataset) + { + register GData *list, *next; + + for (list = dataset->datalist; list; list = next) + { + next = list->next; + func (list->id, list->data, user_data); + } + } + } + else + { + G_UNLOCK (g_dataset_global); + } +} + +EXPORT_C void +g_datalist_foreach (GData **datalist, + GDataForeachFunc func, + gpointer user_data) +{ + register GData *list, *next; + + g_return_if_fail (datalist != NULL); + g_return_if_fail (func != NULL); + + for (list = G_DATALIST_GET_POINTER (datalist); list; list = next) + { + next = list->next; + func (list->id, list->data, user_data); + } +} + +EXPORT_C void +g_datalist_init (GData **datalist) +{ + g_return_if_fail (datalist != NULL); + + g_atomic_pointer_set ((gpointer*) datalist, NULL); +} + +/** + * g_datalist_set_flags: + * @datalist: pointer to the location that holds a list + * @flags: the flags to turn on. The values of the flags are + * restricted by %G_DATALIST_FLAGS_MASK (currently + * 3; giving two possible boolean flags). + * A value for @flags that doesn't fit within the mask is + * an error. + * + * Turns on flag values for a data list. This function is used + * to keep a small number of boolean flags in an object with + * a data list without using any additional space. It is + * not generally useful except in circumstances where space + * is very tight. (It is used in the base #GObject type, for + * example.) + * + * Since: 2.8 + **/ +EXPORT_C void +g_datalist_set_flags (GData **datalist, + guint flags) +{ + gpointer oldvalue; + g_return_if_fail (datalist != NULL); + g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0); + + do + { + oldvalue = g_atomic_pointer_get (datalist); + } + while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, oldvalue, + (gpointer) ((gsize) oldvalue | flags))); +} + +/** + * g_datalist_unset_flags: + * @datalist: pointer to the location that holds a list + * @flags: the flags to turn off. The values of the flags are + * restricted by %G_DATALIST_FLAGS_MASK (currently + * 3: giving two possible boolean flags). + * A value for @flags that doesn't fit within the mask is + * an error. + * + * Turns off flag values for a data list. See g_datalist_unset_flags() + * + * Since: 2.8 + **/ +EXPORT_C void +g_datalist_unset_flags (GData **datalist, + guint flags) +{ + gpointer oldvalue; + g_return_if_fail (datalist != NULL); + g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0); + + do + { + oldvalue = g_atomic_pointer_get (datalist); + } + while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, oldvalue, + (gpointer) ((gsize) oldvalue & ~(gsize) flags))); +} + +/** + * g_datalist_get_flags: + * @datalist: pointer to the location that holds a list + * + * Gets flags values packed in together with the datalist. + * See g_datalist_set_flags(). + * + * Return value: the flags of the datalist + * + * Since: 2.8 + **/ +EXPORT_C guint +g_datalist_get_flags (GData **datalist) +{ + g_return_val_if_fail (datalist != NULL, 0); + + return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */ +} + +/* HOLDS: g_dataset_global_lock */ +static void +g_data_initialize (void) +{ + g_return_if_fail (g_dataset_location_ht == NULL); + + g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL); + g_dataset_cached = NULL; +} + +EXPORT_C GQuark +g_quark_try_string (const gchar *string) +{ + GQuark quark = 0; + g_return_val_if_fail (string != NULL, 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; +} + +/* 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 ? g_strdup (string) : (gchar *)string); + + return quark; +} + +EXPORT_C GQuark +g_quark_from_string (const gchar *string) +{ + GQuark quark; + + g_return_val_if_fail (string != NULL, 0); + + G_LOCK (g_quark_global); + quark = g_quark_from_string_internal (string, TRUE); + G_UNLOCK (g_quark_global); + + return quark; +} + +EXPORT_C GQuark +g_quark_from_static_string (const gchar *string) +{ + GQuark quark; + + g_return_val_if_fail (string != NULL, 0); + + G_LOCK (g_quark_global); + quark = g_quark_from_string_internal (string, FALSE); + G_UNLOCK (g_quark_global); + + return quark; +} + +EXPORT_C G_CONST_RETURN gchar* +g_quark_to_string (GQuark quark) +{ + gchar* result = NULL; + + G_LOCK (g_quark_global); + if (quark < g_quark_seq_id) + result = g_quarks[quark]; + G_UNLOCK (g_quark_global); + + return result; +} + +/* HOLDS: g_quark_global_lock */ +static inline GQuark +g_quark_new (gchar *string) +{ + GQuark quark; + + if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0) + g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE); + 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; + } + + quark = g_quark_seq_id++; + g_quarks[quark] = string; + g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark)); + + return quark; +} + +/** + * g_intern_string: + * @string: 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 + */ +EXPORT_C G_CONST_RETURN 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: 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 + */ +EXPORT_C G_CONST_RETURN 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; +} + + + +#define __G_DATASET_C__ +#include "galiasdef.c"