/* 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); } }
/** * g_datalist_clear: (skip) * @datalist: a datalist. * * Frees all the data elements of the datalist. * The data elements' destroy functions are called * if they have been set. **/ void g_datalist_clear (GData **datalist) { GData *data; gint i; g_return_if_fail (datalist != NULL); g_datalist_lock (datalist); data = G_DATALIST_GET_POINTER (datalist); G_DATALIST_SET_POINTER (datalist, NULL); g_datalist_unlock (datalist); if (data) { for (i = 0; i < data->len; i++) { if (data->data[i].data && data->data[i].destroy) data->data[i].destroy (data->data[i].data); } g_free (data); } }
/** * g_datalist_get_data: * @datalist: a datalist. * @key: the string identifying a data element. * * Gets a data element, using its string identifier. This is slower than * g_datalist_id_get_data() because it compares strings. * * Returns: (transfer none) (nullable): the data element, or %NULL if it * is not found. **/ gpointer g_datalist_get_data (GData **datalist, const gchar *key) { gpointer res = NULL; GData *d; GDataElt *data, *data_end; g_return_val_if_fail (datalist != NULL, NULL); g_datalist_lock (datalist); d = G_DATALIST_GET_POINTER (datalist); if (d) { data = d->data; data_end = data + d->len; while (data < data_end) { if ((g_quark_to_string (data->key) != 0) || (key == 0)) /* This line added by JE - 20-10-14 (probably not needed any more) */ if (g_strcmp0 (g_quark_to_string (data->key), key) == 0) { res = data->data; break; } data++; } } g_datalist_unlock (datalist); return res; }
/** * g_datalist_get_data: * @datalist: a datalist. * @key: the string identifying a data element. * * Gets a data element, using its string identifier. This is slower than * g_datalist_id_get_data() because it compares strings. * * Returns: (transfer none) (nullable): the data element, or %NULL if it * is not found. **/ gpointer g_datalist_get_data (GData **datalist, const gchar *key) { gpointer res = NULL; GData *d; GDataElt *data, *data_end; g_return_val_if_fail (datalist != NULL, NULL); g_datalist_lock (datalist); d = G_DATALIST_GET_POINTER (datalist); if (d) { data = d->data; data_end = data + d->len; while (data < data_end) { if (g_strcmp0 (g_quark_to_string (data->key), key) == 0) { res = data->data; break; } data++; } } g_datalist_unlock (datalist); return res; }
/** * g_datalist_foreach: * @datalist: a datalist. * @func: (scope call): the function to call for each data element. * @user_data: (closure): user data to pass to the function. * * Calls the given function for each data element of the datalist. The * function is called with each data element's #GQuark id and data, * together with the given @user_data parameter. Note that this * function is NOT thread-safe. So unless @datalist can be protected * from any modifications during invocation of this function, it should * not be called. * * @func can make changes to @datalist, but the iteration will not * reflect changes made during the g_datalist_foreach() call, other * than skipping over elements that are removed. **/ void g_datalist_foreach (GData **datalist, GDataForeachFunc func, gpointer user_data) { GData *d; int i, j, len; GQuark *keys; g_return_if_fail (datalist != NULL); g_return_if_fail (func != NULL); d = G_DATALIST_GET_POINTER (datalist); if (d == NULL) return; /* We make a copy of the keys so that we can handle it changing in the callback */ len = d->len; keys = g_new (GQuark, len); for (i = 0; i < len; i++) keys[i] = d->data[i].key; for (i = 0; i < len; i++) { /* A previous callback might have removed a later item, so always check that it still exists before calling */ d = G_DATALIST_GET_POINTER (datalist); if (d == NULL) break; for (j = 0; j < d->len; j++) { if (d->data[j].key == keys[i]) { func (d->data[i].key, d->data[i].data, user_data); break; } } } g_free (keys); }
/** * g_datalist_clear: * @datalist: a datalist. * * Frees all the data elements of the datalist. The data elements' * destroy functions are called if they have been set. **/ 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); }
/** * g_datalist_foreach: * @datalist: a datalist. * @func: the function to call for each data element. * @user_data: user data to pass to the function. * * Calls the given function for each data element of the datalist. The * function is called with each data element's #GQuark id and data, * together with the given @user_data parameter. Note that this * function is NOT thread-safe. So unless @datalist can be protected * from any modifications during invocation of this function, it should * not be called. **/ 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); } }
/** * g_datalist_get_data: * @dl: a datalist. * @k: the string identifying a data element. * @Returns: the data element, or %NULL if it is not found. * * Gets a data element, using its string identifer. This is slower than * g_datalist_id_get_data() because the string is first converted to a * #GQuark. **/ 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; }
/** * g_datalist_id_dup_data: * @datalist: location of a datalist * @key_id: the #GQuark identifying a data element * @dup_func: (allow-none): function to duplicate the old value * @user_data: (allow-none): passed as user_data to @dup_func * * This is a variant of g_datalist_id_get_data() which * returns a 'duplicate' of the value. @dup_func defines the * meaning of 'duplicate' in this context, it could e.g. * take a reference on a ref-counted object. * * If the @key_id is not set in the datalist then @dup_func * will be called with a %NULL argument. * * Note that @dup_func is called while the datalist is locked, so it * is not allowed to read or modify the datalist. * * This function can be useful to avoid races when multiple * threads are using the same datalist and the same key. * * Returns: the result of calling @dup_func on the value * associated with @key_id in @datalist, or %NULL if not set. * If @dup_func is %NULL, the value is returned unmodified. * * Since: 2.34 */ gpointer g_datalist_id_dup_data (GData **datalist, GQuark key_id, GDuplicateFunc dup_func, gpointer user_data) { gpointer val = NULL; gpointer retval = NULL; GData *d; GDataElt *data, *data_end; g_return_val_if_fail (datalist != NULL, NULL); g_return_val_if_fail (key_id != 0, NULL); g_datalist_lock (datalist); d = G_DATALIST_GET_POINTER (datalist); if (d) { data = d->data; data_end = data + d->len - 1; while (data <= data_end) { if (data->key == key_id) { val = data->data; break; } data++; } } if (dup_func) retval = dup_func (val, user_data); else retval = val; g_datalist_unlock (datalist); return retval; }
/* HOLDS: g_dataset_global_lock */ static void g_dataset_destroy_internal (GDataset *dataset) { gconstpointer dataset_location; dataset_location = dataset->location; while (dataset) { if (G_DATALIST_GET_POINTER(&dataset->datalist) == NULL) { 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); } }
/* Called with the datalist lock held, or the dataset global * lock for dataset lists */ static void g_datalist_clear_i (GData **datalist) { GData *data; gint i; data = G_DATALIST_GET_POINTER (datalist); G_DATALIST_SET_POINTER (datalist, NULL); if (data) { G_UNLOCK (g_dataset_global); for (i = 0; i < data->len; i++) { if (data->data[i].data && data->data[i].destroy) data->data[i].destroy (data->data[i].data); } G_LOCK (g_dataset_global); g_free (data); } }
/* 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; }
/** * g_datalist_id_replace_data: (skip) * @datalist: location of a datalist * @key_id: the #GQuark identifying a data element * @oldval: (nullable): the old value to compare against * @newval: (nullable): the new value to replace it with * @destroy: (nullable): destroy notify for the new value * @old_destroy: (out) (optional): destroy notify for the existing value * * Compares the member that is associated with @key_id in * @datalist to @oldval, and if they are the same, replace * @oldval with @newval. * * This is like a typical atomic compare-and-exchange * operation, for a member of @datalist. * * If the previous value was replaced then ownership of the * old value (@oldval) is passed to the caller, including * the registered destroy notify for it (passed out in @old_destroy). * Its up to the caller to free this as he wishes, which may * or may not include using @old_destroy as sometimes replacement * should not destroy the object in the normal way. * * Returns: %TRUE if the existing value for @key_id was replaced * by @newval, %FALSE otherwise. * * Since: 2.34 */ gboolean g_datalist_id_replace_data (GData **datalist, GQuark key_id, gpointer oldval, gpointer newval, GDestroyNotify destroy, GDestroyNotify *old_destroy) { gpointer val = NULL; GData *d; GDataElt *data, *data_end; g_return_val_if_fail (datalist != NULL, FALSE); g_return_val_if_fail (key_id != 0, FALSE); if (old_destroy) *old_destroy = NULL; g_datalist_lock (datalist); d = G_DATALIST_GET_POINTER (datalist); if (d) { data = d->data; data_end = data + d->len - 1; while (data <= data_end) { if (data->key == key_id) { val = data->data; if (val == oldval) { if (old_destroy) *old_destroy = data->destroy; if (newval != NULL) { data->data = newval; data->destroy = destroy; } else { if (data != data_end) *data = *data_end; d->len--; /* We don't bother to shrink, but if all data are now gone * we at least free the memory */ if (d->len == 0) { G_DATALIST_SET_POINTER (datalist, NULL); g_free (d); } } } break; } data++; } } if (val == NULL && oldval == NULL && newval != NULL) { GData *old_d; /* insert newval */ old_d = d; if (d == NULL) { d = g_malloc (sizeof (GData)); d->len = 0; d->alloc = 1; } else if (d->len == d->alloc) { d->alloc = d->alloc * 2; d = g_realloc (d, sizeof (GData) + (d->alloc - 1) * sizeof (GDataElt)); } if (old_d != d) G_DATALIST_SET_POINTER (datalist, d); d->data[d->len].key = key_id; d->data[d->len].data = newval; d->data[d->len].destroy = destroy; d->len++; } g_datalist_unlock (datalist); return val == oldval; }
/* HOLDS: g_dataset_global_lock if dataset != null */ static inline gpointer g_data_set_internal (GData **datalist, GQuark key_id, gpointer new_data, GDestroyNotify new_destroy_func, GDataset *dataset) { GData *d, *old_d; GDataElt old, *data, *data_last, *data_end; g_datalist_lock (datalist); d = G_DATALIST_GET_POINTER (datalist); if (new_data == NULL) /* remove */ { if (d) { data = d->data; data_last = data + d->len - 1; while (data <= data_last) { if (data->key == key_id) { old = *data; if (data != data_last) *data = *data_last; d->len--; /* We don't bother to shrink, but if all data are now gone * we at least free the memory */ if (d->len == 0) { G_DATALIST_SET_POINTER (datalist, NULL); g_free (d); /* datalist may be situated in dataset, so must not be * unlocked after we free it */ g_datalist_unlock (datalist); /* the dataset destruction *must* be done * prior to invocation of the data destroy function */ if (dataset) g_dataset_destroy_internal (dataset); } else { g_datalist_unlock (datalist); } /* We found and removed an old value * the GData struct *must* already be unlinked * when invoking the destroy function. * we use (new_data==NULL && new_destroy_func!=NULL) as * a special hint combination to "steal" * data without destroy notification */ if (old.destroy && !new_destroy_func) { if (dataset) G_UNLOCK (g_dataset_global); old.destroy (old.data); if (dataset) G_LOCK (g_dataset_global); old.data = NULL; } return old.data; } data++; } } } else { old.data = NULL; if (d) { data = d->data; data_end = data + d->len; while (data < data_end) { if (data->key == key_id) { if (!data->destroy) { data->data = new_data; data->destroy = new_destroy_func; g_datalist_unlock (datalist); } else { old = *data; data->data = new_data; data->destroy = new_destroy_func; g_datalist_unlock (datalist); /* We found and replaced an old value * the GData struct *must* already be unlinked * when invoking the destroy function. */ if (dataset) G_UNLOCK (g_dataset_global); old.destroy (old.data); if (dataset) G_LOCK (g_dataset_global); } return NULL; } data++; } } /* The key was not found, insert it */ old_d = d; if (d == NULL) { d = g_malloc (sizeof (GData)); d->len = 0; d->alloc = 1; } else if (d->len == d->alloc) { d->alloc = d->alloc * 2; d = g_realloc (d, sizeof (GData) + (d->alloc - 1) * sizeof (GDataElt)); } if (old_d != d) G_DATALIST_SET_POINTER (datalist, d); d->data[d->len].key = key_id; d->data[d->len].data = new_data; d->data[d->len].destroy = new_destroy_func; d->len++; } g_datalist_unlock (datalist); return NULL; }