* 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-1999. 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 ; FIXME: might still freeze, watch out, not thoroughly
+ * looked at yet.
+ */
+
+#include <string.h>
#include "glib.h"
/* --- defines --- */
-#define G_DATASET_BLOCK_SIZE (512)
-#define G_DATASET_MEM_CHUNK_PREALLOC (64)
-#define G_DATASET_DATA_MEM_CHUNK_PREALLOC (128)
+#define G_QUARK_BLOCK_SIZE (512)
+#define G_DATA_MEM_CHUNK_PREALLOC (128)
+#define G_DATA_CACHE_MAX (512)
+#define G_DATASET_MEM_CHUNK_PREALLOC (32)
/* --- structures --- */
-typedef struct _GDatasetData GDatasetData;
typedef struct _GDataset GDataset;
-struct _GDatasetData
+struct _GData
{
- GDatasetData *next;
- guint id;
+ GData *next;
+ GQuark id;
gpointer data;
GDestroyNotify destroy_func;
};
struct _GDataset
{
gconstpointer location;
- GDatasetData *data_list;
+ GData *datalist;
};
/* --- prototypes --- */
-static inline GDataset* g_dataset_lookup (gconstpointer dataset_location);
-static inline void g_dataset_destroy_i (GDataset *dataset);
-static void g_dataset_initialize (void);
-static void g_dataset_alloc_key (const gchar *string,
- guint **id,
- gchar **key);
-
+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 --- */
-static GHashTable *g_dataset_location_ht = NULL;
-static GHashTable *g_dataset_key_ht = NULL;
-static GDataset *g_dataset_cached = NULL;
-static GMemChunk *g_dataset_mem_chunk = NULL;
-static GMemChunk *g_dataset_data_mem_chunk = NULL;
-static gchar **g_dataset_key_array = NULL;
-static guint g_dataset_seq_id = 0;
+G_LOCK_DEFINE_STATIC (g_dataset_global);
+static GHashTable *g_dataset_location_ht = NULL;
+static GDataset *g_dataset_cached = NULL; /* should this be
+ threadspecific? */
+static GMemChunk *g_dataset_mem_chunk = NULL;
+static GMemChunk *g_data_mem_chunk = NULL;
+static GData *g_data_cache = NULL;
+static guint g_data_cache_length = 0;
+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;
/* --- functions --- */
-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;
-
- if (!g_dataset_location_ht)
- g_dataset_initialize ();
-
- 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 inline void
-g_dataset_destroy_i (GDataset *dataset)
+g_datalist_clear_i (GData **datalist)
{
- register GDatasetData *list;
+ register GData *list;
- if (dataset == g_dataset_cached)
- g_dataset_cached = NULL;
- g_hash_table_remove (g_dataset_location_ht, dataset->location);
-
- list = dataset->data_list;
- g_mem_chunk_free (g_dataset_mem_chunk, dataset);
+ /* unlink *all* items before walking their destructors
+ */
+ list = *datalist;
+ *datalist = NULL;
while (list)
{
- register GDatasetData *prev;
+ register GData *prev;
prev = list;
list = prev->next;
if (prev->destroy_func)
- prev->destroy_func (prev->data);
+ {
+ G_UNLOCK (g_dataset_global);
+ prev->destroy_func (prev->data);
+ G_LOCK (g_dataset_global);
+ }
- g_mem_chunk_free (g_dataset_data_mem_chunk, prev);
+ if (g_data_cache_length < G_DATA_CACHE_MAX)
+ {
+ prev->next = g_data_cache;
+ g_data_cache = prev;
+ g_data_cache_length++;
+ }
+ else
+ g_mem_chunk_free (g_data_mem_chunk, prev);
}
}
void
-g_dataset_destroy (gconstpointer dataset_location)
+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 (*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;
- g_return_if_fail (dataset_location != NULL);
+ if (g_dataset_cached && g_dataset_cached->location == dataset_location)
+ return g_dataset_cached;
- dataset = g_dataset_lookup (dataset_location);
+ dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
if (dataset)
- g_dataset_destroy_i (dataset);
+ g_dataset_cached = dataset;
+
+ return dataset;
}
-void
-g_dataset_id_set_destroy (gconstpointer dataset_location,
- guint key_id,
- GDestroyNotify destroy_func)
+/* HOLDS: g_dataset_global_lock */
+static void
+g_dataset_destroy_internal (GDataset *dataset)
{
- g_return_if_fail (dataset_location != NULL);
+ register gconstpointer dataset_location;
- if (key_id)
+ dataset_location = dataset->location;
+ while (dataset)
{
- register GDataset *dataset;
-
- dataset = g_dataset_lookup (dataset_location);
- if (dataset)
+ if (!dataset->datalist)
{
- register GDatasetData *list;
-
- list = dataset->data_list;
- while (list)
- {
- if (list->id == key_id)
- {
- list->destroy_func = destroy_func;
- return;
- }
- }
+ if (dataset == g_dataset_cached)
+ g_dataset_cached = NULL;
+ g_hash_table_remove (g_dataset_location_ht, dataset_location);
+ g_mem_chunk_free (g_dataset_mem_chunk, dataset);
+ break;
}
+
+ g_datalist_clear_i (&dataset->datalist);
+ dataset = g_dataset_lookup (dataset_location);
}
}
-gpointer
-g_dataset_id_get_data (gconstpointer dataset_location,
- guint key_id)
+void
+g_dataset_destroy (gconstpointer dataset_location)
{
- g_return_val_if_fail (dataset_location != NULL, NULL);
+ g_return_if_fail (dataset_location != NULL);
- if (key_id)
+ G_LOCK (g_dataset_global);
+ if (g_dataset_location_ht)
{
register GDataset *dataset;
-
+
dataset = g_dataset_lookup (dataset_location);
if (dataset)
- {
- register GDatasetData *list;
-
- for (list = dataset->data_list; list; list = list->next)
- if (list->id == key_id)
- return list->data;
- }
+ g_dataset_destroy_internal (dataset);
}
-
- return NULL;
+ G_UNLOCK (g_dataset_global);
}
-void
-g_dataset_id_set_data_full (gconstpointer dataset_location,
- guint key_id,
- gpointer data,
- GDestroyNotify destroy_func)
+/* 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 GDataset *dataset;
- register GDatasetData *list;
-
- g_return_if_fail (dataset_location != NULL);
- g_return_if_fail (key_id > 0);
+ register GData *list;
- dataset = g_dataset_lookup (dataset_location);
- if (!dataset)
- {
- dataset = g_chunk_new (GDataset, g_dataset_mem_chunk);
- dataset->location = dataset_location;
- dataset->data_list = NULL;
- g_hash_table_insert (g_dataset_location_ht,
- (gpointer) dataset->location, /* Yuck */
- dataset);
- }
-
- list = dataset->data_list;
+ list = *datalist;
if (!data)
{
- register GDatasetData *prev;
+ register GData *prev;
prev = NULL;
while (list)
{
if (list->id == key_id)
{
+ gpointer ret_data = NULL;
+
if (prev)
prev->next = list->next;
else
{
- dataset->data_list = list->next;
+ *datalist = list->next;
- if (!dataset->data_list)
- g_dataset_destroy_i (dataset);
+ /* the dataset destruction *must* be done
+ * prior to invokation of the data destroy function
+ */
+ if (!*datalist && dataset)
+ g_dataset_destroy_internal (dataset);
}
- /* we need to have unlinked before invoking the destroy function
+ /* 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)
- list->destroy_func (list->data);
+ 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;
+
+ if (g_data_cache_length < G_DATA_CACHE_MAX)
+ {
+ list->next = g_data_cache;
+ g_data_cache = list;
+ g_data_cache_length++;
+ }
+ else
+ g_mem_chunk_free (g_data_mem_chunk, list);
- g_mem_chunk_free (g_dataset_data_mem_chunk, list);
- break;
+ return ret_data;
}
prev = list;
}
else
{
- register GDatasetData *prev;
-
- prev = NULL;
while (list)
{
if (list->id == key_id)
{
- if (prev)
- prev->next = list->next;
+ if (!list->destroy_func)
+ {
+ list->data = data;
+ list->destroy_func = destroy_func;
+ }
else
- dataset->data_list = list->next;
-
- /* we need to have unlinked before invoking the destroy function
- */
- if (list->destroy_func)
- list->destroy_func (list->data);
+ {
+ 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
+ * invokation of the destroy function
+ */
+ G_UNLOCK (g_dataset_global);
+ dfunc (ddata);
+ G_LOCK (g_dataset_global);
+ }
- break;
+ return NULL;
}
- prev = list;
list = list->next;
}
- if (!list)
- list = g_chunk_new (GDatasetData, g_dataset_data_mem_chunk);
- list->next = dataset->data_list;
+ if (g_data_cache)
+ {
+ list = g_data_cache;
+ g_data_cache = list->next;
+ g_data_cache_length--;
+ }
+ else
+ list = g_chunk_new (GData, g_data_mem_chunk);
+ list->next = *datalist;
list->id = key_id;
list->data = data;
list->destroy_func = destroy_func;
- dataset->data_list = list;
+ *datalist = list;
}
+
+ return NULL;
}
-guint
-g_dataset_try_key (const gchar *key)
+void
+g_dataset_id_set_data_full (gconstpointer dataset_location,
+ GQuark key_id,
+ gpointer data,
+ GDestroyNotify destroy_func)
{
- register guint *id;
- g_return_val_if_fail (key != NULL, 0);
+ register GDataset *dataset;
- if (g_dataset_key_ht)
+ g_return_if_fail (dataset_location != NULL);
+ if (!data)
+ g_return_if_fail (destroy_func == NULL);
+ if (!key_id)
{
- id = g_hash_table_lookup (g_dataset_key_ht, (gpointer) key);
-
- if (id)
- return *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_chunk_new (GDataset, g_dataset_mem_chunk);
+ dataset->location = dataset_location;
+ g_datalist_init (&dataset->datalist);
+ g_hash_table_insert (g_dataset_location_ht,
+ (gpointer) dataset->location,
+ dataset);
}
- return 0;
+ g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
+ G_UNLOCK (g_dataset_global);
}
-guint
-g_dataset_force_id (const gchar *key)
+void
+g_datalist_id_set_data_full (GData **datalist,
+ GQuark key_id,
+ gpointer data,
+ GDestroyNotify destroy_func)
{
- guint *id;
+ 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_return_val_if_fail (key != NULL, 0);
+ g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
+ G_UNLOCK (g_dataset_global);
+}
+
+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);
- if (!g_dataset_key_ht)
- g_dataset_initialize ();
+ G_LOCK (g_dataset_global);
+ if (key_id && g_dataset_location_ht)
+ {
+ GDataset *dataset;
- id = g_hash_table_lookup (g_dataset_key_ht, (gpointer) key);
- if (!id)
+ 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;
+}
+
+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;
+}
+
+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)
{
- gchar *new_key;
+ 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;
+}
- g_dataset_alloc_key (key, &id, &new_key);
- g_hash_table_insert (g_dataset_key_ht, new_key, id);
+gpointer
+g_datalist_id_get_data (GData **datalist,
+ GQuark key_id)
+{
+ g_return_val_if_fail (datalist != NULL, NULL);
+
+ if (key_id)
+ {
+ register GData *list;
+
+ for (list = *datalist; list; list = list->next)
+ if (list->id == key_id)
+ return list->data;
}
- return *id;
+ return NULL;
+}
+
+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;
+
+ for (list = dataset->datalist; list; list = list->next)
+ func (list->id, list->data, user_data);
+ }
+ }
+ else
+ {
+ G_UNLOCK (g_dataset_global);
+ }
+}
+
+void
+g_datalist_foreach (GData **datalist,
+ GDataForeachFunc func,
+ gpointer user_data)
+{
+ register GData *list;
+
+ g_return_if_fail (datalist != NULL);
+ g_return_if_fail (func != NULL);
+
+ for (list = *datalist; list; list = list->next)
+ func (list->id, list->data, user_data);
+}
+
+void
+g_datalist_init (GData **datalist)
+{
+ g_return_if_fail (datalist != NULL);
+
+ *datalist = NULL;
}
+/* HOLDS: g_dataset_global_lock */
static void
-g_dataset_initialize (void)
+g_data_initialize (void)
{
- if (!g_dataset_location_ht)
+ 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;
+ g_dataset_mem_chunk =
+ g_mem_chunk_new ("GDataset MemChunk",
+ sizeof (GDataset),
+ sizeof (GDataset) * G_DATASET_MEM_CHUNK_PREALLOC,
+ G_ALLOC_AND_FREE);
+ g_data_mem_chunk =
+ g_mem_chunk_new ("GData MemChunk",
+ sizeof (GData),
+ sizeof (GData) * G_DATA_MEM_CHUNK_PREALLOC,
+ G_ALLOC_AND_FREE);
+}
+
+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;
+}
+
+GQuark
+g_quark_from_string (const gchar *string)
+{
+ GQuark quark;
+
+ g_return_val_if_fail (string != NULL, 0);
+
+ G_LOCK (g_quark_global);
+ if (g_quark_ht)
+ quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
+ else
+ {
+ g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
+ quark = 0;
+ }
+
+ if (!quark)
+ quark = g_quark_new (g_strdup (string));
+ G_UNLOCK (g_quark_global);
+
+ return quark;
+}
+
+GQuark
+g_quark_from_static_string (const gchar *string)
+{
+ GQuark quark;
+
+ g_return_val_if_fail (string != NULL, 0);
+
+ G_LOCK (g_quark_global);
+ if (g_quark_ht)
+ quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
+ else
{
- g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
- g_dataset_key_ht = g_hash_table_new (g_str_hash, g_str_equal);
- g_dataset_cached = NULL;
- g_dataset_mem_chunk =
- g_mem_chunk_new ("GDataset MemChunk",
- sizeof (GDataset),
- sizeof (GDataset) * G_DATASET_MEM_CHUNK_PREALLOC,
- G_ALLOC_AND_FREE);
- g_dataset_data_mem_chunk =
- g_mem_chunk_new ("GDatasetData MemChunk",
- sizeof (GDatasetData),
- sizeof (GDatasetData) * G_DATASET_DATA_MEM_CHUNK_PREALLOC,
- G_ALLOC_AND_FREE);
+ g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
+ quark = 0;
}
+
+ if (!quark)
+ quark = g_quark_new ((gchar*) string);
+ G_UNLOCK (g_quark_global);
+
+ return quark;
}
gchar*
-g_dataset_retrive_key (guint id)
+g_quark_to_string (GQuark quark)
{
- if (id > 0 && id <= g_dataset_seq_id)
- return g_dataset_key_array[id - 1];
- return NULL;
+ gchar* result = NULL;
+ G_LOCK (g_quark_global);
+ if (quark > 0 && quark <= g_quark_seq_id)
+ result = g_quarks[quark - 1];
+ G_UNLOCK (g_quark_global);
+
+ return result;
}
-static void
-g_dataset_alloc_key (const gchar *string,
- guint **id,
- gchar **key)
+/* HOLDS: g_quark_global_lock */
+static inline GQuark
+g_quark_new (gchar *string)
{
- if (g_dataset_seq_id % G_DATASET_BLOCK_SIZE == 0)
- g_dataset_key_array = g_realloc (g_dataset_key_array,
- (g_dataset_seq_id + G_DATASET_BLOCK_SIZE) * sizeof (gchar*));
-
- *key = g_new (gchar, sizeof (guint) + strlen (string) + 1);
- *id = (guint*) *key;
- *key += sizeof (guint);
- strcpy (*key, string);
- g_dataset_key_array[g_dataset_seq_id] = *key;
- g_dataset_seq_id++;
- **id = g_dataset_seq_id;
+ 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);
+
+ g_quarks[g_quark_seq_id] = string;
+ g_quark_seq_id++;
+ quark = g_quark_seq_id;
+ g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
+
+ return quark;
}