+
+ if (dup_func)
+ retval = dup_func (val, user_data);
+ else
+ retval = val;
+
+ g_datalist_unlock (datalist);
+
+ return retval;
+}
+
+/**
+ * g_datalist_id_replace_data:
+ * @datalist: location of a datalist
+ * @key_id: the #GQuark identifying a data element
+ * @oldval: (allow-none): the old value to compare against
+ * @newval: (allow-none): the new value to replace it with
+ * @destroy: (allow-none): destroy notify for the new value
+ * @old_destroy: (allow-none): 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 registred 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;
+}
+
+/**
+ * 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: 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;