From cc53252c5b9ab6f0534a8059fa0a2308fcdfce47 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 5 Feb 2024 22:04:18 +0100 Subject: [PATCH] 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. --- glib/gdataset.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/glib/gdataset.c b/glib/gdataset.c index 552cf7ce1..3c3c0d642 100644 --- a/glib/gdataset.c +++ b/glib/gdataset.c @@ -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