girepository: Make gi_repository_get_loaded_namespaces() deterministic

As with the previous two commits, the results of calling
`gi_repository_get_loaded_namespaces()` were previously
non-deterministic due to being generated by iterating over a hash table,
which has a non-deterministic iteration order.

Fix that by using the new `ordered_typelibs` and `ordered_lazy_typelibs`
arrays to provide deterministic ordering.

At the same time, significantly reduce the number of allocations needed
to build the return value — previously the results would be built as a
linked list before being turned into an array. The linked list is now
banished to history.

Add some more unit tests to maximise test coverage of this method.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>

Fixes: #3303
This commit is contained in:
Philip Withnall 2024-05-02 16:30:09 +01:00
parent 0d199d6199
commit 03248e7b18
No known key found for this signature in database
GPG Key ID: DCDF5885B1F3ED73
2 changed files with 45 additions and 14 deletions

View File

@ -1232,13 +1232,16 @@ gi_repository_get_object_gtype_interfaces (GIRepository *repository,
}
static void
collect_namespaces (gpointer key,
gpointer value,
gpointer data)
collect_namespaces (GPtrArray *ordered_typelibs,
char **names,
size_t *inout_i)
{
GList **list = data;
*list = g_list_append (*list, key);
for (guint j = 0; j < ordered_typelibs->len; j++)
{
GITypelib *typelib = g_ptr_array_index (ordered_typelibs, j);
const char *namespace = gi_typelib_get_namespace (typelib);
names[(*inout_i)++] = g_strdup (namespace);
}
}
/**
@ -1260,20 +1263,18 @@ char **
gi_repository_get_loaded_namespaces (GIRepository *repository,
size_t *n_namespaces_out)
{
GList *l, *list = NULL;
char **names;
size_t i;
size_t n_typelibs;
g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
g_hash_table_foreach (repository->typelibs, collect_namespaces, &list);
g_hash_table_foreach (repository->lazy_typelibs, collect_namespaces, &list);
names = g_malloc0 (sizeof (char *) * (g_list_length (list) + 1));
n_typelibs = repository->ordered_typelibs->len + repository->ordered_lazy_typelibs->len;
names = g_malloc0 (sizeof (char *) * (n_typelibs + 1));
i = 0;
for (l = list; l; l = l->next)
names[i++] = g_strdup (l->data);
g_list_free (list);
collect_namespaces (repository->ordered_typelibs, names, &i);
collect_namespaces (repository->ordered_lazy_typelibs, names, &i);
if (n_namespaces_out != NULL)
*n_namespaces_out = i;

View File

@ -863,6 +863,35 @@ test_repository_find_by_gtype (RepositoryFixture *fx,
}
}
static void
test_repository_loaded_namespaces (RepositoryFixture *fx,
const void *unused)
{
char **namespaces;
size_t n_namespaces;
/* These should be in alphabetical order */
#if defined(G_OS_UNIX)
const char *expected_namespaces[] = { "GLib", "GModule", "GObject", "Gio", "GioUnix", NULL };
#elif defined(G_OS_WIN32)
const char *expected_namespaces[] = { "GLib", "GModule", "GObject", "Gio", "GioWin32", NULL };
#else
const char *expected_namespaces[] = { "GLib", "GModule", "GObject", "Gio", NULL };
#endif
g_test_summary ("Test listing loaded namespaces");
namespaces = gi_repository_get_loaded_namespaces (fx->repository, &n_namespaces);
g_assert_cmpstrv (namespaces, expected_namespaces);
g_assert_cmpuint (n_namespaces, ==, g_strv_length ((char **) expected_namespaces));
g_strfreev (namespaces);
/* Test again but without passing `n_namespaces`. */
namespaces = gi_repository_get_loaded_namespaces (fx->repository, NULL);
g_assert_cmpstrv (namespaces, expected_namespaces);
g_strfreev (namespaces);
}
int
main (int argc,
char *argv[])
@ -891,6 +920,7 @@ main (int argc,
ADD_REPOSITORY_TEST ("/repository/vfunc-info-with-invoker-on-interface", test_repository_vfunc_info_with_invoker_on_interface, &typelib_load_spec_gio);
ADD_REPOSITORY_TEST ("/repository/vfunc-info-with-invoker-on-object", test_repository_vfunc_info_with_invoker_on_object, &typelib_load_spec_gio);
ADD_REPOSITORY_TEST ("/repository/find-by-gtype", test_repository_find_by_gtype, &typelib_load_spec_gio_platform);
ADD_REPOSITORY_TEST ("/repository/loaded-namespaces", test_repository_loaded_namespaces, &typelib_load_spec_gio_platform);
return g_test_run ();
}