gdataset: add datalist_realloc() helper

This will be more useful next.

Also, try to detect whether realloc() actually moved the pointer. If it
doesn't, we can optimize a bit (not use g_datalist_unlock_and_set()).
Next, we will do more useful optimization in this case.

Note that we cannot just compare dangling pointers. That's would be
undefined behavior. In practice, we probably often compare dangling
pointers, and this tends to work just fine. Still avoid this and compare
only the guintptr values.
This commit is contained in:
Thomas Haller 2024-02-05 22:04:18 +01:00
parent 9a8e2ab263
commit cc53252c5b

View File

@ -165,6 +165,30 @@ datalist_alloc_size (guint32 alloc)
(((gsize) alloc) * sizeof (GDataElt));
}
static GData *
datalist_realloc (GData *data, guint32 alloc, gboolean *out_reallocated)
{
guintptr data_old;
gboolean reallocated;
data_old = (guintptr) ((gpointer) data);
data = g_realloc (data, datalist_alloc_size (alloc));
/* Determine whether realloc() moves the pointer. After a move, the old
* pointer would be dangling and comparing it would be undefined behavior.
* Avoid that by casting to uintptr_t.
*/
reallocated = (((guintptr) ((gpointer) (data))) != data_old);
data->alloc = alloc;
if (out_reallocated)
*out_reallocated = reallocated;
return data;
}
static gboolean
datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify destroy_func)
{
@ -182,7 +206,8 @@ datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify
}
else if (d->len == d->alloc)
{
d->alloc = d->alloc * 2u;
guint32 alloc = d->alloc * 2u;
#if G_ENABLE_DEBUG
/* d->alloc is always a power of two. It thus overflows the first time
* when going to (G_MAXUINT32+1), or when requesting 2^31+1 elements.
@ -190,11 +215,10 @@ datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify
* This is not handled, and we just crash. That's because we track the GData
* in a linear list, which horribly degrades long before we add 2 billion entries.
* Don't ever try to do that. */
g_assert (d->alloc > d->len);
g_assert (alloc > d->len);
#endif
d = g_realloc (d, datalist_alloc_size (d->alloc));
d = datalist_realloc (d, alloc, &reallocated);
*data = d;
reallocated = TRUE;
}
else
reallocated = FALSE;
@ -230,6 +254,7 @@ datalist_remove (GData *data, guint32 idx)
static gboolean
datalist_shrink (GData **data, GData **d_to_free)
{
gboolean reallocated;
guint32 alloc_by_4;
guint32 v;
GData *d;
@ -274,11 +299,10 @@ datalist_shrink (GData **data, GData **d_to_free)
g_assert (v <= d->alloc / 2u);
#endif
d->alloc = v;
d = g_realloc (d, datalist_alloc_size (v));
d = datalist_realloc (d, v, &reallocated);
*d_to_free = NULL;
*data = d;
return TRUE;
return reallocated;
}
static void