gdataset: factor out common code for find/append of key

The duplication is cumbersome. Factor out common pieces to finding the
data by key and appending a new data.
This commit is contained in:
Thomas Haller 2024-01-05 16:52:02 +01:00
parent f6b24a0a20
commit 5e1875dbdc

View File

@ -157,6 +157,66 @@ g_datalist_unlock_and_set (GData **datalist, gpointer ptr)
g_pointer_bit_unlock_and_set ((void **) datalist, DATALIST_LOCK_BIT, ptr, G_DATALIST_FLAGS_MASK_INTERNAL); g_pointer_bit_unlock_and_set ((void **) datalist, DATALIST_LOCK_BIT, ptr, G_DATALIST_FLAGS_MASK_INTERNAL);
} }
static gboolean
datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify destroy_func)
{
gboolean reallocated;
GData *d;
d = *data;
if (!d)
{
d = g_malloc (sizeof (GData));
d->len = 0;
d->alloc = 1;
*data = d;
reallocated = TRUE;
}
else if (d->len == d->alloc)
{
d->alloc = d->alloc * 2u;
d = g_realloc (d, G_STRUCT_OFFSET (GData, data) + d->alloc * sizeof (GDataElt));
*data = d;
reallocated = TRUE;
}
else
reallocated = FALSE;
d->data[d->len] = (GDataElt){
.key = key_id,
.data = new_data,
.destroy = destroy_func,
};
d->len++;
return reallocated;
}
static GDataElt *
datalist_find (GData *data, GQuark key_id, guint32 *out_idx)
{
guint32 i;
if (data)
{
for (i = 0; i < data->len; i++)
{
GDataElt *data_elt = &data->data[i];
if (data_elt->key == key_id)
{
if (out_idx)
*out_idx = i;
return data_elt;
}
}
}
if (out_idx)
*out_idx = G_MAXUINT32;
return NULL;
}
/** /**
* g_datalist_clear: (skip) * g_datalist_clear: (skip)
* @datalist: a datalist. * @datalist: a datalist.
@ -275,134 +335,101 @@ g_data_set_internal (GData **datalist,
GDestroyNotify new_destroy_func, GDestroyNotify new_destroy_func,
GDataset *dataset) GDataset *dataset)
{ {
GData *d, *old_d; GData *d;
GData *new_d = NULL; GData *new_d = NULL;
GDataElt old, *data, *data_last, *data_end; GDataElt old, *data;
guint32 idx;
d = g_datalist_lock_and_get (datalist); d = g_datalist_lock_and_get (datalist);
data = datalist_find (d, key_id, &idx);
if (new_data == NULL) /* remove */ if (new_data == NULL) /* remove */
{ {
if (d) if (data)
{ {
data = d->data; old = *data;
data_last = data + d->len - 1; if (idx != d->len - 1u)
while (data <= data_last) *data = d->data[d->len - 1u];
{ d->len--;
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 don't bother to shrink, but if all data are now gone
* we at least free the memory * we at least free the memory
*/ */
if (d->len == 0) if (d->len == 0)
{ {
/* datalist may be situated in dataset, so must not be /* datalist may be situated in dataset, so must not be
* unlocked when we free it * unlocked when we free it
*/ */
g_datalist_unlock_and_set (datalist, NULL); g_datalist_unlock_and_set (datalist, NULL);
g_free (d); g_free (d);
/* the dataset destruction *must* be done /* the dataset destruction *must* be done
* prior to invocation of the data destroy function * prior to invocation of the data destroy function
*/ */
if (dataset) if (dataset)
g_dataset_destroy_internal (dataset); g_dataset_destroy_internal (dataset);
} }
else else
{ {
g_datalist_unlock (datalist); g_datalist_unlock (datalist);
} }
/* We found and removed an old value /* We found and removed an old value
* the GData struct *must* already be unlinked * the GData struct *must* already be unlinked
* when invoking the destroy function. * when invoking the destroy function.
* we use (new_data==NULL && new_destroy_func!=NULL) as * we use (new_data==NULL && new_destroy_func!=NULL) as
* a special hint combination to "steal" * a special hint combination to "steal"
* data without destroy notification * data without destroy notification
*/ */
if (old.destroy && !new_destroy_func) if (old.destroy && !new_destroy_func)
{ {
if (dataset) if (dataset)
G_UNLOCK (g_dataset_global); G_UNLOCK (g_dataset_global);
old.destroy (old.data); old.destroy (old.data);
if (dataset) if (dataset)
G_LOCK (g_dataset_global); G_LOCK (g_dataset_global);
old.data = NULL; old.data = NULL;
} }
return old.data; return old.data;
} }
data++;
}
}
} }
else else
{ {
old.data = NULL; if (data)
if (d) {
{ if (!data->destroy)
data = d->data; {
data_end = data + d->len; data->data = new_data;
while (data < data_end) data->destroy = new_destroy_func;
{ g_datalist_unlock (datalist);
if (data->key == key_id) }
{ else
if (!data->destroy) {
{ old = *data;
data->data = new_data; data->data = new_data;
data->destroy = new_destroy_func; 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); g_datalist_unlock (datalist);
/* We found and replaced an old value /* We found and replaced an old value
* the GData struct *must* already be unlinked * the GData struct *must* already be unlinked
* when invoking the destroy function. * when invoking the destroy function.
*/ */
if (dataset) if (dataset)
G_UNLOCK (g_dataset_global); G_UNLOCK (g_dataset_global);
old.destroy (old.data); old.destroy (old.data);
if (dataset) if (dataset)
G_LOCK (g_dataset_global); G_LOCK (g_dataset_global);
} }
return NULL; return NULL;
} }
data++;
}
}
/* The key was not found, insert it */ /* The key was not found, insert it */
old_d = d; if (datalist_append (&d, key_id, new_data, new_destroy_func))
if (d == NULL) new_d = d;
{
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)
new_d = 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++;
} }
if (new_d) if (new_d)
@ -910,24 +937,13 @@ g_datalist_id_dup_data (GData **datalist,
gpointer val = NULL; gpointer val = NULL;
gpointer retval = NULL; gpointer retval = NULL;
GData *d; GData *d;
GDataElt *data, *data_end; GDataElt *data;
d = g_datalist_lock_and_get (datalist); d = g_datalist_lock_and_get (datalist);
if (d)
{ data = datalist_find (d, key_id, NULL);
data = d->data; if (data)
data_end = data + d->len; val = data->data;
do
{
if (data->key == key_id)
{
val = data->data;
break;
}
data++;
}
while (data < data_end);
}
if (dup_func) if (dup_func)
retval = dup_func (val, user_data); retval = dup_func (val, user_data);
@ -978,9 +994,10 @@ g_datalist_id_replace_data (GData **datalist,
gpointer val = NULL; gpointer val = NULL;
GData *d; GData *d;
GData *new_d = NULL; GData *new_d = NULL;
GDataElt *data, *data_end; GDataElt *data;
gboolean free_d = FALSE; gboolean free_d = FALSE;
gboolean set_new_d = FALSE; gboolean set_new_d = FALSE;
guint32 idx;
g_return_val_if_fail (datalist != NULL, FALSE); g_return_val_if_fail (datalist != NULL, FALSE);
g_return_val_if_fail (key_id != 0, FALSE); g_return_val_if_fail (key_id != 0, FALSE);
@ -989,73 +1006,45 @@ g_datalist_id_replace_data (GData **datalist,
*old_destroy = NULL; *old_destroy = NULL;
d = g_datalist_lock_and_get (datalist); d = g_datalist_lock_and_get (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 data = datalist_find (d, key_id, &idx);
* we at least free the memory if (data)
*/ {
if (d->len == 0) val = data->data;
{ if (val == oldval)
set_new_d = TRUE; {
free_d = TRUE; if (old_destroy)
} *old_destroy = data->destroy;
} if (newval != NULL)
} {
break; data->data = newval;
data->destroy = destroy;
}
else
{
if (idx != d->len - 1u)
*data = d->data[d->len - 1u];
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)
{
set_new_d = TRUE;
free_d = TRUE;
}
} }
data++;
} }
} }
if (val == NULL && oldval == NULL && newval != NULL) if (val == NULL && oldval == NULL && newval != NULL)
{ {
GData *old_d; if (datalist_append (&d, key_id, newval, destroy))
/* 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)
{ {
new_d = d; new_d = d;
set_new_d = TRUE; set_new_d = TRUE;
} }
d->data[d->len].key = key_id;
d->data[d->len].data = newval;
d->data[d->len].destroy = destroy;
d->len++;
} }
if (set_new_d) if (set_new_d)